mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
NFSv4/pnfs: Confirm a deviceID delete via GETDEVICEINFO
RFC 8881 Section 18.40.4: if TEST_STATEID says at least one layout referring to the deleted deviceID is still valid, the delete cannot be trusted -- verify it with GETDEVICEINFO. The device really being gone while the server also considers a referring layout valid means the server is faulty; recover by re-establishing the client ID and drop the cached device. Any other answer -- including the device existing, i.e. an erroneous DELETE -- keeps the cached device and the layout intact. Re-establishing the client ID is nfs4_reset_all_state(), which sets NFS4CLNT_PURGE_STATE so the state manager runs nfs4_purge_lease(): a fresh EXCHANGE_ID, then state reclaim with no grace period. The grace-less reclaim is the point -- the server has not rebooted, so there is nothing to reclaim under CLAIM_PREVIOUS, and the new client ID orphans the state held under the old one. The obvious-looking nfs4_schedule_lease_recovery() is not the right call here: it sets NFS4CLNT_CHECK_LEASE, which the state manager turns into a lease renewal, and on a healthy session -- which this one is, the server having just answered TEST_STATEID and GETDEVICEINFO on it -- that renewal succeeds and no EXCHANGE_ID is ever sent. This is the only path on which a device notification can escalate to a full client-ID reset, and every open, lock and delegation on the client is reclaimed as a result. From userspace that is indistinguishable from a spontaneous lease expiry, so the escalation is announced with a rate-limited warning naming the server. The raw-status probe calls nfs4_proc_getdeviceinfo() directly because nfs4_get_device_info() swallows the RPC status and cannot distinguish NFS4ERR_NOENT from a transient failure. A one-page reply buffer is enough: a device too large for it fails with something other than -ENOENT, which still proves existence. Still nothing enqueues suspects; no behavior change. Assisted-by: Claude:claude-fable-5 Signed-off-by: Benjamin Coddington <bcodding@hammerspace.com> Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
This commit is contained in:
parent
59b065e645
commit
14286f49f0
|
|
@ -511,6 +511,7 @@ extern void nfs_inode_find_state_and_recover(struct inode *inode,
|
|||
const nfs4_stateid *stateid);
|
||||
extern int nfs4_state_mark_reclaim_nograce(struct nfs_client *, struct nfs4_state *);
|
||||
extern void nfs4_schedule_lease_recovery(struct nfs_client *);
|
||||
extern void nfs4_reset_all_state(struct nfs_client *);
|
||||
extern int nfs4_wait_clnt_recover(struct nfs_client *clp);
|
||||
extern int nfs4_client_recover_expired_lease(struct nfs_client *clp);
|
||||
extern void nfs4_schedule_state_manager(struct nfs_client *);
|
||||
|
|
|
|||
|
|
@ -10506,19 +10506,50 @@ static int nfs41_free_stateid(struct nfs_server *server,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* GETDEVICEINFO surfacing the raw status; nfs4_get_device_info()
|
||||
* swallows it. A device too large for one page fails with something
|
||||
* other than -ENOENT, which still proves existence.
|
||||
*/
|
||||
static int nfs4_deviceid_validate(struct nfs_server *server,
|
||||
const struct pnfs_layoutdriver_type *ld,
|
||||
const struct nfs4_deviceid *id, const struct cred *cred)
|
||||
{
|
||||
struct pnfs_device pdev;
|
||||
struct page *page;
|
||||
int status;
|
||||
|
||||
page = alloc_page(GFP_KERNEL);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(&pdev, 0, sizeof(pdev));
|
||||
memcpy(&pdev.dev_id, id, sizeof(pdev.dev_id));
|
||||
pdev.layout_type = ld->id;
|
||||
pdev.pages = &page;
|
||||
pdev.pglen = PAGE_SIZE;
|
||||
pdev.maxcount = PAGE_SIZE - nfs41_maxgetdevinfo_overhead;
|
||||
|
||||
status = nfs4_proc_getdeviceinfo(server, &pdev, cred);
|
||||
__free_page(page);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* A DELETE for a deviceID we still hold layouts on implies the server
|
||||
* revoked them: run the RFC 8881 Section 18.40.4 recovery.
|
||||
* revoked them: run the RFC 8881 Section 18.40.4 recovery. A layout the
|
||||
* server still calls valid leaves the revocations unable to confirm the
|
||||
* delete, so verify it with GETDEVICEINFO.
|
||||
*/
|
||||
static void nfs4_deviceid_delete_recover(struct nfs_client *clp,
|
||||
const struct pnfs_layoutdriver_type *ld,
|
||||
const struct nfs4_deviceid *id)
|
||||
{
|
||||
LIST_HEAD(layouts);
|
||||
struct nfs4_deviceid_ref *ref;
|
||||
struct nfs4_deviceid_ref *ref, *confirm = NULL;
|
||||
bool revoked = false;
|
||||
bool referenced = false;
|
||||
bool inconclusive = false;
|
||||
int status;
|
||||
|
||||
if (pnfs_layout_collect_deviceid_refs(clp, ld, id, &layouts)) {
|
||||
/* Only a partial list -- an allocation failed, or an inode is
|
||||
|
|
@ -10539,14 +10570,14 @@ static void nfs4_deviceid_delete_recover(struct nfs_client *clp,
|
|||
struct inode *inode = ref->inode;
|
||||
bool invalidated = false;
|
||||
LIST_HEAD(head);
|
||||
int status;
|
||||
|
||||
status = nfs41_test_stateid(NFS_SERVER(inode), &ref->stateid,
|
||||
ref->cred);
|
||||
switch (status) {
|
||||
case NFS_OK:
|
||||
case -NFS4ERR_OLD_STATEID:
|
||||
referenced = true;
|
||||
if (!confirm)
|
||||
confirm = ref;
|
||||
break;
|
||||
case -NFS4ERR_ADMIN_REVOKED:
|
||||
case -NFS4ERR_DELEG_REVOKED:
|
||||
|
|
@ -10572,10 +10603,24 @@ static void nfs4_deviceid_delete_recover(struct nfs_client *clp,
|
|||
break;
|
||||
}
|
||||
}
|
||||
pnfs_layout_put_deviceid_refs(&layouts);
|
||||
|
||||
if (revoked && !referenced && !inconclusive)
|
||||
if (confirm) {
|
||||
status = nfs4_deviceid_validate(NFS_SERVER(confirm->inode),
|
||||
ld, id, confirm->cred);
|
||||
if (status == -ENOENT) {
|
||||
/* Section 18.40.4 prescribes EXCHANGE_ID here;
|
||||
* nfs4_schedule_lease_recovery() would only renew
|
||||
* the existing lease.
|
||||
*/
|
||||
pr_warn_ratelimited("NFS: server %s deleted a deviceID referred to by a layout it still considers valid; re-establishing the client ID\n",
|
||||
clp->cl_hostname);
|
||||
nfs4_reset_all_state(clp);
|
||||
nfs4_delete_deviceid(ld, clp, id);
|
||||
}
|
||||
} else if (revoked && !inconclusive) {
|
||||
nfs4_delete_deviceid(ld, clp, id);
|
||||
}
|
||||
pnfs_layout_put_deviceid_refs(&layouts);
|
||||
}
|
||||
|
||||
void nfs4_deviceid_delete_recover_run(struct nfs_client *clp)
|
||||
|
|
|
|||
|
|
@ -2354,7 +2354,7 @@ void nfs41_notify_server(struct nfs_client *clp)
|
|||
nfs4_schedule_state_manager(clp);
|
||||
}
|
||||
|
||||
static void nfs4_reset_all_state(struct nfs_client *clp)
|
||||
void nfs4_reset_all_state(struct nfs_client *clp)
|
||||
{
|
||||
if (test_and_set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) {
|
||||
set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user