Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'nfsd-6.12-2' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux

Pull nfsd fixes from Chuck Lever:

- Fix a couple of use-after-free bugs

* tag 'nfsd-6.12-2' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux:
nfsd: cancel nfsd_shrinker_work using sync mode in nfs4_state_shutdown_net
nfsd: fix race between laundromat and free_stateid

+43 -9
+41 -9
fs/nfsd/nfs4state.c
··· 1359 1359 destroy_unhashed_deleg(dp); 1360 1360 } 1361 1361 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 + */ 1362 1384 static void revoke_delegation(struct nfs4_delegation *dp) 1363 1385 { 1364 1386 struct nfs4_client *clp = dp->dl_stid.sc_client; 1365 1387 1366 1388 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))); 1367 1391 1368 1392 trace_nfsd_stid_revoke(&dp->dl_stid); 1369 1393 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; 1376 1398 } 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); 1377 1403 destroy_unhashed_deleg(dp); 1378 1404 } 1379 1405 ··· 1806 1780 mutex_unlock(&stp->st_mutex); 1807 1781 break; 1808 1782 case SC_TYPE_DELEG: 1783 + refcount_inc(&stid->sc_count); 1809 1784 dp = delegstateid(stid); 1810 1785 spin_lock(&state_lock); 1811 1786 if (!unhash_delegation_locked( ··· 6572 6545 dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); 6573 6546 if (!state_expired(&lt, dp->dl_time)) 6574 6547 break; 6548 + refcount_inc(&dp->dl_stid.sc_count); 6575 6549 unhash_delegation_locked(dp, SC_STATUS_REVOKED); 6576 6550 list_add(&dp->dl_recall_lru, &reaplist); 6577 6551 } ··· 7185 7157 s->sc_status |= SC_STATUS_CLOSED; 7186 7158 spin_unlock(&s->sc_lock); 7187 7159 dp = delegstateid(s); 7188 - list_del_init(&dp->dl_recall_lru); 7160 + if (s->sc_status & SC_STATUS_FREEABLE) 7161 + list_del_init(&dp->dl_recall_lru); 7162 + s->sc_status |= SC_STATUS_FREED; 7189 7163 spin_unlock(&cl->cl_lock); 7190 7164 nfs4_put_stid(s); 7191 7165 ret = nfs_ok; ··· 7517 7487 if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) 7518 7488 return status; 7519 7489 7520 - status = nfsd4_lookup_stateid(cstate, stateid, SC_TYPE_DELEG, 0, &s, nn); 7490 + status = nfsd4_lookup_stateid(cstate, stateid, SC_TYPE_DELEG, 7491 + SC_STATUS_REVOKED | SC_STATUS_FREEABLE, 7492 + &s, nn); 7521 7493 if (status) 7522 7494 goto out; 7523 7495 dp = delegstateid(s); ··· 8716 8684 struct nfsd_net *nn = net_generic(net, nfsd_net_id); 8717 8685 8718 8686 shrinker_free(nn->nfsd_client_shrinker); 8719 - cancel_work(&nn->nfsd_shrinker_work); 8687 + cancel_work_sync(&nn->nfsd_shrinker_work); 8720 8688 cancel_delayed_work_sync(&nn->laundromat_work); 8721 8689 locks_end_grace(&nn->nfsd4_manager); 8722 8690
+2
fs/nfsd/state.h
··· 114 114 /* For a deleg stateid kept around only to process free_stateid's: */ 115 115 #define SC_STATUS_REVOKED BIT(1) 116 116 #define SC_STATUS_ADMIN_REVOKED BIT(2) 117 + #define SC_STATUS_FREEABLE BIT(3) 118 + #define SC_STATUS_FREED BIT(4) 117 119 unsigned short sc_status; 118 120 119 121 struct list_head sc_cp_list;