diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2011-08-02 14:46:29 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-08-29 16:29:08 -0400 |
commit | f4bc412bc2f46d644375403b601f42d8487949da (patch) | |
tree | 29dd599a646a6d11a16109ae566e971549cbed7b /fs/nfs | |
parent | b861a2580da034f6a57517c687ded68e20f99763 (diff) |
NFSv4.1: Fix the callback 'highest_used_slotid' behaviour
commit 55a673990ec04cf63005318bcf08c2b0046e5778 upstream.
Currently, there is no guarantee that we will call nfs4_cb_take_slot() even
though nfs4_callback_compound() will consistently call
nfs4_cb_free_slot() provided the cb_process_state has set the 'clp' field.
The result is that we can trigger the BUG_ON() upon the next call to
nfs4_cb_take_slot().
This patch fixes the above problem by using the slot id that was taken in
the CB_SEQUENCE operation as a flag for whether or not we need to call
nfs4_cb_free_slot().
It also fixes an atomicity problem: we need to set tbl->highest_used_slotid
atomically with the check for NFS4_SESSION_DRAINING, otherwise we end up
racing with the various tests in nfs4_begin_drain_session().
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/callback.h | 2 | ||||
-rw-r--r-- | fs/nfs/callback_proc.c | 20 | ||||
-rw-r--r-- | fs/nfs/callback_xdr.c | 24 |
3 files changed, 22 insertions, 24 deletions
diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index b257383bb56..07df5f1d85e 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h | |||
@@ -38,6 +38,7 @@ enum nfs4_callback_opnum { | |||
38 | struct cb_process_state { | 38 | struct cb_process_state { |
39 | __be32 drc_status; | 39 | __be32 drc_status; |
40 | struct nfs_client *clp; | 40 | struct nfs_client *clp; |
41 | int slotid; | ||
41 | }; | 42 | }; |
42 | 43 | ||
43 | struct cb_compound_hdr_arg { | 44 | struct cb_compound_hdr_arg { |
@@ -166,7 +167,6 @@ extern unsigned nfs4_callback_layoutrecall( | |||
166 | void *dummy, struct cb_process_state *cps); | 167 | void *dummy, struct cb_process_state *cps); |
167 | 168 | ||
168 | extern void nfs4_check_drain_bc_complete(struct nfs4_session *ses); | 169 | extern void nfs4_check_drain_bc_complete(struct nfs4_session *ses); |
169 | extern void nfs4_cb_take_slot(struct nfs_client *clp); | ||
170 | 170 | ||
171 | struct cb_devicenotifyitem { | 171 | struct cb_devicenotifyitem { |
172 | uint32_t cbd_notify_type; | 172 | uint32_t cbd_notify_type; |
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index d4d1954e9bb..31407ecd0e5 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c | |||
@@ -333,7 +333,7 @@ validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args) | |||
333 | /* Normal */ | 333 | /* Normal */ |
334 | if (likely(args->csa_sequenceid == slot->seq_nr + 1)) { | 334 | if (likely(args->csa_sequenceid == slot->seq_nr + 1)) { |
335 | slot->seq_nr++; | 335 | slot->seq_nr++; |
336 | return htonl(NFS4_OK); | 336 | goto out_ok; |
337 | } | 337 | } |
338 | 338 | ||
339 | /* Replay */ | 339 | /* Replay */ |
@@ -352,11 +352,14 @@ validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args) | |||
352 | /* Wraparound */ | 352 | /* Wraparound */ |
353 | if (args->csa_sequenceid == 1 && (slot->seq_nr + 1) == 0) { | 353 | if (args->csa_sequenceid == 1 && (slot->seq_nr + 1) == 0) { |
354 | slot->seq_nr = 1; | 354 | slot->seq_nr = 1; |
355 | return htonl(NFS4_OK); | 355 | goto out_ok; |
356 | } | 356 | } |
357 | 357 | ||
358 | /* Misordered request */ | 358 | /* Misordered request */ |
359 | return htonl(NFS4ERR_SEQ_MISORDERED); | 359 | return htonl(NFS4ERR_SEQ_MISORDERED); |
360 | out_ok: | ||
361 | tbl->highest_used_slotid = args->csa_slotid; | ||
362 | return htonl(NFS4_OK); | ||
360 | } | 363 | } |
361 | 364 | ||
362 | /* | 365 | /* |
@@ -418,26 +421,32 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args, | |||
418 | struct cb_sequenceres *res, | 421 | struct cb_sequenceres *res, |
419 | struct cb_process_state *cps) | 422 | struct cb_process_state *cps) |
420 | { | 423 | { |
424 | struct nfs4_slot_table *tbl; | ||
421 | struct nfs_client *clp; | 425 | struct nfs_client *clp; |
422 | int i; | 426 | int i; |
423 | __be32 status = htonl(NFS4ERR_BADSESSION); | 427 | __be32 status = htonl(NFS4ERR_BADSESSION); |
424 | 428 | ||
425 | cps->clp = NULL; | ||
426 | |||
427 | clp = nfs4_find_client_sessionid(args->csa_addr, &args->csa_sessionid); | 429 | clp = nfs4_find_client_sessionid(args->csa_addr, &args->csa_sessionid); |
428 | if (clp == NULL) | 430 | if (clp == NULL) |
429 | goto out; | 431 | goto out; |
430 | 432 | ||
433 | tbl = &clp->cl_session->bc_slot_table; | ||
434 | |||
435 | spin_lock(&tbl->slot_tbl_lock); | ||
431 | /* state manager is resetting the session */ | 436 | /* state manager is resetting the session */ |
432 | if (test_bit(NFS4_SESSION_DRAINING, &clp->cl_session->session_state)) { | 437 | if (test_bit(NFS4_SESSION_DRAINING, &clp->cl_session->session_state)) { |
433 | status = NFS4ERR_DELAY; | 438 | spin_unlock(&tbl->slot_tbl_lock); |
439 | status = htonl(NFS4ERR_DELAY); | ||
434 | goto out; | 440 | goto out; |
435 | } | 441 | } |
436 | 442 | ||
437 | status = validate_seqid(&clp->cl_session->bc_slot_table, args); | 443 | status = validate_seqid(&clp->cl_session->bc_slot_table, args); |
444 | spin_unlock(&tbl->slot_tbl_lock); | ||
438 | if (status) | 445 | if (status) |
439 | goto out; | 446 | goto out; |
440 | 447 | ||
448 | cps->slotid = args->csa_slotid; | ||
449 | |||
441 | /* | 450 | /* |
442 | * Check for pending referring calls. If a match is found, a | 451 | * Check for pending referring calls. If a match is found, a |
443 | * related callback was received before the response to the original | 452 | * related callback was received before the response to the original |
@@ -454,7 +463,6 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args, | |||
454 | res->csr_slotid = args->csa_slotid; | 463 | res->csr_slotid = args->csa_slotid; |
455 | res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; | 464 | res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; |
456 | res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; | 465 | res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; |
457 | nfs4_cb_take_slot(clp); | ||
458 | 466 | ||
459 | out: | 467 | out: |
460 | cps->clp = clp; /* put in nfs4_callback_compound */ | 468 | cps->clp = clp; /* put in nfs4_callback_compound */ |
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index c6c86a77e04..918ad647afe 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c | |||
@@ -754,26 +754,15 @@ static void nfs4_callback_free_slot(struct nfs4_session *session) | |||
754 | * Let the state manager know callback processing done. | 754 | * Let the state manager know callback processing done. |
755 | * A single slot, so highest used slotid is either 0 or -1 | 755 | * A single slot, so highest used slotid is either 0 or -1 |
756 | */ | 756 | */ |
757 | tbl->highest_used_slotid--; | 757 | tbl->highest_used_slotid = -1; |
758 | nfs4_check_drain_bc_complete(session); | 758 | nfs4_check_drain_bc_complete(session); |
759 | spin_unlock(&tbl->slot_tbl_lock); | 759 | spin_unlock(&tbl->slot_tbl_lock); |
760 | } | 760 | } |
761 | 761 | ||
762 | static void nfs4_cb_free_slot(struct nfs_client *clp) | 762 | static void nfs4_cb_free_slot(struct cb_process_state *cps) |
763 | { | 763 | { |
764 | if (clp && clp->cl_session) | 764 | if (cps->slotid != -1) |
765 | nfs4_callback_free_slot(clp->cl_session); | 765 | nfs4_callback_free_slot(cps->clp->cl_session); |
766 | } | ||
767 | |||
768 | /* A single slot, so highest used slotid is either 0 or -1 */ | ||
769 | void nfs4_cb_take_slot(struct nfs_client *clp) | ||
770 | { | ||
771 | struct nfs4_slot_table *tbl = &clp->cl_session->bc_slot_table; | ||
772 | |||
773 | spin_lock(&tbl->slot_tbl_lock); | ||
774 | tbl->highest_used_slotid++; | ||
775 | BUG_ON(tbl->highest_used_slotid != 0); | ||
776 | spin_unlock(&tbl->slot_tbl_lock); | ||
777 | } | 766 | } |
778 | 767 | ||
779 | #else /* CONFIG_NFS_V4_1 */ | 768 | #else /* CONFIG_NFS_V4_1 */ |
@@ -784,7 +773,7 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op) | |||
784 | return htonl(NFS4ERR_MINOR_VERS_MISMATCH); | 773 | return htonl(NFS4ERR_MINOR_VERS_MISMATCH); |
785 | } | 774 | } |
786 | 775 | ||
787 | static void nfs4_cb_free_slot(struct nfs_client *clp) | 776 | static void nfs4_cb_free_slot(struct cb_process_state *cps) |
788 | { | 777 | { |
789 | } | 778 | } |
790 | #endif /* CONFIG_NFS_V4_1 */ | 779 | #endif /* CONFIG_NFS_V4_1 */ |
@@ -866,6 +855,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r | |||
866 | struct cb_process_state cps = { | 855 | struct cb_process_state cps = { |
867 | .drc_status = 0, | 856 | .drc_status = 0, |
868 | .clp = NULL, | 857 | .clp = NULL, |
858 | .slotid = -1, | ||
869 | }; | 859 | }; |
870 | unsigned int nops = 0; | 860 | unsigned int nops = 0; |
871 | 861 | ||
@@ -906,7 +896,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r | |||
906 | 896 | ||
907 | *hdr_res.status = status; | 897 | *hdr_res.status = status; |
908 | *hdr_res.nops = htonl(nops); | 898 | *hdr_res.nops = htonl(nops); |
909 | nfs4_cb_free_slot(cps.clp); | 899 | nfs4_cb_free_slot(&cps); |
910 | nfs_put_client(cps.clp); | 900 | nfs_put_client(cps.clp); |
911 | dprintk("%s: done, status = %u\n", __func__, ntohl(status)); | 901 | dprintk("%s: done, status = %u\n", __func__, ntohl(status)); |
912 | return rpc_success; | 902 | return rpc_success; |