aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2011-08-02 14:46:29 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-08-29 16:29:08 -0400
commitf4bc412bc2f46d644375403b601f42d8487949da (patch)
tree29dd599a646a6d11a16109ae566e971549cbed7b /fs/nfs
parentb861a2580da034f6a57517c687ded68e20f99763 (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.h2
-rw-r--r--fs/nfs/callback_proc.c20
-rw-r--r--fs/nfs/callback_xdr.c24
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 {
38struct cb_process_state { 38struct 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
43struct cb_compound_hdr_arg { 44struct 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
168extern void nfs4_check_drain_bc_complete(struct nfs4_session *ses); 169extern void nfs4_check_drain_bc_complete(struct nfs4_session *ses);
169extern void nfs4_cb_take_slot(struct nfs_client *clp);
170 170
171struct cb_devicenotifyitem { 171struct 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);
360out_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
459out: 467out:
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
762static void nfs4_cb_free_slot(struct nfs_client *clp) 762static 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 */
769void 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
787static void nfs4_cb_free_slot(struct nfs_client *clp) 776static 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;