Skip to content

Commit bc0c907

Browse files
olgakorn1amschuma-ntap
authored andcommitted
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 <[email protected]> Signed-off-by: Anna Schumaker <[email protected]>
1 parent 62164f3 commit bc0c907

File tree

4 files changed

+50
-5
lines changed

4 files changed

+50
-5
lines changed

fs/nfs/callback_proc.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -681,25 +681,36 @@ __be32 nfs4_callback_offload(void *data, void *dummy,
681681
struct cb_offloadargs *args = data;
682682
struct nfs_server *server;
683683
struct nfs4_copy_state *copy;
684+
bool found = false;
684685

686+
spin_lock(&cps->clp->cl_lock);
685687
rcu_read_lock();
686688
list_for_each_entry_rcu(server, &cps->clp->cl_superblocks,
687689
client_link) {
688-
spin_lock(&server->nfs_client->cl_lock);
689690
list_for_each_entry(copy, &server->ss_copies, copies) {
690691
if (memcmp(args->coa_stateid.other,
691692
copy->stateid.other,
692693
sizeof(args->coa_stateid.other)))
693694
continue;
694695
nfs4_copy_cb_args(copy, args);
695696
complete(&copy->completion);
696-
spin_unlock(&server->nfs_client->cl_lock);
697+
found = true;
697698
goto out;
698699
}
699-
spin_unlock(&server->nfs_client->cl_lock);
700700
}
701701
out:
702702
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);
703714

704715
return 0;
705716
}

fs/nfs/nfs42proc.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,21 +138,39 @@ static int handle_async_copy(struct nfs42_copy_res *res,
138138
{
139139
struct nfs4_copy_state *copy;
140140
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+
}
141157

142158
copy = kzalloc(sizeof(struct nfs4_copy_state), GFP_NOFS);
143-
if (!copy)
159+
if (!copy) {
160+
spin_unlock(&server->nfs_client->cl_lock);
144161
return -ENOMEM;
162+
}
145163
memcpy(&copy->stateid, &res->write_res.stateid, NFS4_STATEID_SIZE);
146164
init_completion(&copy->completion);
147165

148-
spin_lock(&server->nfs_client->cl_lock);
149166
list_add_tail(&copy->copies, &server->ss_copies);
150167
spin_unlock(&server->nfs_client->cl_lock);
151168

152169
wait_for_completion_interruptible(&copy->completion);
153170
spin_lock(&server->nfs_client->cl_lock);
154171
list_del_init(&copy->copies);
155172
spin_unlock(&server->nfs_client->cl_lock);
173+
out:
156174
res->write_res.count = copy->count;
157175
memcpy(&res->write_res.verifier, &copy->verf, sizeof(copy->verf));
158176
status = -copy->error;

fs/nfs/nfs4client.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,23 @@ nfs4_shutdown_ds_clients(struct nfs_client *clp)
156156
}
157157
}
158158

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+
159172
void nfs41_shutdown_client(struct nfs_client *clp)
160173
{
161174
if (nfs4_has_session(clp)) {
175+
nfs4_cleanup_callback(clp);
162176
nfs4_shutdown_ds_clients(clp);
163177
nfs4_destroy_session(clp->cl_session);
164178
nfs4_destroy_clientid(clp);
@@ -202,6 +216,7 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
202216
#if IS_ENABLED(CONFIG_NFS_V4_1)
203217
init_waitqueue_head(&clp->cl_lock_waitq);
204218
#endif
219+
INIT_LIST_HEAD(&clp->pending_cb_stateids);
205220
return clp;
206221

207222
error:

include/linux/nfs_fs_sb.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ struct nfs_client {
121121
#endif
122122

123123
struct net *cl_net;
124+
struct list_head pending_cb_stateids;
124125
};
125126

126127
/*

0 commit comments

Comments
 (0)