162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
462306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/nfs_fs.h>
862306a36Sopenharmony_ci#include <linux/nfs_mount.h>
962306a36Sopenharmony_ci#include <linux/sunrpc/addr.h>
1062306a36Sopenharmony_ci#include <linux/sunrpc/auth.h>
1162306a36Sopenharmony_ci#include <linux/sunrpc/xprt.h>
1262306a36Sopenharmony_ci#include <linux/sunrpc/bc_xprt.h>
1362306a36Sopenharmony_ci#include <linux/sunrpc/rpc_pipe_fs.h>
1462306a36Sopenharmony_ci#include "internal.h"
1562306a36Sopenharmony_ci#include "callback.h"
1662306a36Sopenharmony_ci#include "delegation.h"
1762306a36Sopenharmony_ci#include "nfs4session.h"
1862306a36Sopenharmony_ci#include "nfs4idmap.h"
1962306a36Sopenharmony_ci#include "pnfs.h"
2062306a36Sopenharmony_ci#include "netns.h"
2162306a36Sopenharmony_ci#include "sysfs.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define NFSDBG_FACILITY		NFSDBG_CLIENT
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * Get a unique NFSv4.0 callback identifier which will be used
2762306a36Sopenharmony_ci * by the V4.0 callback service to lookup the nfs_client struct
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_cistatic int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	int ret = 0;
3262306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (clp->rpc_ops->version != 4 || minorversion != 0)
3562306a36Sopenharmony_ci		return ret;
3662306a36Sopenharmony_ci	idr_preload(GFP_KERNEL);
3762306a36Sopenharmony_ci	spin_lock(&nn->nfs_client_lock);
3862306a36Sopenharmony_ci	ret = idr_alloc(&nn->cb_ident_idr, clp, 1, 0, GFP_NOWAIT);
3962306a36Sopenharmony_ci	if (ret >= 0)
4062306a36Sopenharmony_ci		clp->cl_cb_ident = ret;
4162306a36Sopenharmony_ci	spin_unlock(&nn->nfs_client_lock);
4262306a36Sopenharmony_ci	idr_preload_end();
4362306a36Sopenharmony_ci	return ret < 0 ? ret : 0;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_1
4762306a36Sopenharmony_ci/*
4862306a36Sopenharmony_ci * Per auth flavor data server rpc clients
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_cistruct nfs4_ds_server {
5162306a36Sopenharmony_ci	struct list_head	list;   /* ds_clp->cl_ds_clients */
5262306a36Sopenharmony_ci	struct rpc_clnt		*rpc_clnt;
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/**
5662306a36Sopenharmony_ci * nfs4_find_ds_client - Common lookup case for DS I/O
5762306a36Sopenharmony_ci * @ds_clp: pointer to the DS's nfs_client
5862306a36Sopenharmony_ci * @flavor: rpc auth flavour to match
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_cistatic struct nfs4_ds_server *
6162306a36Sopenharmony_cinfs4_find_ds_client(struct nfs_client *ds_clp, rpc_authflavor_t flavor)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct nfs4_ds_server *dss;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	rcu_read_lock();
6662306a36Sopenharmony_ci	list_for_each_entry_rcu(dss, &ds_clp->cl_ds_clients, list) {
6762306a36Sopenharmony_ci		if (dss->rpc_clnt->cl_auth->au_flavor != flavor)
6862306a36Sopenharmony_ci			continue;
6962306a36Sopenharmony_ci		goto out;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci	dss = NULL;
7262306a36Sopenharmony_ciout:
7362306a36Sopenharmony_ci	rcu_read_unlock();
7462306a36Sopenharmony_ci	return dss;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic struct nfs4_ds_server *
7862306a36Sopenharmony_cinfs4_add_ds_client(struct nfs_client *ds_clp, rpc_authflavor_t flavor,
7962306a36Sopenharmony_ci			   struct nfs4_ds_server *new)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct nfs4_ds_server *dss;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	spin_lock(&ds_clp->cl_lock);
8462306a36Sopenharmony_ci	list_for_each_entry(dss, &ds_clp->cl_ds_clients, list) {
8562306a36Sopenharmony_ci		if (dss->rpc_clnt->cl_auth->au_flavor != flavor)
8662306a36Sopenharmony_ci			continue;
8762306a36Sopenharmony_ci		goto out;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	if (new)
9062306a36Sopenharmony_ci		list_add_rcu(&new->list, &ds_clp->cl_ds_clients);
9162306a36Sopenharmony_ci	dss = new;
9262306a36Sopenharmony_ciout:
9362306a36Sopenharmony_ci	spin_unlock(&ds_clp->cl_lock); /* need some lock to protect list */
9462306a36Sopenharmony_ci	return dss;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic struct nfs4_ds_server *
9862306a36Sopenharmony_cinfs4_alloc_ds_server(struct nfs_client *ds_clp, rpc_authflavor_t flavor)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct nfs4_ds_server *dss;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	dss = kmalloc(sizeof(*dss), GFP_NOFS);
10362306a36Sopenharmony_ci	if (dss == NULL)
10462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	dss->rpc_clnt = rpc_clone_client_set_auth(ds_clp->cl_rpcclient, flavor);
10762306a36Sopenharmony_ci	if (IS_ERR(dss->rpc_clnt)) {
10862306a36Sopenharmony_ci		int err = PTR_ERR(dss->rpc_clnt);
10962306a36Sopenharmony_ci		kfree (dss);
11062306a36Sopenharmony_ci		return ERR_PTR(err);
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci	INIT_LIST_HEAD(&dss->list);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return dss;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void
11862306a36Sopenharmony_cinfs4_free_ds_server(struct nfs4_ds_server *dss)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	rpc_release_client(dss->rpc_clnt);
12162306a36Sopenharmony_ci	kfree(dss);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/**
12562306a36Sopenharmony_ci * nfs4_find_or_create_ds_client - Find or create a DS rpc client
12662306a36Sopenharmony_ci * @ds_clp: pointer to the DS's nfs_client
12762306a36Sopenharmony_ci * @inode: pointer to the inode
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci * Find or create a DS rpc client with th MDS server rpc client auth flavor
13062306a36Sopenharmony_ci * in the nfs_client cl_ds_clients list.
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_cistruct rpc_clnt *
13362306a36Sopenharmony_cinfs4_find_or_create_ds_client(struct nfs_client *ds_clp, struct inode *inode)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct nfs4_ds_server *dss, *new;
13662306a36Sopenharmony_ci	rpc_authflavor_t flavor = NFS_SERVER(inode)->client->cl_auth->au_flavor;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	dss = nfs4_find_ds_client(ds_clp, flavor);
13962306a36Sopenharmony_ci	if (dss != NULL)
14062306a36Sopenharmony_ci		goto out;
14162306a36Sopenharmony_ci	new = nfs4_alloc_ds_server(ds_clp, flavor);
14262306a36Sopenharmony_ci	if (IS_ERR(new))
14362306a36Sopenharmony_ci		return ERR_CAST(new);
14462306a36Sopenharmony_ci	dss = nfs4_add_ds_client(ds_clp, flavor, new);
14562306a36Sopenharmony_ci	if (dss != new)
14662306a36Sopenharmony_ci		nfs4_free_ds_server(new);
14762306a36Sopenharmony_ciout:
14862306a36Sopenharmony_ci	return dss->rpc_clnt;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_find_or_create_ds_client);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic void
15362306a36Sopenharmony_cinfs4_shutdown_ds_clients(struct nfs_client *clp)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct nfs4_ds_server *dss;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	while (!list_empty(&clp->cl_ds_clients)) {
15862306a36Sopenharmony_ci		dss = list_entry(clp->cl_ds_clients.next,
15962306a36Sopenharmony_ci					struct nfs4_ds_server, list);
16062306a36Sopenharmony_ci		list_del(&dss->list);
16162306a36Sopenharmony_ci		rpc_shutdown_client(dss->rpc_clnt);
16262306a36Sopenharmony_ci		kfree (dss);
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic void
16762306a36Sopenharmony_cinfs4_cleanup_callback(struct nfs_client *clp)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct nfs4_copy_state *cp_state;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	while (!list_empty(&clp->pending_cb_stateids)) {
17262306a36Sopenharmony_ci		cp_state = list_entry(clp->pending_cb_stateids.next,
17362306a36Sopenharmony_ci					struct nfs4_copy_state, copies);
17462306a36Sopenharmony_ci		list_del(&cp_state->copies);
17562306a36Sopenharmony_ci		kfree(cp_state);
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_civoid nfs41_shutdown_client(struct nfs_client *clp)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	if (nfs4_has_session(clp)) {
18262306a36Sopenharmony_ci		nfs4_cleanup_callback(clp);
18362306a36Sopenharmony_ci		nfs4_shutdown_ds_clients(clp);
18462306a36Sopenharmony_ci		nfs4_destroy_session(clp->cl_session);
18562306a36Sopenharmony_ci		nfs4_destroy_clientid(clp);
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci#endif	/* CONFIG_NFS_V4_1 */
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_civoid nfs40_shutdown_client(struct nfs_client *clp)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	if (clp->cl_slot_tbl) {
19462306a36Sopenharmony_ci		nfs4_shutdown_slot_table(clp->cl_slot_tbl);
19562306a36Sopenharmony_ci		kfree(clp->cl_slot_tbl);
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistruct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	char buf[INET6_ADDRSTRLEN + 1];
20262306a36Sopenharmony_ci	const char *ip_addr = cl_init->ip_addr;
20362306a36Sopenharmony_ci	struct nfs_client *clp = nfs_alloc_client(cl_init);
20462306a36Sopenharmony_ci	int err;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (IS_ERR(clp))
20762306a36Sopenharmony_ci		return clp;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	err = nfs_get_cb_ident_idr(clp, cl_init->minorversion);
21062306a36Sopenharmony_ci	if (err)
21162306a36Sopenharmony_ci		goto error;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (cl_init->minorversion > NFS4_MAX_MINOR_VERSION) {
21462306a36Sopenharmony_ci		err = -EINVAL;
21562306a36Sopenharmony_ci		goto error;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	spin_lock_init(&clp->cl_lock);
21962306a36Sopenharmony_ci	INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
22062306a36Sopenharmony_ci	INIT_LIST_HEAD(&clp->cl_ds_clients);
22162306a36Sopenharmony_ci	rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
22262306a36Sopenharmony_ci	clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
22362306a36Sopenharmony_ci	clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion];
22462306a36Sopenharmony_ci	clp->cl_mig_gen = 1;
22562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NFS_V4_1)
22662306a36Sopenharmony_ci	init_waitqueue_head(&clp->cl_lock_waitq);
22762306a36Sopenharmony_ci#endif
22862306a36Sopenharmony_ci	INIT_LIST_HEAD(&clp->pending_cb_stateids);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (cl_init->minorversion != 0)
23162306a36Sopenharmony_ci		__set_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags);
23262306a36Sopenharmony_ci	__set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
23362306a36Sopenharmony_ci	__set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (test_bit(NFS_CS_DS, &cl_init->init_flags))
23662306a36Sopenharmony_ci		__set_bit(NFS_CS_DS, &clp->cl_flags);
23762306a36Sopenharmony_ci	/*
23862306a36Sopenharmony_ci	 * Set up the connection to the server before we add add to the
23962306a36Sopenharmony_ci	 * global list.
24062306a36Sopenharmony_ci	 */
24162306a36Sopenharmony_ci	err = nfs_create_rpc_client(clp, cl_init, RPC_AUTH_GSS_KRB5I);
24262306a36Sopenharmony_ci	if (err == -EINVAL)
24362306a36Sopenharmony_ci		err = nfs_create_rpc_client(clp, cl_init, RPC_AUTH_UNIX);
24462306a36Sopenharmony_ci	if (err < 0)
24562306a36Sopenharmony_ci		goto error;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* If no clientaddr= option was specified, find a usable cb address */
24862306a36Sopenharmony_ci	if (ip_addr == NULL) {
24962306a36Sopenharmony_ci		struct sockaddr_storage cb_addr;
25062306a36Sopenharmony_ci		struct sockaddr *sap = (struct sockaddr *)&cb_addr;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci		err = rpc_localaddr(clp->cl_rpcclient, sap, sizeof(cb_addr));
25362306a36Sopenharmony_ci		if (err < 0)
25462306a36Sopenharmony_ci			goto error;
25562306a36Sopenharmony_ci		err = rpc_ntop(sap, buf, sizeof(buf));
25662306a36Sopenharmony_ci		if (err < 0)
25762306a36Sopenharmony_ci			goto error;
25862306a36Sopenharmony_ci		ip_addr = (const char *)buf;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci	strscpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	err = nfs_idmap_new(clp);
26362306a36Sopenharmony_ci	if (err < 0) {
26462306a36Sopenharmony_ci		dprintk("%s: failed to create idmapper. Error = %d\n",
26562306a36Sopenharmony_ci			__func__, err);
26662306a36Sopenharmony_ci		goto error;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci	__set_bit(NFS_CS_IDMAP, &clp->cl_res_state);
26962306a36Sopenharmony_ci	return clp;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cierror:
27262306a36Sopenharmony_ci	nfs_free_client(clp);
27362306a36Sopenharmony_ci	return ERR_PTR(err);
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/*
27762306a36Sopenharmony_ci * Destroy the NFS4 callback service
27862306a36Sopenharmony_ci */
27962306a36Sopenharmony_cistatic void nfs4_destroy_callback(struct nfs_client *clp)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
28262306a36Sopenharmony_ci		nfs_callback_down(clp->cl_mvops->minor_version, clp->cl_net);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic void nfs4_shutdown_client(struct nfs_client *clp)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
28862306a36Sopenharmony_ci		nfs4_kill_renewd(clp);
28962306a36Sopenharmony_ci	clp->cl_mvops->shutdown_client(clp);
29062306a36Sopenharmony_ci	nfs4_destroy_callback(clp);
29162306a36Sopenharmony_ci	if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
29262306a36Sopenharmony_ci		nfs_idmap_delete(clp);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	rpc_destroy_wait_queue(&clp->cl_rpcwaitq);
29562306a36Sopenharmony_ci	kfree(clp->cl_serverowner);
29662306a36Sopenharmony_ci	kfree(clp->cl_serverscope);
29762306a36Sopenharmony_ci	kfree(clp->cl_implid);
29862306a36Sopenharmony_ci	kfree(clp->cl_owner_id);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_civoid nfs4_free_client(struct nfs_client *clp)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	nfs4_shutdown_client(clp);
30462306a36Sopenharmony_ci	nfs_free_client(clp);
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci/*
30862306a36Sopenharmony_ci * Initialize the NFS4 callback service
30962306a36Sopenharmony_ci */
31062306a36Sopenharmony_cistatic int nfs4_init_callback(struct nfs_client *clp)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	struct rpc_xprt *xprt;
31362306a36Sopenharmony_ci	int error;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	xprt = rcu_dereference_raw(clp->cl_rpcclient->cl_xprt);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (nfs4_has_session(clp)) {
31862306a36Sopenharmony_ci		error = xprt_setup_backchannel(xprt, NFS41_BC_MIN_CALLBACKS);
31962306a36Sopenharmony_ci		if (error < 0)
32062306a36Sopenharmony_ci			return error;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	error = nfs_callback_up(clp->cl_mvops->minor_version, xprt);
32462306a36Sopenharmony_ci	if (error < 0) {
32562306a36Sopenharmony_ci		dprintk("%s: failed to start callback. Error = %d\n",
32662306a36Sopenharmony_ci			__func__, error);
32762306a36Sopenharmony_ci		return error;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci	__set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	return 0;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci/**
33562306a36Sopenharmony_ci * nfs40_init_client - nfs_client initialization tasks for NFSv4.0
33662306a36Sopenharmony_ci * @clp: nfs_client to initialize
33762306a36Sopenharmony_ci *
33862306a36Sopenharmony_ci * Returns zero on success, or a negative errno if some error occurred.
33962306a36Sopenharmony_ci */
34062306a36Sopenharmony_ciint nfs40_init_client(struct nfs_client *clp)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct nfs4_slot_table *tbl;
34362306a36Sopenharmony_ci	int ret;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	tbl = kzalloc(sizeof(*tbl), GFP_NOFS);
34662306a36Sopenharmony_ci	if (tbl == NULL)
34762306a36Sopenharmony_ci		return -ENOMEM;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	ret = nfs4_setup_slot_table(tbl, NFS4_MAX_SLOT_TABLE,
35062306a36Sopenharmony_ci					"NFSv4.0 transport Slot table");
35162306a36Sopenharmony_ci	if (ret) {
35262306a36Sopenharmony_ci		nfs4_shutdown_slot_table(tbl);
35362306a36Sopenharmony_ci		kfree(tbl);
35462306a36Sopenharmony_ci		return ret;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	clp->cl_slot_tbl = tbl;
35862306a36Sopenharmony_ci	return 0;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci#if defined(CONFIG_NFS_V4_1)
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci/**
36462306a36Sopenharmony_ci * nfs41_init_client - nfs_client initialization tasks for NFSv4.1+
36562306a36Sopenharmony_ci * @clp: nfs_client to initialize
36662306a36Sopenharmony_ci *
36762306a36Sopenharmony_ci * Returns zero on success, or a negative errno if some error occurred.
36862306a36Sopenharmony_ci */
36962306a36Sopenharmony_ciint nfs41_init_client(struct nfs_client *clp)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct nfs4_session *session = NULL;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	/*
37462306a36Sopenharmony_ci	 * Create the session and mark it expired.
37562306a36Sopenharmony_ci	 * When a SEQUENCE operation encounters the expired session
37662306a36Sopenharmony_ci	 * it will do session recovery to initialize it.
37762306a36Sopenharmony_ci	 */
37862306a36Sopenharmony_ci	session = nfs4_alloc_session(clp);
37962306a36Sopenharmony_ci	if (!session)
38062306a36Sopenharmony_ci		return -ENOMEM;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	clp->cl_session = session;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	/*
38562306a36Sopenharmony_ci	 * The create session reply races with the server back
38662306a36Sopenharmony_ci	 * channel probe. Mark the client NFS_CS_SESSION_INITING
38762306a36Sopenharmony_ci	 * so that the client back channel can find the
38862306a36Sopenharmony_ci	 * nfs_client struct
38962306a36Sopenharmony_ci	 */
39062306a36Sopenharmony_ci	nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING);
39162306a36Sopenharmony_ci	return 0;
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci#endif	/* CONFIG_NFS_V4_1 */
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci/*
39762306a36Sopenharmony_ci * Initialize the minor version specific parts of an NFS4 client record
39862306a36Sopenharmony_ci */
39962306a36Sopenharmony_cistatic int nfs4_init_client_minor_version(struct nfs_client *clp)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	int ret;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	ret = clp->cl_mvops->init_client(clp);
40462306a36Sopenharmony_ci	if (ret)
40562306a36Sopenharmony_ci		return ret;
40662306a36Sopenharmony_ci	return nfs4_init_callback(clp);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic void nfs4_add_trunk(struct nfs_client *clp, struct nfs_client *old)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct sockaddr_storage clp_addr, old_addr;
41262306a36Sopenharmony_ci	struct sockaddr *clp_sap = (struct sockaddr *)&clp_addr;
41362306a36Sopenharmony_ci	struct sockaddr *old_sap = (struct sockaddr *)&old_addr;
41462306a36Sopenharmony_ci	size_t clp_salen;
41562306a36Sopenharmony_ci	struct xprt_create xprt_args = {
41662306a36Sopenharmony_ci		.ident = old->cl_proto,
41762306a36Sopenharmony_ci		.net = old->cl_net,
41862306a36Sopenharmony_ci		.servername = old->cl_hostname,
41962306a36Sopenharmony_ci	};
42062306a36Sopenharmony_ci	int max_connect = test_bit(NFS_CS_PNFS, &clp->cl_flags) ?
42162306a36Sopenharmony_ci		clp->cl_max_connect : old->cl_max_connect;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (clp->cl_proto != old->cl_proto)
42462306a36Sopenharmony_ci		return;
42562306a36Sopenharmony_ci	clp_salen = rpc_peeraddr(clp->cl_rpcclient, clp_sap, sizeof(clp_addr));
42662306a36Sopenharmony_ci	rpc_peeraddr(old->cl_rpcclient, old_sap, sizeof(old_addr));
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	if (clp_addr.ss_family != old_addr.ss_family)
42962306a36Sopenharmony_ci		return;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	xprt_args.dstaddr = clp_sap;
43262306a36Sopenharmony_ci	xprt_args.addrlen = clp_salen;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	rpc_clnt_add_xprt(old->cl_rpcclient, &xprt_args,
43562306a36Sopenharmony_ci			  rpc_clnt_test_and_add_xprt, &max_connect);
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci/**
43962306a36Sopenharmony_ci * nfs4_init_client - Initialise an NFS4 client record
44062306a36Sopenharmony_ci *
44162306a36Sopenharmony_ci * @clp: nfs_client to initialise
44262306a36Sopenharmony_ci * @cl_init: pointer to nfs_client_initdata
44362306a36Sopenharmony_ci *
44462306a36Sopenharmony_ci * Returns pointer to an NFS client, or an ERR_PTR value.
44562306a36Sopenharmony_ci */
44662306a36Sopenharmony_cistruct nfs_client *nfs4_init_client(struct nfs_client *clp,
44762306a36Sopenharmony_ci				    const struct nfs_client_initdata *cl_init)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct nfs_client *old;
45062306a36Sopenharmony_ci	int error;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if (clp->cl_cons_state == NFS_CS_READY)
45362306a36Sopenharmony_ci		/* the client is initialised already */
45462306a36Sopenharmony_ci		return clp;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	error = nfs4_init_client_minor_version(clp);
45762306a36Sopenharmony_ci	if (error < 0)
45862306a36Sopenharmony_ci		goto error;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	error = nfs4_discover_server_trunking(clp, &old);
46162306a36Sopenharmony_ci	if (error < 0)
46262306a36Sopenharmony_ci		goto error;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (clp != old) {
46562306a36Sopenharmony_ci		clp->cl_preserve_clid = true;
46662306a36Sopenharmony_ci		/*
46762306a36Sopenharmony_ci		 * Mark the client as having failed initialization so other
46862306a36Sopenharmony_ci		 * processes walking the nfs_client_list in nfs_match_client()
46962306a36Sopenharmony_ci		 * won't try to use it.
47062306a36Sopenharmony_ci		 */
47162306a36Sopenharmony_ci		nfs_mark_client_ready(clp, -EPERM);
47262306a36Sopenharmony_ci		if (old->cl_mvops->session_trunk)
47362306a36Sopenharmony_ci			nfs4_add_trunk(clp, old);
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci	clear_bit(NFS_CS_TSM_POSSIBLE, &clp->cl_flags);
47662306a36Sopenharmony_ci	nfs_put_client(clp);
47762306a36Sopenharmony_ci	return old;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cierror:
48062306a36Sopenharmony_ci	nfs_mark_client_ready(clp, error);
48162306a36Sopenharmony_ci	nfs_put_client(clp);
48262306a36Sopenharmony_ci	return ERR_PTR(error);
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci/*
48662306a36Sopenharmony_ci * SETCLIENTID just did a callback update with the callback ident in
48762306a36Sopenharmony_ci * "drop," but server trunking discovery claims "drop" and "keep" are
48862306a36Sopenharmony_ci * actually the same server.  Swap the callback IDs so that "keep"
48962306a36Sopenharmony_ci * will continue to use the callback ident the server now knows about,
49062306a36Sopenharmony_ci * and so that "keep"'s original callback ident is destroyed when
49162306a36Sopenharmony_ci * "drop" is freed.
49262306a36Sopenharmony_ci */
49362306a36Sopenharmony_cistatic void nfs4_swap_callback_idents(struct nfs_client *keep,
49462306a36Sopenharmony_ci				      struct nfs_client *drop)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(keep->cl_net, nfs_net_id);
49762306a36Sopenharmony_ci	unsigned int save = keep->cl_cb_ident;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (keep->cl_cb_ident == drop->cl_cb_ident)
50062306a36Sopenharmony_ci		return;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	dprintk("%s: keeping callback ident %u and dropping ident %u\n",
50362306a36Sopenharmony_ci		__func__, keep->cl_cb_ident, drop->cl_cb_ident);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	spin_lock(&nn->nfs_client_lock);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	idr_replace(&nn->cb_ident_idr, keep, drop->cl_cb_ident);
50862306a36Sopenharmony_ci	keep->cl_cb_ident = drop->cl_cb_ident;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	idr_replace(&nn->cb_ident_idr, drop, save);
51162306a36Sopenharmony_ci	drop->cl_cb_ident = save;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	spin_unlock(&nn->nfs_client_lock);
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic bool nfs4_match_client_owner_id(const struct nfs_client *clp1,
51762306a36Sopenharmony_ci		const struct nfs_client *clp2)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	if (clp1->cl_owner_id == NULL || clp2->cl_owner_id == NULL)
52062306a36Sopenharmony_ci		return true;
52162306a36Sopenharmony_ci	return strcmp(clp1->cl_owner_id, clp2->cl_owner_id) == 0;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic bool nfs4_same_verifier(nfs4_verifier *v1, nfs4_verifier *v2)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	return memcmp(v1->data, v2->data, sizeof(v1->data)) == 0;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic int nfs4_match_client(struct nfs_client  *pos,  struct nfs_client *new,
53062306a36Sopenharmony_ci			     struct nfs_client **prev, struct nfs_net *nn)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	int status;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	if (pos->rpc_ops != new->rpc_ops)
53562306a36Sopenharmony_ci		return 1;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (pos->cl_minorversion != new->cl_minorversion)
53862306a36Sopenharmony_ci		return 1;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	/* If "pos" isn't marked ready, we can't trust the
54162306a36Sopenharmony_ci	 * remaining fields in "pos", especially the client
54262306a36Sopenharmony_ci	 * ID and serverowner fields.  Wait for CREATE_SESSION
54362306a36Sopenharmony_ci	 * to finish. */
54462306a36Sopenharmony_ci	if (pos->cl_cons_state > NFS_CS_READY) {
54562306a36Sopenharmony_ci		refcount_inc(&pos->cl_count);
54662306a36Sopenharmony_ci		spin_unlock(&nn->nfs_client_lock);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		nfs_put_client(*prev);
54962306a36Sopenharmony_ci		*prev = pos;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		status = nfs_wait_client_init_complete(pos);
55262306a36Sopenharmony_ci		spin_lock(&nn->nfs_client_lock);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci		if (status < 0)
55562306a36Sopenharmony_ci			return status;
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	if (pos->cl_cons_state != NFS_CS_READY)
55962306a36Sopenharmony_ci		return 1;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (pos->cl_clientid != new->cl_clientid)
56262306a36Sopenharmony_ci		return 1;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* NFSv4.1 always uses the uniform string, however someone
56562306a36Sopenharmony_ci	 * might switch the uniquifier string on us.
56662306a36Sopenharmony_ci	 */
56762306a36Sopenharmony_ci	if (!nfs4_match_client_owner_id(pos, new))
56862306a36Sopenharmony_ci		return 1;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	return 0;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci/**
57462306a36Sopenharmony_ci * nfs40_walk_client_list - Find server that recognizes a client ID
57562306a36Sopenharmony_ci *
57662306a36Sopenharmony_ci * @new: nfs_client with client ID to test
57762306a36Sopenharmony_ci * @result: OUT: found nfs_client, or new
57862306a36Sopenharmony_ci * @cred: credential to use for trunking test
57962306a36Sopenharmony_ci *
58062306a36Sopenharmony_ci * Returns zero, a negative errno, or a negative NFS4ERR status.
58162306a36Sopenharmony_ci * If zero is returned, an nfs_client pointer is planted in "result."
58262306a36Sopenharmony_ci *
58362306a36Sopenharmony_ci * NB: nfs40_walk_client_list() relies on the new nfs_client being
58462306a36Sopenharmony_ci *     the last nfs_client on the list.
58562306a36Sopenharmony_ci */
58662306a36Sopenharmony_ciint nfs40_walk_client_list(struct nfs_client *new,
58762306a36Sopenharmony_ci			   struct nfs_client **result,
58862306a36Sopenharmony_ci			   const struct cred *cred)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id);
59162306a36Sopenharmony_ci	struct nfs_client *pos, *prev = NULL;
59262306a36Sopenharmony_ci	struct nfs4_setclientid_res clid = {
59362306a36Sopenharmony_ci		.clientid	= new->cl_clientid,
59462306a36Sopenharmony_ci		.confirm	= new->cl_confirm,
59562306a36Sopenharmony_ci	};
59662306a36Sopenharmony_ci	int status = -NFS4ERR_STALE_CLIENTID;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	spin_lock(&nn->nfs_client_lock);
59962306a36Sopenharmony_ci	list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) {
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci		if (pos == new)
60262306a36Sopenharmony_ci			goto found;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		status = nfs4_match_client(pos, new, &prev, nn);
60562306a36Sopenharmony_ci		if (status < 0)
60662306a36Sopenharmony_ci			goto out_unlock;
60762306a36Sopenharmony_ci		if (status != 0)
60862306a36Sopenharmony_ci			continue;
60962306a36Sopenharmony_ci		/*
61062306a36Sopenharmony_ci		 * We just sent a new SETCLIENTID, which should have
61162306a36Sopenharmony_ci		 * caused the server to return a new cl_confirm.  So if
61262306a36Sopenharmony_ci		 * cl_confirm is the same, then this is a different
61362306a36Sopenharmony_ci		 * server that just returned the same cl_confirm by
61462306a36Sopenharmony_ci		 * coincidence:
61562306a36Sopenharmony_ci		 */
61662306a36Sopenharmony_ci		if ((new != pos) && nfs4_same_verifier(&pos->cl_confirm,
61762306a36Sopenharmony_ci						       &new->cl_confirm))
61862306a36Sopenharmony_ci			continue;
61962306a36Sopenharmony_ci		/*
62062306a36Sopenharmony_ci		 * But if the cl_confirm's are different, then the only
62162306a36Sopenharmony_ci		 * way that a SETCLIENTID_CONFIRM to pos can succeed is
62262306a36Sopenharmony_ci		 * if new and pos point to the same server:
62362306a36Sopenharmony_ci		 */
62462306a36Sopenharmony_cifound:
62562306a36Sopenharmony_ci		refcount_inc(&pos->cl_count);
62662306a36Sopenharmony_ci		spin_unlock(&nn->nfs_client_lock);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci		nfs_put_client(prev);
62962306a36Sopenharmony_ci		prev = pos;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci		status = nfs4_proc_setclientid_confirm(pos, &clid, cred);
63262306a36Sopenharmony_ci		switch (status) {
63362306a36Sopenharmony_ci		case -NFS4ERR_STALE_CLIENTID:
63462306a36Sopenharmony_ci			break;
63562306a36Sopenharmony_ci		case 0:
63662306a36Sopenharmony_ci			nfs4_swap_callback_idents(pos, new);
63762306a36Sopenharmony_ci			pos->cl_confirm = new->cl_confirm;
63862306a36Sopenharmony_ci			nfs_mark_client_ready(pos, NFS_CS_READY);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci			prev = NULL;
64162306a36Sopenharmony_ci			*result = pos;
64262306a36Sopenharmony_ci			goto out;
64362306a36Sopenharmony_ci		case -ERESTARTSYS:
64462306a36Sopenharmony_ci		case -ETIMEDOUT:
64562306a36Sopenharmony_ci			/* The callback path may have been inadvertently
64662306a36Sopenharmony_ci			 * changed. Schedule recovery!
64762306a36Sopenharmony_ci			 */
64862306a36Sopenharmony_ci			nfs4_schedule_path_down_recovery(pos);
64962306a36Sopenharmony_ci			goto out;
65062306a36Sopenharmony_ci		default:
65162306a36Sopenharmony_ci			goto out;
65262306a36Sopenharmony_ci		}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci		spin_lock(&nn->nfs_client_lock);
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ciout_unlock:
65762306a36Sopenharmony_ci	spin_unlock(&nn->nfs_client_lock);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/* No match found. The server lost our clientid */
66062306a36Sopenharmony_ciout:
66162306a36Sopenharmony_ci	nfs_put_client(prev);
66262306a36Sopenharmony_ci	return status;
66362306a36Sopenharmony_ci}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_1
66662306a36Sopenharmony_ci/*
66762306a36Sopenharmony_ci * Returns true if the server major ids match
66862306a36Sopenharmony_ci */
66962306a36Sopenharmony_cibool
67062306a36Sopenharmony_cinfs4_check_serverowner_major_id(struct nfs41_server_owner *o1,
67162306a36Sopenharmony_ci				struct nfs41_server_owner *o2)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	if (o1->major_id_sz != o2->major_id_sz)
67462306a36Sopenharmony_ci		return false;
67562306a36Sopenharmony_ci	return memcmp(o1->major_id, o2->major_id, o1->major_id_sz) == 0;
67662306a36Sopenharmony_ci}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci/*
67962306a36Sopenharmony_ci * Returns true if the server scopes match
68062306a36Sopenharmony_ci */
68162306a36Sopenharmony_cistatic bool
68262306a36Sopenharmony_cinfs4_check_server_scope(struct nfs41_server_scope *s1,
68362306a36Sopenharmony_ci			struct nfs41_server_scope *s2)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	if (s1->server_scope_sz != s2->server_scope_sz)
68662306a36Sopenharmony_ci		return false;
68762306a36Sopenharmony_ci	return memcmp(s1->server_scope, s2->server_scope,
68862306a36Sopenharmony_ci					s1->server_scope_sz) == 0;
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci/**
69262306a36Sopenharmony_ci * nfs4_detect_session_trunking - Checks for session trunking.
69362306a36Sopenharmony_ci * @clp:    original mount nfs_client
69462306a36Sopenharmony_ci * @res:    result structure from an exchange_id using the original mount
69562306a36Sopenharmony_ci *          nfs_client with a new multi_addr transport
69662306a36Sopenharmony_ci * @xprt:   pointer to the transport to add.
69762306a36Sopenharmony_ci *
69862306a36Sopenharmony_ci * Called after a successful EXCHANGE_ID on a multi-addr connection.
69962306a36Sopenharmony_ci * Upon success, add the transport.
70062306a36Sopenharmony_ci *
70162306a36Sopenharmony_ci * Returns zero on success, otherwise -EINVAL
70262306a36Sopenharmony_ci *
70362306a36Sopenharmony_ci * Note: since the exchange_id for the new multi_addr transport uses the
70462306a36Sopenharmony_ci * same nfs_client from the original mount, the cl_owner_id is reused,
70562306a36Sopenharmony_ci * so eir_clientowner is the same.
70662306a36Sopenharmony_ci */
70762306a36Sopenharmony_ciint nfs4_detect_session_trunking(struct nfs_client *clp,
70862306a36Sopenharmony_ci				 struct nfs41_exchange_id_res *res,
70962306a36Sopenharmony_ci				 struct rpc_xprt *xprt)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	/* Check eir_clientid */
71262306a36Sopenharmony_ci	if (clp->cl_clientid != res->clientid)
71362306a36Sopenharmony_ci		goto out_err;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	/* Check eir_server_owner so_major_id */
71662306a36Sopenharmony_ci	if (!nfs4_check_serverowner_major_id(clp->cl_serverowner,
71762306a36Sopenharmony_ci					     res->server_owner))
71862306a36Sopenharmony_ci		goto out_err;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	/* Check eir_server_owner so_minor_id */
72162306a36Sopenharmony_ci	if (clp->cl_serverowner->minor_id != res->server_owner->minor_id)
72262306a36Sopenharmony_ci		goto out_err;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	/* Check eir_server_scope */
72562306a36Sopenharmony_ci	if (!nfs4_check_server_scope(clp->cl_serverscope, res->server_scope))
72662306a36Sopenharmony_ci		goto out_err;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	pr_info("NFS:  %s: Session trunking succeeded for %s\n",
72962306a36Sopenharmony_ci		clp->cl_hostname,
73062306a36Sopenharmony_ci		xprt->address_strings[RPC_DISPLAY_ADDR]);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	return 0;
73362306a36Sopenharmony_ciout_err:
73462306a36Sopenharmony_ci	pr_info("NFS:  %s: Session trunking failed for %s\n", clp->cl_hostname,
73562306a36Sopenharmony_ci		xprt->address_strings[RPC_DISPLAY_ADDR]);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	return -EINVAL;
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci/**
74162306a36Sopenharmony_ci * nfs41_walk_client_list - Find nfs_client that matches a client/server owner
74262306a36Sopenharmony_ci *
74362306a36Sopenharmony_ci * @new: nfs_client with client ID to test
74462306a36Sopenharmony_ci * @result: OUT: found nfs_client, or new
74562306a36Sopenharmony_ci * @cred: credential to use for trunking test
74662306a36Sopenharmony_ci *
74762306a36Sopenharmony_ci * Returns zero, a negative errno, or a negative NFS4ERR status.
74862306a36Sopenharmony_ci * If zero is returned, an nfs_client pointer is planted in "result."
74962306a36Sopenharmony_ci *
75062306a36Sopenharmony_ci * NB: nfs41_walk_client_list() relies on the new nfs_client being
75162306a36Sopenharmony_ci *     the last nfs_client on the list.
75262306a36Sopenharmony_ci */
75362306a36Sopenharmony_ciint nfs41_walk_client_list(struct nfs_client *new,
75462306a36Sopenharmony_ci			   struct nfs_client **result,
75562306a36Sopenharmony_ci			   const struct cred *cred)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id);
75862306a36Sopenharmony_ci	struct nfs_client *pos, *prev = NULL;
75962306a36Sopenharmony_ci	int status = -NFS4ERR_STALE_CLIENTID;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	spin_lock(&nn->nfs_client_lock);
76262306a36Sopenharmony_ci	list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) {
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		if (pos == new)
76562306a36Sopenharmony_ci			goto found;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci		status = nfs4_match_client(pos, new, &prev, nn);
76862306a36Sopenharmony_ci		if (status < 0)
76962306a36Sopenharmony_ci			goto out;
77062306a36Sopenharmony_ci		if (status != 0)
77162306a36Sopenharmony_ci			continue;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci		/*
77462306a36Sopenharmony_ci		 * Note that session trunking is just a special subcase of
77562306a36Sopenharmony_ci		 * client id trunking. In either case, we want to fall back
77662306a36Sopenharmony_ci		 * to using the existing nfs_client.
77762306a36Sopenharmony_ci		 */
77862306a36Sopenharmony_ci		if (!nfs4_check_serverowner_major_id(pos->cl_serverowner,
77962306a36Sopenharmony_ci						     new->cl_serverowner))
78062306a36Sopenharmony_ci			continue;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_cifound:
78362306a36Sopenharmony_ci		refcount_inc(&pos->cl_count);
78462306a36Sopenharmony_ci		*result = pos;
78562306a36Sopenharmony_ci		status = 0;
78662306a36Sopenharmony_ci		break;
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ciout:
79062306a36Sopenharmony_ci	spin_unlock(&nn->nfs_client_lock);
79162306a36Sopenharmony_ci	nfs_put_client(prev);
79262306a36Sopenharmony_ci	return status;
79362306a36Sopenharmony_ci}
79462306a36Sopenharmony_ci#endif	/* CONFIG_NFS_V4_1 */
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic void nfs4_destroy_server(struct nfs_server *server)
79762306a36Sopenharmony_ci{
79862306a36Sopenharmony_ci	LIST_HEAD(freeme);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	nfs_server_return_all_delegations(server);
80162306a36Sopenharmony_ci	unset_pnfs_layoutdriver(server);
80262306a36Sopenharmony_ci	nfs4_purge_state_owners(server, &freeme);
80362306a36Sopenharmony_ci	nfs4_free_state_owners(&freeme);
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci/*
80762306a36Sopenharmony_ci * NFSv4.0 callback thread helper
80862306a36Sopenharmony_ci *
80962306a36Sopenharmony_ci * Find a client by callback identifier
81062306a36Sopenharmony_ci */
81162306a36Sopenharmony_cistruct nfs_client *
81262306a36Sopenharmony_cinfs4_find_client_ident(struct net *net, int cb_ident)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	struct nfs_client *clp;
81562306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(net, nfs_net_id);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	spin_lock(&nn->nfs_client_lock);
81862306a36Sopenharmony_ci	clp = idr_find(&nn->cb_ident_idr, cb_ident);
81962306a36Sopenharmony_ci	if (clp)
82062306a36Sopenharmony_ci		refcount_inc(&clp->cl_count);
82162306a36Sopenharmony_ci	spin_unlock(&nn->nfs_client_lock);
82262306a36Sopenharmony_ci	return clp;
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci#if defined(CONFIG_NFS_V4_1)
82662306a36Sopenharmony_ci/* Common match routine for v4.0 and v4.1 callback services */
82762306a36Sopenharmony_cistatic bool nfs4_cb_match_client(const struct sockaddr *addr,
82862306a36Sopenharmony_ci		struct nfs_client *clp, u32 minorversion)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	/* Don't match clients that failed to initialise */
83362306a36Sopenharmony_ci	if (!(clp->cl_cons_state == NFS_CS_READY ||
83462306a36Sopenharmony_ci	    clp->cl_cons_state == NFS_CS_SESSION_INITING))
83562306a36Sopenharmony_ci		return false;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	smp_rmb();
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	/* Match the version and minorversion */
84062306a36Sopenharmony_ci	if (clp->rpc_ops->version != 4 ||
84162306a36Sopenharmony_ci	    clp->cl_minorversion != minorversion)
84262306a36Sopenharmony_ci		return false;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	/* Match only the IP address, not the port number */
84562306a36Sopenharmony_ci	return rpc_cmp_addr(addr, clap);
84662306a36Sopenharmony_ci}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci/*
84962306a36Sopenharmony_ci * NFSv4.1 callback thread helper
85062306a36Sopenharmony_ci * For CB_COMPOUND calls, find a client by IP address, protocol version,
85162306a36Sopenharmony_ci * minorversion, and sessionID
85262306a36Sopenharmony_ci *
85362306a36Sopenharmony_ci * Returns NULL if no such client
85462306a36Sopenharmony_ci */
85562306a36Sopenharmony_cistruct nfs_client *
85662306a36Sopenharmony_cinfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
85762306a36Sopenharmony_ci			   struct nfs4_sessionid *sid, u32 minorversion)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	struct nfs_client *clp;
86062306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(net, nfs_net_id);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	spin_lock(&nn->nfs_client_lock);
86362306a36Sopenharmony_ci	list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
86462306a36Sopenharmony_ci		if (!nfs4_cb_match_client(addr, clp, minorversion))
86562306a36Sopenharmony_ci			continue;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci		if (!nfs4_has_session(clp))
86862306a36Sopenharmony_ci			continue;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci		/* Match sessionid*/
87162306a36Sopenharmony_ci		if (memcmp(clp->cl_session->sess_id.data,
87262306a36Sopenharmony_ci		    sid->data, NFS4_MAX_SESSIONID_LEN) != 0)
87362306a36Sopenharmony_ci			continue;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci		refcount_inc(&clp->cl_count);
87662306a36Sopenharmony_ci		spin_unlock(&nn->nfs_client_lock);
87762306a36Sopenharmony_ci		return clp;
87862306a36Sopenharmony_ci	}
87962306a36Sopenharmony_ci	spin_unlock(&nn->nfs_client_lock);
88062306a36Sopenharmony_ci	return NULL;
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci#else /* CONFIG_NFS_V4_1 */
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_cistruct nfs_client *
88662306a36Sopenharmony_cinfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
88762306a36Sopenharmony_ci			   struct nfs4_sessionid *sid, u32 minorversion)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	return NULL;
89062306a36Sopenharmony_ci}
89162306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_1 */
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci/*
89462306a36Sopenharmony_ci * Set up an NFS4 client
89562306a36Sopenharmony_ci */
89662306a36Sopenharmony_cistatic int nfs4_set_client(struct nfs_server *server,
89762306a36Sopenharmony_ci		const char *hostname,
89862306a36Sopenharmony_ci		const struct sockaddr_storage *addr,
89962306a36Sopenharmony_ci		const size_t addrlen,
90062306a36Sopenharmony_ci		const char *ip_addr,
90162306a36Sopenharmony_ci		int proto, const struct rpc_timeout *timeparms,
90262306a36Sopenharmony_ci		u32 minorversion, unsigned int nconnect,
90362306a36Sopenharmony_ci		unsigned int max_connect,
90462306a36Sopenharmony_ci		struct net *net,
90562306a36Sopenharmony_ci		struct xprtsec_parms *xprtsec)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	struct nfs_client_initdata cl_init = {
90862306a36Sopenharmony_ci		.hostname = hostname,
90962306a36Sopenharmony_ci		.addr = addr,
91062306a36Sopenharmony_ci		.addrlen = addrlen,
91162306a36Sopenharmony_ci		.ip_addr = ip_addr,
91262306a36Sopenharmony_ci		.nfs_mod = &nfs_v4,
91362306a36Sopenharmony_ci		.proto = proto,
91462306a36Sopenharmony_ci		.minorversion = minorversion,
91562306a36Sopenharmony_ci		.net = net,
91662306a36Sopenharmony_ci		.timeparms = timeparms,
91762306a36Sopenharmony_ci		.cred = server->cred,
91862306a36Sopenharmony_ci		.xprtsec = *xprtsec,
91962306a36Sopenharmony_ci	};
92062306a36Sopenharmony_ci	struct nfs_client *clp;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	if (minorversion == 0)
92362306a36Sopenharmony_ci		__set_bit(NFS_CS_REUSEPORT, &cl_init.init_flags);
92462306a36Sopenharmony_ci	else
92562306a36Sopenharmony_ci		cl_init.max_connect = max_connect;
92662306a36Sopenharmony_ci	switch (proto) {
92762306a36Sopenharmony_ci	case XPRT_TRANSPORT_TCP:
92862306a36Sopenharmony_ci	case XPRT_TRANSPORT_TCP_TLS:
92962306a36Sopenharmony_ci		cl_init.nconnect = nconnect;
93062306a36Sopenharmony_ci	}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	if (server->flags & NFS_MOUNT_NORESVPORT)
93362306a36Sopenharmony_ci		__set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
93462306a36Sopenharmony_ci	if (server->options & NFS_OPTION_MIGRATION)
93562306a36Sopenharmony_ci		__set_bit(NFS_CS_MIGRATION, &cl_init.init_flags);
93662306a36Sopenharmony_ci	if (test_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status))
93762306a36Sopenharmony_ci		__set_bit(NFS_CS_TSM_POSSIBLE, &cl_init.init_flags);
93862306a36Sopenharmony_ci	server->port = rpc_get_port((struct sockaddr *)addr);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	/* Allocate or find a client reference we can use */
94162306a36Sopenharmony_ci	clp = nfs_get_client(&cl_init);
94262306a36Sopenharmony_ci	if (IS_ERR(clp))
94362306a36Sopenharmony_ci		return PTR_ERR(clp);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if (server->nfs_client == clp) {
94662306a36Sopenharmony_ci		nfs_put_client(clp);
94762306a36Sopenharmony_ci		return -ELOOP;
94862306a36Sopenharmony_ci	}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	/*
95162306a36Sopenharmony_ci	 * Query for the lease time on clientid setup or renewal
95262306a36Sopenharmony_ci	 *
95362306a36Sopenharmony_ci	 * Note that this will be set on nfs_clients that were created
95462306a36Sopenharmony_ci	 * only for the DS role and did not set this bit, but now will
95562306a36Sopenharmony_ci	 * serve a dual role.
95662306a36Sopenharmony_ci	 */
95762306a36Sopenharmony_ci	set_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	server->nfs_client = clp;
96062306a36Sopenharmony_ci	nfs_sysfs_add_server(server);
96162306a36Sopenharmony_ci	nfs_sysfs_link_rpc_client(server, clp->cl_rpcclient, "_state");
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	return 0;
96462306a36Sopenharmony_ci}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci/*
96762306a36Sopenharmony_ci * Set up a pNFS Data Server client.
96862306a36Sopenharmony_ci *
96962306a36Sopenharmony_ci * Return any existing nfs_client that matches server address,port,version
97062306a36Sopenharmony_ci * and minorversion.
97162306a36Sopenharmony_ci *
97262306a36Sopenharmony_ci * For a new nfs_client, use a soft mount (default), a low retrans and a
97362306a36Sopenharmony_ci * low timeout interval so that if a connection is lost, we retry through
97462306a36Sopenharmony_ci * the MDS.
97562306a36Sopenharmony_ci */
97662306a36Sopenharmony_cistruct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv,
97762306a36Sopenharmony_ci		const struct sockaddr_storage *ds_addr, int ds_addrlen,
97862306a36Sopenharmony_ci		int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans,
97962306a36Sopenharmony_ci		u32 minor_version)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	struct rpc_timeout ds_timeout;
98262306a36Sopenharmony_ci	struct nfs_client *mds_clp = mds_srv->nfs_client;
98362306a36Sopenharmony_ci	struct nfs_client_initdata cl_init = {
98462306a36Sopenharmony_ci		.addr = ds_addr,
98562306a36Sopenharmony_ci		.addrlen = ds_addrlen,
98662306a36Sopenharmony_ci		.nodename = mds_clp->cl_rpcclient->cl_nodename,
98762306a36Sopenharmony_ci		.ip_addr = mds_clp->cl_ipaddr,
98862306a36Sopenharmony_ci		.nfs_mod = &nfs_v4,
98962306a36Sopenharmony_ci		.proto = ds_proto,
99062306a36Sopenharmony_ci		.minorversion = minor_version,
99162306a36Sopenharmony_ci		.net = mds_clp->cl_net,
99262306a36Sopenharmony_ci		.timeparms = &ds_timeout,
99362306a36Sopenharmony_ci		.cred = mds_srv->cred,
99462306a36Sopenharmony_ci		.xprtsec = mds_srv->nfs_client->cl_xprtsec,
99562306a36Sopenharmony_ci	};
99662306a36Sopenharmony_ci	char buf[INET6_ADDRSTRLEN + 1];
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	if (rpc_ntop((struct sockaddr *)ds_addr, buf, sizeof(buf)) <= 0)
99962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
100062306a36Sopenharmony_ci	cl_init.hostname = buf;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	switch (ds_proto) {
100362306a36Sopenharmony_ci	case XPRT_TRANSPORT_TCP:
100462306a36Sopenharmony_ci	case XPRT_TRANSPORT_TCP_TLS:
100562306a36Sopenharmony_ci		if (mds_clp->cl_nconnect > 1) {
100662306a36Sopenharmony_ci			cl_init.nconnect = mds_clp->cl_nconnect;
100762306a36Sopenharmony_ci			cl_init.max_connect = NFS_MAX_TRANSPORTS;
100862306a36Sopenharmony_ci		}
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	if (mds_srv->flags & NFS_MOUNT_NORESVPORT)
101262306a36Sopenharmony_ci		__set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	__set_bit(NFS_CS_DS, &cl_init.init_flags);
101562306a36Sopenharmony_ci	__set_bit(NFS_CS_PNFS, &cl_init.init_flags);
101662306a36Sopenharmony_ci	cl_init.max_connect = NFS_MAX_TRANSPORTS;
101762306a36Sopenharmony_ci	/*
101862306a36Sopenharmony_ci	 * Set an authflavor equual to the MDS value. Use the MDS nfs_client
101962306a36Sopenharmony_ci	 * cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS
102062306a36Sopenharmony_ci	 * (section 13.1 RFC 5661).
102162306a36Sopenharmony_ci	 */
102262306a36Sopenharmony_ci	nfs_init_timeout_values(&ds_timeout, ds_proto, ds_timeo, ds_retrans);
102362306a36Sopenharmony_ci	return nfs_get_client(&cl_init);
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_set_ds_client);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci/*
102862306a36Sopenharmony_ci * Session has been established, and the client marked ready.
102962306a36Sopenharmony_ci * Limit the mount rsize, wsize and dtsize using negotiated fore
103062306a36Sopenharmony_ci * channel attributes.
103162306a36Sopenharmony_ci */
103262306a36Sopenharmony_cistatic void nfs4_session_limit_rwsize(struct nfs_server *server)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_1
103562306a36Sopenharmony_ci	struct nfs4_session *sess;
103662306a36Sopenharmony_ci	u32 server_resp_sz;
103762306a36Sopenharmony_ci	u32 server_rqst_sz;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	if (!nfs4_has_session(server->nfs_client))
104062306a36Sopenharmony_ci		return;
104162306a36Sopenharmony_ci	sess = server->nfs_client->cl_session;
104262306a36Sopenharmony_ci	server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead;
104362306a36Sopenharmony_ci	server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	if (server->dtsize > server_resp_sz)
104662306a36Sopenharmony_ci		server->dtsize = server_resp_sz;
104762306a36Sopenharmony_ci	if (server->rsize > server_resp_sz)
104862306a36Sopenharmony_ci		server->rsize = server_resp_sz;
104962306a36Sopenharmony_ci	if (server->wsize > server_rqst_sz)
105062306a36Sopenharmony_ci		server->wsize = server_rqst_sz;
105162306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_1 */
105262306a36Sopenharmony_ci}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci/*
105562306a36Sopenharmony_ci * Limit xattr sizes using the channel attributes.
105662306a36Sopenharmony_ci */
105762306a36Sopenharmony_cistatic void nfs4_session_limit_xasize(struct nfs_server *server)
105862306a36Sopenharmony_ci{
105962306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_2
106062306a36Sopenharmony_ci	struct nfs4_session *sess;
106162306a36Sopenharmony_ci	u32 server_gxa_sz;
106262306a36Sopenharmony_ci	u32 server_sxa_sz;
106362306a36Sopenharmony_ci	u32 server_lxa_sz;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	if (!nfs4_has_session(server->nfs_client))
106662306a36Sopenharmony_ci		return;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	sess = server->nfs_client->cl_session;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	server_gxa_sz = sess->fc_attrs.max_resp_sz - nfs42_maxgetxattr_overhead;
107162306a36Sopenharmony_ci	server_sxa_sz = sess->fc_attrs.max_rqst_sz - nfs42_maxsetxattr_overhead;
107262306a36Sopenharmony_ci	server_lxa_sz = sess->fc_attrs.max_resp_sz -
107362306a36Sopenharmony_ci	    nfs42_maxlistxattrs_overhead;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	if (server->gxasize > server_gxa_sz)
107662306a36Sopenharmony_ci		server->gxasize = server_gxa_sz;
107762306a36Sopenharmony_ci	if (server->sxasize > server_sxa_sz)
107862306a36Sopenharmony_ci		server->sxasize = server_sxa_sz;
107962306a36Sopenharmony_ci	if (server->lxasize > server_lxa_sz)
108062306a36Sopenharmony_ci		server->lxasize = server_lxa_sz;
108162306a36Sopenharmony_ci#endif
108262306a36Sopenharmony_ci}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_civoid nfs4_server_set_init_caps(struct nfs_server *server)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	/* Set the basic capabilities */
108762306a36Sopenharmony_ci	server->caps |= server->nfs_client->cl_mvops->init_caps;
108862306a36Sopenharmony_ci	if (server->flags & NFS_MOUNT_NORDIRPLUS)
108962306a36Sopenharmony_ci			server->caps &= ~NFS_CAP_READDIRPLUS;
109062306a36Sopenharmony_ci	if (server->nfs_client->cl_proto == XPRT_TRANSPORT_RDMA)
109162306a36Sopenharmony_ci		server->caps &= ~NFS_CAP_READ_PLUS;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	/*
109462306a36Sopenharmony_ci	 * Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower
109562306a36Sopenharmony_ci	 * authentication.
109662306a36Sopenharmony_ci	 */
109762306a36Sopenharmony_ci	if (nfs4_disable_idmapping &&
109862306a36Sopenharmony_ci			server->client->cl_auth->au_flavor == RPC_AUTH_UNIX)
109962306a36Sopenharmony_ci		server->caps |= NFS_CAP_UIDGID_NOMAP;
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_cistatic int nfs4_server_common_setup(struct nfs_server *server,
110362306a36Sopenharmony_ci		struct nfs_fh *mntfh, bool auth_probe)
110462306a36Sopenharmony_ci{
110562306a36Sopenharmony_ci	int error;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	/* data servers support only a subset of NFSv4.1 */
110862306a36Sopenharmony_ci	if (is_ds_only_client(server->nfs_client))
110962306a36Sopenharmony_ci		return -EPROTONOSUPPORT;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	/* We must ensure the session is initialised first */
111262306a36Sopenharmony_ci	error = nfs4_init_session(server->nfs_client);
111362306a36Sopenharmony_ci	if (error < 0)
111462306a36Sopenharmony_ci		goto out;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	nfs4_server_set_init_caps(server);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	/* Probe the root fh to retrieve its FSID and filehandle */
111962306a36Sopenharmony_ci	error = nfs4_get_rootfh(server, mntfh, auth_probe);
112062306a36Sopenharmony_ci	if (error < 0)
112162306a36Sopenharmony_ci		goto out;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	dprintk("Server FSID: %llx:%llx\n",
112462306a36Sopenharmony_ci			(unsigned long long) server->fsid.major,
112562306a36Sopenharmony_ci			(unsigned long long) server->fsid.minor);
112662306a36Sopenharmony_ci	nfs_display_fhandle(mntfh, "Pseudo-fs root FH");
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	error = nfs_probe_server(server, mntfh);
112962306a36Sopenharmony_ci	if (error < 0)
113062306a36Sopenharmony_ci		goto out;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	nfs4_session_limit_rwsize(server);
113362306a36Sopenharmony_ci	nfs4_session_limit_xasize(server);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
113662306a36Sopenharmony_ci		server->namelen = NFS4_MAXNAMLEN;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	nfs_server_insert_lists(server);
113962306a36Sopenharmony_ci	server->mount_time = jiffies;
114062306a36Sopenharmony_ci	server->destroy = nfs4_destroy_server;
114162306a36Sopenharmony_ciout:
114262306a36Sopenharmony_ci	return error;
114362306a36Sopenharmony_ci}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci/*
114662306a36Sopenharmony_ci * Create a version 4 volume record
114762306a36Sopenharmony_ci */
114862306a36Sopenharmony_cistatic int nfs4_init_server(struct nfs_server *server, struct fs_context *fc)
114962306a36Sopenharmony_ci{
115062306a36Sopenharmony_ci	struct nfs_fs_context *ctx = nfs_fc2context(fc);
115162306a36Sopenharmony_ci	struct rpc_timeout timeparms;
115262306a36Sopenharmony_ci	int error;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	nfs_init_timeout_values(&timeparms, ctx->nfs_server.protocol,
115562306a36Sopenharmony_ci				ctx->timeo, ctx->retrans);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	/* Initialise the client representation from the mount data */
115862306a36Sopenharmony_ci	server->flags = ctx->flags;
115962306a36Sopenharmony_ci	server->options = ctx->options;
116062306a36Sopenharmony_ci	server->auth_info = ctx->auth_info;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	/* Use the first specified auth flavor. If this flavor isn't
116362306a36Sopenharmony_ci	 * allowed by the server, use the SECINFO path to try the
116462306a36Sopenharmony_ci	 * other specified flavors */
116562306a36Sopenharmony_ci	if (ctx->auth_info.flavor_len >= 1)
116662306a36Sopenharmony_ci		ctx->selected_flavor = ctx->auth_info.flavors[0];
116762306a36Sopenharmony_ci	else
116862306a36Sopenharmony_ci		ctx->selected_flavor = RPC_AUTH_UNIX;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	/* Get a client record */
117162306a36Sopenharmony_ci	error = nfs4_set_client(server,
117262306a36Sopenharmony_ci				ctx->nfs_server.hostname,
117362306a36Sopenharmony_ci				&ctx->nfs_server._address,
117462306a36Sopenharmony_ci				ctx->nfs_server.addrlen,
117562306a36Sopenharmony_ci				ctx->client_address,
117662306a36Sopenharmony_ci				ctx->nfs_server.protocol,
117762306a36Sopenharmony_ci				&timeparms,
117862306a36Sopenharmony_ci				ctx->minorversion,
117962306a36Sopenharmony_ci				ctx->nfs_server.nconnect,
118062306a36Sopenharmony_ci				ctx->nfs_server.max_connect,
118162306a36Sopenharmony_ci				fc->net_ns,
118262306a36Sopenharmony_ci				&ctx->xprtsec);
118362306a36Sopenharmony_ci	if (error < 0)
118462306a36Sopenharmony_ci		return error;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	if (ctx->rsize)
118762306a36Sopenharmony_ci		server->rsize = nfs_io_size(ctx->rsize, server->nfs_client->cl_proto);
118862306a36Sopenharmony_ci	if (ctx->wsize)
118962306a36Sopenharmony_ci		server->wsize = nfs_io_size(ctx->wsize, server->nfs_client->cl_proto);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	server->acregmin = ctx->acregmin * HZ;
119262306a36Sopenharmony_ci	server->acregmax = ctx->acregmax * HZ;
119362306a36Sopenharmony_ci	server->acdirmin = ctx->acdirmin * HZ;
119462306a36Sopenharmony_ci	server->acdirmax = ctx->acdirmax * HZ;
119562306a36Sopenharmony_ci	server->port     = ctx->nfs_server.port;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	return nfs_init_server_rpcclient(server, &timeparms,
119862306a36Sopenharmony_ci					 ctx->selected_flavor);
119962306a36Sopenharmony_ci}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci/*
120262306a36Sopenharmony_ci * Create a version 4 volume record
120362306a36Sopenharmony_ci * - keyed on server and FSID
120462306a36Sopenharmony_ci */
120562306a36Sopenharmony_cistruct nfs_server *nfs4_create_server(struct fs_context *fc)
120662306a36Sopenharmony_ci{
120762306a36Sopenharmony_ci	struct nfs_fs_context *ctx = nfs_fc2context(fc);
120862306a36Sopenharmony_ci	struct nfs_server *server;
120962306a36Sopenharmony_ci	bool auth_probe;
121062306a36Sopenharmony_ci	int error;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	server = nfs_alloc_server();
121362306a36Sopenharmony_ci	if (!server)
121462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	server->cred = get_cred(fc->cred);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	auth_probe = ctx->auth_info.flavor_len < 1;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	/* set up the general RPC client */
122162306a36Sopenharmony_ci	error = nfs4_init_server(server, fc);
122262306a36Sopenharmony_ci	if (error < 0)
122362306a36Sopenharmony_ci		goto error;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	error = nfs4_server_common_setup(server, ctx->mntfh, auth_probe);
122662306a36Sopenharmony_ci	if (error < 0)
122762306a36Sopenharmony_ci		goto error;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	return server;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_cierror:
123262306a36Sopenharmony_ci	nfs_free_server(server);
123362306a36Sopenharmony_ci	return ERR_PTR(error);
123462306a36Sopenharmony_ci}
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci/*
123762306a36Sopenharmony_ci * Create an NFS4 referral server record
123862306a36Sopenharmony_ci */
123962306a36Sopenharmony_cistruct nfs_server *nfs4_create_referral_server(struct fs_context *fc)
124062306a36Sopenharmony_ci{
124162306a36Sopenharmony_ci	struct nfs_fs_context *ctx = nfs_fc2context(fc);
124262306a36Sopenharmony_ci	struct nfs_client *parent_client;
124362306a36Sopenharmony_ci	struct nfs_server *server, *parent_server;
124462306a36Sopenharmony_ci	int proto, error;
124562306a36Sopenharmony_ci	bool auth_probe;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	server = nfs_alloc_server();
124862306a36Sopenharmony_ci	if (!server)
124962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	parent_server = NFS_SB(ctx->clone_data.sb);
125262306a36Sopenharmony_ci	parent_client = parent_server->nfs_client;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	server->cred = get_cred(parent_server->cred);
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	/* Initialise the client representation from the parent server */
125762306a36Sopenharmony_ci	nfs_server_copy_userdata(server, parent_server);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	/* Get a client representation */
126062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA)
126162306a36Sopenharmony_ci	rpc_set_port(&ctx->nfs_server.address, NFS_RDMA_PORT);
126262306a36Sopenharmony_ci	error = nfs4_set_client(server,
126362306a36Sopenharmony_ci				ctx->nfs_server.hostname,
126462306a36Sopenharmony_ci				&ctx->nfs_server._address,
126562306a36Sopenharmony_ci				ctx->nfs_server.addrlen,
126662306a36Sopenharmony_ci				parent_client->cl_ipaddr,
126762306a36Sopenharmony_ci				XPRT_TRANSPORT_RDMA,
126862306a36Sopenharmony_ci				parent_server->client->cl_timeout,
126962306a36Sopenharmony_ci				parent_client->cl_mvops->minor_version,
127062306a36Sopenharmony_ci				parent_client->cl_nconnect,
127162306a36Sopenharmony_ci				parent_client->cl_max_connect,
127262306a36Sopenharmony_ci				parent_client->cl_net,
127362306a36Sopenharmony_ci				&parent_client->cl_xprtsec);
127462306a36Sopenharmony_ci	if (!error)
127562306a36Sopenharmony_ci		goto init_server;
127662306a36Sopenharmony_ci#endif	/* IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA) */
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	proto = XPRT_TRANSPORT_TCP;
127962306a36Sopenharmony_ci	if (parent_client->cl_xprtsec.policy != RPC_XPRTSEC_NONE)
128062306a36Sopenharmony_ci		proto = XPRT_TRANSPORT_TCP_TLS;
128162306a36Sopenharmony_ci	rpc_set_port(&ctx->nfs_server.address, NFS_PORT);
128262306a36Sopenharmony_ci	error = nfs4_set_client(server,
128362306a36Sopenharmony_ci				ctx->nfs_server.hostname,
128462306a36Sopenharmony_ci				&ctx->nfs_server._address,
128562306a36Sopenharmony_ci				ctx->nfs_server.addrlen,
128662306a36Sopenharmony_ci				parent_client->cl_ipaddr,
128762306a36Sopenharmony_ci				proto,
128862306a36Sopenharmony_ci				parent_server->client->cl_timeout,
128962306a36Sopenharmony_ci				parent_client->cl_mvops->minor_version,
129062306a36Sopenharmony_ci				parent_client->cl_nconnect,
129162306a36Sopenharmony_ci				parent_client->cl_max_connect,
129262306a36Sopenharmony_ci				parent_client->cl_net,
129362306a36Sopenharmony_ci				&parent_client->cl_xprtsec);
129462306a36Sopenharmony_ci	if (error < 0)
129562306a36Sopenharmony_ci		goto error;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA)
129862306a36Sopenharmony_ciinit_server:
129962306a36Sopenharmony_ci#endif
130062306a36Sopenharmony_ci	error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout,
130162306a36Sopenharmony_ci					  ctx->selected_flavor);
130262306a36Sopenharmony_ci	if (error < 0)
130362306a36Sopenharmony_ci		goto error;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	auth_probe = parent_server->auth_info.flavor_len < 1;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	error = nfs4_server_common_setup(server, ctx->mntfh, auth_probe);
130862306a36Sopenharmony_ci	if (error < 0)
130962306a36Sopenharmony_ci		goto error;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	return server;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_cierror:
131462306a36Sopenharmony_ci	nfs_free_server(server);
131562306a36Sopenharmony_ci	return ERR_PTR(error);
131662306a36Sopenharmony_ci}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci/**
131962306a36Sopenharmony_ci * nfs4_update_server - Move an nfs_server to a different nfs_client
132062306a36Sopenharmony_ci *
132162306a36Sopenharmony_ci * @server: represents FSID to be moved
132262306a36Sopenharmony_ci * @hostname: new end-point's hostname
132362306a36Sopenharmony_ci * @sap: new end-point's socket address
132462306a36Sopenharmony_ci * @salen: size of "sap"
132562306a36Sopenharmony_ci * @net: net namespace
132662306a36Sopenharmony_ci *
132762306a36Sopenharmony_ci * The nfs_server must be quiescent before this function is invoked.
132862306a36Sopenharmony_ci * Either its session is drained (NFSv4.1+), or its transport is
132962306a36Sopenharmony_ci * plugged and drained (NFSv4.0).
133062306a36Sopenharmony_ci *
133162306a36Sopenharmony_ci * Returns zero on success, or a negative errno value.
133262306a36Sopenharmony_ci */
133362306a36Sopenharmony_ciint nfs4_update_server(struct nfs_server *server, const char *hostname,
133462306a36Sopenharmony_ci		       struct sockaddr_storage *sap, size_t salen, struct net *net)
133562306a36Sopenharmony_ci{
133662306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
133762306a36Sopenharmony_ci	struct rpc_clnt *clnt = server->client;
133862306a36Sopenharmony_ci	struct xprt_create xargs = {
133962306a36Sopenharmony_ci		.ident		= clp->cl_proto,
134062306a36Sopenharmony_ci		.net		= net,
134162306a36Sopenharmony_ci		.dstaddr	= (struct sockaddr *)sap,
134262306a36Sopenharmony_ci		.addrlen	= salen,
134362306a36Sopenharmony_ci		.servername	= hostname,
134462306a36Sopenharmony_ci		/* cel: bleh. We might need to pass TLS parameters here */
134562306a36Sopenharmony_ci	};
134662306a36Sopenharmony_ci	char buf[INET6_ADDRSTRLEN + 1];
134762306a36Sopenharmony_ci	struct sockaddr_storage address;
134862306a36Sopenharmony_ci	struct sockaddr *localaddr = (struct sockaddr *)&address;
134962306a36Sopenharmony_ci	int error;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	error = rpc_switch_client_transport(clnt, &xargs, clnt->cl_timeout);
135262306a36Sopenharmony_ci	if (error != 0)
135362306a36Sopenharmony_ci		return error;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	error = rpc_localaddr(clnt, localaddr, sizeof(address));
135662306a36Sopenharmony_ci	if (error != 0)
135762306a36Sopenharmony_ci		return error;
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	if (rpc_ntop(localaddr, buf, sizeof(buf)) == 0)
136062306a36Sopenharmony_ci		return -EAFNOSUPPORT;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	nfs_server_remove_lists(server);
136362306a36Sopenharmony_ci	set_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status);
136462306a36Sopenharmony_ci	error = nfs4_set_client(server, hostname, sap, salen, buf,
136562306a36Sopenharmony_ci				clp->cl_proto, clnt->cl_timeout,
136662306a36Sopenharmony_ci				clp->cl_minorversion,
136762306a36Sopenharmony_ci				clp->cl_nconnect, clp->cl_max_connect,
136862306a36Sopenharmony_ci				net, &clp->cl_xprtsec);
136962306a36Sopenharmony_ci	clear_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status);
137062306a36Sopenharmony_ci	if (error != 0) {
137162306a36Sopenharmony_ci		nfs_server_insert_lists(server);
137262306a36Sopenharmony_ci		return error;
137362306a36Sopenharmony_ci	}
137462306a36Sopenharmony_ci	nfs_put_client(clp);
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	if (server->nfs_client->cl_hostname == NULL) {
137762306a36Sopenharmony_ci		server->nfs_client->cl_hostname = kstrdup(hostname, GFP_KERNEL);
137862306a36Sopenharmony_ci		if (server->nfs_client->cl_hostname == NULL)
137962306a36Sopenharmony_ci			return -ENOMEM;
138062306a36Sopenharmony_ci	}
138162306a36Sopenharmony_ci	nfs_server_insert_lists(server);
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	return nfs_probe_server(server, NFS_FH(d_inode(server->super->s_root)));
138462306a36Sopenharmony_ci}
1385