@@ -1359,21 +1359,47 @@ static void destroy_delegation(struct nfs4_delegation *dp)
13591359 destroy_unhashed_deleg (dp );
13601360}
13611361
1362+ /**
1363+ * revoke_delegation - perform nfs4 delegation structure cleanup
1364+ * @dp: pointer to the delegation
1365+ *
1366+ * This function assumes that it's called either from the administrative
1367+ * interface (nfsd4_revoke_states()) that's revoking a specific delegation
1368+ * stateid or it's called from a laundromat thread (nfsd4_landromat()) that
1369+ * determined that this specific state has expired and needs to be revoked
1370+ * (both mark state with the appropriate stid sc_status mode). It is also
1371+ * assumed that a reference was taken on the @dp state.
1372+ *
1373+ * If this function finds that the @dp state is SC_STATUS_FREED it means
1374+ * that a FREE_STATEID operation for this stateid has been processed and
1375+ * we can proceed to removing it from recalled list. However, if @dp state
1376+ * isn't marked SC_STATUS_FREED, it means we need place it on the cl_revoked
1377+ * list and wait for the FREE_STATEID to arrive from the client. At the same
1378+ * time, we need to mark it as SC_STATUS_FREEABLE to indicate to the
1379+ * nfsd4_free_stateid() function that this stateid has already been added
1380+ * to the cl_revoked list and that nfsd4_free_stateid() is now responsible
1381+ * for removing it from the list. Inspection of where the delegation state
1382+ * in the revocation process is protected by the clp->cl_lock.
1383+ */
13621384static void revoke_delegation (struct nfs4_delegation * dp )
13631385{
13641386 struct nfs4_client * clp = dp -> dl_stid .sc_client ;
13651387
13661388 WARN_ON (!list_empty (& dp -> dl_recall_lru ));
1389+ WARN_ON_ONCE (!(dp -> dl_stid .sc_status &
1390+ (SC_STATUS_REVOKED | SC_STATUS_ADMIN_REVOKED )));
13671391
13681392 trace_nfsd_stid_revoke (& dp -> dl_stid );
13691393
1370- if (dp -> dl_stid .sc_status &
1371- (SC_STATUS_REVOKED | SC_STATUS_ADMIN_REVOKED )) {
1372- spin_lock (& clp -> cl_lock );
1373- refcount_inc (& dp -> dl_stid .sc_count );
1374- list_add (& dp -> dl_recall_lru , & clp -> cl_revoked );
1375- spin_unlock (& clp -> cl_lock );
1394+ spin_lock (& clp -> cl_lock );
1395+ if (dp -> dl_stid .sc_status & SC_STATUS_FREED ) {
1396+ list_del_init (& dp -> dl_recall_lru );
1397+ goto out ;
13761398 }
1399+ list_add (& dp -> dl_recall_lru , & clp -> cl_revoked );
1400+ dp -> dl_stid .sc_status |= SC_STATUS_FREEABLE ;
1401+ out :
1402+ spin_unlock (& clp -> cl_lock );
13771403 destroy_unhashed_deleg (dp );
13781404}
13791405
@@ -1780,6 +1806,7 @@ void nfsd4_revoke_states(struct net *net, struct super_block *sb)
17801806 mutex_unlock (& stp -> st_mutex );
17811807 break ;
17821808 case SC_TYPE_DELEG :
1809+ refcount_inc (& stid -> sc_count );
17831810 dp = delegstateid (stid );
17841811 spin_lock (& state_lock );
17851812 if (!unhash_delegation_locked (
@@ -6545,6 +6572,7 @@ nfs4_laundromat(struct nfsd_net *nn)
65456572 dp = list_entry (pos , struct nfs4_delegation , dl_recall_lru );
65466573 if (!state_expired (& lt , dp -> dl_time ))
65476574 break ;
6575+ refcount_inc (& dp -> dl_stid .sc_count );
65486576 unhash_delegation_locked (dp , SC_STATUS_REVOKED );
65496577 list_add (& dp -> dl_recall_lru , & reaplist );
65506578 }
@@ -7157,7 +7185,9 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
71577185 s -> sc_status |= SC_STATUS_CLOSED ;
71587186 spin_unlock (& s -> sc_lock );
71597187 dp = delegstateid (s );
7160- list_del_init (& dp -> dl_recall_lru );
7188+ if (s -> sc_status & SC_STATUS_FREEABLE )
7189+ list_del_init (& dp -> dl_recall_lru );
7190+ s -> sc_status |= SC_STATUS_FREED ;
71617191 spin_unlock (& cl -> cl_lock );
71627192 nfs4_put_stid (s );
71637193 ret = nfs_ok ;
@@ -7487,7 +7517,9 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
74877517 if ((status = fh_verify (rqstp , & cstate -> current_fh , S_IFREG , 0 )))
74887518 return status ;
74897519
7490- status = nfsd4_lookup_stateid (cstate , stateid , SC_TYPE_DELEG , 0 , & s , nn );
7520+ status = nfsd4_lookup_stateid (cstate , stateid , SC_TYPE_DELEG ,
7521+ SC_STATUS_REVOKED | SC_STATUS_FREEABLE ,
7522+ & s , nn );
74917523 if (status )
74927524 goto out ;
74937525 dp = delegstateid (s );
@@ -8684,7 +8716,7 @@ nfs4_state_shutdown_net(struct net *net)
86848716 struct nfsd_net * nn = net_generic (net , nfsd_net_id );
86858717
86868718 shrinker_free (nn -> nfsd_client_shrinker );
8687- cancel_work (& nn -> nfsd_shrinker_work );
8719+ cancel_work_sync (& nn -> nfsd_shrinker_work );
86888720 cancel_delayed_work_sync (& nn -> laundromat_work );
86898721 locks_end_grace (& nn -> nfsd4_manager );
86908722
0 commit comments