diff options
author | Olga Kornievskaia <kolga@netapp.com> | 2018-07-09 15:13:32 -0400 |
---|---|---|
committer | Anna Schumaker <Anna.Schumaker@Netapp.com> | 2018-08-09 12:56:39 -0400 |
commit | bc0c9079b48ddcf1f8a6e1aaa277288b263c78d8 (patch) | |
tree | df6f4429ec4d1aeffcdced295f86410c9368a3c0 | |
parent | 62164f317972fcd36590578888f33a1994dda519 (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.c | 17 | ||||
-rw-r--r-- | fs/nfs/nfs42proc.c | 22 | ||||
-rw-r--r-- | fs/nfs/nfs4client.c | 15 | ||||
-rw-r--r-- | include/linux/nfs_fs_sb.h | 1 |
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(©->completion); | 696 | complete(©->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 | } |
701 | out: | 701 | out: |
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(©->stateid, &args->coa_stateid, NFS4_STATEID_SIZE); | ||
710 | nfs4_copy_cb_args(copy, args); | ||
711 | list_add_tail(©->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, ©->stateid, | ||
147 | NFS4_STATEID_SIZE)) | ||
148 | continue; | ||
149 | found_pending = true; | ||
150 | list_del(©->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(©->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE); | 163 | memcpy(©->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE); |
146 | init_completion(©->completion); | 164 | init_completion(©->completion); |
147 | 165 | ||
148 | spin_lock(&server->nfs_client->cl_lock); | ||
149 | list_add_tail(©->copies, &server->ss_copies); | 166 | list_add_tail(©->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(©->copies); | 171 | list_del_init(©->copies); |
155 | spin_unlock(&server->nfs_client->cl_lock); | 172 | spin_unlock(&server->nfs_client->cl_lock); |
173 | out: | ||
156 | res->write_res.count = copy->count; | 174 | res->write_res.count = copy->count; |
157 | memcpy(&res->write_res.verifier, ©->verf, sizeof(copy->verf)); | 175 | memcpy(&res->write_res.verifier, ©->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 | ||
159 | static void | ||
160 | nfs4_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 | |||
159 | void nfs41_shutdown_client(struct nfs_client *clp) | 172 | void 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 | ||
207 | error: | 222 | error: |
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 | /* |