162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* client.c: NFS client sharing and management code
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/sched.h>
1262306a36Sopenharmony_ci#include <linux/time.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/mm.h>
1562306a36Sopenharmony_ci#include <linux/string.h>
1662306a36Sopenharmony_ci#include <linux/stat.h>
1762306a36Sopenharmony_ci#include <linux/errno.h>
1862306a36Sopenharmony_ci#include <linux/unistd.h>
1962306a36Sopenharmony_ci#include <linux/sunrpc/addr.h>
2062306a36Sopenharmony_ci#include <linux/sunrpc/clnt.h>
2162306a36Sopenharmony_ci#include <linux/sunrpc/stats.h>
2262306a36Sopenharmony_ci#include <linux/sunrpc/metrics.h>
2362306a36Sopenharmony_ci#include <linux/sunrpc/xprtsock.h>
2462306a36Sopenharmony_ci#include <linux/sunrpc/xprtrdma.h>
2562306a36Sopenharmony_ci#include <linux/nfs_fs.h>
2662306a36Sopenharmony_ci#include <linux/nfs_mount.h>
2762306a36Sopenharmony_ci#include <linux/nfs4_mount.h>
2862306a36Sopenharmony_ci#include <linux/lockd/bind.h>
2962306a36Sopenharmony_ci#include <linux/seq_file.h>
3062306a36Sopenharmony_ci#include <linux/mount.h>
3162306a36Sopenharmony_ci#include <linux/vfs.h>
3262306a36Sopenharmony_ci#include <linux/inet.h>
3362306a36Sopenharmony_ci#include <linux/in6.h>
3462306a36Sopenharmony_ci#include <linux/slab.h>
3562306a36Sopenharmony_ci#include <linux/idr.h>
3662306a36Sopenharmony_ci#include <net/ipv6.h>
3762306a36Sopenharmony_ci#include <linux/nfs_xdr.h>
3862306a36Sopenharmony_ci#include <linux/sunrpc/bc_xprt.h>
3962306a36Sopenharmony_ci#include <linux/nsproxy.h>
4062306a36Sopenharmony_ci#include <linux/pid_namespace.h>
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#include "nfs4_fs.h"
4462306a36Sopenharmony_ci#include "callback.h"
4562306a36Sopenharmony_ci#include "delegation.h"
4662306a36Sopenharmony_ci#include "iostat.h"
4762306a36Sopenharmony_ci#include "internal.h"
4862306a36Sopenharmony_ci#include "fscache.h"
4962306a36Sopenharmony_ci#include "pnfs.h"
5062306a36Sopenharmony_ci#include "nfs.h"
5162306a36Sopenharmony_ci#include "netns.h"
5262306a36Sopenharmony_ci#include "sysfs.h"
5362306a36Sopenharmony_ci#include "nfs42.h"
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define NFSDBG_FACILITY		NFSDBG_CLIENT
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);
5862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(nfs_version_lock);
5962306a36Sopenharmony_cistatic DEFINE_MUTEX(nfs_version_mutex);
6062306a36Sopenharmony_cistatic LIST_HEAD(nfs_versions);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/*
6362306a36Sopenharmony_ci * RPC cruft for NFS
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_cistatic const struct rpc_version *nfs_version[5] = {
6662306a36Sopenharmony_ci	[2] = NULL,
6762306a36Sopenharmony_ci	[3] = NULL,
6862306a36Sopenharmony_ci	[4] = NULL,
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ciconst struct rpc_program nfs_program = {
7262306a36Sopenharmony_ci	.name			= "nfs",
7362306a36Sopenharmony_ci	.number			= NFS_PROGRAM,
7462306a36Sopenharmony_ci	.nrvers			= ARRAY_SIZE(nfs_version),
7562306a36Sopenharmony_ci	.version		= nfs_version,
7662306a36Sopenharmony_ci	.stats			= &nfs_rpcstat,
7762306a36Sopenharmony_ci	.pipe_dir_name		= NFS_PIPE_DIRNAME,
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistruct rpc_stat nfs_rpcstat = {
8162306a36Sopenharmony_ci	.program		= &nfs_program
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic struct nfs_subversion *find_nfs_version(unsigned int version)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct nfs_subversion *nfs;
8762306a36Sopenharmony_ci	spin_lock(&nfs_version_lock);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	list_for_each_entry(nfs, &nfs_versions, list) {
9062306a36Sopenharmony_ci		if (nfs->rpc_ops->version == version) {
9162306a36Sopenharmony_ci			spin_unlock(&nfs_version_lock);
9262306a36Sopenharmony_ci			return nfs;
9362306a36Sopenharmony_ci		}
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	spin_unlock(&nfs_version_lock);
9762306a36Sopenharmony_ci	return ERR_PTR(-EPROTONOSUPPORT);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistruct nfs_subversion *get_nfs_version(unsigned int version)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct nfs_subversion *nfs = find_nfs_version(version);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (IS_ERR(nfs)) {
10562306a36Sopenharmony_ci		mutex_lock(&nfs_version_mutex);
10662306a36Sopenharmony_ci		request_module("nfsv%d", version);
10762306a36Sopenharmony_ci		nfs = find_nfs_version(version);
10862306a36Sopenharmony_ci		mutex_unlock(&nfs_version_mutex);
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (!IS_ERR(nfs) && !try_module_get(nfs->owner))
11262306a36Sopenharmony_ci		return ERR_PTR(-EAGAIN);
11362306a36Sopenharmony_ci	return nfs;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_civoid put_nfs_version(struct nfs_subversion *nfs)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	module_put(nfs->owner);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_civoid register_nfs_version(struct nfs_subversion *nfs)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	spin_lock(&nfs_version_lock);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	list_add(&nfs->list, &nfs_versions);
12662306a36Sopenharmony_ci	nfs_version[nfs->rpc_ops->version] = nfs->rpc_vers;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	spin_unlock(&nfs_version_lock);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(register_nfs_version);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_civoid unregister_nfs_version(struct nfs_subversion *nfs)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	spin_lock(&nfs_version_lock);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	nfs_version[nfs->rpc_ops->version] = NULL;
13762306a36Sopenharmony_ci	list_del(&nfs->list);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	spin_unlock(&nfs_version_lock);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(unregister_nfs_version);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/*
14462306a36Sopenharmony_ci * Allocate a shared client record
14562306a36Sopenharmony_ci *
14662306a36Sopenharmony_ci * Since these are allocated/deallocated very rarely, we don't
14762306a36Sopenharmony_ci * bother putting them in a slab cache...
14862306a36Sopenharmony_ci */
14962306a36Sopenharmony_cistruct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct nfs_client *clp;
15262306a36Sopenharmony_ci	int err = -ENOMEM;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)
15562306a36Sopenharmony_ci		goto error_0;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	clp->cl_minorversion = cl_init->minorversion;
15862306a36Sopenharmony_ci	clp->cl_nfs_mod = cl_init->nfs_mod;
15962306a36Sopenharmony_ci	if (!try_module_get(clp->cl_nfs_mod->owner))
16062306a36Sopenharmony_ci		goto error_dealloc;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	clp->rpc_ops = clp->cl_nfs_mod->rpc_ops;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	refcount_set(&clp->cl_count, 1);
16562306a36Sopenharmony_ci	clp->cl_cons_state = NFS_CS_INITING;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	memcpy(&clp->cl_addr, cl_init->addr, cl_init->addrlen);
16862306a36Sopenharmony_ci	clp->cl_addrlen = cl_init->addrlen;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (cl_init->hostname) {
17162306a36Sopenharmony_ci		err = -ENOMEM;
17262306a36Sopenharmony_ci		clp->cl_hostname = kstrdup(cl_init->hostname, GFP_KERNEL);
17362306a36Sopenharmony_ci		if (!clp->cl_hostname)
17462306a36Sopenharmony_ci			goto error_cleanup;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	INIT_LIST_HEAD(&clp->cl_superblocks);
17862306a36Sopenharmony_ci	clp->cl_rpcclient = ERR_PTR(-EINVAL);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	clp->cl_flags = cl_init->init_flags;
18162306a36Sopenharmony_ci	clp->cl_proto = cl_init->proto;
18262306a36Sopenharmony_ci	clp->cl_nconnect = cl_init->nconnect;
18362306a36Sopenharmony_ci	clp->cl_max_connect = cl_init->max_connect ? cl_init->max_connect : 1;
18462306a36Sopenharmony_ci	clp->cl_net = get_net(cl_init->net);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	clp->cl_principal = "*";
18762306a36Sopenharmony_ci	clp->cl_xprtsec = cl_init->xprtsec;
18862306a36Sopenharmony_ci	return clp;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cierror_cleanup:
19162306a36Sopenharmony_ci	put_nfs_version(clp->cl_nfs_mod);
19262306a36Sopenharmony_cierror_dealloc:
19362306a36Sopenharmony_ci	kfree(clp);
19462306a36Sopenharmony_cierror_0:
19562306a36Sopenharmony_ci	return ERR_PTR(err);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_alloc_client);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NFS_V4)
20062306a36Sopenharmony_cistatic void nfs_cleanup_cb_ident_idr(struct net *net)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(net, nfs_net_id);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	idr_destroy(&nn->cb_ident_idr);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci/* nfs_client_lock held */
20862306a36Sopenharmony_cistatic void nfs_cb_idr_remove_locked(struct nfs_client *clp)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (clp->cl_cb_ident)
21362306a36Sopenharmony_ci		idr_remove(&nn->cb_ident_idr, clp->cl_cb_ident);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic void pnfs_init_server(struct nfs_server *server)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC");
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci#else
22262306a36Sopenharmony_cistatic void nfs_cleanup_cb_ident_idr(struct net *net)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic void nfs_cb_idr_remove_locked(struct nfs_client *clp)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic void pnfs_init_server(struct nfs_server *server)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4 */
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci/*
23762306a36Sopenharmony_ci * Destroy a shared client record
23862306a36Sopenharmony_ci */
23962306a36Sopenharmony_civoid nfs_free_client(struct nfs_client *clp)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	/* -EIO all pending I/O */
24262306a36Sopenharmony_ci	if (!IS_ERR(clp->cl_rpcclient))
24362306a36Sopenharmony_ci		rpc_shutdown_client(clp->cl_rpcclient);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	put_net(clp->cl_net);
24662306a36Sopenharmony_ci	put_nfs_version(clp->cl_nfs_mod);
24762306a36Sopenharmony_ci	kfree(clp->cl_hostname);
24862306a36Sopenharmony_ci	kfree(clp->cl_acceptor);
24962306a36Sopenharmony_ci	kfree(clp);
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_free_client);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/*
25462306a36Sopenharmony_ci * Release a reference to a shared client record
25562306a36Sopenharmony_ci */
25662306a36Sopenharmony_civoid nfs_put_client(struct nfs_client *clp)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct nfs_net *nn;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (!clp)
26162306a36Sopenharmony_ci		return;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	nn = net_generic(clp->cl_net, nfs_net_id);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (refcount_dec_and_lock(&clp->cl_count, &nn->nfs_client_lock)) {
26662306a36Sopenharmony_ci		list_del(&clp->cl_share_link);
26762306a36Sopenharmony_ci		nfs_cb_idr_remove_locked(clp);
26862306a36Sopenharmony_ci		spin_unlock(&nn->nfs_client_lock);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		WARN_ON_ONCE(!list_empty(&clp->cl_superblocks));
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci		clp->rpc_ops->free_client(clp);
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_put_client);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/*
27862306a36Sopenharmony_ci * Find an nfs_client on the list that matches the initialisation data
27962306a36Sopenharmony_ci * that is supplied.
28062306a36Sopenharmony_ci */
28162306a36Sopenharmony_cistatic struct nfs_client *nfs_match_client(const struct nfs_client_initdata *data)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct nfs_client *clp;
28462306a36Sopenharmony_ci	const struct sockaddr *sap = (struct sockaddr *)data->addr;
28562306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(data->net, nfs_net_id);
28662306a36Sopenharmony_ci	int error;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ciagain:
28962306a36Sopenharmony_ci	list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
29062306a36Sopenharmony_ci	        const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
29162306a36Sopenharmony_ci		/* Don't match clients that failed to initialise properly */
29262306a36Sopenharmony_ci		if (clp->cl_cons_state < 0)
29362306a36Sopenharmony_ci			continue;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		/* If a client is still initializing then we need to wait */
29662306a36Sopenharmony_ci		if (clp->cl_cons_state > NFS_CS_READY) {
29762306a36Sopenharmony_ci			refcount_inc(&clp->cl_count);
29862306a36Sopenharmony_ci			spin_unlock(&nn->nfs_client_lock);
29962306a36Sopenharmony_ci			error = nfs_wait_client_init_complete(clp);
30062306a36Sopenharmony_ci			nfs_put_client(clp);
30162306a36Sopenharmony_ci			spin_lock(&nn->nfs_client_lock);
30262306a36Sopenharmony_ci			if (error < 0)
30362306a36Sopenharmony_ci				return ERR_PTR(error);
30462306a36Sopenharmony_ci			goto again;
30562306a36Sopenharmony_ci		}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		/* Different NFS versions cannot share the same nfs_client */
30862306a36Sopenharmony_ci		if (clp->rpc_ops != data->nfs_mod->rpc_ops)
30962306a36Sopenharmony_ci			continue;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		if (clp->cl_proto != data->proto)
31262306a36Sopenharmony_ci			continue;
31362306a36Sopenharmony_ci		/* Match nfsv4 minorversion */
31462306a36Sopenharmony_ci		if (clp->cl_minorversion != data->minorversion)
31562306a36Sopenharmony_ci			continue;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci		/* Match request for a dedicated DS */
31862306a36Sopenharmony_ci		if (test_bit(NFS_CS_DS, &data->init_flags) !=
31962306a36Sopenharmony_ci		    test_bit(NFS_CS_DS, &clp->cl_flags))
32062306a36Sopenharmony_ci			continue;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		/* Match the full socket address */
32362306a36Sopenharmony_ci		if (!rpc_cmp_addr_port(sap, clap))
32462306a36Sopenharmony_ci			/* Match all xprt_switch full socket addresses */
32562306a36Sopenharmony_ci			if (IS_ERR(clp->cl_rpcclient) ||
32662306a36Sopenharmony_ci                            !rpc_clnt_xprt_switch_has_addr(clp->cl_rpcclient,
32762306a36Sopenharmony_ci							   sap))
32862306a36Sopenharmony_ci				continue;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci		/* Match the xprt security policy */
33162306a36Sopenharmony_ci		if (clp->cl_xprtsec.policy != data->xprtsec.policy)
33262306a36Sopenharmony_ci			continue;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci		refcount_inc(&clp->cl_count);
33562306a36Sopenharmony_ci		return clp;
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci	return NULL;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci/*
34162306a36Sopenharmony_ci * Return true if @clp is done initializing, false if still working on it.
34262306a36Sopenharmony_ci *
34362306a36Sopenharmony_ci * Use nfs_client_init_status to check if it was successful.
34462306a36Sopenharmony_ci */
34562306a36Sopenharmony_cibool nfs_client_init_is_complete(const struct nfs_client *clp)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	return clp->cl_cons_state <= NFS_CS_READY;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_client_init_is_complete);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci/*
35262306a36Sopenharmony_ci * Return 0 if @clp was successfully initialized, -errno otherwise.
35362306a36Sopenharmony_ci *
35462306a36Sopenharmony_ci * This must be called *after* nfs_client_init_is_complete() returns true,
35562306a36Sopenharmony_ci * otherwise it will pop WARN_ON_ONCE and return -EINVAL
35662306a36Sopenharmony_ci */
35762306a36Sopenharmony_ciint nfs_client_init_status(const struct nfs_client *clp)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	/* called without checking nfs_client_init_is_complete */
36062306a36Sopenharmony_ci	if (clp->cl_cons_state > NFS_CS_READY) {
36162306a36Sopenharmony_ci		WARN_ON_ONCE(1);
36262306a36Sopenharmony_ci		return -EINVAL;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci	return clp->cl_cons_state;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_client_init_status);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ciint nfs_wait_client_init_complete(const struct nfs_client *clp)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	return wait_event_killable(nfs_client_active_wq,
37162306a36Sopenharmony_ci			nfs_client_init_is_complete(clp));
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_wait_client_init_complete);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci/*
37662306a36Sopenharmony_ci * Found an existing client.  Make sure it's ready before returning.
37762306a36Sopenharmony_ci */
37862306a36Sopenharmony_cistatic struct nfs_client *
37962306a36Sopenharmony_cinfs_found_client(const struct nfs_client_initdata *cl_init,
38062306a36Sopenharmony_ci		 struct nfs_client *clp)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	int error;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	error = nfs_wait_client_init_complete(clp);
38562306a36Sopenharmony_ci	if (error < 0) {
38662306a36Sopenharmony_ci		nfs_put_client(clp);
38762306a36Sopenharmony_ci		return ERR_PTR(-ERESTARTSYS);
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (clp->cl_cons_state < NFS_CS_READY) {
39162306a36Sopenharmony_ci		error = clp->cl_cons_state;
39262306a36Sopenharmony_ci		nfs_put_client(clp);
39362306a36Sopenharmony_ci		return ERR_PTR(error);
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	smp_rmb();
39762306a36Sopenharmony_ci	return clp;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci/*
40162306a36Sopenharmony_ci * Look up a client by IP address and protocol version
40262306a36Sopenharmony_ci * - creates a new record if one doesn't yet exist
40362306a36Sopenharmony_ci */
40462306a36Sopenharmony_cistruct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct nfs_client *clp, *new = NULL;
40762306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id);
40862306a36Sopenharmony_ci	const struct nfs_rpc_ops *rpc_ops = cl_init->nfs_mod->rpc_ops;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	if (cl_init->hostname == NULL) {
41162306a36Sopenharmony_ci		WARN_ON(1);
41262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* see if the client already exists */
41662306a36Sopenharmony_ci	do {
41762306a36Sopenharmony_ci		spin_lock(&nn->nfs_client_lock);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		clp = nfs_match_client(cl_init);
42062306a36Sopenharmony_ci		if (clp) {
42162306a36Sopenharmony_ci			spin_unlock(&nn->nfs_client_lock);
42262306a36Sopenharmony_ci			if (new)
42362306a36Sopenharmony_ci				new->rpc_ops->free_client(new);
42462306a36Sopenharmony_ci			if (IS_ERR(clp))
42562306a36Sopenharmony_ci				return clp;
42662306a36Sopenharmony_ci			return nfs_found_client(cl_init, clp);
42762306a36Sopenharmony_ci		}
42862306a36Sopenharmony_ci		if (new) {
42962306a36Sopenharmony_ci			list_add_tail(&new->cl_share_link,
43062306a36Sopenharmony_ci					&nn->nfs_client_list);
43162306a36Sopenharmony_ci			spin_unlock(&nn->nfs_client_lock);
43262306a36Sopenharmony_ci			return rpc_ops->init_client(new, cl_init);
43362306a36Sopenharmony_ci		}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		spin_unlock(&nn->nfs_client_lock);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		new = rpc_ops->alloc_client(cl_init);
43862306a36Sopenharmony_ci	} while (!IS_ERR(new));
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	return new;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_get_client);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci/*
44562306a36Sopenharmony_ci * Mark a server as ready or failed
44662306a36Sopenharmony_ci */
44762306a36Sopenharmony_civoid nfs_mark_client_ready(struct nfs_client *clp, int state)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	smp_wmb();
45062306a36Sopenharmony_ci	clp->cl_cons_state = state;
45162306a36Sopenharmony_ci	wake_up_all(&nfs_client_active_wq);
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_mark_client_ready);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci/*
45662306a36Sopenharmony_ci * Initialise the timeout values for a connection
45762306a36Sopenharmony_ci */
45862306a36Sopenharmony_civoid nfs_init_timeout_values(struct rpc_timeout *to, int proto,
45962306a36Sopenharmony_ci				    int timeo, int retrans)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	to->to_initval = timeo * HZ / 10;
46262306a36Sopenharmony_ci	to->to_retries = retrans;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	switch (proto) {
46562306a36Sopenharmony_ci	case XPRT_TRANSPORT_TCP:
46662306a36Sopenharmony_ci	case XPRT_TRANSPORT_TCP_TLS:
46762306a36Sopenharmony_ci	case XPRT_TRANSPORT_RDMA:
46862306a36Sopenharmony_ci		if (retrans == NFS_UNSPEC_RETRANS)
46962306a36Sopenharmony_ci			to->to_retries = NFS_DEF_TCP_RETRANS;
47062306a36Sopenharmony_ci		if (timeo == NFS_UNSPEC_TIMEO || to->to_initval == 0)
47162306a36Sopenharmony_ci			to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10;
47262306a36Sopenharmony_ci		if (to->to_initval > NFS_MAX_TCP_TIMEOUT)
47362306a36Sopenharmony_ci			to->to_initval = NFS_MAX_TCP_TIMEOUT;
47462306a36Sopenharmony_ci		to->to_increment = to->to_initval;
47562306a36Sopenharmony_ci		to->to_maxval = to->to_initval + (to->to_increment * to->to_retries);
47662306a36Sopenharmony_ci		if (to->to_maxval > NFS_MAX_TCP_TIMEOUT)
47762306a36Sopenharmony_ci			to->to_maxval = NFS_MAX_TCP_TIMEOUT;
47862306a36Sopenharmony_ci		if (to->to_maxval < to->to_initval)
47962306a36Sopenharmony_ci			to->to_maxval = to->to_initval;
48062306a36Sopenharmony_ci		to->to_exponential = 0;
48162306a36Sopenharmony_ci		break;
48262306a36Sopenharmony_ci	case XPRT_TRANSPORT_UDP:
48362306a36Sopenharmony_ci		if (retrans == NFS_UNSPEC_RETRANS)
48462306a36Sopenharmony_ci			to->to_retries = NFS_DEF_UDP_RETRANS;
48562306a36Sopenharmony_ci		if (timeo == NFS_UNSPEC_TIMEO || to->to_initval == 0)
48662306a36Sopenharmony_ci			to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10;
48762306a36Sopenharmony_ci		if (to->to_initval > NFS_MAX_UDP_TIMEOUT)
48862306a36Sopenharmony_ci			to->to_initval = NFS_MAX_UDP_TIMEOUT;
48962306a36Sopenharmony_ci		to->to_maxval = NFS_MAX_UDP_TIMEOUT;
49062306a36Sopenharmony_ci		to->to_exponential = 1;
49162306a36Sopenharmony_ci		break;
49262306a36Sopenharmony_ci	default:
49362306a36Sopenharmony_ci		BUG();
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_init_timeout_values);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci/*
49962306a36Sopenharmony_ci * Create an RPC client handle
50062306a36Sopenharmony_ci */
50162306a36Sopenharmony_ciint nfs_create_rpc_client(struct nfs_client *clp,
50262306a36Sopenharmony_ci			  const struct nfs_client_initdata *cl_init,
50362306a36Sopenharmony_ci			  rpc_authflavor_t flavor)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	struct rpc_clnt		*clnt = NULL;
50662306a36Sopenharmony_ci	struct rpc_create_args args = {
50762306a36Sopenharmony_ci		.net		= clp->cl_net,
50862306a36Sopenharmony_ci		.protocol	= clp->cl_proto,
50962306a36Sopenharmony_ci		.nconnect	= clp->cl_nconnect,
51062306a36Sopenharmony_ci		.address	= (struct sockaddr *)&clp->cl_addr,
51162306a36Sopenharmony_ci		.addrsize	= clp->cl_addrlen,
51262306a36Sopenharmony_ci		.timeout	= cl_init->timeparms,
51362306a36Sopenharmony_ci		.servername	= clp->cl_hostname,
51462306a36Sopenharmony_ci		.nodename	= cl_init->nodename,
51562306a36Sopenharmony_ci		.program	= &nfs_program,
51662306a36Sopenharmony_ci		.version	= clp->rpc_ops->version,
51762306a36Sopenharmony_ci		.authflavor	= flavor,
51862306a36Sopenharmony_ci		.cred		= cl_init->cred,
51962306a36Sopenharmony_ci		.xprtsec	= cl_init->xprtsec,
52062306a36Sopenharmony_ci		.connect_timeout = cl_init->connect_timeout,
52162306a36Sopenharmony_ci		.reconnect_timeout = cl_init->reconnect_timeout,
52262306a36Sopenharmony_ci	};
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (test_bit(NFS_CS_DISCRTRY, &clp->cl_flags))
52562306a36Sopenharmony_ci		args.flags |= RPC_CLNT_CREATE_DISCRTRY;
52662306a36Sopenharmony_ci	if (test_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags))
52762306a36Sopenharmony_ci		args.flags |= RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT;
52862306a36Sopenharmony_ci	if (test_bit(NFS_CS_NORESVPORT, &clp->cl_flags))
52962306a36Sopenharmony_ci		args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
53062306a36Sopenharmony_ci	if (test_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags))
53162306a36Sopenharmony_ci		args.flags |= RPC_CLNT_CREATE_INFINITE_SLOTS;
53262306a36Sopenharmony_ci	if (test_bit(NFS_CS_NOPING, &clp->cl_flags))
53362306a36Sopenharmony_ci		args.flags |= RPC_CLNT_CREATE_NOPING;
53462306a36Sopenharmony_ci	if (test_bit(NFS_CS_REUSEPORT, &clp->cl_flags))
53562306a36Sopenharmony_ci		args.flags |= RPC_CLNT_CREATE_REUSEPORT;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (!IS_ERR(clp->cl_rpcclient))
53862306a36Sopenharmony_ci		return 0;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	clnt = rpc_create(&args);
54162306a36Sopenharmony_ci	if (IS_ERR(clnt)) {
54262306a36Sopenharmony_ci		dprintk("%s: cannot create RPC client. Error = %ld\n",
54362306a36Sopenharmony_ci				__func__, PTR_ERR(clnt));
54462306a36Sopenharmony_ci		return PTR_ERR(clnt);
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	clnt->cl_principal = clp->cl_principal;
54862306a36Sopenharmony_ci	clp->cl_rpcclient = clnt;
54962306a36Sopenharmony_ci	clnt->cl_max_connect = clp->cl_max_connect;
55062306a36Sopenharmony_ci	return 0;
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_create_rpc_client);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci/*
55562306a36Sopenharmony_ci * Version 2 or 3 client destruction
55662306a36Sopenharmony_ci */
55762306a36Sopenharmony_cistatic void nfs_destroy_server(struct nfs_server *server)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	if (server->nlm_host)
56062306a36Sopenharmony_ci		nlmclnt_done(server->nlm_host);
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci/*
56462306a36Sopenharmony_ci * Version 2 or 3 lockd setup
56562306a36Sopenharmony_ci */
56662306a36Sopenharmony_cistatic int nfs_start_lockd(struct nfs_server *server)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct nlm_host *host;
56962306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
57062306a36Sopenharmony_ci	struct nlmclnt_initdata nlm_init = {
57162306a36Sopenharmony_ci		.hostname	= clp->cl_hostname,
57262306a36Sopenharmony_ci		.address	= (struct sockaddr *)&clp->cl_addr,
57362306a36Sopenharmony_ci		.addrlen	= clp->cl_addrlen,
57462306a36Sopenharmony_ci		.nfs_version	= clp->rpc_ops->version,
57562306a36Sopenharmony_ci		.noresvport	= server->flags & NFS_MOUNT_NORESVPORT ?
57662306a36Sopenharmony_ci					1 : 0,
57762306a36Sopenharmony_ci		.net		= clp->cl_net,
57862306a36Sopenharmony_ci		.nlmclnt_ops 	= clp->cl_nfs_mod->rpc_ops->nlmclnt_ops,
57962306a36Sopenharmony_ci		.cred		= server->cred,
58062306a36Sopenharmony_ci	};
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	if (nlm_init.nfs_version > 3)
58362306a36Sopenharmony_ci		return 0;
58462306a36Sopenharmony_ci	if ((server->flags & NFS_MOUNT_LOCAL_FLOCK) &&
58562306a36Sopenharmony_ci			(server->flags & NFS_MOUNT_LOCAL_FCNTL))
58662306a36Sopenharmony_ci		return 0;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	switch (clp->cl_proto) {
58962306a36Sopenharmony_ci		default:
59062306a36Sopenharmony_ci			nlm_init.protocol = IPPROTO_TCP;
59162306a36Sopenharmony_ci			break;
59262306a36Sopenharmony_ci#ifndef CONFIG_NFS_DISABLE_UDP_SUPPORT
59362306a36Sopenharmony_ci		case XPRT_TRANSPORT_UDP:
59462306a36Sopenharmony_ci			nlm_init.protocol = IPPROTO_UDP;
59562306a36Sopenharmony_ci#endif
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	host = nlmclnt_init(&nlm_init);
59962306a36Sopenharmony_ci	if (IS_ERR(host))
60062306a36Sopenharmony_ci		return PTR_ERR(host);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	server->nlm_host = host;
60362306a36Sopenharmony_ci	server->destroy = nfs_destroy_server;
60462306a36Sopenharmony_ci	nfs_sysfs_link_rpc_client(server, nlmclnt_rpc_clnt(host), NULL);
60562306a36Sopenharmony_ci	return 0;
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci/*
60962306a36Sopenharmony_ci * Create a general RPC client
61062306a36Sopenharmony_ci */
61162306a36Sopenharmony_ciint nfs_init_server_rpcclient(struct nfs_server *server,
61262306a36Sopenharmony_ci		const struct rpc_timeout *timeo,
61362306a36Sopenharmony_ci		rpc_authflavor_t pseudoflavour)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	server->client = rpc_clone_client_set_auth(clp->cl_rpcclient,
61862306a36Sopenharmony_ci							pseudoflavour);
61962306a36Sopenharmony_ci	if (IS_ERR(server->client)) {
62062306a36Sopenharmony_ci		dprintk("%s: couldn't create rpc_client!\n", __func__);
62162306a36Sopenharmony_ci		return PTR_ERR(server->client);
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	memcpy(&server->client->cl_timeout_default,
62562306a36Sopenharmony_ci			timeo,
62662306a36Sopenharmony_ci			sizeof(server->client->cl_timeout_default));
62762306a36Sopenharmony_ci	server->client->cl_timeout = &server->client->cl_timeout_default;
62862306a36Sopenharmony_ci	server->client->cl_softrtry = 0;
62962306a36Sopenharmony_ci	if (server->flags & NFS_MOUNT_SOFTERR)
63062306a36Sopenharmony_ci		server->client->cl_softerr = 1;
63162306a36Sopenharmony_ci	if (server->flags & NFS_MOUNT_SOFT)
63262306a36Sopenharmony_ci		server->client->cl_softrtry = 1;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	nfs_sysfs_link_rpc_client(server, server->client, NULL);
63562306a36Sopenharmony_ci	return 0;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_init_server_rpcclient);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci/**
64062306a36Sopenharmony_ci * nfs_init_client - Initialise an NFS2 or NFS3 client
64162306a36Sopenharmony_ci *
64262306a36Sopenharmony_ci * @clp: nfs_client to initialise
64362306a36Sopenharmony_ci * @cl_init: Initialisation parameters
64462306a36Sopenharmony_ci *
64562306a36Sopenharmony_ci * Returns pointer to an NFS client, or an ERR_PTR value.
64662306a36Sopenharmony_ci */
64762306a36Sopenharmony_cistruct nfs_client *nfs_init_client(struct nfs_client *clp,
64862306a36Sopenharmony_ci				   const struct nfs_client_initdata *cl_init)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	int error;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	/* the client is already initialised */
65362306a36Sopenharmony_ci	if (clp->cl_cons_state == NFS_CS_READY)
65462306a36Sopenharmony_ci		return clp;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	/*
65762306a36Sopenharmony_ci	 * Create a client RPC handle for doing FSSTAT with UNIX auth only
65862306a36Sopenharmony_ci	 * - RFC 2623, sec 2.3.2
65962306a36Sopenharmony_ci	 */
66062306a36Sopenharmony_ci	error = nfs_create_rpc_client(clp, cl_init, RPC_AUTH_UNIX);
66162306a36Sopenharmony_ci	nfs_mark_client_ready(clp, error == 0 ? NFS_CS_READY : error);
66262306a36Sopenharmony_ci	if (error < 0) {
66362306a36Sopenharmony_ci		nfs_put_client(clp);
66462306a36Sopenharmony_ci		clp = ERR_PTR(error);
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci	return clp;
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_init_client);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci/*
67162306a36Sopenharmony_ci * Create a version 2 or 3 client
67262306a36Sopenharmony_ci */
67362306a36Sopenharmony_cistatic int nfs_init_server(struct nfs_server *server,
67462306a36Sopenharmony_ci			   const struct fs_context *fc)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	const struct nfs_fs_context *ctx = nfs_fc2context(fc);
67762306a36Sopenharmony_ci	struct rpc_timeout timeparms;
67862306a36Sopenharmony_ci	struct nfs_client_initdata cl_init = {
67962306a36Sopenharmony_ci		.hostname = ctx->nfs_server.hostname,
68062306a36Sopenharmony_ci		.addr = &ctx->nfs_server._address,
68162306a36Sopenharmony_ci		.addrlen = ctx->nfs_server.addrlen,
68262306a36Sopenharmony_ci		.nfs_mod = ctx->nfs_mod,
68362306a36Sopenharmony_ci		.proto = ctx->nfs_server.protocol,
68462306a36Sopenharmony_ci		.net = fc->net_ns,
68562306a36Sopenharmony_ci		.timeparms = &timeparms,
68662306a36Sopenharmony_ci		.cred = server->cred,
68762306a36Sopenharmony_ci		.nconnect = ctx->nfs_server.nconnect,
68862306a36Sopenharmony_ci		.init_flags = (1UL << NFS_CS_REUSEPORT),
68962306a36Sopenharmony_ci		.xprtsec = ctx->xprtsec,
69062306a36Sopenharmony_ci	};
69162306a36Sopenharmony_ci	struct nfs_client *clp;
69262306a36Sopenharmony_ci	int error;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	nfs_init_timeout_values(&timeparms, ctx->nfs_server.protocol,
69562306a36Sopenharmony_ci				ctx->timeo, ctx->retrans);
69662306a36Sopenharmony_ci	if (ctx->flags & NFS_MOUNT_NORESVPORT)
69762306a36Sopenharmony_ci		set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/* Allocate or find a client reference we can use */
70062306a36Sopenharmony_ci	clp = nfs_get_client(&cl_init);
70162306a36Sopenharmony_ci	if (IS_ERR(clp))
70262306a36Sopenharmony_ci		return PTR_ERR(clp);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	server->nfs_client = clp;
70562306a36Sopenharmony_ci	nfs_sysfs_add_server(server);
70662306a36Sopenharmony_ci	nfs_sysfs_link_rpc_client(server, clp->cl_rpcclient, "_state");
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	/* Initialise the client representation from the mount data */
70962306a36Sopenharmony_ci	server->flags = ctx->flags;
71062306a36Sopenharmony_ci	server->options = ctx->options;
71162306a36Sopenharmony_ci	server->caps |= NFS_CAP_HARDLINKS | NFS_CAP_SYMLINKS;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	switch (clp->rpc_ops->version) {
71462306a36Sopenharmony_ci	case 2:
71562306a36Sopenharmony_ci		server->fattr_valid = NFS_ATTR_FATTR_V2;
71662306a36Sopenharmony_ci		break;
71762306a36Sopenharmony_ci	case 3:
71862306a36Sopenharmony_ci		server->fattr_valid = NFS_ATTR_FATTR_V3;
71962306a36Sopenharmony_ci		break;
72062306a36Sopenharmony_ci	default:
72162306a36Sopenharmony_ci		server->fattr_valid = NFS_ATTR_FATTR_V4;
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	if (ctx->rsize)
72562306a36Sopenharmony_ci		server->rsize = nfs_io_size(ctx->rsize, clp->cl_proto);
72662306a36Sopenharmony_ci	if (ctx->wsize)
72762306a36Sopenharmony_ci		server->wsize = nfs_io_size(ctx->wsize, clp->cl_proto);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	server->acregmin = ctx->acregmin * HZ;
73062306a36Sopenharmony_ci	server->acregmax = ctx->acregmax * HZ;
73162306a36Sopenharmony_ci	server->acdirmin = ctx->acdirmin * HZ;
73262306a36Sopenharmony_ci	server->acdirmax = ctx->acdirmax * HZ;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	/* Start lockd here, before we might error out */
73562306a36Sopenharmony_ci	error = nfs_start_lockd(server);
73662306a36Sopenharmony_ci	if (error < 0)
73762306a36Sopenharmony_ci		goto error;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	server->port = ctx->nfs_server.port;
74062306a36Sopenharmony_ci	server->auth_info = ctx->auth_info;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	error = nfs_init_server_rpcclient(server, &timeparms,
74362306a36Sopenharmony_ci					  ctx->selected_flavor);
74462306a36Sopenharmony_ci	if (error < 0)
74562306a36Sopenharmony_ci		goto error;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	/* Preserve the values of mount_server-related mount options */
74862306a36Sopenharmony_ci	if (ctx->mount_server.addrlen) {
74962306a36Sopenharmony_ci		memcpy(&server->mountd_address, &ctx->mount_server.address,
75062306a36Sopenharmony_ci			ctx->mount_server.addrlen);
75162306a36Sopenharmony_ci		server->mountd_addrlen = ctx->mount_server.addrlen;
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci	server->mountd_version = ctx->mount_server.version;
75462306a36Sopenharmony_ci	server->mountd_port = ctx->mount_server.port;
75562306a36Sopenharmony_ci	server->mountd_protocol = ctx->mount_server.protocol;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	server->namelen  = ctx->namlen;
75862306a36Sopenharmony_ci	return 0;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cierror:
76162306a36Sopenharmony_ci	server->nfs_client = NULL;
76262306a36Sopenharmony_ci	nfs_put_client(clp);
76362306a36Sopenharmony_ci	return error;
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci/*
76762306a36Sopenharmony_ci * Load up the server record from information gained in an fsinfo record
76862306a36Sopenharmony_ci */
76962306a36Sopenharmony_cistatic void nfs_server_set_fsinfo(struct nfs_server *server,
77062306a36Sopenharmony_ci				  struct nfs_fsinfo *fsinfo)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
77362306a36Sopenharmony_ci	unsigned long max_rpc_payload, raw_max_rpc_payload;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	/* Work out a lot of parameters */
77662306a36Sopenharmony_ci	if (server->rsize == 0)
77762306a36Sopenharmony_ci		server->rsize = nfs_io_size(fsinfo->rtpref, clp->cl_proto);
77862306a36Sopenharmony_ci	if (server->wsize == 0)
77962306a36Sopenharmony_ci		server->wsize = nfs_io_size(fsinfo->wtpref, clp->cl_proto);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	if (fsinfo->rtmax >= 512 && server->rsize > fsinfo->rtmax)
78262306a36Sopenharmony_ci		server->rsize = nfs_io_size(fsinfo->rtmax, clp->cl_proto);
78362306a36Sopenharmony_ci	if (fsinfo->wtmax >= 512 && server->wsize > fsinfo->wtmax)
78462306a36Sopenharmony_ci		server->wsize = nfs_io_size(fsinfo->wtmax, clp->cl_proto);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	raw_max_rpc_payload = rpc_max_payload(server->client);
78762306a36Sopenharmony_ci	max_rpc_payload = nfs_block_size(raw_max_rpc_payload, NULL);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	if (server->rsize > max_rpc_payload)
79062306a36Sopenharmony_ci		server->rsize = max_rpc_payload;
79162306a36Sopenharmony_ci	if (server->rsize > NFS_MAX_FILE_IO_SIZE)
79262306a36Sopenharmony_ci		server->rsize = NFS_MAX_FILE_IO_SIZE;
79362306a36Sopenharmony_ci	server->rpages = (server->rsize + PAGE_SIZE - 1) >> PAGE_SHIFT;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	if (server->wsize > max_rpc_payload)
79662306a36Sopenharmony_ci		server->wsize = max_rpc_payload;
79762306a36Sopenharmony_ci	if (server->wsize > NFS_MAX_FILE_IO_SIZE)
79862306a36Sopenharmony_ci		server->wsize = NFS_MAX_FILE_IO_SIZE;
79962306a36Sopenharmony_ci	server->wpages = (server->wsize + PAGE_SIZE - 1) >> PAGE_SHIFT;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	server->dtsize = nfs_block_size(fsinfo->dtpref, NULL);
80462306a36Sopenharmony_ci	if (server->dtsize > NFS_MAX_FILE_IO_SIZE)
80562306a36Sopenharmony_ci		server->dtsize = NFS_MAX_FILE_IO_SIZE;
80662306a36Sopenharmony_ci	if (server->dtsize > server->rsize)
80762306a36Sopenharmony_ci		server->dtsize = server->rsize;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if (server->flags & NFS_MOUNT_NOAC) {
81062306a36Sopenharmony_ci		server->acregmin = server->acregmax = 0;
81162306a36Sopenharmony_ci		server->acdirmin = server->acdirmax = 0;
81262306a36Sopenharmony_ci	}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	server->maxfilesize = fsinfo->maxfilesize;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	server->time_delta = fsinfo->time_delta;
81762306a36Sopenharmony_ci	server->change_attr_type = fsinfo->change_attr_type;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	server->clone_blksize = fsinfo->clone_blksize;
82062306a36Sopenharmony_ci	/* We're airborne Set socket buffersize */
82162306a36Sopenharmony_ci	rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_2
82462306a36Sopenharmony_ci	/*
82562306a36Sopenharmony_ci	 * Defaults until limited by the session parameters.
82662306a36Sopenharmony_ci	 */
82762306a36Sopenharmony_ci	server->gxasize = min_t(unsigned int, raw_max_rpc_payload,
82862306a36Sopenharmony_ci				XATTR_SIZE_MAX);
82962306a36Sopenharmony_ci	server->sxasize = min_t(unsigned int, raw_max_rpc_payload,
83062306a36Sopenharmony_ci				XATTR_SIZE_MAX);
83162306a36Sopenharmony_ci	server->lxasize = min_t(unsigned int, raw_max_rpc_payload,
83262306a36Sopenharmony_ci				nfs42_listxattr_xdrsize(XATTR_LIST_MAX));
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	if (fsinfo->xattr_support)
83562306a36Sopenharmony_ci		server->caps |= NFS_CAP_XATTR;
83662306a36Sopenharmony_ci#endif
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci/*
84062306a36Sopenharmony_ci * Probe filesystem information, including the FSID on v2/v3
84162306a36Sopenharmony_ci */
84262306a36Sopenharmony_cistatic int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	struct nfs_fsinfo fsinfo;
84562306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
84662306a36Sopenharmony_ci	int error;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	if (clp->rpc_ops->set_capabilities != NULL) {
84962306a36Sopenharmony_ci		error = clp->rpc_ops->set_capabilities(server, mntfh);
85062306a36Sopenharmony_ci		if (error < 0)
85162306a36Sopenharmony_ci			return error;
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	fsinfo.fattr = fattr;
85562306a36Sopenharmony_ci	fsinfo.nlayouttypes = 0;
85662306a36Sopenharmony_ci	memset(fsinfo.layouttype, 0, sizeof(fsinfo.layouttype));
85762306a36Sopenharmony_ci	error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo);
85862306a36Sopenharmony_ci	if (error < 0)
85962306a36Sopenharmony_ci		return error;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	nfs_server_set_fsinfo(server, &fsinfo);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	/* Get some general file system info */
86462306a36Sopenharmony_ci	if (server->namelen == 0) {
86562306a36Sopenharmony_ci		struct nfs_pathconf pathinfo;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci		pathinfo.fattr = fattr;
86862306a36Sopenharmony_ci		nfs_fattr_init(fattr);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci		if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0)
87162306a36Sopenharmony_ci			server->namelen = pathinfo.max_namelen;
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (clp->rpc_ops->discover_trunking != NULL &&
87562306a36Sopenharmony_ci			(server->caps & NFS_CAP_FS_LOCATIONS &&
87662306a36Sopenharmony_ci			 (server->flags & NFS_MOUNT_TRUNK_DISCOVERY))) {
87762306a36Sopenharmony_ci		error = clp->rpc_ops->discover_trunking(server, mntfh);
87862306a36Sopenharmony_ci		if (error < 0)
87962306a36Sopenharmony_ci			return error;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	return 0;
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci/*
88662306a36Sopenharmony_ci * Grab the destination's particulars, including lease expiry time.
88762306a36Sopenharmony_ci *
88862306a36Sopenharmony_ci * Returns zero if probe succeeded and retrieved FSID matches the FSID
88962306a36Sopenharmony_ci * we have cached.
89062306a36Sopenharmony_ci */
89162306a36Sopenharmony_ciint nfs_probe_server(struct nfs_server *server, struct nfs_fh *mntfh)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	struct nfs_fattr *fattr;
89462306a36Sopenharmony_ci	int error;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	fattr = nfs_alloc_fattr();
89762306a36Sopenharmony_ci	if (fattr == NULL)
89862306a36Sopenharmony_ci		return -ENOMEM;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	/* Sanity: the probe won't work if the destination server
90162306a36Sopenharmony_ci	 * does not recognize the migrated FH. */
90262306a36Sopenharmony_ci	error = nfs_probe_fsinfo(server, mntfh, fattr);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	nfs_free_fattr(fattr);
90562306a36Sopenharmony_ci	return error;
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_probe_server);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci/*
91062306a36Sopenharmony_ci * Copy useful information when duplicating a server record
91162306a36Sopenharmony_ci */
91262306a36Sopenharmony_civoid nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	target->flags = source->flags;
91562306a36Sopenharmony_ci	target->rsize = source->rsize;
91662306a36Sopenharmony_ci	target->wsize = source->wsize;
91762306a36Sopenharmony_ci	target->acregmin = source->acregmin;
91862306a36Sopenharmony_ci	target->acregmax = source->acregmax;
91962306a36Sopenharmony_ci	target->acdirmin = source->acdirmin;
92062306a36Sopenharmony_ci	target->acdirmax = source->acdirmax;
92162306a36Sopenharmony_ci	target->caps = source->caps;
92262306a36Sopenharmony_ci	target->options = source->options;
92362306a36Sopenharmony_ci	target->auth_info = source->auth_info;
92462306a36Sopenharmony_ci	target->port = source->port;
92562306a36Sopenharmony_ci}
92662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_server_copy_userdata);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_civoid nfs_server_insert_lists(struct nfs_server *server)
92962306a36Sopenharmony_ci{
93062306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
93162306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	spin_lock(&nn->nfs_client_lock);
93462306a36Sopenharmony_ci	list_add_tail_rcu(&server->client_link, &clp->cl_superblocks);
93562306a36Sopenharmony_ci	list_add_tail(&server->master_link, &nn->nfs_volume_list);
93662306a36Sopenharmony_ci	clear_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state);
93762306a36Sopenharmony_ci	spin_unlock(&nn->nfs_client_lock);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci}
94062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_server_insert_lists);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_civoid nfs_server_remove_lists(struct nfs_server *server)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	struct nfs_client *clp = server->nfs_client;
94562306a36Sopenharmony_ci	struct nfs_net *nn;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	if (clp == NULL)
94862306a36Sopenharmony_ci		return;
94962306a36Sopenharmony_ci	nn = net_generic(clp->cl_net, nfs_net_id);
95062306a36Sopenharmony_ci	spin_lock(&nn->nfs_client_lock);
95162306a36Sopenharmony_ci	list_del_rcu(&server->client_link);
95262306a36Sopenharmony_ci	if (list_empty(&clp->cl_superblocks))
95362306a36Sopenharmony_ci		set_bit(NFS_CS_STOP_RENEW, &clp->cl_res_state);
95462306a36Sopenharmony_ci	list_del(&server->master_link);
95562306a36Sopenharmony_ci	spin_unlock(&nn->nfs_client_lock);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	synchronize_rcu();
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_server_remove_lists);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_cistatic DEFINE_IDA(s_sysfs_ids);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci/*
96462306a36Sopenharmony_ci * Allocate and initialise a server record
96562306a36Sopenharmony_ci */
96662306a36Sopenharmony_cistruct nfs_server *nfs_alloc_server(void)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	struct nfs_server *server;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL);
97162306a36Sopenharmony_ci	if (!server)
97262306a36Sopenharmony_ci		return NULL;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	server->s_sysfs_id = ida_alloc(&s_sysfs_ids, GFP_KERNEL);
97562306a36Sopenharmony_ci	if (server->s_sysfs_id < 0) {
97662306a36Sopenharmony_ci		kfree(server);
97762306a36Sopenharmony_ci		return NULL;
97862306a36Sopenharmony_ci	}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	server->client = server->client_acl = ERR_PTR(-EINVAL);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	/* Zero out the NFS state stuff */
98362306a36Sopenharmony_ci	INIT_LIST_HEAD(&server->client_link);
98462306a36Sopenharmony_ci	INIT_LIST_HEAD(&server->master_link);
98562306a36Sopenharmony_ci	INIT_LIST_HEAD(&server->delegations);
98662306a36Sopenharmony_ci	INIT_LIST_HEAD(&server->layouts);
98762306a36Sopenharmony_ci	INIT_LIST_HEAD(&server->state_owners_lru);
98862306a36Sopenharmony_ci	INIT_LIST_HEAD(&server->ss_copies);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	atomic_set(&server->active, 0);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	server->io_stats = nfs_alloc_iostats();
99362306a36Sopenharmony_ci	if (!server->io_stats) {
99462306a36Sopenharmony_ci		kfree(server);
99562306a36Sopenharmony_ci		return NULL;
99662306a36Sopenharmony_ci	}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	server->change_attr_type = NFS4_CHANGE_TYPE_IS_UNDEFINED;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	ida_init(&server->openowner_id);
100162306a36Sopenharmony_ci	ida_init(&server->lockowner_id);
100262306a36Sopenharmony_ci	pnfs_init_server(server);
100362306a36Sopenharmony_ci	rpc_init_wait_queue(&server->uoc_rpcwaitq, "NFS UOC");
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	return server;
100662306a36Sopenharmony_ci}
100762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_alloc_server);
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci/*
101062306a36Sopenharmony_ci * Free up a server record
101162306a36Sopenharmony_ci */
101262306a36Sopenharmony_civoid nfs_free_server(struct nfs_server *server)
101362306a36Sopenharmony_ci{
101462306a36Sopenharmony_ci	nfs_server_remove_lists(server);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	if (server->destroy != NULL)
101762306a36Sopenharmony_ci		server->destroy(server);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	if (!IS_ERR(server->client_acl))
102062306a36Sopenharmony_ci		rpc_shutdown_client(server->client_acl);
102162306a36Sopenharmony_ci	if (!IS_ERR(server->client))
102262306a36Sopenharmony_ci		rpc_shutdown_client(server->client);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	nfs_put_client(server->nfs_client);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	if (server->kobj.state_initialized) {
102762306a36Sopenharmony_ci		nfs_sysfs_remove_server(server);
102862306a36Sopenharmony_ci		kobject_put(&server->kobj);
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci	ida_free(&s_sysfs_ids, server->s_sysfs_id);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	ida_destroy(&server->lockowner_id);
103362306a36Sopenharmony_ci	ida_destroy(&server->openowner_id);
103462306a36Sopenharmony_ci	nfs_free_iostats(server->io_stats);
103562306a36Sopenharmony_ci	put_cred(server->cred);
103662306a36Sopenharmony_ci	kfree(server);
103762306a36Sopenharmony_ci	nfs_release_automount_timer();
103862306a36Sopenharmony_ci}
103962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_free_server);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci/*
104262306a36Sopenharmony_ci * Create a version 2 or 3 volume record
104362306a36Sopenharmony_ci * - keyed on server and FSID
104462306a36Sopenharmony_ci */
104562306a36Sopenharmony_cistruct nfs_server *nfs_create_server(struct fs_context *fc)
104662306a36Sopenharmony_ci{
104762306a36Sopenharmony_ci	struct nfs_fs_context *ctx = nfs_fc2context(fc);
104862306a36Sopenharmony_ci	struct nfs_server *server;
104962306a36Sopenharmony_ci	struct nfs_fattr *fattr;
105062306a36Sopenharmony_ci	int error;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	server = nfs_alloc_server();
105362306a36Sopenharmony_ci	if (!server)
105462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	server->cred = get_cred(fc->cred);
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	error = -ENOMEM;
105962306a36Sopenharmony_ci	fattr = nfs_alloc_fattr();
106062306a36Sopenharmony_ci	if (fattr == NULL)
106162306a36Sopenharmony_ci		goto error;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	/* Get a client representation */
106462306a36Sopenharmony_ci	error = nfs_init_server(server, fc);
106562306a36Sopenharmony_ci	if (error < 0)
106662306a36Sopenharmony_ci		goto error;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	/* Probe the root fh to retrieve its FSID */
106962306a36Sopenharmony_ci	error = nfs_probe_fsinfo(server, ctx->mntfh, fattr);
107062306a36Sopenharmony_ci	if (error < 0)
107162306a36Sopenharmony_ci		goto error;
107262306a36Sopenharmony_ci	if (server->nfs_client->rpc_ops->version == 3) {
107362306a36Sopenharmony_ci		if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
107462306a36Sopenharmony_ci			server->namelen = NFS3_MAXNAMLEN;
107562306a36Sopenharmony_ci		if (!(ctx->flags & NFS_MOUNT_NORDIRPLUS))
107662306a36Sopenharmony_ci			server->caps |= NFS_CAP_READDIRPLUS;
107762306a36Sopenharmony_ci	} else {
107862306a36Sopenharmony_ci		if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN)
107962306a36Sopenharmony_ci			server->namelen = NFS2_MAXNAMLEN;
108062306a36Sopenharmony_ci	}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	if (!(fattr->valid & NFS_ATTR_FATTR)) {
108362306a36Sopenharmony_ci		error = ctx->nfs_mod->rpc_ops->getattr(server, ctx->mntfh,
108462306a36Sopenharmony_ci						       fattr, NULL);
108562306a36Sopenharmony_ci		if (error < 0) {
108662306a36Sopenharmony_ci			dprintk("nfs_create_server: getattr error = %d\n", -error);
108762306a36Sopenharmony_ci			goto error;
108862306a36Sopenharmony_ci		}
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci	memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid));
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	dprintk("Server FSID: %llx:%llx\n",
109362306a36Sopenharmony_ci		(unsigned long long) server->fsid.major,
109462306a36Sopenharmony_ci		(unsigned long long) server->fsid.minor);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	nfs_server_insert_lists(server);
109762306a36Sopenharmony_ci	server->mount_time = jiffies;
109862306a36Sopenharmony_ci	nfs_free_fattr(fattr);
109962306a36Sopenharmony_ci	return server;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_cierror:
110262306a36Sopenharmony_ci	nfs_free_fattr(fattr);
110362306a36Sopenharmony_ci	nfs_free_server(server);
110462306a36Sopenharmony_ci	return ERR_PTR(error);
110562306a36Sopenharmony_ci}
110662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_create_server);
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci/*
110962306a36Sopenharmony_ci * Clone an NFS2, NFS3 or NFS4 server record
111062306a36Sopenharmony_ci */
111162306a36Sopenharmony_cistruct nfs_server *nfs_clone_server(struct nfs_server *source,
111262306a36Sopenharmony_ci				    struct nfs_fh *fh,
111362306a36Sopenharmony_ci				    struct nfs_fattr *fattr,
111462306a36Sopenharmony_ci				    rpc_authflavor_t flavor)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	struct nfs_server *server;
111762306a36Sopenharmony_ci	int error;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	server = nfs_alloc_server();
112062306a36Sopenharmony_ci	if (!server)
112162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	server->cred = get_cred(source->cred);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	/* Copy data from the source */
112662306a36Sopenharmony_ci	server->nfs_client = source->nfs_client;
112762306a36Sopenharmony_ci	server->destroy = source->destroy;
112862306a36Sopenharmony_ci	refcount_inc(&server->nfs_client->cl_count);
112962306a36Sopenharmony_ci	nfs_server_copy_userdata(server, source);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	server->fsid = fattr->fsid;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	nfs_sysfs_add_server(server);
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	nfs_sysfs_link_rpc_client(server,
113662306a36Sopenharmony_ci		server->nfs_client->cl_rpcclient, "_state");
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	error = nfs_init_server_rpcclient(server,
113962306a36Sopenharmony_ci			source->client->cl_timeout,
114062306a36Sopenharmony_ci			flavor);
114162306a36Sopenharmony_ci	if (error < 0)
114262306a36Sopenharmony_ci		goto out_free_server;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	/* probe the filesystem info for this server filesystem */
114562306a36Sopenharmony_ci	error = nfs_probe_server(server, fh);
114662306a36Sopenharmony_ci	if (error < 0)
114762306a36Sopenharmony_ci		goto out_free_server;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
115062306a36Sopenharmony_ci		server->namelen = NFS4_MAXNAMLEN;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	error = nfs_start_lockd(server);
115362306a36Sopenharmony_ci	if (error < 0)
115462306a36Sopenharmony_ci		goto out_free_server;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	nfs_server_insert_lists(server);
115762306a36Sopenharmony_ci	server->mount_time = jiffies;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	return server;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ciout_free_server:
116262306a36Sopenharmony_ci	nfs_free_server(server);
116362306a36Sopenharmony_ci	return ERR_PTR(error);
116462306a36Sopenharmony_ci}
116562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_clone_server);
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_civoid nfs_clients_init(struct net *net)
116862306a36Sopenharmony_ci{
116962306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(net, nfs_net_id);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	INIT_LIST_HEAD(&nn->nfs_client_list);
117262306a36Sopenharmony_ci	INIT_LIST_HEAD(&nn->nfs_volume_list);
117362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NFS_V4)
117462306a36Sopenharmony_ci	idr_init(&nn->cb_ident_idr);
117562306a36Sopenharmony_ci#endif
117662306a36Sopenharmony_ci	spin_lock_init(&nn->nfs_client_lock);
117762306a36Sopenharmony_ci	nn->boot_time = ktime_get_real();
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	nfs_netns_sysfs_setup(nn, net);
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_civoid nfs_clients_exit(struct net *net)
118362306a36Sopenharmony_ci{
118462306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(net, nfs_net_id);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	nfs_netns_sysfs_destroy(nn);
118762306a36Sopenharmony_ci	nfs_cleanup_cb_ident_idr(net);
118862306a36Sopenharmony_ci	WARN_ON_ONCE(!list_empty(&nn->nfs_client_list));
118962306a36Sopenharmony_ci	WARN_ON_ONCE(!list_empty(&nn->nfs_volume_list));
119062306a36Sopenharmony_ci}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
119362306a36Sopenharmony_cistatic void *nfs_server_list_start(struct seq_file *p, loff_t *pos);
119462306a36Sopenharmony_cistatic void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos);
119562306a36Sopenharmony_cistatic void nfs_server_list_stop(struct seq_file *p, void *v);
119662306a36Sopenharmony_cistatic int nfs_server_list_show(struct seq_file *m, void *v);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_cistatic const struct seq_operations nfs_server_list_ops = {
119962306a36Sopenharmony_ci	.start	= nfs_server_list_start,
120062306a36Sopenharmony_ci	.next	= nfs_server_list_next,
120162306a36Sopenharmony_ci	.stop	= nfs_server_list_stop,
120262306a36Sopenharmony_ci	.show	= nfs_server_list_show,
120362306a36Sopenharmony_ci};
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_cistatic void *nfs_volume_list_start(struct seq_file *p, loff_t *pos);
120662306a36Sopenharmony_cistatic void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos);
120762306a36Sopenharmony_cistatic void nfs_volume_list_stop(struct seq_file *p, void *v);
120862306a36Sopenharmony_cistatic int nfs_volume_list_show(struct seq_file *m, void *v);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_cistatic const struct seq_operations nfs_volume_list_ops = {
121162306a36Sopenharmony_ci	.start	= nfs_volume_list_start,
121262306a36Sopenharmony_ci	.next	= nfs_volume_list_next,
121362306a36Sopenharmony_ci	.stop	= nfs_volume_list_stop,
121462306a36Sopenharmony_ci	.show	= nfs_volume_list_show,
121562306a36Sopenharmony_ci};
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci/*
121862306a36Sopenharmony_ci * set up the iterator to start reading from the server list and return the first item
121962306a36Sopenharmony_ci */
122062306a36Sopenharmony_cistatic void *nfs_server_list_start(struct seq_file *m, loff_t *_pos)
122162306a36Sopenharmony_ci				__acquires(&nn->nfs_client_lock)
122262306a36Sopenharmony_ci{
122362306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	/* lock the list against modification */
122662306a36Sopenharmony_ci	spin_lock(&nn->nfs_client_lock);
122762306a36Sopenharmony_ci	return seq_list_start_head(&nn->nfs_client_list, *_pos);
122862306a36Sopenharmony_ci}
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci/*
123162306a36Sopenharmony_ci * move to next server
123262306a36Sopenharmony_ci */
123362306a36Sopenharmony_cistatic void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)
123462306a36Sopenharmony_ci{
123562306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	return seq_list_next(v, &nn->nfs_client_list, pos);
123862306a36Sopenharmony_ci}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci/*
124162306a36Sopenharmony_ci * clean up after reading from the transports list
124262306a36Sopenharmony_ci */
124362306a36Sopenharmony_cistatic void nfs_server_list_stop(struct seq_file *p, void *v)
124462306a36Sopenharmony_ci				__releases(&nn->nfs_client_lock)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	spin_unlock(&nn->nfs_client_lock);
124962306a36Sopenharmony_ci}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci/*
125262306a36Sopenharmony_ci * display a header line followed by a load of call lines
125362306a36Sopenharmony_ci */
125462306a36Sopenharmony_cistatic int nfs_server_list_show(struct seq_file *m, void *v)
125562306a36Sopenharmony_ci{
125662306a36Sopenharmony_ci	struct nfs_client *clp;
125762306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	/* display header on line 1 */
126062306a36Sopenharmony_ci	if (v == &nn->nfs_client_list) {
126162306a36Sopenharmony_ci		seq_puts(m, "NV SERVER   PORT USE HOSTNAME\n");
126262306a36Sopenharmony_ci		return 0;
126362306a36Sopenharmony_ci	}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	/* display one transport per line on subsequent lines */
126662306a36Sopenharmony_ci	clp = list_entry(v, struct nfs_client, cl_share_link);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	/* Check if the client is initialized */
126962306a36Sopenharmony_ci	if (clp->cl_cons_state != NFS_CS_READY)
127062306a36Sopenharmony_ci		return 0;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	rcu_read_lock();
127362306a36Sopenharmony_ci	seq_printf(m, "v%u %s %s %3d %s\n",
127462306a36Sopenharmony_ci		   clp->rpc_ops->version,
127562306a36Sopenharmony_ci		   rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR),
127662306a36Sopenharmony_ci		   rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT),
127762306a36Sopenharmony_ci		   refcount_read(&clp->cl_count),
127862306a36Sopenharmony_ci		   clp->cl_hostname);
127962306a36Sopenharmony_ci	rcu_read_unlock();
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	return 0;
128262306a36Sopenharmony_ci}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci/*
128562306a36Sopenharmony_ci * set up the iterator to start reading from the volume list and return the first item
128662306a36Sopenharmony_ci */
128762306a36Sopenharmony_cistatic void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)
128862306a36Sopenharmony_ci				__acquires(&nn->nfs_client_lock)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	/* lock the list against modification */
129362306a36Sopenharmony_ci	spin_lock(&nn->nfs_client_lock);
129462306a36Sopenharmony_ci	return seq_list_start_head(&nn->nfs_volume_list, *_pos);
129562306a36Sopenharmony_ci}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci/*
129862306a36Sopenharmony_ci * move to next volume
129962306a36Sopenharmony_ci */
130062306a36Sopenharmony_cistatic void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos)
130162306a36Sopenharmony_ci{
130262306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	return seq_list_next(v, &nn->nfs_volume_list, pos);
130562306a36Sopenharmony_ci}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci/*
130862306a36Sopenharmony_ci * clean up after reading from the transports list
130962306a36Sopenharmony_ci */
131062306a36Sopenharmony_cistatic void nfs_volume_list_stop(struct seq_file *p, void *v)
131162306a36Sopenharmony_ci				__releases(&nn->nfs_client_lock)
131262306a36Sopenharmony_ci{
131362306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	spin_unlock(&nn->nfs_client_lock);
131662306a36Sopenharmony_ci}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci/*
131962306a36Sopenharmony_ci * display a header line followed by a load of call lines
132062306a36Sopenharmony_ci */
132162306a36Sopenharmony_cistatic int nfs_volume_list_show(struct seq_file *m, void *v)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	struct nfs_server *server;
132462306a36Sopenharmony_ci	struct nfs_client *clp;
132562306a36Sopenharmony_ci	char dev[13];	// 8 for 2^24, 1 for ':', 3 for 2^8, 1 for '\0'
132662306a36Sopenharmony_ci	char fsid[34];	// 2 * 16 for %llx, 1 for ':', 1 for '\0'
132762306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	/* display header on line 1 */
133062306a36Sopenharmony_ci	if (v == &nn->nfs_volume_list) {
133162306a36Sopenharmony_ci		seq_puts(m, "NV SERVER   PORT DEV          FSID"
133262306a36Sopenharmony_ci			    "                              FSC\n");
133362306a36Sopenharmony_ci		return 0;
133462306a36Sopenharmony_ci	}
133562306a36Sopenharmony_ci	/* display one transport per line on subsequent lines */
133662306a36Sopenharmony_ci	server = list_entry(v, struct nfs_server, master_link);
133762306a36Sopenharmony_ci	clp = server->nfs_client;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	snprintf(dev, sizeof(dev), "%u:%u",
134062306a36Sopenharmony_ci		 MAJOR(server->s_dev), MINOR(server->s_dev));
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	snprintf(fsid, sizeof(fsid), "%llx:%llx",
134362306a36Sopenharmony_ci		 (unsigned long long) server->fsid.major,
134462306a36Sopenharmony_ci		 (unsigned long long) server->fsid.minor);
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	rcu_read_lock();
134762306a36Sopenharmony_ci	seq_printf(m, "v%u %s %s %-12s %-33s %s\n",
134862306a36Sopenharmony_ci		   clp->rpc_ops->version,
134962306a36Sopenharmony_ci		   rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_ADDR),
135062306a36Sopenharmony_ci		   rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_HEX_PORT),
135162306a36Sopenharmony_ci		   dev,
135262306a36Sopenharmony_ci		   fsid,
135362306a36Sopenharmony_ci		   nfs_server_fscache_state(server));
135462306a36Sopenharmony_ci	rcu_read_unlock();
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	return 0;
135762306a36Sopenharmony_ci}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ciint nfs_fs_proc_net_init(struct net *net)
136062306a36Sopenharmony_ci{
136162306a36Sopenharmony_ci	struct nfs_net *nn = net_generic(net, nfs_net_id);
136262306a36Sopenharmony_ci	struct proc_dir_entry *p;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	nn->proc_nfsfs = proc_net_mkdir(net, "nfsfs", net->proc_net);
136562306a36Sopenharmony_ci	if (!nn->proc_nfsfs)
136662306a36Sopenharmony_ci		goto error_0;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	/* a file of servers with which we're dealing */
136962306a36Sopenharmony_ci	p = proc_create_net("servers", S_IFREG|S_IRUGO, nn->proc_nfsfs,
137062306a36Sopenharmony_ci			&nfs_server_list_ops, sizeof(struct seq_net_private));
137162306a36Sopenharmony_ci	if (!p)
137262306a36Sopenharmony_ci		goto error_1;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	/* a file of volumes that we have mounted */
137562306a36Sopenharmony_ci	p = proc_create_net("volumes", S_IFREG|S_IRUGO, nn->proc_nfsfs,
137662306a36Sopenharmony_ci			&nfs_volume_list_ops, sizeof(struct seq_net_private));
137762306a36Sopenharmony_ci	if (!p)
137862306a36Sopenharmony_ci		goto error_1;
137962306a36Sopenharmony_ci	return 0;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_cierror_1:
138262306a36Sopenharmony_ci	remove_proc_subtree("nfsfs", net->proc_net);
138362306a36Sopenharmony_cierror_0:
138462306a36Sopenharmony_ci	return -ENOMEM;
138562306a36Sopenharmony_ci}
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_civoid nfs_fs_proc_net_exit(struct net *net)
138862306a36Sopenharmony_ci{
138962306a36Sopenharmony_ci	remove_proc_subtree("nfsfs", net->proc_net);
139062306a36Sopenharmony_ci}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci/*
139362306a36Sopenharmony_ci * initialise the /proc/fs/nfsfs/ directory
139462306a36Sopenharmony_ci */
139562306a36Sopenharmony_ciint __init nfs_fs_proc_init(void)
139662306a36Sopenharmony_ci{
139762306a36Sopenharmony_ci	if (!proc_mkdir("fs/nfsfs", NULL))
139862306a36Sopenharmony_ci		goto error_0;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	/* a file of servers with which we're dealing */
140162306a36Sopenharmony_ci	if (!proc_symlink("fs/nfsfs/servers", NULL, "../../net/nfsfs/servers"))
140262306a36Sopenharmony_ci		goto error_1;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	/* a file of volumes that we have mounted */
140562306a36Sopenharmony_ci	if (!proc_symlink("fs/nfsfs/volumes", NULL, "../../net/nfsfs/volumes"))
140662306a36Sopenharmony_ci		goto error_1;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	return 0;
140962306a36Sopenharmony_cierror_1:
141062306a36Sopenharmony_ci	remove_proc_subtree("fs/nfsfs", NULL);
141162306a36Sopenharmony_cierror_0:
141262306a36Sopenharmony_ci	return -ENOMEM;
141362306a36Sopenharmony_ci}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci/*
141662306a36Sopenharmony_ci * clean up the /proc/fs/nfsfs/ directory
141762306a36Sopenharmony_ci */
141862306a36Sopenharmony_civoid nfs_fs_proc_exit(void)
141962306a36Sopenharmony_ci{
142062306a36Sopenharmony_ci	remove_proc_subtree("fs/nfsfs", NULL);
142162306a36Sopenharmony_ci	ida_destroy(&s_sysfs_ids);
142262306a36Sopenharmony_ci}
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */
1425