[PATCH 09/21] pNFS: Add a reresolve_deviceid layout driver hook
Benjamin Coddington <ben.coddington-F/[email protected]>
| Newsgroups | gmane.linux.nfs |
|---|---|
| Message-ID | <84ed176fdf6b301e87e45fab837683f8683cbe40.1786653063.git.bcodding@hammerspace.com> |
RFC 8881 Section 12.2.10 lets a server change a deviceid's mapping under live layouts by sending CB_NOTIFY_DEVICEID CHANGE instead of recalling the layouts, but the client's only response today is to unhash the cached device, which never reaches references pinned inside a layout driver's segments. Add a reresolve_deviceid hook to pnfs_layoutdriver_type and a generic driver, pnfs_layout_reresolve_deviceid_byclid(), that walks every layout on every server of the client and invokes the hook under the layout inode's i_lock. Because the final put of a device node can sleep (it may tear down the DS nfs_client), the hook must not drop references itself: it collects the nodes it un-pins on a list through the new nfs4_deviceid_node.put_list member, and the generic driver puts them once all locks are dropped. No driver implements the hook yet, and nothing calls the walker: no behavior change. Assisted-by: Claude:claude-fable-5 Signed-off-by: Benjamin Coddington <bcodding-F/[email protected]> --- fs/nfs/pnfs.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++ fs/nfs/pnfs.h | 19 ++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index a21128321c0a..3020eee50918 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -2876,6 +2876,74 @@ pnfs_layout_return_unused_byclid(struct nfs_client *clp, &range); } +struct pnfs_reresolve_deviceid_args { + const struct pnfs_layoutdriver_type *ld; + const struct nfs4_deviceid *id; + bool immediate; + struct list_head put_list; +}; + +static int pnfs_layout_reresolve_deviceid_byserver(struct nfs_server *server, + void *data) +{ + struct pnfs_reresolve_deviceid_args *args = data; + struct pnfs_layout_hdr *lo; + struct inode *inode; + + if (server->pnfs_curr_ld != args->ld) + return 0; + + rcu_read_lock(); + list_for_each_entry_rcu(lo, &server->layouts, plh_layouts) { + inode = lo->plh_inode; + if (!inode) + continue; + spin_lock(&inode->i_lock); + if (lo->plh_inode == inode && pnfs_layout_is_valid(lo)) + args->ld->reresolve_deviceid(lo, args->id, + args->immediate, + &args->put_list); + spin_unlock(&inode->i_lock); + } + rcu_read_unlock(); + return 0; +} + +/* + * pnfs_layout_reresolve_deviceid_byclid - re-point live layouts at a + * changed deviceid mapping (CB_NOTIFY_DEVICEID CHANGE) + * + * Walk every layout of @clp's servers using @ld and invoke the driver's + * reresolve_deviceid hook for @id. The hook runs under each layout + * inode's i_lock and defers its device node puts to the put_list, which + * is drained here once all locks are dropped (the final put can sleep). + */ +void +pnfs_layout_reresolve_deviceid_byclid(struct nfs_client *clp, + const struct pnfs_layoutdriver_type *ld, + const struct nfs4_deviceid *id, + bool immediate) +{ + struct pnfs_reresolve_deviceid_args args = { + .ld = ld, + .id = id, + .immediate = immediate, + .put_list = LIST_HEAD_INIT(args.put_list), + }; + struct nfs4_deviceid_node *node, *tmp; + + if (!ld->reresolve_deviceid) + return; + + nfs_client_for_each_server(clp, + pnfs_layout_reresolve_deviceid_byserver, &args); + + list_for_each_entry_safe(node, tmp, &args.put_list, put_list) { + list_del(&node->put_list); + nfs4_put_deviceid_node(node); + } +} + /* Check if we have we have a valid layout but if there isn't an intersection * between the request and the pgio->pg_lseg, put this pgio->pg_lseg away. */ diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index bdce7f930c6a..9ec0ffebe3c4 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -170,6 +170,17 @@ struct pnfs_layoutdriver_type { struct nfs4_deviceid_node * (*alloc_deviceid_node) (struct nfs_server *server, struct pnfs_device *pdev, gfp_t gfp_flags); + /* + * Re-resolve any of @lo's references to the changed deviceid @id + * (CB_NOTIFY_DEVICEID CHANGE). Called under @lo's inode i_lock; + * must not sleep. Device node references given up here must be + * collected on @put_list (via node->put_list) for the caller to + * put once all locks are dropped. + */ + void (*reresolve_deviceid)(struct pnfs_layout_hdr *lo, + const struct nfs4_deviceid *id, + bool immediate, + struct list_head *put_list); int (*prepare_layoutreturn) (struct nfs4_layoutreturn_args *); @@ -353,6 +364,10 @@ void pnfs_error_mark_layout_for_return(struct inode *inode, struct pnfs_layout_segment *lseg); void pnfs_layout_return_unused_byclid(struct nfs_client *clp, enum pnfs_iomode iomode); +void pnfs_layout_reresolve_deviceid_byclid(struct nfs_client *clp, + const struct pnfs_layoutdriver_type *ld, + const struct nfs4_deviceid *id, + bool immediate); int pnfs_layout_handle_reboot(struct nfs_client *clp); /* nfs4_deviceid_flags */ @@ -373,6 +388,10 @@ struct nfs4_deviceid_node { struct nfs4_deviceid deviceid; struct rcu_head rcu; atomic_t ref; + /* deferred-put linkage for in-place re-resolve; owned by whoever + * exchanged this node out of its pinning pointer + */ + struct list_head put_list; }; struct nfs4_deviceid_node * -- 2.53.0