Skip to content

Commit 83e7331

Browse files
neilbrownchucklever
authored andcommitted
nfsd: avoid race after unhash_delegation_locked()
NFS4_CLOSED_DELEG_STID and NFS4_REVOKED_DELEG_STID are similar in purpose. REVOKED is used for NFSv4.1 states which have been revoked because the lease has expired. CLOSED is used in other cases. The difference has two practical effects. 1/ REVOKED states are on the ->cl_revoked list 2/ REVOKED states result in nfserr_deleg_revoked from nfsd4_verify_open_stid() and nfsd4_validate_stateid while CLOSED states result in nfserr_bad_stid. Currently a state that is being revoked is first set to "CLOSED" in unhash_delegation_locked(), then possibly to "REVOKED" in revoke_delegation(), at which point it is added to the cl_revoked list. It is possible that a stateid test could see the CLOSED state which really should be REVOKED, and so return the wrong error code. So it is safest to remove this window of inconsistency. With this patch, unhash_delegation_locked() always sets the state correctly, and revoke_delegation() no longer changes the state. Also remove a redundant test on minorversion when NFS4_REVOKED_DELEG_STID is seen - it can only be seen when minorversion is non-zero. Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: NeilBrown <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent c654002 commit 83e7331

File tree

1 file changed

+10
-10
lines changed

1 file changed

+10
-10
lines changed

fs/nfsd/nfs4state.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,7 +1329,7 @@ static bool delegation_hashed(struct nfs4_delegation *dp)
13291329
}
13301330

13311331
static bool
1332-
unhash_delegation_locked(struct nfs4_delegation *dp)
1332+
unhash_delegation_locked(struct nfs4_delegation *dp, unsigned char type)
13331333
{
13341334
struct nfs4_file *fp = dp->dl_stid.sc_file;
13351335

@@ -1338,7 +1338,9 @@ unhash_delegation_locked(struct nfs4_delegation *dp)
13381338
if (!delegation_hashed(dp))
13391339
return false;
13401340

1341-
dp->dl_stid.sc_type = NFS4_CLOSED_DELEG_STID;
1341+
if (dp->dl_stid.sc_client->cl_minorversion == 0)
1342+
type = NFS4_CLOSED_DELEG_STID;
1343+
dp->dl_stid.sc_type = type;
13421344
/* Ensure that deleg break won't try to requeue it */
13431345
++dp->dl_time;
13441346
spin_lock(&fp->fi_lock);
@@ -1354,7 +1356,7 @@ static void destroy_delegation(struct nfs4_delegation *dp)
13541356
bool unhashed;
13551357

13561358
spin_lock(&state_lock);
1357-
unhashed = unhash_delegation_locked(dp);
1359+
unhashed = unhash_delegation_locked(dp, NFS4_CLOSED_DELEG_STID);
13581360
spin_unlock(&state_lock);
13591361
if (unhashed)
13601362
destroy_unhashed_deleg(dp);
@@ -1368,9 +1370,8 @@ static void revoke_delegation(struct nfs4_delegation *dp)
13681370

13691371
trace_nfsd_stid_revoke(&dp->dl_stid);
13701372

1371-
if (clp->cl_minorversion) {
1373+
if (dp->dl_stid.sc_type == NFS4_REVOKED_DELEG_STID) {
13721374
spin_lock(&clp->cl_lock);
1373-
dp->dl_stid.sc_type = NFS4_REVOKED_DELEG_STID;
13741375
refcount_inc(&dp->dl_stid.sc_count);
13751376
list_add(&dp->dl_recall_lru, &clp->cl_revoked);
13761377
spin_unlock(&clp->cl_lock);
@@ -2229,7 +2230,7 @@ __destroy_client(struct nfs4_client *clp)
22292230
spin_lock(&state_lock);
22302231
while (!list_empty(&clp->cl_delegations)) {
22312232
dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt);
2232-
unhash_delegation_locked(dp);
2233+
unhash_delegation_locked(dp, NFS4_CLOSED_DELEG_STID);
22332234
list_add(&dp->dl_recall_lru, &reaplist);
22342235
}
22352236
spin_unlock(&state_lock);
@@ -5144,8 +5145,7 @@ nfs4_check_deleg(struct nfs4_client *cl, struct nfsd4_open *open,
51445145
goto out;
51455146
if (deleg->dl_stid.sc_type == NFS4_REVOKED_DELEG_STID) {
51465147
nfs4_put_stid(&deleg->dl_stid);
5147-
if (cl->cl_minorversion)
5148-
status = nfserr_deleg_revoked;
5148+
status = nfserr_deleg_revoked;
51495149
goto out;
51505150
}
51515151
flags = share_access_to_flags(open->op_share_access);
@@ -6169,7 +6169,7 @@ nfs4_laundromat(struct nfsd_net *nn)
61696169
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
61706170
if (!state_expired(&lt, dp->dl_time))
61716171
break;
6172-
unhash_delegation_locked(dp);
6172+
unhash_delegation_locked(dp, NFS4_REVOKED_DELEG_STID);
61736173
list_add(&dp->dl_recall_lru, &reaplist);
61746174
}
61756175
spin_unlock(&state_lock);
@@ -8292,7 +8292,7 @@ nfs4_state_shutdown_net(struct net *net)
82928292
spin_lock(&state_lock);
82938293
list_for_each_safe(pos, next, &nn->del_recall_lru) {
82948294
dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru);
8295-
unhash_delegation_locked(dp);
8295+
unhash_delegation_locked(dp, NFS4_CLOSED_DELEG_STID);
82968296
list_add(&dp->dl_recall_lru, &reaplist);
82978297
}
82988298
spin_unlock(&state_lock);

0 commit comments

Comments
 (0)