From: Chuck Lever <chuck.lever@xxxxxxxxxx> The NFSv4.2 protocol requires that a client match a CB_OFFLOAD callback to a COPY reply containing the same copy state ID. However, it's possible that the order of the callback and reply processing on the client can cause the CB_OFFLOAD to be received and processed /before/ the client has dealt with the COPY reply. Currently, in this case, the Linux NFS client will queue a fresh struct nfs4_copy_state in the CB_OFFLOAD handler. handle_async_copy() then checks for a matching nfs4_copy_state before settling down to wait for a CB_OFFLOAD reply. But it would be simpler for the client's callback service to respond to such a CB_OFFLOAD with "I'm not ready yet" and have the server send the CB_OFFLOAD again later. This avoids the need for the client's CB_OFFLOAD processing to allocate an extra struct nfs4_copy_state -- in most cases that allocation will be tossed immediately, and it's one less memory allocation that we have to worry about accidentally leaking or accumulating over time. Suggested-by: Jeff Layton <jlayton@xxxxxxxxxx> Signed-off-by: Chuck Lever <chuck.lever@xxxxxxxxxx> --- fs/nfs/callback_proc.c | 14 ++------------ fs/nfs/nfs42proc.c | 21 +-------------------- 2 files changed, 3 insertions(+), 32 deletions(-) Compile-tested only and pushed to my "fix-async-copy" branch... File this in the "crazy ideas" bin, or maybe it makes sense ? diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 8397c43358bd..cd2c3d196f22 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -712,14 +712,10 @@ __be32 nfs4_callback_offload(void *data, void *dummy, struct cb_process_state *cps) { struct cb_offloadargs *args = data; + struct nfs4_copy_state *tmp_copy; struct nfs_server *server; - struct nfs4_copy_state *copy, *tmp_copy; bool found = false; - copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_KERNEL); - if (!copy) - return cpu_to_be32(NFS4ERR_DELAY); - spin_lock(&cps->clp->cl_lock); rcu_read_lock(); list_for_each_entry_rcu(server, &cps->clp->cl_superblocks, @@ -737,17 +733,11 @@ __be32 nfs4_callback_offload(void *data, void *dummy, } out: rcu_read_unlock(); - if (!found) { - memcpy(©->stateid, &args->coa_stateid, NFS4_STATEID_SIZE); - nfs4_copy_cb_args(copy, args); - list_add_tail(©->copies, &cps->clp->pending_cb_stateids); - } else - kfree(copy); spin_unlock(&cps->clp->cl_lock); trace_nfs4_cb_offload(&args->coa_fh, &args->coa_stateid, args->wr_count, args->error, args->wr_writeverf.committed); - return 0; + return found ? cpu_to_be32(NFS4_OK) : cpu_to_be32(NFS4ERR_DELAY); } #endif /* CONFIG_NFS_V4_2 */ diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c index 6e00bef97915..b332eafac8a2 100644 --- a/fs/nfs/nfs42proc.c +++ b/fs/nfs/nfs42proc.c @@ -197,11 +197,11 @@ static int handle_async_copy(struct nfs42_copy_res *res, nfs4_stateid *src_stateid, bool *restart) { - struct nfs4_copy_state *copy, *tmp_copy = NULL, *iter; struct nfs_open_context *dst_ctx = nfs_file_open_context(dst); struct nfs_open_context *src_ctx = nfs_file_open_context(src); struct nfs_client *clp = dst_server->nfs_client; unsigned long timeout = clp->cl_lease_time >> 1; + struct nfs4_copy_state *copy; int status = NFS4_OK; bool retry = false; u64 copied; @@ -211,28 +211,10 @@ static int handle_async_copy(struct nfs42_copy_res *res, return -ENOMEM; spin_lock(&dst_server->nfs_client->cl_lock); - list_for_each_entry(iter, - &dst_server->nfs_client->pending_cb_stateids, - copies) { - if (memcmp(&res->write_res.stateid, &iter->stateid, - NFS4_STATEID_SIZE)) - continue; - tmp_copy = iter; - list_del(&iter->copies); - break; - } - if (tmp_copy) { - spin_unlock(&dst_server->nfs_client->cl_lock); - kfree(copy); - copy = tmp_copy; - goto out; - } - memcpy(©->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE); init_completion(©->completion); copy->parent_dst_state = dst_ctx->state; copy->parent_src_state = src_ctx->state; - list_add_tail(©->copies, &dst_server->ss_copies); spin_unlock(&dst_server->nfs_client->cl_lock); @@ -255,7 +237,6 @@ static int handle_async_copy(struct nfs42_copy_res *res, *restart = true; goto out_cancel; } -out: res->write_res.count = copy->count; /* Copy out the updated write verifier provided by CB_OFFLOAD. */ memcpy(&res->write_res.verifier, ©->verf, sizeof(copy->verf)); -- 2.47.0