aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorTrond Myklebust <trond.myklebust@primarydata.com>2016-11-14 11:19:55 -0500
committerAnna Schumaker <Anna.Schumaker@Netapp.com>2016-11-18 13:35:58 -0500
commit3e7dfb1659c2888fc0152ec2b02a5e932397bb0a (patch)
tree301cbdf7e52009221b137a1d028a1b916d38e167 /fs
parent23ea44c2150d14b97518435a65cc74111804fbeb (diff)
NFSv4: Fix CLOSE races with OPEN
If the reply to a successful CLOSE call races with an OPEN to the same file, we can end up scribbling over the stateid that represents the new open state. The race looks like: Client Server ====== ====== CLOSE stateid A on file "foo" CLOSE stateid A, return stateid C OPEN file "foo" OPEN "foo", return stateid B Receive reply to OPEN Reset open state for "foo" Associate stateid B to "foo" Receive CLOSE for A Reset open state for "foo" Replace stateid B with C The fix is to examine the argument of the CLOSE, and check for a match with the current stateid "other" field. If the two do not match, then the above race occurred, and we should just ignore the CLOSE. Reported-by: Benjamin Coddington <bcodding@redhat.com> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/nfs/nfs4_fs.h7
-rw-r--r--fs/nfs/nfs4proc.c12
2 files changed, 13 insertions, 6 deletions
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 9b3a82abab07..1452177c822d 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -542,6 +542,13 @@ static inline bool nfs4_valid_open_stateid(const struct nfs4_state *state)
542 return test_bit(NFS_STATE_RECOVERY_FAILED, &state->flags) == 0; 542 return test_bit(NFS_STATE_RECOVERY_FAILED, &state->flags) == 0;
543} 543}
544 544
545static inline bool nfs4_state_match_open_stateid_other(const struct nfs4_state *state,
546 const nfs4_stateid *stateid)
547{
548 return test_bit(NFS_OPEN_STATE, &state->flags) &&
549 nfs4_stateid_match_other(&state->open_stateid, stateid);
550}
551
545#else 552#else
546 553
547#define nfs4_close_state(a, b) do { } while (0) 554#define nfs4_close_state(a, b) do { } while (0)
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 8e25327077e2..0b3cdf856333 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -1451,7 +1451,6 @@ static void nfs_resync_open_stateid_locked(struct nfs4_state *state)
1451} 1451}
1452 1452
1453static void nfs_clear_open_stateid_locked(struct nfs4_state *state, 1453static void nfs_clear_open_stateid_locked(struct nfs4_state *state,
1454 nfs4_stateid *arg_stateid,
1455 nfs4_stateid *stateid, fmode_t fmode) 1454 nfs4_stateid *stateid, fmode_t fmode)
1456{ 1455{
1457 clear_bit(NFS_O_RDWR_STATE, &state->flags); 1456 clear_bit(NFS_O_RDWR_STATE, &state->flags);
@@ -1469,10 +1468,9 @@ static void nfs_clear_open_stateid_locked(struct nfs4_state *state,
1469 } 1468 }
1470 if (stateid == NULL) 1469 if (stateid == NULL)
1471 return; 1470 return;
1472 /* Handle races with OPEN */ 1471 /* Handle OPEN+OPEN_DOWNGRADE races */
1473 if (!nfs4_stateid_match_other(arg_stateid, &state->open_stateid) || 1472 if (nfs4_stateid_match_other(stateid, &state->open_stateid) &&
1474 (nfs4_stateid_match_other(stateid, &state->open_stateid) && 1473 !nfs4_stateid_is_newer(stateid, &state->open_stateid)) {
1475 !nfs4_stateid_is_newer(stateid, &state->open_stateid))) {
1476 nfs_resync_open_stateid_locked(state); 1474 nfs_resync_open_stateid_locked(state);
1477 return; 1475 return;
1478 } 1476 }
@@ -1486,7 +1484,9 @@ static void nfs_clear_open_stateid(struct nfs4_state *state,
1486 nfs4_stateid *stateid, fmode_t fmode) 1484 nfs4_stateid *stateid, fmode_t fmode)
1487{ 1485{
1488 write_seqlock(&state->seqlock); 1486 write_seqlock(&state->seqlock);
1489 nfs_clear_open_stateid_locked(state, arg_stateid, stateid, fmode); 1487 /* Ignore, if the CLOSE argment doesn't match the current stateid */
1488 if (nfs4_state_match_open_stateid_other(state, arg_stateid))
1489 nfs_clear_open_stateid_locked(state, stateid, fmode);
1490 write_sequnlock(&state->seqlock); 1490 write_sequnlock(&state->seqlock);
1491 if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) 1491 if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags))
1492 nfs4_schedule_state_manager(state->owner->so_server->nfs_client); 1492 nfs4_schedule_state_manager(state->owner->so_server->nfs_client);