From c96c05fcfe395e93f738d924dfd40571ca4302ca Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Thu, 13 Nov 2025 11:10:17 -0500 Subject: [PATCH] NFS: Split out the nfs40_reboot_recovery_ops into nfs40client.c Signed-off-by: Anna Schumaker --- fs/nfs/Makefile | 2 +- fs/nfs/internal.h | 3 - fs/nfs/nfs40.h | 7 ++ fs/nfs/nfs40client.c | 186 +++++++++++++++++++++++++++++++++++++++++++ fs/nfs/nfs40proc.c | 9 +++ fs/nfs/nfs4_fs.h | 7 ++ fs/nfs/nfs4client.c | 132 +----------------------------- fs/nfs/nfs4proc.c | 13 +-- fs/nfs/nfs4state.c | 49 ------------ 9 files changed, 214 insertions(+), 194 deletions(-) create mode 100644 fs/nfs/nfs40client.c diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 937c775a04a3d..d05e69c00fe1e 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -27,7 +27,7 @@ CFLAGS_nfs4trace.o += -I$(src) nfsv4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \ delegation.o nfs4idmap.o callback.o callback_xdr.o callback_proc.o \ nfs4namespace.o nfs4getroot.o nfs4client.o nfs4session.o \ - dns_resolve.o nfs4trace.o nfs40proc.o + dns_resolve.o nfs4trace.o nfs40proc.o nfs40client.o nfsv4-$(CONFIG_NFS_USE_LEGACY_DNS) += cache_lib.o nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o nfsv4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o pnfs_nfs.o diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 2e596244799f3..e99998e515c03 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -739,9 +739,6 @@ extern ssize_t nfs_dreq_bytes_left(struct nfs_direct_req *dreq, loff_t offset); /* nfs4proc.c */ extern struct nfs_client *nfs4_init_client(struct nfs_client *clp, const struct nfs_client_initdata *); -extern int nfs40_walk_client_list(struct nfs_client *clp, - struct nfs_client **result, - const struct cred *cred); extern int nfs41_walk_client_list(struct nfs_client *clp, struct nfs_client **result, const struct cred *cred); diff --git a/fs/nfs/nfs40.h b/fs/nfs/nfs40.h index 58a59109987a9..ad57172b49a3a 100644 --- a/fs/nfs/nfs40.h +++ b/fs/nfs/nfs40.h @@ -3,6 +3,13 @@ #define __LINUX_FS_NFS_NFS4_0_H +/* nfs40proc.c */ extern const struct rpc_call_ops nfs40_call_sync_ops; +extern const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops; + +/* nfs40state.c */ +int nfs40_discover_server_trunking(struct nfs_client *clp, + struct nfs_client **result, + const struct cred *cred); #endif /* __LINUX_FS_NFS_NFS4_0_H */ diff --git a/fs/nfs/nfs40client.c b/fs/nfs/nfs40client.c new file mode 100644 index 0000000000000..fae4ff584b1bb --- /dev/null +++ b/fs/nfs/nfs40client.c @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include "nfs4_fs.h" +#include "callback.h" +#include "internal.h" +#include "netns.h" +#include "nfs40.h" + +#define NFSDBG_FACILITY NFSDBG_CLIENT + +/* + * SETCLIENTID just did a callback update with the callback ident in + * "drop," but server trunking discovery claims "drop" and "keep" are + * actually the same server. Swap the callback IDs so that "keep" + * will continue to use the callback ident the server now knows about, + * and so that "keep"'s original callback ident is destroyed when + * "drop" is freed. + */ +static void nfs4_swap_callback_idents(struct nfs_client *keep, + struct nfs_client *drop) +{ + struct nfs_net *nn = net_generic(keep->cl_net, nfs_net_id); + unsigned int save = keep->cl_cb_ident; + + if (keep->cl_cb_ident == drop->cl_cb_ident) + return; + + dprintk("%s: keeping callback ident %u and dropping ident %u\n", + __func__, keep->cl_cb_ident, drop->cl_cb_ident); + + spin_lock(&nn->nfs_client_lock); + + idr_replace(&nn->cb_ident_idr, keep, drop->cl_cb_ident); + keep->cl_cb_ident = drop->cl_cb_ident; + + idr_replace(&nn->cb_ident_idr, drop, save); + drop->cl_cb_ident = save; + + spin_unlock(&nn->nfs_client_lock); +} + +static bool nfs4_same_verifier(nfs4_verifier *v1, nfs4_verifier *v2) +{ + return memcmp(v1->data, v2->data, sizeof(v1->data)) == 0; +} + +/** + * nfs40_walk_client_list - Find server that recognizes a client ID + * + * @new: nfs_client with client ID to test + * @result: OUT: found nfs_client, or new + * @cred: credential to use for trunking test + * + * Returns zero, a negative errno, or a negative NFS4ERR status. + * If zero is returned, an nfs_client pointer is planted in "result." + * + * NB: nfs40_walk_client_list() relies on the new nfs_client being + * the last nfs_client on the list. + */ +static int nfs40_walk_client_list(struct nfs_client *new, + struct nfs_client **result, + const struct cred *cred) +{ + struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id); + struct nfs_client *pos, *prev = NULL; + struct nfs4_setclientid_res clid = { + .clientid = new->cl_clientid, + .confirm = new->cl_confirm, + }; + int status = -NFS4ERR_STALE_CLIENTID; + + spin_lock(&nn->nfs_client_lock); + list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) { + + if (pos == new) + goto found; + + status = nfs4_match_client(pos, new, &prev, nn); + if (status < 0) + goto out_unlock; + if (status != 0) + continue; + /* + * We just sent a new SETCLIENTID, which should have + * caused the server to return a new cl_confirm. So if + * cl_confirm is the same, then this is a different + * server that just returned the same cl_confirm by + * coincidence: + */ + if ((new != pos) && nfs4_same_verifier(&pos->cl_confirm, + &new->cl_confirm)) + continue; + /* + * But if the cl_confirm's are different, then the only + * way that a SETCLIENTID_CONFIRM to pos can succeed is + * if new and pos point to the same server: + */ +found: + refcount_inc(&pos->cl_count); + spin_unlock(&nn->nfs_client_lock); + + nfs_put_client(prev); + prev = pos; + + status = nfs4_proc_setclientid_confirm(pos, &clid, cred); + switch (status) { + case -NFS4ERR_STALE_CLIENTID: + break; + case 0: + nfs4_swap_callback_idents(pos, new); + pos->cl_confirm = new->cl_confirm; + nfs_mark_client_ready(pos, NFS_CS_READY); + + prev = NULL; + *result = pos; + goto out; + case -ERESTARTSYS: + case -ETIMEDOUT: + /* The callback path may have been inadvertently + * changed. Schedule recovery! + */ + nfs4_schedule_path_down_recovery(pos); + goto out; + default: + goto out; + } + + spin_lock(&nn->nfs_client_lock); + } +out_unlock: + spin_unlock(&nn->nfs_client_lock); + + /* No match found. The server lost our clientid */ +out: + nfs_put_client(prev); + return status; +} + +/** + * nfs40_discover_server_trunking - Detect server IP address trunking (mv0) + * + * @clp: nfs_client under test + * @result: OUT: found nfs_client, or clp + * @cred: credential to use for trunking test + * + * Returns zero, a negative errno, or a negative NFS4ERR status. + * If zero is returned, an nfs_client pointer is planted in + * "result". + * + * Note: The returned client may not yet be marked ready. + */ +int nfs40_discover_server_trunking(struct nfs_client *clp, + struct nfs_client **result, + const struct cred *cred) +{ + struct nfs4_setclientid_res clid = { + .clientid = clp->cl_clientid, + .confirm = clp->cl_confirm, + }; + struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); + unsigned short port; + int status; + + port = nn->nfs_callback_tcpport; + if (clp->cl_addr.ss_family == AF_INET6) + port = nn->nfs_callback_tcpport6; + + status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid); + if (status != 0) + goto out; + clp->cl_clientid = clid.clientid; + clp->cl_confirm = clid.confirm; + + status = nfs40_walk_client_list(clp, result, cred); + if (status == 0) { + /* Sustain the lease, even if it's empty. If the clientid4 + * goes stale it's of no use for trunking discovery. */ + nfs4_schedule_state_renewal(*result); + + /* If the client state need to recover, do it. */ + if (clp->cl_state) + nfs4_schedule_state_manager(clp); + } +out: + return status; +} diff --git a/fs/nfs/nfs40proc.c b/fs/nfs/nfs40proc.c index 6d27dedad0556..b2cc7519b2265 100644 --- a/fs/nfs/nfs40proc.c +++ b/fs/nfs/nfs40proc.c @@ -22,3 +22,12 @@ const struct rpc_call_ops nfs40_call_sync_ops = { .rpc_call_prepare = nfs40_call_sync_prepare, .rpc_call_done = nfs40_call_sync_done, }; + +const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = { + .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT, + .state_flag_bit = NFS_STATE_RECLAIM_REBOOT, + .recover_open = nfs4_open_reclaim, + .recover_lock = nfs4_lock_reclaim, + .establish_clid = nfs4_init_clientid, + .detect_trunking = nfs40_discover_server_trunking, +}; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 575343261c9a8..246a43d172f99 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -277,6 +277,11 @@ int nfs_atomic_open(struct inode *, struct dentry *, struct file *, /* fs_context.c */ extern struct file_system_type nfs4_fs_type; +/* nfs4client.c */ +struct nfs_net; +int nfs4_match_client(struct nfs_client *pos, struct nfs_client *new, + struct nfs_client **prev, struct nfs_net *nn); + /* nfs4namespace.c */ struct rpc_clnt *nfs4_negotiate_security(struct rpc_clnt *, struct inode *, const struct qstr *); @@ -345,6 +350,8 @@ extern void nfs4_update_changeattr(struct inode *dir, unsigned long cache_validity); extern int nfs4_buf_to_pages_noslab(const void *buf, size_t buflen, struct page **pages); +extern int nfs4_open_reclaim(struct nfs4_state_owner *, struct nfs4_state *); +extern int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request); #if defined(CONFIG_NFS_V4_1) extern int nfs41_sequence_done(struct rpc_task *, struct nfs4_sequence_res *); diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 96bccefbe2cb2..517cf8af29431 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -490,37 +490,6 @@ error: return ERR_PTR(error); } -/* - * SETCLIENTID just did a callback update with the callback ident in - * "drop," but server trunking discovery claims "drop" and "keep" are - * actually the same server. Swap the callback IDs so that "keep" - * will continue to use the callback ident the server now knows about, - * and so that "keep"'s original callback ident is destroyed when - * "drop" is freed. - */ -static void nfs4_swap_callback_idents(struct nfs_client *keep, - struct nfs_client *drop) -{ - struct nfs_net *nn = net_generic(keep->cl_net, nfs_net_id); - unsigned int save = keep->cl_cb_ident; - - if (keep->cl_cb_ident == drop->cl_cb_ident) - return; - - dprintk("%s: keeping callback ident %u and dropping ident %u\n", - __func__, keep->cl_cb_ident, drop->cl_cb_ident); - - spin_lock(&nn->nfs_client_lock); - - idr_replace(&nn->cb_ident_idr, keep, drop->cl_cb_ident); - keep->cl_cb_ident = drop->cl_cb_ident; - - idr_replace(&nn->cb_ident_idr, drop, save); - drop->cl_cb_ident = save; - - spin_unlock(&nn->nfs_client_lock); -} - static bool nfs4_match_client_owner_id(const struct nfs_client *clp1, const struct nfs_client *clp2) { @@ -529,13 +498,8 @@ static bool nfs4_match_client_owner_id(const struct nfs_client *clp1, return strcmp(clp1->cl_owner_id, clp2->cl_owner_id) == 0; } -static bool nfs4_same_verifier(nfs4_verifier *v1, nfs4_verifier *v2) -{ - return memcmp(v1->data, v2->data, sizeof(v1->data)) == 0; -} - -static int nfs4_match_client(struct nfs_client *pos, struct nfs_client *new, - struct nfs_client **prev, struct nfs_net *nn) +int nfs4_match_client(struct nfs_client *pos, struct nfs_client *new, + struct nfs_client **prev, struct nfs_net *nn) { int status; @@ -578,98 +542,6 @@ static int nfs4_match_client(struct nfs_client *pos, struct nfs_client *new, return 0; } -/** - * nfs40_walk_client_list - Find server that recognizes a client ID - * - * @new: nfs_client with client ID to test - * @result: OUT: found nfs_client, or new - * @cred: credential to use for trunking test - * - * Returns zero, a negative errno, or a negative NFS4ERR status. - * If zero is returned, an nfs_client pointer is planted in "result." - * - * NB: nfs40_walk_client_list() relies on the new nfs_client being - * the last nfs_client on the list. - */ -int nfs40_walk_client_list(struct nfs_client *new, - struct nfs_client **result, - const struct cred *cred) -{ - struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id); - struct nfs_client *pos, *prev = NULL; - struct nfs4_setclientid_res clid = { - .clientid = new->cl_clientid, - .confirm = new->cl_confirm, - }; - int status = -NFS4ERR_STALE_CLIENTID; - - spin_lock(&nn->nfs_client_lock); - list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) { - - if (pos == new) - goto found; - - status = nfs4_match_client(pos, new, &prev, nn); - if (status < 0) - goto out_unlock; - if (status != 0) - continue; - /* - * We just sent a new SETCLIENTID, which should have - * caused the server to return a new cl_confirm. So if - * cl_confirm is the same, then this is a different - * server that just returned the same cl_confirm by - * coincidence: - */ - if ((new != pos) && nfs4_same_verifier(&pos->cl_confirm, - &new->cl_confirm)) - continue; - /* - * But if the cl_confirm's are different, then the only - * way that a SETCLIENTID_CONFIRM to pos can succeed is - * if new and pos point to the same server: - */ -found: - refcount_inc(&pos->cl_count); - spin_unlock(&nn->nfs_client_lock); - - nfs_put_client(prev); - prev = pos; - - status = nfs4_proc_setclientid_confirm(pos, &clid, cred); - switch (status) { - case -NFS4ERR_STALE_CLIENTID: - break; - case 0: - nfs4_swap_callback_idents(pos, new); - pos->cl_confirm = new->cl_confirm; - nfs_mark_client_ready(pos, NFS_CS_READY); - - prev = NULL; - *result = pos; - goto out; - case -ERESTARTSYS: - case -ETIMEDOUT: - /* The callback path may have been inadvertently - * changed. Schedule recovery! - */ - nfs4_schedule_path_down_recovery(pos); - goto out; - default: - goto out; - } - - spin_lock(&nn->nfs_client_lock); - } -out_unlock: - spin_unlock(&nn->nfs_client_lock); - - /* No match found. The server lost our clientid */ -out: - nfs_put_client(prev); - return status; -} - #ifdef CONFIG_NFS_V4_1 /* * Returns true if the server major ids match diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index fbbc4e37b4b16..e3047bd989a51 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2331,7 +2331,7 @@ static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state return err; } -static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) +int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) { struct nfs_open_context *ctx; int ret; @@ -7649,7 +7649,7 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f return ret; } -static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request) +int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request) { struct nfs_server *server = NFS_SERVER(state->inode); struct nfs4_exception exception = { @@ -10804,15 +10804,6 @@ static bool nfs4_match_stateid(const nfs4_stateid *s1, } -static const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = { - .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT, - .state_flag_bit = NFS_STATE_RECLAIM_REBOOT, - .recover_open = nfs4_open_reclaim, - .recover_lock = nfs4_lock_reclaim, - .establish_clid = nfs4_init_clientid, - .detect_trunking = nfs40_discover_server_trunking, -}; - #if defined(CONFIG_NFS_V4_1) static const struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = { .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT, diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index dba51c622cf36..ead4d1ac4d0bb 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -142,55 +142,6 @@ out: return status; } -/** - * nfs40_discover_server_trunking - Detect server IP address trunking (mv0) - * - * @clp: nfs_client under test - * @result: OUT: found nfs_client, or clp - * @cred: credential to use for trunking test - * - * Returns zero, a negative errno, or a negative NFS4ERR status. - * If zero is returned, an nfs_client pointer is planted in - * "result". - * - * Note: The returned client may not yet be marked ready. - */ -int nfs40_discover_server_trunking(struct nfs_client *clp, - struct nfs_client **result, - const struct cred *cred) -{ - struct nfs4_setclientid_res clid = { - .clientid = clp->cl_clientid, - .confirm = clp->cl_confirm, - }; - struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); - unsigned short port; - int status; - - port = nn->nfs_callback_tcpport; - if (clp->cl_addr.ss_family == AF_INET6) - port = nn->nfs_callback_tcpport6; - - status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid); - if (status != 0) - goto out; - clp->cl_clientid = clid.clientid; - clp->cl_confirm = clid.confirm; - - status = nfs40_walk_client_list(clp, result, cred); - if (status == 0) { - /* Sustain the lease, even if it's empty. If the clientid4 - * goes stale it's of no use for trunking discovery. */ - nfs4_schedule_state_renewal(*result); - - /* If the client state need to recover, do it. */ - if (clp->cl_state) - nfs4_schedule_state_manager(clp); - } -out: - return status; -} - const struct cred *nfs4_get_machine_cred(struct nfs_client *clp) { return get_cred(rpc_machine_cred()); -- 2.47.3