Loading fs/nfs/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading fs/nfs/internal.h +0 −3 Original line number Diff line number Diff line Loading @@ -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); Loading fs/nfs/nfs40.h +7 −0 Original line number Diff line number Diff line Loading @@ -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 */ fs/nfs/nfs40client.c 0 → 100644 +186 −0 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0 */ #include <linux/nfs_fs.h> #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; } fs/nfs/nfs40proc.c +9 −0 Original line number Diff line number Diff line Loading @@ -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, }; Loading
fs/nfs/Makefile +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
fs/nfs/internal.h +0 −3 Original line number Diff line number Diff line Loading @@ -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); Loading
fs/nfs/nfs40.h +7 −0 Original line number Diff line number Diff line Loading @@ -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 */
fs/nfs/nfs40client.c 0 → 100644 +186 −0 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0 */ #include <linux/nfs_fs.h> #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; }
fs/nfs/nfs40proc.c +9 −0 Original line number Diff line number Diff line Loading @@ -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, };