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
/* 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);
#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 */
--- /dev/null
+/* 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;
+}
.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,
+};
/* 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 *);
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 *);
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)
{
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;
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
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;
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 = {
}
-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,
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());