From: Olga Kornievskaia <ko...@netapp.com>

[ Upstream commit 99f2c55591fb5c1b536263970d98c2ebc2089906 ]

Bruce pointed out that we shouldn't allocate memory while holding
a lock in the nfs4_callback_offload() and handle_async_copy()
that deal with a racing CB_OFFLOAD and reply to COPY case.

Signed-off-by: Olga Kornievskaia <ko...@netapp.com>
Signed-off-by: Trond Myklebust <trond.mykleb...@hammerspace.com>
Signed-off-by: Sasha Levin <sas...@kernel.org>
---
 fs/nfs/callback_proc.c | 22 +++++++++++-----------
 fs/nfs/nfs42proc.c     | 19 ++++++++++---------
 2 files changed, 21 insertions(+), 20 deletions(-)

diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index fa515d5ea5ba..48b2e9063b0a 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -686,20 +686,24 @@ __be32 nfs4_callback_offload(void *data, void *dummy,
 {
        struct cb_offloadargs *args = data;
        struct nfs_server *server;
-       struct nfs4_copy_state *copy;
+       struct nfs4_copy_state *copy, *tmp_copy;
        bool found = false;
 
+       copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
+       if (!copy)
+               return htonl(NFS4ERR_SERVERFAULT);
+
        spin_lock(&cps->clp->cl_lock);
        rcu_read_lock();
        list_for_each_entry_rcu(server, &cps->clp->cl_superblocks,
                                client_link) {
-               list_for_each_entry(copy, &server->ss_copies, copies) {
+               list_for_each_entry(tmp_copy, &server->ss_copies, copies) {
                        if (memcmp(args->coa_stateid.other,
-                                       copy->stateid.other,
+                                       tmp_copy->stateid.other,
                                        sizeof(args->coa_stateid.other)))
                                continue;
-                       nfs4_copy_cb_args(copy, args);
-                       complete(&copy->completion);
+                       nfs4_copy_cb_args(tmp_copy, args);
+                       complete(&tmp_copy->completion);
                        found = true;
                        goto out;
                }
@@ -707,15 +711,11 @@ __be32 nfs4_callback_offload(void *data, void *dummy,
 out:
        rcu_read_unlock();
        if (!found) {
-               copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
-               if (!copy) {
-                       spin_unlock(&cps->clp->cl_lock);
-                       return htonl(NFS4ERR_SERVERFAULT);
-               }
                memcpy(&copy->stateid, &args->coa_stateid, NFS4_STATEID_SIZE);
                nfs4_copy_cb_args(copy, args);
                list_add_tail(&copy->copies, &cps->clp->pending_cb_stateids);
-       }
+       } else
+               kfree(copy);
        spin_unlock(&cps->clp->cl_lock);
 
        return 0;
diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c
index ac5b784a1de0..fed06fd9998d 100644
--- a/fs/nfs/nfs42proc.c
+++ b/fs/nfs/nfs42proc.c
@@ -137,31 +137,32 @@ static int handle_async_copy(struct nfs42_copy_res *res,
                             struct file *dst,
                             nfs4_stateid *src_stateid)
 {
-       struct nfs4_copy_state *copy;
+       struct nfs4_copy_state *copy, *tmp_copy;
        int status = NFS4_OK;
        bool found_pending = false;
        struct nfs_open_context *ctx = nfs_file_open_context(dst);
 
+       copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
+       if (!copy)
+               return -ENOMEM;
+
        spin_lock(&server->nfs_client->cl_lock);
-       list_for_each_entry(copy, &server->nfs_client->pending_cb_stateids,
+       list_for_each_entry(tmp_copy, &server->nfs_client->pending_cb_stateids,
                                copies) {
-               if (memcmp(&res->write_res.stateid, &copy->stateid,
+               if (memcmp(&res->write_res.stateid, &tmp_copy->stateid,
                                NFS4_STATEID_SIZE))
                        continue;
                found_pending = true;
-               list_del(&copy->copies);
+               list_del(&tmp_copy->copies);
                break;
        }
        if (found_pending) {
                spin_unlock(&server->nfs_client->cl_lock);
+               kfree(copy);
+               copy = tmp_copy;
                goto out;
        }
 
-       copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
-       if (!copy) {
-               spin_unlock(&server->nfs_client->cl_lock);
-               return -ENOMEM;
-       }
        memcpy(&copy->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE);
        init_completion(&copy->completion);
        copy->parent_state = ctx->state;
-- 
2.17.1

Reply via email to