Merge tag 'for-linus-20190524' of git://git.kernel.dk/linux-block
[linux-2.6-microblaze.git] / fs / afs / yfsclient.c
index b3ee999..18722aa 100644 (file)
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
 /* YFS File Server client stubs
  *
  * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public Licence
- * as published by the Free Software Foundation; either version
- * 2 of the Licence, or (at your option) any later version.
  */
 
 #include <linux/init.h>
@@ -183,24 +179,19 @@ static void xdr_dump_bad(const __be32 *bp)
 /*
  * Decode a YFSFetchStatus block
  */
-static int xdr_decode_YFSFetchStatus(struct afs_call *call,
-                                    const __be32 **_bp,
-                                    struct afs_file_status *status,
-                                    struct afs_vnode *vnode,
-                                    const afs_dataversion_t *expected_version,
-                                    struct afs_read *read_req)
+static int xdr_decode_YFSFetchStatus(const __be32 **_bp,
+                                    struct afs_call *call,
+                                    struct afs_status_cb *scb)
 {
        const struct yfs_xdr_YFSFetchStatus *xdr = (const void *)*_bp;
+       struct afs_file_status *status = &scb->status;
        u32 type;
-       u8 flags = 0;
 
        status->abort_code = ntohl(xdr->abort_code);
        if (status->abort_code != 0) {
-               if (vnode && status->abort_code == VNOVNODE) {
-                       set_bit(AFS_VNODE_DELETED, &vnode->flags);
+               if (status->abort_code == VNOVNODE)
                        status->nlink = 0;
-                       __afs_break_callback(vnode);
-               }
+               scb->have_error = true;
                return 0;
        }
 
@@ -209,77 +200,28 @@ static int xdr_decode_YFSFetchStatus(struct afs_call *call,
        case AFS_FTYPE_FILE:
        case AFS_FTYPE_DIR:
        case AFS_FTYPE_SYMLINK:
-               if (type != status->type &&
-                   vnode &&
-                   !test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
-                       pr_warning("Vnode %llx:%llx:%x changed type %u to %u\n",
-                                  vnode->fid.vid,
-                                  vnode->fid.vnode,
-                                  vnode->fid.unique,
-                                  status->type, type);
-                       goto bad;
-               }
                status->type = type;
                break;
        default:
                goto bad;
        }
 
-#define EXTRACT_M4(FIELD)                                      \
-       do {                                                    \
-               u32 x = ntohl(xdr->FIELD);                      \
-               if (status->FIELD != x) {                       \
-                       flags |= AFS_VNODE_META_CHANGED;        \
-                       status->FIELD = x;                      \
-               }                                               \
-       } while (0)
-
-#define EXTRACT_M8(FIELD)                                      \
-       do {                                                    \
-               u64 x = xdr_to_u64(xdr->FIELD);                 \
-               if (status->FIELD != x) {                       \
-                       flags |= AFS_VNODE_META_CHANGED;        \
-                       status->FIELD = x;                      \
-               }                                               \
-       } while (0)
-
-#define EXTRACT_D8(FIELD)                                      \
-       do {                                                    \
-               u64 x = xdr_to_u64(xdr->FIELD);                 \
-               if (status->FIELD != x) {                       \
-                       flags |= AFS_VNODE_DATA_CHANGED;        \
-                       status->FIELD = x;                      \
-               }                                               \
-       } while (0)
-
-       EXTRACT_M4(nlink);
-       EXTRACT_D8(size);
-       EXTRACT_D8(data_version);
-       EXTRACT_M8(author);
-       EXTRACT_M8(owner);
-       EXTRACT_M8(group);
-       EXTRACT_M4(mode);
-       EXTRACT_M4(caller_access); /* call ticket dependent */
-       EXTRACT_M4(anon_access);
-
-       status->mtime_client = xdr_to_time(xdr->mtime_client);
-       status->mtime_server = xdr_to_time(xdr->mtime_server);
-       status->lock_count   = ntohl(xdr->lock_count);
-
-       if (read_req) {
-               read_req->data_version = status->data_version;
-               read_req->file_size = status->size;
-       }
+       status->nlink           = ntohl(xdr->nlink);
+       status->author          = xdr_to_u64(xdr->author);
+       status->owner           = xdr_to_u64(xdr->owner);
+       status->caller_access   = ntohl(xdr->caller_access); /* Ticket dependent */
+       status->anon_access     = ntohl(xdr->anon_access);
+       status->mode            = ntohl(xdr->mode) & S_IALLUGO;
+       status->group           = xdr_to_u64(xdr->group);
+       status->lock_count      = ntohl(xdr->lock_count);
+
+       status->mtime_client    = xdr_to_time(xdr->mtime_client);
+       status->mtime_server    = xdr_to_time(xdr->mtime_server);
+       status->size            = xdr_to_u64(xdr->size);
+       status->data_version    = xdr_to_u64(xdr->data_version);
+       scb->have_status        = true;
 
        *_bp += xdr_size(xdr);
-
-       if (vnode) {
-               if (test_bit(AFS_VNODE_UNSET, &vnode->flags))
-                       flags |= AFS_VNODE_NOT_YET_SET;
-               afs_update_inode_from_status(vnode, status, expected_version,
-                                            flags);
-       }
-
        return 0;
 
 bad:
@@ -288,75 +230,23 @@ bad:
 }
 
 /*
- * Decode the file status.  We need to lock the target vnode if we're going to
- * update its status so that stat() sees the attributes update atomically.
+ * Decode a YFSCallBack block
  */
-static int yfs_decode_status(struct afs_call *call,
-                            const __be32 **_bp,
-                            struct afs_file_status *status,
-                            struct afs_vnode *vnode,
-                            const afs_dataversion_t *expected_version,
-                            struct afs_read *read_req)
-{
-       int ret;
-
-       if (!vnode)
-               return xdr_decode_YFSFetchStatus(call, _bp, status, vnode,
-                                                expected_version, read_req);
-
-       write_seqlock(&vnode->cb_lock);
-       ret = xdr_decode_YFSFetchStatus(call, _bp, status, vnode,
-                                       expected_version, read_req);
-       write_sequnlock(&vnode->cb_lock);
-       return ret;
-}
-
-static void xdr_decode_YFSCallBack_raw(struct afs_call *call,
-                                      struct afs_callback *cb,
-                                      const __be32 **_bp)
+static void xdr_decode_YFSCallBack(const __be32 **_bp,
+                                  struct afs_call *call,
+                                  struct afs_status_cb *scb)
 {
        struct yfs_xdr_YFSCallBack *x = (void *)*_bp;
+       struct afs_callback *cb = &scb->callback;
        ktime_t cb_expiry;
 
        cb_expiry = call->reply_time;
        cb_expiry = ktime_add(cb_expiry, xdr_to_u64(x->expiration_time) * 100);
        cb->expires_at  = ktime_divns(cb_expiry, NSEC_PER_SEC);
-       cb->version     = ntohl(x->version);
-       cb->type        = ntohl(x->type);
-
+       scb->have_cb    = true;
        *_bp += xdr_size(x);
 }
 
-/*
- * Decode a YFSCallBack block
- */
-static void xdr_decode_YFSCallBack(struct afs_call *call,
-                                  struct afs_vnode *vnode,
-                                  const __be32 **_bp)
-{
-       struct afs_cb_interest *old, *cbi = call->cbi;
-       struct afs_callback cb;
-
-       xdr_decode_YFSCallBack_raw(call, &cb, _bp);
-       
-       write_seqlock(&vnode->cb_lock);
-
-       if (!afs_cb_is_broken(call->cb_break, vnode, cbi)) {
-               vnode->cb_version       = cb.version;
-               vnode->cb_type          = cb.type;
-               vnode->cb_expires_at    = cb.expires_at;
-               old = vnode->cb_interest;
-               if (old != call->cbi) {
-                       vnode->cb_interest = cbi;
-                       cbi = old;
-               }
-               set_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
-       }
-
-       write_sequnlock(&vnode->cb_lock);
-       call->cbi = cbi;
-}
-
 /*
  * Decode a YFSVolSync block
  */
@@ -441,11 +331,10 @@ static void xdr_decode_YFSFetchVolumeStatus(const __be32 **_bp,
 }
 
 /*
- * deliver reply data to an FS.FetchStatus
+ * Deliver a reply that's a status, callback and volsync.
  */
-static int yfs_deliver_fs_fetch_status_vnode(struct afs_call *call)
+static int yfs_deliver_fs_status_cb_and_volsync(struct afs_call *call)
 {
-       struct afs_vnode *vnode = call->xvnode;
        const __be32 *bp;
        int ret;
 
@@ -453,15 +342,35 @@ static int yfs_deliver_fs_fetch_status_vnode(struct afs_call *call)
        if (ret < 0)
                return ret;
 
-       _enter("{%llx:%llu}", vnode->fid.vid, vnode->fid.vnode);
-
        /* unmarshall the reply once we've received all of it */
        bp = call->buffer;
-       ret = yfs_decode_status(call, &bp, &vnode->status, vnode,
-                               &call->expected_version, NULL);
+       ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb);
+       if (ret < 0)
+               return ret;
+       xdr_decode_YFSCallBack(&bp, call, call->out_scb);
+       xdr_decode_YFSVolSync(&bp, call->out_volsync);
+
+       _leave(" = 0 [done]");
+       return 0;
+}
+
+/*
+ * Deliver reply data to operations that just return a file status and a volume
+ * sync record.
+ */
+static int yfs_deliver_status_and_volsync(struct afs_call *call)
+{
+       const __be32 *bp;
+       int ret;
+
+       ret = afs_transfer_reply(call);
+       if (ret < 0)
+               return ret;
+
+       bp = call->buffer;
+       ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb);
        if (ret < 0)
                return ret;
-       xdr_decode_YFSCallBack(call, vnode, &bp);
        xdr_decode_YFSVolSync(&bp, call->out_volsync);
 
        _leave(" = 0 [done]");
@@ -474,15 +383,15 @@ static int yfs_deliver_fs_fetch_status_vnode(struct afs_call *call)
 static const struct afs_call_type yfs_RXYFSFetchStatus_vnode = {
        .name           = "YFS.FetchStatus(vnode)",
        .op             = yfs_FS_FetchStatus,
-       .deliver        = yfs_deliver_fs_fetch_status_vnode,
+       .deliver        = yfs_deliver_fs_status_cb_and_volsync,
        .destructor     = afs_flat_call_destructor,
 };
 
 /*
  * Fetch the status information for a file.
  */
-int yfs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_volsync *volsync,
-                            bool new_inode)
+int yfs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_status_cb *scb,
+                            struct afs_volsync *volsync)
 {
        struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
@@ -504,9 +413,8 @@ int yfs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_volsync *volsy
        }
 
        call->key = fc->key;
-       call->xvnode = vnode;
+       call->out_scb = scb;
        call->out_volsync = volsync;
-       call->expected_version = new_inode ? 1 : vnode->status.data_version;
 
        /* marshall the parameters */
        bp = call->request;
@@ -515,7 +423,6 @@ int yfs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_volsync *volsy
        bp = xdr_encode_YFSFid(bp, &vnode->fid);
        yfs_check_req(call, bp);
 
-       call->cb_break = fc->cb_break;
        afs_use_fs_server(call, fc->cbi);
        trace_afs_make_fs_call(call, &vnode->fid);
        afs_set_fc_call(call, fc);
@@ -528,7 +435,6 @@ int yfs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_volsync *volsy
  */
 static int yfs_deliver_fs_fetch_data64(struct afs_call *call)
 {
-       struct afs_vnode *vnode = call->xvnode;
        struct afs_read *req = call->read_request;
        const __be32 *bp;
        unsigned int size;
@@ -586,7 +492,7 @@ static int yfs_deliver_fs_fetch_data64(struct afs_call *call)
                if (req->offset == PAGE_SIZE) {
                        req->offset = 0;
                        if (req->page_done)
-                               req->page_done(call, req);
+                               req->page_done(req);
                        req->index++;
                        if (req->remain > 0)
                                goto begin_page;
@@ -623,13 +529,15 @@ static int yfs_deliver_fs_fetch_data64(struct afs_call *call)
                        return ret;
 
                bp = call->buffer;
-               ret = yfs_decode_status(call, &bp, &vnode->status, vnode,
-                                       &vnode->status.data_version, req);
+               ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb);
                if (ret < 0)
                        return ret;
-               xdr_decode_YFSCallBack(call, vnode, &bp);
+               xdr_decode_YFSCallBack(&bp, call, call->out_scb);
                xdr_decode_YFSVolSync(&bp, call->out_volsync);
 
+               req->data_version = call->out_scb->status.data_version;
+               req->file_size = call->out_scb->status.size;
+
                call->unmarshall++;
 
                /* Fall through */
@@ -642,7 +550,7 @@ static int yfs_deliver_fs_fetch_data64(struct afs_call *call)
                        zero_user_segment(req->pages[req->index],
                                          req->offset, PAGE_SIZE);
                if (req->page_done)
-                       req->page_done(call, req);
+                       req->page_done(req);
                req->offset = 0;
        }
 
@@ -669,7 +577,8 @@ static const struct afs_call_type yfs_RXYFSFetchData64 = {
 /*
  * Fetch data from a file.
  */
-int yfs_fs_fetch_data(struct afs_fs_cursor *fc, struct afs_read *req)
+int yfs_fs_fetch_data(struct afs_fs_cursor *fc, struct afs_status_cb *scb,
+                     struct afs_read *req)
 {
        struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
@@ -691,10 +600,9 @@ int yfs_fs_fetch_data(struct afs_fs_cursor *fc, struct afs_read *req)
                return -ENOMEM;
 
        call->key = fc->key;
-       call->xvnode = vnode;
+       call->out_scb = scb;
        call->out_volsync = NULL;
        call->read_request = req;
-       call->expected_version = vnode->status.data_version;
 
        /* marshall the parameters */
        bp = call->request;
@@ -706,7 +614,6 @@ int yfs_fs_fetch_data(struct afs_fs_cursor *fc, struct afs_read *req)
        yfs_check_req(call, bp);
 
        refcount_inc(&req->usage);
-       call->cb_break = fc->cb_break;
        afs_use_fs_server(call, fc->cbi);
        trace_afs_make_fs_call(call, &vnode->fid);
        afs_set_fc_call(call, fc);
@@ -719,7 +626,6 @@ int yfs_fs_fetch_data(struct afs_fs_cursor *fc, struct afs_read *req)
  */
 static int yfs_deliver_fs_create_vnode(struct afs_call *call)
 {
-       struct afs_vnode *dvnode = call->dvnode;
        const __be32 *bp;
        int ret;
 
@@ -732,15 +638,14 @@ static int yfs_deliver_fs_create_vnode(struct afs_call *call)
        /* unmarshall the reply once we've received all of it */
        bp = call->buffer;
        xdr_decode_YFSFid(&bp, call->out_fid);
-       ret = yfs_decode_status(call, &bp, call->out_extra_status, NULL, NULL, NULL);
+       ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb);
        if (ret < 0)
                return ret;
-       ret = yfs_decode_status(call, &bp, &dvnode->status, dvnode,
-                               &call->expected_version, NULL);
+       ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb);
        if (ret < 0)
                return ret;
-       xdr_decode_YFSCallBack_raw(call, call->out_cb, &bp);
-       xdr_decode_YFSVolSync(&bp, NULL);
+       xdr_decode_YFSCallBack(&bp, call, call->out_scb);
+       xdr_decode_YFSVolSync(&bp, call->out_volsync);
 
        _leave(" = 0 [done]");
        return 0;
@@ -762,10 +667,9 @@ static const struct afs_call_type afs_RXFSCreateFile = {
 int yfs_fs_create_file(struct afs_fs_cursor *fc,
                       const char *name,
                       umode_t mode,
-                      u64 current_data_version,
+                      struct afs_status_cb *dvnode_scb,
                       struct afs_fid *newfid,
-                      struct afs_file_status *newstatus,
-                      struct afs_callback *newcb)
+                      struct afs_status_cb *new_scb)
 {
        struct afs_vnode *dvnode = fc->vnode;
        struct afs_call *call;
@@ -793,11 +697,9 @@ int yfs_fs_create_file(struct afs_fs_cursor *fc,
                return -ENOMEM;
 
        call->key = fc->key;
-       call->dvnode = dvnode;
+       call->out_dir_scb = dvnode_scb;
        call->out_fid = newfid;
-       call->out_extra_status = newstatus;
-       call->out_cb = newcb;
-       call->expected_version = current_data_version + 1;
+       call->out_scb = new_scb;
 
        /* marshall the parameters */
        bp = call->request;
@@ -829,10 +731,9 @@ static const struct afs_call_type yfs_RXFSMakeDir = {
 int yfs_fs_make_dir(struct afs_fs_cursor *fc,
                    const char *name,
                    umode_t mode,
-                   u64 current_data_version,
+                   struct afs_status_cb *dvnode_scb,
                    struct afs_fid *newfid,
-                   struct afs_file_status *newstatus,
-                   struct afs_callback *newcb)
+                   struct afs_status_cb *new_scb)
 {
        struct afs_vnode *dvnode = fc->vnode;
        struct afs_call *call;
@@ -859,11 +760,9 @@ int yfs_fs_make_dir(struct afs_fs_cursor *fc,
                return -ENOMEM;
 
        call->key = fc->key;
-       call->dvnode = dvnode;
+       call->out_dir_scb = dvnode_scb;
        call->out_fid = newfid;
-       call->out_extra_status = newstatus;
-       call->out_cb = newcb;
-       call->expected_version = current_data_version + 1;
+       call->out_scb = new_scb;
 
        /* marshall the parameters */
        bp = call->request;
@@ -886,8 +785,6 @@ int yfs_fs_make_dir(struct afs_fs_cursor *fc,
  */
 static int yfs_deliver_fs_remove_file2(struct afs_call *call)
 {
-       struct afs_vnode *dvnode = call->dvnode;
-       struct afs_vnode *vnode = call->xvnode;
        struct afs_fid fid;
        const __be32 *bp;
        int ret;
@@ -898,20 +795,18 @@ static int yfs_deliver_fs_remove_file2(struct afs_call *call)
        if (ret < 0)
                return ret;
 
-       /* unmarshall the reply once we've received all of it */
        bp = call->buffer;
-       ret = yfs_decode_status(call, &bp, &dvnode->status, dvnode,
-                               &call->expected_version, NULL);
+       ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb);
        if (ret < 0)
                return ret;
 
        xdr_decode_YFSFid(&bp, &fid);
-       ret = yfs_decode_status(call, &bp, &vnode->status, vnode, NULL, NULL);
+       ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb);
        if (ret < 0)
                return ret;
        /* Was deleted if vnode->status.abort_code == VNOVNODE. */
 
-       xdr_decode_YFSVolSync(&bp, NULL);
+       xdr_decode_YFSVolSync(&bp, call->out_volsync);
        return 0;
 }
 
@@ -929,7 +824,8 @@ static const struct afs_call_type yfs_RXYFSRemoveFile2 = {
  * Remove a file and retrieve new file status.
  */
 int yfs_fs_remove_file2(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
-                       const char *name, u64 current_data_version)
+                       const char *name, struct afs_status_cb *dvnode_scb,
+                       struct afs_status_cb *vnode_scb)
 {
        struct afs_vnode *dvnode = fc->vnode;
        struct afs_call *call;
@@ -954,9 +850,8 @@ int yfs_fs_remove_file2(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
                return -ENOMEM;
 
        call->key = fc->key;
-       call->dvnode = dvnode;
-       call->xvnode = vnode;
-       call->expected_version = current_data_version + 1;
+       call->out_dir_scb = dvnode_scb;
+       call->out_scb = vnode_scb;
 
        /* marshall the parameters */
        bp = call->request;
@@ -978,7 +873,6 @@ int yfs_fs_remove_file2(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
  */
 static int yfs_deliver_fs_remove(struct afs_call *call)
 {
-       struct afs_vnode *dvnode = call->dvnode;
        const __be32 *bp;
        int ret;
 
@@ -988,14 +882,12 @@ static int yfs_deliver_fs_remove(struct afs_call *call)
        if (ret < 0)
                return ret;
 
-       /* unmarshall the reply once we've received all of it */
        bp = call->buffer;
-       ret = yfs_decode_status(call, &bp, &dvnode->status, dvnode,
-                               &call->expected_version, NULL);
+       ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb);
        if (ret < 0)
                return ret;
 
-       xdr_decode_YFSVolSync(&bp, NULL);
+       xdr_decode_YFSVolSync(&bp, call->out_volsync);
        return 0;
 }
 
@@ -1020,7 +912,8 @@ static const struct afs_call_type yfs_RXYFSRemoveDir = {
  * remove a file or directory
  */
 int yfs_fs_remove(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
-                 const char *name, bool isdir, u64 current_data_version)
+                 const char *name, bool isdir,
+                 struct afs_status_cb *dvnode_scb)
 {
        struct afs_vnode *dvnode = fc->vnode;
        struct afs_call *call;
@@ -1043,9 +936,7 @@ int yfs_fs_remove(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
                return -ENOMEM;
 
        call->key = fc->key;
-       call->dvnode = dvnode;
-       call->xvnode = vnode;
-       call->expected_version = current_data_version + 1;
+       call->out_dir_scb = dvnode_scb;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1067,7 +958,6 @@ int yfs_fs_remove(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
  */
 static int yfs_deliver_fs_link(struct afs_call *call)
 {
-       struct afs_vnode *dvnode = call->dvnode, *vnode = call->xvnode;
        const __be32 *bp;
        int ret;
 
@@ -1077,16 +967,14 @@ static int yfs_deliver_fs_link(struct afs_call *call)
        if (ret < 0)
                return ret;
 
-       /* unmarshall the reply once we've received all of it */
        bp = call->buffer;
-       ret = yfs_decode_status(call, &bp, &vnode->status, vnode, NULL, NULL);
+       ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb);
        if (ret < 0)
                return ret;
-       ret = yfs_decode_status(call, &bp, &dvnode->status, dvnode,
-                               &call->expected_version, NULL);
+       ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb);
        if (ret < 0)
                return ret;
-       xdr_decode_YFSVolSync(&bp, NULL);
+       xdr_decode_YFSVolSync(&bp, call->out_volsync);
        _leave(" = 0 [done]");
        return 0;
 }
@@ -1105,7 +993,9 @@ static const struct afs_call_type yfs_RXYFSLink = {
  * Make a hard link.
  */
 int yfs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
-               const char *name, u64 current_data_version)
+               const char *name,
+               struct afs_status_cb *dvnode_scb,
+               struct afs_status_cb *vnode_scb)
 {
        struct afs_vnode *dvnode = fc->vnode;
        struct afs_call *call;
@@ -1129,9 +1019,8 @@ int yfs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
                return -ENOMEM;
 
        call->key = fc->key;
-       call->dvnode = dvnode;
-       call->xvnode = vnode;
-       call->expected_version = current_data_version + 1;
+       call->out_dir_scb = dvnode_scb;
+       call->out_scb = vnode_scb;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1154,7 +1043,6 @@ int yfs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
  */
 static int yfs_deliver_fs_symlink(struct afs_call *call)
 {
-       struct afs_vnode *dvnode = call->dvnode;
        const __be32 *bp;
        int ret;
 
@@ -1167,14 +1055,13 @@ static int yfs_deliver_fs_symlink(struct afs_call *call)
        /* unmarshall the reply once we've received all of it */
        bp = call->buffer;
        xdr_decode_YFSFid(&bp, call->out_fid);
-       ret = yfs_decode_status(call, &bp, call->out_extra_status, NULL, NULL, NULL);
+       ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb);
        if (ret < 0)
                return ret;
-       ret = yfs_decode_status(call, &bp, &dvnode->status, dvnode,
-                               &call->expected_version, NULL);
+       ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb);
        if (ret < 0)
                return ret;
-       xdr_decode_YFSVolSync(&bp, NULL);
+       xdr_decode_YFSVolSync(&bp, call->out_volsync);
 
        _leave(" = 0 [done]");
        return 0;
@@ -1196,9 +1083,9 @@ static const struct afs_call_type yfs_RXYFSSymlink = {
 int yfs_fs_symlink(struct afs_fs_cursor *fc,
                   const char *name,
                   const char *contents,
-                  u64 current_data_version,
+                  struct afs_status_cb *dvnode_scb,
                   struct afs_fid *newfid,
-                  struct afs_file_status *newstatus)
+                  struct afs_status_cb *vnode_scb)
 {
        struct afs_vnode *dvnode = fc->vnode;
        struct afs_call *call;
@@ -1225,10 +1112,9 @@ int yfs_fs_symlink(struct afs_fs_cursor *fc,
                return -ENOMEM;
 
        call->key = fc->key;
-       call->dvnode = dvnode;
+       call->out_dir_scb = dvnode_scb;
        call->out_fid = newfid;
-       call->out_extra_status = newstatus;
-       call->expected_version = current_data_version + 1;
+       call->out_scb = vnode_scb;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1252,8 +1138,6 @@ int yfs_fs_symlink(struct afs_fs_cursor *fc,
  */
 static int yfs_deliver_fs_rename(struct afs_call *call)
 {
-       struct afs_vnode *orig_dvnode = call->dvnode;
-       struct afs_vnode *new_dvnode = call->xvnode;
        const __be32 *bp;
        int ret;
 
@@ -1263,20 +1147,17 @@ static int yfs_deliver_fs_rename(struct afs_call *call)
        if (ret < 0)
                return ret;
 
-       /* unmarshall the reply once we've received all of it */
        bp = call->buffer;
-       ret = yfs_decode_status(call, &bp, &orig_dvnode->status, orig_dvnode,
-                               &call->expected_version, NULL);
+       ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_dir_scb);
        if (ret < 0)
                return ret;
-       if (new_dvnode != orig_dvnode) {
-               ret = yfs_decode_status(call, &bp, &new_dvnode->status, new_dvnode,
-                                       &call->expected_version_2, NULL);
+       if (call->out_dir_scb != call->out_scb) {
+               ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb);
                if (ret < 0)
                        return ret;
        }
 
-       xdr_decode_YFSVolSync(&bp, NULL);
+       xdr_decode_YFSVolSync(&bp, call->out_volsync);
        _leave(" = 0 [done]");
        return 0;
 }
@@ -1298,8 +1179,8 @@ int yfs_fs_rename(struct afs_fs_cursor *fc,
                  const char *orig_name,
                  struct afs_vnode *new_dvnode,
                  const char *new_name,
-                 u64 current_orig_data_version,
-                 u64 current_new_data_version)
+                 struct afs_status_cb *orig_dvnode_scb,
+                 struct afs_status_cb *new_dvnode_scb)
 {
        struct afs_vnode *orig_dvnode = fc->vnode;
        struct afs_call *call;
@@ -1325,10 +1206,8 @@ int yfs_fs_rename(struct afs_fs_cursor *fc,
                return -ENOMEM;
 
        call->key = fc->key;
-       call->dvnode = orig_dvnode;
-       call->xvnode = new_dvnode;
-       call->expected_version = current_orig_data_version + 1;
-       call->expected_version_2 = current_new_data_version + 1;
+       call->out_dir_scb = orig_dvnode_scb;
+       call->out_scb = new_dvnode_scb;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1347,42 +1226,13 @@ int yfs_fs_rename(struct afs_fs_cursor *fc,
        return afs_wait_for_call_to_complete(call, &fc->ac);
 }
 
-/*
- * Deliver reply data to a YFS.StoreData64 operation.
- */
-static int yfs_deliver_fs_store_data(struct afs_call *call)
-{
-       struct afs_vnode *vnode = call->xvnode;
-       const __be32 *bp;
-       int ret;
-
-       _enter("");
-
-       ret = afs_transfer_reply(call);
-       if (ret < 0)
-               return ret;
-
-       /* unmarshall the reply once we've received all of it */
-       bp = call->buffer;
-       ret = yfs_decode_status(call, &bp, &vnode->status, vnode,
-                               &call->expected_version, NULL);
-       if (ret < 0)
-               return ret;
-       xdr_decode_YFSVolSync(&bp, NULL);
-
-       afs_pages_written_back(vnode, call);
-
-       _leave(" = 0 [done]");
-       return 0;
-}
-
 /*
  * YFS.StoreData64 operation type.
  */
 static const struct afs_call_type yfs_RXYFSStoreData64 = {
        .name           = "YFS.StoreData64",
        .op             = yfs_FS_StoreData64,
-       .deliver        = yfs_deliver_fs_store_data,
+       .deliver        = yfs_deliver_status_and_volsync,
        .destructor     = afs_flat_call_destructor,
 };
 
@@ -1391,7 +1241,8 @@ static const struct afs_call_type yfs_RXYFSStoreData64 = {
  */
 int yfs_fs_store_data(struct afs_fs_cursor *fc, struct address_space *mapping,
                      pgoff_t first, pgoff_t last,
-                     unsigned offset, unsigned to)
+                     unsigned offset, unsigned to,
+                     struct afs_status_cb *scb)
 {
        struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
@@ -1429,13 +1280,12 @@ int yfs_fs_store_data(struct afs_fs_cursor *fc, struct address_space *mapping,
 
        call->key = fc->key;
        call->mapping = mapping;
-       call->xvnode = vnode;
        call->first = first;
        call->last = last;
        call->first_offset = offset;
        call->last_to = to;
        call->send_pages = true;
-       call->expected_version = vnode->status.data_version + 1;
+       call->out_scb = scb;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1455,47 +1305,20 @@ int yfs_fs_store_data(struct afs_fs_cursor *fc, struct address_space *mapping,
        return afs_wait_for_call_to_complete(call, &fc->ac);
 }
 
-/*
- * deliver reply data to an FS.StoreStatus
- */
-static int yfs_deliver_fs_store_status(struct afs_call *call)
-{
-       struct afs_vnode *vnode = call->xvnode;
-       const __be32 *bp;
-       int ret;
-
-       _enter("");
-
-       ret = afs_transfer_reply(call);
-       if (ret < 0)
-               return ret;
-
-       /* unmarshall the reply once we've received all of it */
-       bp = call->buffer;
-       ret = yfs_decode_status(call, &bp, &vnode->status, vnode,
-                               &call->expected_version, NULL);
-       if (ret < 0)
-               return ret;
-       xdr_decode_YFSVolSync(&bp, NULL);
-
-       _leave(" = 0 [done]");
-       return 0;
-}
-
 /*
  * YFS.StoreStatus operation type
  */
 static const struct afs_call_type yfs_RXYFSStoreStatus = {
        .name           = "YFS.StoreStatus",
        .op             = yfs_FS_StoreStatus,
-       .deliver        = yfs_deliver_fs_store_status,
+       .deliver        = yfs_deliver_status_and_volsync,
        .destructor     = afs_flat_call_destructor,
 };
 
 static const struct afs_call_type yfs_RXYFSStoreData64_as_Status = {
        .name           = "YFS.StoreData64",
        .op             = yfs_FS_StoreData64,
-       .deliver        = yfs_deliver_fs_store_status,
+       .deliver        = yfs_deliver_status_and_volsync,
        .destructor     = afs_flat_call_destructor,
 };
 
@@ -1503,7 +1326,8 @@ static const struct afs_call_type yfs_RXYFSStoreData64_as_Status = {
  * Set the attributes on a file, using YFS.StoreData64 rather than
  * YFS.StoreStatus so as to alter the file size also.
  */
-static int yfs_fs_setattr_size(struct afs_fs_cursor *fc, struct iattr *attr)
+static int yfs_fs_setattr_size(struct afs_fs_cursor *fc, struct iattr *attr,
+                              struct afs_status_cb *scb)
 {
        struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
@@ -1524,8 +1348,7 @@ static int yfs_fs_setattr_size(struct afs_fs_cursor *fc, struct iattr *attr)
                return -ENOMEM;
 
        call->key = fc->key;
-       call->xvnode = vnode;
-       call->expected_version = vnode->status.data_version + 1;
+       call->out_scb = scb;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1549,7 +1372,8 @@ static int yfs_fs_setattr_size(struct afs_fs_cursor *fc, struct iattr *attr)
  * Set the attributes on a file, using YFS.StoreData64 if there's a change in
  * file size, and YFS.StoreStatus otherwise.
  */
-int yfs_fs_setattr(struct afs_fs_cursor *fc, struct iattr *attr)
+int yfs_fs_setattr(struct afs_fs_cursor *fc, struct iattr *attr,
+                  struct afs_status_cb *scb)
 {
        struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
@@ -1557,7 +1381,7 @@ int yfs_fs_setattr(struct afs_fs_cursor *fc, struct iattr *attr)
        __be32 *bp;
 
        if (attr->ia_valid & ATTR_SIZE)
-               return yfs_fs_setattr_size(fc, attr);
+               return yfs_fs_setattr_size(fc, attr, scb);
 
        _enter(",%x,{%llx:%llu},,",
               key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
@@ -1572,8 +1396,7 @@ int yfs_fs_setattr(struct afs_fs_cursor *fc, struct iattr *attr)
                return -ENOMEM;
 
        call->key = fc->key;
-       call->xvnode = vnode;
-       call->expected_version = vnode->status.data_version;
+       call->out_scb = scb;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1763,34 +1586,6 @@ int yfs_fs_get_volume_status(struct afs_fs_cursor *fc,
        return afs_wait_for_call_to_complete(call, &fc->ac);
 }
 
-/*
- * Deliver reply data to operations that just return a file status and a volume
- * sync record.
- */
-static int yfs_deliver_status_and_volsync(struct afs_call *call)
-{
-       struct afs_vnode *vnode = call->xvnode;
-       const __be32 *bp;
-       int ret;
-
-       _enter("{%u}", call->unmarshall);
-
-       ret = afs_transfer_reply(call);
-       if (ret < 0)
-               return ret;
-
-       /* unmarshall the reply once we've received all of it */
-       bp = call->buffer;
-       ret = yfs_decode_status(call, &bp, &vnode->status, vnode,
-                               &call->expected_version, NULL);
-       if (ret < 0)
-               return ret;
-       xdr_decode_YFSVolSync(&bp, NULL);
-
-       _leave(" = 0 [done]");
-       return 0;
-}
-
 /*
  * YFS.SetLock operation type
  */
@@ -1826,7 +1621,8 @@ static const struct afs_call_type yfs_RXYFSReleaseLock = {
 /*
  * Set a lock on a file
  */
-int yfs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type)
+int yfs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type,
+                   struct afs_status_cb *scb)
 {
        struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
@@ -1845,7 +1641,8 @@ int yfs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type)
                return -ENOMEM;
 
        call->key = fc->key;
-       call->xvnode = vnode;
+       call->lvnode = vnode;
+       call->out_scb = scb;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1865,7 +1662,7 @@ int yfs_fs_set_lock(struct afs_fs_cursor *fc, afs_lock_type_t type)
 /*
  * extend a lock on a file
  */
-int yfs_fs_extend_lock(struct afs_fs_cursor *fc)
+int yfs_fs_extend_lock(struct afs_fs_cursor *fc, struct afs_status_cb *scb)
 {
        struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
@@ -1883,7 +1680,8 @@ int yfs_fs_extend_lock(struct afs_fs_cursor *fc)
                return -ENOMEM;
 
        call->key = fc->key;
-       call->xvnode = vnode;
+       call->lvnode = vnode;
+       call->out_scb = scb;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1902,7 +1700,7 @@ int yfs_fs_extend_lock(struct afs_fs_cursor *fc)
 /*
  * release a lock on a file
  */
-int yfs_fs_release_lock(struct afs_fs_cursor *fc)
+int yfs_fs_release_lock(struct afs_fs_cursor *fc, struct afs_status_cb *scb)
 {
        struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
@@ -1920,7 +1718,8 @@ int yfs_fs_release_lock(struct afs_fs_cursor *fc)
                return -ENOMEM;
 
        call->key = fc->key;
-       call->xvnode = vnode;
+       call->lvnode = vnode;
+       call->out_scb = scb;
 
        /* marshall the parameters */
        bp = call->request;
@@ -1936,43 +1735,13 @@ int yfs_fs_release_lock(struct afs_fs_cursor *fc)
        return afs_wait_for_call_to_complete(call, &fc->ac);
 }
 
-/*
- * Deliver reply data to an FS.FetchStatus with no vnode.
- */
-static int yfs_deliver_fs_fetch_status(struct afs_call *call)
-{
-       struct afs_file_status *status = call->out_extra_status;
-       struct afs_callback *callback = call->out_cb;
-       struct afs_volsync *volsync = call->out_volsync;
-       const __be32 *bp;
-       int ret;
-
-       ret = afs_transfer_reply(call);
-       if (ret < 0)
-               return ret;
-
-       _enter("");
-
-       /* unmarshall the reply once we've received all of it */
-       bp = call->buffer;
-       ret = yfs_decode_status(call, &bp, status, NULL,
-                               &call->expected_version, NULL);
-       if (ret < 0)
-               return ret;
-       xdr_decode_YFSCallBack_raw(call, callback, &bp);
-       xdr_decode_YFSVolSync(&bp, volsync);
-
-       _leave(" = 0 [done]");
-       return 0;
-}
-
 /*
  * YFS.FetchStatus operation type
  */
 static const struct afs_call_type yfs_RXYFSFetchStatus = {
        .name           = "YFS.FetchStatus",
        .op             = yfs_FS_FetchStatus,
-       .deliver        = yfs_deliver_fs_fetch_status,
+       .deliver        = yfs_deliver_fs_status_cb_and_volsync,
        .destructor     = afs_flat_call_destructor,
 };
 
@@ -1982,8 +1751,7 @@ static const struct afs_call_type yfs_RXYFSFetchStatus = {
 int yfs_fs_fetch_status(struct afs_fs_cursor *fc,
                        struct afs_net *net,
                        struct afs_fid *fid,
-                       struct afs_file_status *status,
-                       struct afs_callback *callback,
+                       struct afs_status_cb *scb,
                        struct afs_volsync *volsync)
 {
        struct afs_call *call;
@@ -2004,10 +1772,8 @@ int yfs_fs_fetch_status(struct afs_fs_cursor *fc,
        }
 
        call->key = fc->key;
-       call->out_extra_status = status;
-       call->out_cb = callback;
+       call->out_scb = scb;
        call->out_volsync = volsync;
-       call->expected_version = 1; /* vnode->status.data_version */
 
        /* marshall the parameters */
        bp = call->request;
@@ -2016,7 +1782,6 @@ int yfs_fs_fetch_status(struct afs_fs_cursor *fc,
        bp = xdr_encode_YFSFid(bp, fid);
        yfs_check_req(call, bp);
 
-       call->cb_break = fc->cb_break;
        afs_use_fs_server(call, fc->cbi);
        trace_afs_make_fs_call(call, fid);
        afs_set_fc_call(call, fc);
@@ -2069,8 +1834,7 @@ static int yfs_deliver_fs_inline_bulk_status(struct afs_call *call)
 
                bp = call->buffer;
                scb = &call->out_scb[call->count];
-               ret = yfs_decode_status(call, &bp, &scb->status,
-                                       NULL, NULL, NULL);
+               ret = xdr_decode_YFSFetchStatus(&bp, call, scb);
                if (ret < 0)
                        return ret;
 
@@ -2110,8 +1874,7 @@ static int yfs_deliver_fs_inline_bulk_status(struct afs_call *call)
                _debug("unmarshall CB array");
                bp = call->buffer;
                scb = &call->out_scb[call->count];
-               xdr_decode_YFSCallBack_raw(call, &scb->callback, &bp);
-               scb->have_cb = true;
+               xdr_decode_YFSCallBack(&bp, call, scb);
                call->count++;
                if (call->count < call->count2)
                        goto more_cbs;
@@ -2191,7 +1954,6 @@ int yfs_fs_inline_bulk_status(struct afs_fs_cursor *fc,
                bp = xdr_encode_YFSFid(bp, &fids[i]);
        yfs_check_req(call, bp);
 
-       call->cb_break = fc->cb_break;
        afs_use_fs_server(call, fc->cbi);
        trace_afs_make_fs_call(call, &fids[0]);
        afs_set_fc_call(call, fc);
@@ -2204,8 +1966,6 @@ int yfs_fs_inline_bulk_status(struct afs_fs_cursor *fc,
  */
 static int yfs_deliver_fs_fetch_opaque_acl(struct afs_call *call)
 {
-       struct afs_volsync *volsync = call->out_volsync;
-       struct afs_vnode *vnode = call->xvnode;
        struct yfs_acl *yacl = call->out_yacl;
        struct afs_acl *acl;
        const __be32 *bp;
@@ -2291,11 +2051,10 @@ static int yfs_deliver_fs_fetch_opaque_acl(struct afs_call *call)
                bp = call->buffer;
                yacl->inherit_flag = ntohl(*bp++);
                yacl->num_cleaned = ntohl(*bp++);
-               ret = yfs_decode_status(call, &bp, &vnode->status, vnode,
-                                       &call->expected_version, NULL);
+               ret = xdr_decode_YFSFetchStatus(&bp, call, call->out_scb);
                if (ret < 0)
                        return ret;
-               xdr_decode_YFSVolSync(&bp, volsync);
+               xdr_decode_YFSVolSync(&bp, call->out_volsync);
 
                call->unmarshall++;
 
@@ -2330,7 +2089,8 @@ static const struct afs_call_type yfs_RXYFSFetchOpaqueACL = {
  * Fetch the YFS advanced ACLs for a file.
  */
 struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_fs_cursor *fc,
-                                       struct yfs_acl *yacl)
+                                       struct yfs_acl *yacl,
+                                       struct afs_status_cb *scb)
 {
        struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
@@ -2353,8 +2113,8 @@ struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_fs_cursor *fc,
 
        call->key = fc->key;
        call->out_yacl = yacl;
-       call->xvnode = vnode;
-       call->out_volsync = NULL; /* volsync */
+       call->out_scb = scb;
+       call->out_volsync = NULL;
 
        /* marshall the parameters */
        bp = call->request;
@@ -2363,7 +2123,6 @@ struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_fs_cursor *fc,
        bp = xdr_encode_YFSFid(bp, &vnode->fid);
        yfs_check_req(call, bp);
 
-       call->cb_break = fc->cb_break;
        afs_use_fs_server(call, fc->cbi);
        trace_afs_make_fs_call(call, &vnode->fid);
        afs_make_call(&fc->ac, call, GFP_KERNEL);
@@ -2383,7 +2142,8 @@ static const struct afs_call_type yfs_RXYFSStoreOpaqueACL2 = {
 /*
  * Fetch the YFS ACL for a file.
  */
-int yfs_fs_store_opaque_acl2(struct afs_fs_cursor *fc, const struct afs_acl *acl)
+int yfs_fs_store_opaque_acl2(struct afs_fs_cursor *fc, const struct afs_acl *acl,
+                            struct afs_status_cb *scb)
 {
        struct afs_vnode *vnode = fc->vnode;
        struct afs_call *call;
@@ -2407,7 +2167,7 @@ int yfs_fs_store_opaque_acl2(struct afs_fs_cursor *fc, const struct afs_acl *acl
        }
 
        call->key = fc->key;
-       call->xvnode = vnode;
+       call->out_scb = scb;
        call->out_volsync = NULL;
 
        /* marshall the parameters */