Skip to content

Commit b4868b4

Browse files
bcodding-rhamschuma-ntap
authored andcommitted
NFSv4: Wait for stateid updates after CLOSE/OPEN_DOWNGRADE
Since commit 0e0cb35 ("NFSv4: Handle NFS4ERR_OLD_STATEID in CLOSE/OPEN_DOWNGRADE") the following livelock may occur if a CLOSE races with the update of the nfs_state: Process 1 Process 2 Server ========= ========= ======== OPEN file OPEN file Reply OPEN (1) Reply OPEN (2) Update state (1) CLOSE file (1) Reply OLD_STATEID (1) CLOSE file (2) Reply CLOSE (-1) Update state (2) wait for state change OPEN file wake CLOSE file OPEN file wake CLOSE file ... ... We can avoid this situation by not issuing an immediate retry with a bumped seqid when CLOSE/OPEN_DOWNGRADE receives NFS4ERR_OLD_STATEID. Instead, take the same approach used by OPEN and wait at least 5 seconds for outstanding stateid updates to complete if we can detect that we're out of sequence. Note that after this change it is still possible (though unlikely) that CLOSE waits a full 5 seconds, bumps the seqid, and retries -- and that attempt races with another OPEN at the same time. In order to avoid this race (which would result in the livelock), update nfs_need_update_open_stateid() to handle the case where: - the state is NFS_OPEN_STATE, and - the stateid doesn't match the current open stateid Finally, nfs_need_update_open_stateid() is modified to be idempotent and renamed to better suit the purpose of signaling that the stateid passed is the next stateid in sequence. Fixes: 0e0cb35 ("NFSv4: Handle NFS4ERR_OLD_STATEID in CLOSE/OPEN_DOWNGRADE") Cc: stable@vger.kernel.org # v5.4+ Signed-off-by: Benjamin Coddington <bcodding@redhat.com> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
1 parent fb08334 commit b4868b4

File tree

3 files changed

+56
-34
lines changed

3 files changed

+56
-34
lines changed

fs/nfs/nfs4_fs.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,14 @@ static inline bool nfs4_stateid_is_newer(const nfs4_stateid *s1, const nfs4_stat
599599
return (s32)(be32_to_cpu(s1->seqid) - be32_to_cpu(s2->seqid)) > 0;
600600
}
601601

602+
static inline bool nfs4_stateid_is_next(const nfs4_stateid *s1, const nfs4_stateid *s2)
603+
{
604+
u32 seq1 = be32_to_cpu(s1->seqid);
605+
u32 seq2 = be32_to_cpu(s2->seqid);
606+
607+
return seq2 == seq1 + 1U || (seq2 == 1U && seq1 == 0xffffffffU);
608+
}
609+
602610
static inline bool nfs4_stateid_match_or_older(const nfs4_stateid *dst, const nfs4_stateid *src)
603611
{
604612
return nfs4_stateid_match_other(dst, src) &&

fs/nfs/nfs4proc.c

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,19 +1550,6 @@ static void nfs_state_log_update_open_stateid(struct nfs4_state *state)
15501550
wake_up_all(&state->waitq);
15511551
}
15521552

1553-
static void nfs_state_log_out_of_order_open_stateid(struct nfs4_state *state,
1554-
const nfs4_stateid *stateid)
1555-
{
1556-
u32 state_seqid = be32_to_cpu(state->open_stateid.seqid);
1557-
u32 stateid_seqid = be32_to_cpu(stateid->seqid);
1558-
1559-
if (stateid_seqid == state_seqid + 1U ||
1560-
(stateid_seqid == 1U && state_seqid == 0xffffffffU))
1561-
nfs_state_log_update_open_stateid(state);
1562-
else
1563-
set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
1564-
}
1565-
15661553
static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state)
15671554
{
15681555
struct nfs_client *clp = state->owner->so_server->nfs_client;
@@ -1588,21 +1575,19 @@ static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state)
15881575
* i.e. The stateid seqids have to be initialised to 1, and
15891576
* are then incremented on every state transition.
15901577
*/
1591-
static bool nfs_need_update_open_stateid(struct nfs4_state *state,
1578+
static bool nfs_stateid_is_sequential(struct nfs4_state *state,
15921579
const nfs4_stateid *stateid)
15931580
{
1594-
if (test_bit(NFS_OPEN_STATE, &state->flags) == 0 ||
1595-
!nfs4_stateid_match_other(stateid, &state->open_stateid)) {
1581+
if (test_bit(NFS_OPEN_STATE, &state->flags)) {
1582+
/* The common case - we're updating to a new sequence number */
1583+
if (nfs4_stateid_match_other(stateid, &state->open_stateid) &&
1584+
nfs4_stateid_is_next(&state->open_stateid, stateid)) {
1585+
return true;
1586+
}
1587+
} else {
1588+
/* This is the first OPEN in this generation */
15961589
if (stateid->seqid == cpu_to_be32(1))
1597-
nfs_state_log_update_open_stateid(state);
1598-
else
1599-
set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
1600-
return true;
1601-
}
1602-
1603-
if (nfs4_stateid_is_newer(stateid, &state->open_stateid)) {
1604-
nfs_state_log_out_of_order_open_stateid(state, stateid);
1605-
return true;
1590+
return true;
16061591
}
16071592
return false;
16081593
}
@@ -1676,16 +1661,16 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state,
16761661
int status = 0;
16771662
for (;;) {
16781663

1679-
if (!nfs_need_update_open_stateid(state, stateid))
1680-
return;
1681-
if (!test_bit(NFS_STATE_CHANGE_WAIT, &state->flags))
1664+
if (nfs_stateid_is_sequential(state, stateid))
16821665
break;
1666+
16831667
if (status)
16841668
break;
16851669
/* Rely on seqids for serialisation with NFSv4.0 */
16861670
if (!nfs4_has_session(NFS_SERVER(state->inode)->nfs_client))
16871671
break;
16881672

1673+
set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
16891674
prepare_to_wait(&state->waitq, &wait, TASK_KILLABLE);
16901675
/*
16911676
* Ensure we process the state changes in the same order
@@ -1696,6 +1681,7 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state,
16961681
spin_unlock(&state->owner->so_lock);
16971682
rcu_read_unlock();
16981683
trace_nfs4_open_stateid_update_wait(state->inode, stateid, 0);
1684+
16991685
if (!signal_pending(current)) {
17001686
if (schedule_timeout(5*HZ) == 0)
17011687
status = -EAGAIN;
@@ -3438,7 +3424,8 @@ static bool nfs4_refresh_open_old_stateid(nfs4_stateid *dst,
34383424
__be32 seqid_open;
34393425
u32 dst_seqid;
34403426
bool ret;
3441-
int seq;
3427+
int seq, status = -EAGAIN;
3428+
DEFINE_WAIT(wait);
34423429

34433430
for (;;) {
34443431
ret = false;
@@ -3450,15 +3437,41 @@ static bool nfs4_refresh_open_old_stateid(nfs4_stateid *dst,
34503437
continue;
34513438
break;
34523439
}
3440+
3441+
write_seqlock(&state->seqlock);
34533442
seqid_open = state->open_stateid.seqid;
3454-
if (read_seqretry(&state->seqlock, seq))
3455-
continue;
34563443

34573444
dst_seqid = be32_to_cpu(dst->seqid);
3458-
if ((s32)(dst_seqid - be32_to_cpu(seqid_open)) >= 0)
3459-
dst->seqid = cpu_to_be32(dst_seqid + 1);
3460-
else
3445+
3446+
/* Did another OPEN bump the state's seqid? try again: */
3447+
if ((s32)(be32_to_cpu(seqid_open) - dst_seqid) > 0) {
34613448
dst->seqid = seqid_open;
3449+
write_sequnlock(&state->seqlock);
3450+
ret = true;
3451+
break;
3452+
}
3453+
3454+
/* server says we're behind but we haven't seen the update yet */
3455+
set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
3456+
prepare_to_wait(&state->waitq, &wait, TASK_KILLABLE);
3457+
write_sequnlock(&state->seqlock);
3458+
trace_nfs4_close_stateid_update_wait(state->inode, dst, 0);
3459+
3460+
if (signal_pending(current))
3461+
status = -EINTR;
3462+
else
3463+
if (schedule_timeout(5*HZ) != 0)
3464+
status = 0;
3465+
3466+
finish_wait(&state->waitq, &wait);
3467+
3468+
if (!status)
3469+
continue;
3470+
if (status == -EINTR)
3471+
break;
3472+
3473+
/* we slept the whole 5 seconds, we must have lost a seqid */
3474+
dst->seqid = cpu_to_be32(dst_seqid + 1);
34623475
ret = true;
34633476
break;
34643477
}

fs/nfs/nfs4trace.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,6 +1511,7 @@ DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_setattr);
15111511
DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_delegreturn);
15121512
DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_open_stateid_update);
15131513
DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_open_stateid_update_wait);
1514+
DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_close_stateid_update_wait);
15141515

15151516
DECLARE_EVENT_CLASS(nfs4_getattr_event,
15161517
TP_PROTO(

0 commit comments

Comments
 (0)