summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlga Kornievskaia <kolga@netapp.com>2018-07-09 15:13:32 -0400
committerAnna Schumaker <Anna.Schumaker@Netapp.com>2018-08-09 12:56:39 -0400
commitbc0c9079b48ddcf1f8a6e1aaa277288b263c78d8 (patch)
treedf6f4429ec4d1aeffcdced295f86410c9368a3c0
parent62164f317972fcd36590578888f33a1994dda519 (diff)
NFS handle COPY reply CB_OFFLOAD call race
It's possible that server replies back with CB_OFFLOAD call and COPY reply at the same time such that client will process CB_OFFLOAD before reply to COPY. For that keep a list of pending callback stateids received and then before waiting on completion check the pending list. Cleanup any pending copies on the client shutdown. Signed-off-by: Olga Kornievskaia <kolga@netapp.com> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
-rw-r--r--fs/nfs/callback_proc.c17
-rw-r--r--fs/nfs/nfs42proc.c22
-rw-r--r--fs/nfs/nfs4client.c15
-rw-r--r--include/linux/nfs_fs_sb.h1
4 files changed, 50 insertions, 5 deletions
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index acdda259912e..cd733649646b 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -681,11 +681,12 @@ __be32 nfs4_callback_offload(void *data, void *dummy,
681 struct cb_offloadargs *args = data; 681 struct cb_offloadargs *args = data;
682 struct nfs_server *server; 682 struct nfs_server *server;
683 struct nfs4_copy_state *copy; 683 struct nfs4_copy_state *copy;
684 bool found = false;
684 685
686 spin_lock(&cps->clp->cl_lock);
685 rcu_read_lock(); 687 rcu_read_lock();
686 list_for_each_entry_rcu(server, &cps->clp->cl_superblocks, 688 list_for_each_entry_rcu(server, &cps->clp->cl_superblocks,
687 client_link) { 689 client_link) {
688 spin_lock(&server->nfs_client->cl_lock);
689 list_for_each_entry(copy, &server->ss_copies, copies) { 690 list_for_each_entry(copy, &server->ss_copies, copies) {
690 if (memcmp(args->coa_stateid.other, 691 if (memcmp(args->coa_stateid.other,
691 copy->stateid.other, 692 copy->stateid.other,
@@ -693,13 +694,23 @@ __be32 nfs4_callback_offload(void *data, void *dummy,
693 continue; 694 continue;
694 nfs4_copy_cb_args(copy, args); 695 nfs4_copy_cb_args(copy, args);
695 complete(&copy->completion); 696 complete(&copy->completion);
696 spin_unlock(&server->nfs_client->cl_lock); 697 found = true;
697 goto out; 698 goto out;
698 } 699 }
699 spin_unlock(&server->nfs_client->cl_lock);
700 } 700 }
701out: 701out:
702 rcu_read_unlock(); 702 rcu_read_unlock();
703 if (!found) {
704 copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
705 if (!copy) {
706 spin_unlock(&cps->clp->cl_lock);
707 return htonl(NFS4ERR_SERVERFAULT);
708 }
709 memcpy(&copy->stateid, &args->coa_stateid, NFS4_STATEID_SIZE);
710 nfs4_copy_cb_args(copy, args);
711 list_add_tail(&copy->copies, &cps->clp->pending_cb_stateids);
712 }
713 spin_unlock(&cps->clp->cl_lock);
703 714
704 return 0; 715 return 0;
705} 716}
diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c
index 023aea8f6cf1..c7d31f72070e 100644
--- a/fs/nfs/nfs42proc.c
+++ b/fs/nfs/nfs42proc.c
@@ -138,14 +138,31 @@ static int handle_async_copy(struct nfs42_copy_res *res,
138{ 138{
139 struct nfs4_copy_state *copy; 139 struct nfs4_copy_state *copy;
140 int status = NFS4_OK; 140 int status = NFS4_OK;
141 bool found_pending = false;
142
143 spin_lock(&server->nfs_client->cl_lock);
144 list_for_each_entry(copy, &server->nfs_client->pending_cb_stateids,
145 copies) {
146 if (memcmp(&res->write_res.stateid, &copy->stateid,
147 NFS4_STATEID_SIZE))
148 continue;
149 found_pending = true;
150 list_del(&copy->copies);
151 break;
152 }
153 if (found_pending) {
154 spin_unlock(&server->nfs_client->cl_lock);
155 goto out;
156 }
141 157
142 copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS); 158 copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
143 if (!copy) 159 if (!copy) {
160 spin_unlock(&server->nfs_client->cl_lock);
144 return -ENOMEM; 161 return -ENOMEM;
162 }
145 memcpy(&copy->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE); 163 memcpy(&copy->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE);
146 init_completion(&copy->completion); 164 init_completion(&copy->completion);
147 165
148 spin_lock(&server->nfs_client->cl_lock);
149 list_add_tail(&copy->copies, &server->ss_copies); 166 list_add_tail(&copy->copies, &server->ss_copies);
150 spin_unlock(&server->nfs_client->cl_lock); 167 spin_unlock(&server->nfs_client->cl_lock);
151 168
@@ -153,6 +170,7 @@ static int handle_async_copy(struct nfs42_copy_res *res,
153 spin_lock(&server->nfs_client->cl_lock); 170 spin_lock(&server->nfs_client->cl_lock);
154 list_del_init(&copy->copies); 171 list_del_init(&copy->copies);
155 spin_unlock(&server->nfs_client->cl_lock); 172 spin_unlock(&server->nfs_client->cl_lock);
173out:
156 res->write_res.count = copy->count; 174 res->write_res.count = copy->count;
157 memcpy(&res->write_res.verifier, &copy->verf, sizeof(copy->verf)); 175 memcpy(&res->write_res.verifier, &copy->verf, sizeof(copy->verf));
158 status = -copy->error; 176 status = -copy->error;
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index d7124fb12041..146e30862234 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -156,9 +156,23 @@ nfs4_shutdown_ds_clients(struct nfs_client *clp)
156 } 156 }
157} 157}
158 158
159static void
160nfs4_cleanup_callback(struct nfs_client *clp)
161{
162 struct nfs4_copy_state *cp_state;
163
164 while (!list_empty(&clp->pending_cb_stateids)) {
165 cp_state = list_entry(clp->pending_cb_stateids.next,
166 struct nfs4_copy_state, copies);
167 list_del(&cp_state->copies);
168 kfree(cp_state);
169 }
170}
171
159void nfs41_shutdown_client(struct nfs_client *clp) 172void nfs41_shutdown_client(struct nfs_client *clp)
160{ 173{
161 if (nfs4_has_session(clp)) { 174 if (nfs4_has_session(clp)) {
175 nfs4_cleanup_callback(clp);
162 nfs4_shutdown_ds_clients(clp); 176 nfs4_shutdown_ds_clients(clp);
163 nfs4_destroy_session(clp->cl_session); 177 nfs4_destroy_session(clp->cl_session);
164 nfs4_destroy_clientid(clp); 178 nfs4_destroy_clientid(clp);
@@ -202,6 +216,7 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
202#if IS_ENABLED(CONFIG_NFS_V4_1) 216#if IS_ENABLED(CONFIG_NFS_V4_1)
203 init_waitqueue_head(&clp->cl_lock_waitq); 217 init_waitqueue_head(&clp->cl_lock_waitq);
204#endif 218#endif
219 INIT_LIST_HEAD(&clp->pending_cb_stateids);
205 return clp; 220 return clp;
206 221
207error: 222error:
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index f88952d7b9fb..bf39d9c92201 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -121,6 +121,7 @@ struct nfs_client {
121#endif 121#endif
122 122
123 struct net *cl_net; 123 struct net *cl_net;
124 struct list_head pending_cb_stateids;
124}; 125};
125 126
126/* 127/*