NFSv4.2 copy do not allocate memory under the lock
authorOlga Kornievskaia <kolga@netapp.com>
Wed, 21 Nov 2018 16:24:22 +0000 (11:24 -0500)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Thu, 22 Nov 2018 18:54:46 +0000 (13:54 -0500)
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 <kolga@netapp.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
fs/nfs/callback_proc.c
fs/nfs/nfs42proc.c

index 7b861bbc0b43f38285866dd0d0b2eb6afd45a9b8..3159673549540f063124d62a6cc7098f86ec6ae8 100644 (file)
@@ -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;
index ac5b784a1de05c864958f1a16646fac034b402d5..fed06fd9998d322a202befd46f8c2546dedbba84 100644 (file)
@@ -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;