xref: /openbmc/linux/fs/nfsd/nfssvc.c (revision 26d0dfbb16fcb17d128a79dc70f3020ea6992af0)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Central processing for nfsd.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Authors:	Olaf Kirch (okir@monad.swb.de)
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
103f07c014SIngo Molnar #include <linux/sched/signal.h>
1183144186SRafael J. Wysocki #include <linux/freezer.h>
12143cb494SPaul Gortmaker #include <linux/module.h>
131da177e4SLinus Torvalds #include <linux/fs_struct.h>
14c3d06f9cSAndy Adamson #include <linux/swap.h>
1591d2e9b5SChuck Lever #include <linux/siphash.h>
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds #include <linux/sunrpc/stats.h>
181da177e4SLinus Torvalds #include <linux/sunrpc/svcsock.h>
1936684996SScott Mayhew #include <linux/sunrpc/svc_xprt.h>
201da177e4SLinus Torvalds #include <linux/lockd/bind.h>
21a257cdd0SAndreas Gruenbacher #include <linux/nfsacl.h>
22ed2d8aedSRyusei Yamaguchi #include <linux/seq_file.h>
2336684996SScott Mayhew #include <linux/inetdevice.h>
2436684996SScott Mayhew #include <net/addrconf.h>
2536684996SScott Mayhew #include <net/ipv6.h>
26fc5d00b0SPavel Emelyanov #include <net/net_namespace.h>
279a74af21SBoaz Harrosh #include "nfsd.h"
289a74af21SBoaz Harrosh #include "cache.h"
290a3adadeSJ. Bruce Fields #include "vfs.h"
302c2fe290SStanislav Kinsbursky #include "netns.h"
3165294c1fSJeff Layton #include "filecache.h"
321da177e4SLinus Torvalds 
330dfdad1cSChuck Lever #include "trace.h"
340dfdad1cSChuck Lever 
351da177e4SLinus Torvalds #define NFSDDBG_FACILITY	NFSDDBG_SVC
361da177e4SLinus Torvalds 
379ae63aabSJosef Bacik atomic_t			nfsd_th_cnt = ATOMIC_INIT(0);
381da177e4SLinus Torvalds extern struct svc_program	nfsd_program;
399867d76cSJeff Layton static int			nfsd(void *vrqstp);
40029be5d0STrond Myklebust #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
41029be5d0STrond Myklebust static int			nfsd_acl_rpcbind_set(struct net *,
42029be5d0STrond Myklebust 						     const struct svc_program *,
43029be5d0STrond Myklebust 						     u32, int,
44029be5d0STrond Myklebust 						     unsigned short,
45029be5d0STrond Myklebust 						     unsigned short);
46e333f3bbSTrond Myklebust static __be32			nfsd_acl_init_request(struct svc_rqst *,
47e333f3bbSTrond Myklebust 						const struct svc_program *,
48e333f3bbSTrond Myklebust 						struct svc_process_info *);
49029be5d0STrond Myklebust #endif
50029be5d0STrond Myklebust static int			nfsd_rpcbind_set(struct net *,
51029be5d0STrond Myklebust 						 const struct svc_program *,
52029be5d0STrond Myklebust 						 u32, int,
53029be5d0STrond Myklebust 						 unsigned short,
54029be5d0STrond Myklebust 						 unsigned short);
55e333f3bbSTrond Myklebust static __be32			nfsd_init_request(struct svc_rqst *,
56e333f3bbSTrond Myklebust 						const struct svc_program *,
57e333f3bbSTrond Myklebust 						struct svc_process_info *);
581da177e4SLinus Torvalds 
59bedbdd8bSNeil Brown /*
602a36395fSNeilBrown  * nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and some members
612a36395fSNeilBrown  * of the svc_serv struct such as ->sv_temp_socks and ->sv_permsocks.
62bedbdd8bSNeil Brown  *
639dd9845fSStanislav Kinsbursky  * If (out side the lock) nn->nfsd_serv is non-NULL, then it must point to a
64ec52361dSNeilBrown  * properly initialised 'struct svc_serv' with ->sv_nrthreads > 0 (unless
65ec52361dSNeilBrown  * nn->keep_active is set).  That number of nfsd threads must
66ec52361dSNeilBrown  * exist and each must be listed in ->sp_all_threads in some entry of
67ec52361dSNeilBrown  * ->sv_pools[].
68bedbdd8bSNeil Brown  *
69ec52361dSNeilBrown  * Each active thread holds a counted reference on nn->nfsd_serv, as does
70ec52361dSNeilBrown  * the nn->keep_active flag and various transient calls to svc_get().
713dd98a3bSJeff Layton  *
723dd98a3bSJeff Layton  * Finally, the nfsd_mutex also protects some of the global variables that are
733dd98a3bSJeff Layton  * accessed when nfsd starts and that are settable via the write_* routines in
743dd98a3bSJeff Layton  * nfsctl.c. In particular:
753dd98a3bSJeff Layton  *
763dd98a3bSJeff Layton  *	user_recovery_dirname
773dd98a3bSJeff Layton  *	user_lease_time
783dd98a3bSJeff Layton  *	nfsd_versions
79bedbdd8bSNeil Brown  */
80bedbdd8bSNeil Brown DEFINE_MUTEX(nfsd_mutex);
81bedbdd8bSNeil Brown 
824bd9b0f4SAndy Adamson /*
834bd9b0f4SAndy Adamson  * nfsd_drc_lock protects nfsd_drc_max_pages and nfsd_drc_pages_used.
844bd9b0f4SAndy Adamson  * nfsd_drc_max_pages limits the total amount of memory available for
854bd9b0f4SAndy Adamson  * version 4.1 DRC caches.
864bd9b0f4SAndy Adamson  * nfsd_drc_pages_used tracks the current version 4.1 DRC memory usage.
874bd9b0f4SAndy Adamson  */
88b73ac680SGuobin Huang DEFINE_SPINLOCK(nfsd_drc_lock);
89697ce9beSZhang Yanfei unsigned long	nfsd_drc_max_mem;
90697ce9beSZhang Yanfei unsigned long	nfsd_drc_mem_used;
914bd9b0f4SAndy Adamson 
923fb803a9SAndreas Gruenbacher #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
93e9679189SChristoph Hellwig static const struct svc_version *nfsd_acl_version[] = {
942f3a4b2aSJeff Layton # if defined(CONFIG_NFSD_V2_ACL)
953fb803a9SAndreas Gruenbacher 	[2] = &nfsd_acl_version2,
962f3a4b2aSJeff Layton # endif
972f3a4b2aSJeff Layton # if defined(CONFIG_NFSD_V3_ACL)
983fb803a9SAndreas Gruenbacher 	[3] = &nfsd_acl_version3,
992f3a4b2aSJeff Layton # endif
1003fb803a9SAndreas Gruenbacher };
1013fb803a9SAndreas Gruenbacher 
1023fb803a9SAndreas Gruenbacher #define NFSD_ACL_MINVERS            2
103e8c96f8cSTobias Klauser #define NFSD_ACL_NRVERS		ARRAY_SIZE(nfsd_acl_version)
1043fb803a9SAndreas Gruenbacher 
1053fb803a9SAndreas Gruenbacher static struct svc_program	nfsd_acl_program = {
1063fb803a9SAndreas Gruenbacher 	.pg_prog		= NFS_ACL_PROGRAM,
1073fb803a9SAndreas Gruenbacher 	.pg_nvers		= NFSD_ACL_NRVERS,
1087c149057SJ. Bruce Fields 	.pg_vers		= nfsd_acl_version,
1091a8eff6dSNeilBrown 	.pg_name		= "nfsacl",
1103fb803a9SAndreas Gruenbacher 	.pg_class		= "nfsd",
1113fb803a9SAndreas Gruenbacher 	.pg_authenticate	= &svc_set_client,
112e333f3bbSTrond Myklebust 	.pg_init_request	= nfsd_acl_init_request,
113029be5d0STrond Myklebust 	.pg_rpcbind_set		= nfsd_acl_rpcbind_set,
1143fb803a9SAndreas Gruenbacher };
1153fb803a9SAndreas Gruenbacher 
1163fb803a9SAndreas Gruenbacher #endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
1173fb803a9SAndreas Gruenbacher 
118e9679189SChristoph Hellwig static const struct svc_version *nfsd_version[] = {
1192f3a4b2aSJeff Layton #if defined(CONFIG_NFSD_V2)
12070c3b76cSNeilBrown 	[2] = &nfsd_version2,
1212f3a4b2aSJeff Layton #endif
12270c3b76cSNeilBrown 	[3] = &nfsd_version3,
12370c3b76cSNeilBrown #if defined(CONFIG_NFSD_V4)
12470c3b76cSNeilBrown 	[4] = &nfsd_version4,
12570c3b76cSNeilBrown #endif
12670c3b76cSNeilBrown };
12770c3b76cSNeilBrown 
12870c3b76cSNeilBrown #define NFSD_MINVERS    	2
129e8c96f8cSTobias Klauser #define NFSD_NRVERS		ARRAY_SIZE(nfsd_version)
13070c3b76cSNeilBrown 
13170c3b76cSNeilBrown struct svc_program		nfsd_program = {
1323fb803a9SAndreas Gruenbacher #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
1333fb803a9SAndreas Gruenbacher 	.pg_next		= &nfsd_acl_program,
1343fb803a9SAndreas Gruenbacher #endif
13570c3b76cSNeilBrown 	.pg_prog		= NFS_PROGRAM,		/* program number */
13670c3b76cSNeilBrown 	.pg_nvers		= NFSD_NRVERS,		/* nr of entries in nfsd_version */
137e333f3bbSTrond Myklebust 	.pg_vers		= nfsd_version,		/* version table */
13870c3b76cSNeilBrown 	.pg_name		= "nfsd",		/* program name */
13970c3b76cSNeilBrown 	.pg_class		= "nfsd",		/* authentication class */
14070c3b76cSNeilBrown 	.pg_authenticate	= &svc_set_client,	/* export authentication */
141e333f3bbSTrond Myklebust 	.pg_init_request	= nfsd_init_request,
142029be5d0STrond Myklebust 	.pg_rpcbind_set		= nfsd_rpcbind_set,
14370c3b76cSNeilBrown };
14470c3b76cSNeilBrown 
145e333f3bbSTrond Myklebust static bool
nfsd_support_version(int vers)146e333f3bbSTrond Myklebust nfsd_support_version(int vers)
147e333f3bbSTrond Myklebust {
148e333f3bbSTrond Myklebust 	if (vers >= NFSD_MINVERS && vers < NFSD_NRVERS)
149e333f3bbSTrond Myklebust 		return nfsd_version[vers] != NULL;
150e333f3bbSTrond Myklebust 	return false;
151e333f3bbSTrond Myklebust }
1528daf220aSBenny Halevy 
153e333f3bbSTrond Myklebust static bool *
nfsd_alloc_versions(void)154e333f3bbSTrond Myklebust nfsd_alloc_versions(void)
155e333f3bbSTrond Myklebust {
156e333f3bbSTrond Myklebust 	bool *vers = kmalloc_array(NFSD_NRVERS, sizeof(bool), GFP_KERNEL);
157e333f3bbSTrond Myklebust 	unsigned i;
158e333f3bbSTrond Myklebust 
159e333f3bbSTrond Myklebust 	if (vers) {
160e333f3bbSTrond Myklebust 		/* All compiled versions are enabled by default */
161e333f3bbSTrond Myklebust 		for (i = 0; i < NFSD_NRVERS; i++)
162e333f3bbSTrond Myklebust 			vers[i] = nfsd_support_version(i);
163e333f3bbSTrond Myklebust 	}
164e333f3bbSTrond Myklebust 	return vers;
165e333f3bbSTrond Myklebust }
166e333f3bbSTrond Myklebust 
167e333f3bbSTrond Myklebust static bool *
nfsd_alloc_minorversions(void)168e333f3bbSTrond Myklebust nfsd_alloc_minorversions(void)
169e333f3bbSTrond Myklebust {
170e333f3bbSTrond Myklebust 	bool *vers = kmalloc_array(NFSD_SUPPORTED_MINOR_VERSION + 1,
171e333f3bbSTrond Myklebust 			sizeof(bool), GFP_KERNEL);
172e333f3bbSTrond Myklebust 	unsigned i;
173e333f3bbSTrond Myklebust 
174e333f3bbSTrond Myklebust 	if (vers) {
175e333f3bbSTrond Myklebust 		/* All minor versions are enabled by default */
176e333f3bbSTrond Myklebust 		for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++)
177e333f3bbSTrond Myklebust 			vers[i] = nfsd_support_version(4);
178e333f3bbSTrond Myklebust 	}
179e333f3bbSTrond Myklebust 	return vers;
180e333f3bbSTrond Myklebust }
181e333f3bbSTrond Myklebust 
182e333f3bbSTrond Myklebust void
nfsd_netns_free_versions(struct nfsd_net * nn)183e333f3bbSTrond Myklebust nfsd_netns_free_versions(struct nfsd_net *nn)
184e333f3bbSTrond Myklebust {
185e333f3bbSTrond Myklebust 	kfree(nn->nfsd_versions);
186e333f3bbSTrond Myklebust 	kfree(nn->nfsd4_minorversions);
187e333f3bbSTrond Myklebust 	nn->nfsd_versions = NULL;
188e333f3bbSTrond Myklebust 	nn->nfsd4_minorversions = NULL;
189e333f3bbSTrond Myklebust }
190e333f3bbSTrond Myklebust 
191e333f3bbSTrond Myklebust static void
nfsd_netns_init_versions(struct nfsd_net * nn)192e333f3bbSTrond Myklebust nfsd_netns_init_versions(struct nfsd_net *nn)
193e333f3bbSTrond Myklebust {
194e333f3bbSTrond Myklebust 	if (!nn->nfsd_versions) {
195e333f3bbSTrond Myklebust 		nn->nfsd_versions = nfsd_alloc_versions();
196e333f3bbSTrond Myklebust 		nn->nfsd4_minorversions = nfsd_alloc_minorversions();
197e333f3bbSTrond Myklebust 		if (!nn->nfsd_versions || !nn->nfsd4_minorversions)
198e333f3bbSTrond Myklebust 			nfsd_netns_free_versions(nn);
199e333f3bbSTrond Myklebust 	}
200e333f3bbSTrond Myklebust }
201e333f3bbSTrond Myklebust 
nfsd_vers(struct nfsd_net * nn,int vers,enum vers_op change)202e333f3bbSTrond Myklebust int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change)
2036658d3a7SNeilBrown {
2046658d3a7SNeilBrown 	if (vers < NFSD_MINVERS || vers >= NFSD_NRVERS)
20515ddb4aeSPavel Emelyanov 		return 0;
2066658d3a7SNeilBrown 	switch(change) {
2076658d3a7SNeilBrown 	case NFSD_SET:
208e333f3bbSTrond Myklebust 		if (nn->nfsd_versions)
209e333f3bbSTrond Myklebust 			nn->nfsd_versions[vers] = nfsd_support_version(vers);
2101a8eff6dSNeilBrown 		break;
2116658d3a7SNeilBrown 	case NFSD_CLEAR:
212e333f3bbSTrond Myklebust 		nfsd_netns_init_versions(nn);
213e333f3bbSTrond Myklebust 		if (nn->nfsd_versions)
214e333f3bbSTrond Myklebust 			nn->nfsd_versions[vers] = false;
2156658d3a7SNeilBrown 		break;
2166658d3a7SNeilBrown 	case NFSD_TEST:
217e333f3bbSTrond Myklebust 		if (nn->nfsd_versions)
218e333f3bbSTrond Myklebust 			return nn->nfsd_versions[vers];
219df561f66SGustavo A. R. Silva 		fallthrough;
2206658d3a7SNeilBrown 	case NFSD_AVAIL:
221e333f3bbSTrond Myklebust 		return nfsd_support_version(vers);
2226658d3a7SNeilBrown 	}
2236658d3a7SNeilBrown 	return 0;
2246658d3a7SNeilBrown }
2258daf220aSBenny Halevy 
226d3635ff0STrond Myklebust static void
nfsd_adjust_nfsd_versions4(struct nfsd_net * nn)227e333f3bbSTrond Myklebust nfsd_adjust_nfsd_versions4(struct nfsd_net *nn)
228d3635ff0STrond Myklebust {
229d3635ff0STrond Myklebust 	unsigned i;
230d3635ff0STrond Myklebust 
231d3635ff0STrond Myklebust 	for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++) {
232e333f3bbSTrond Myklebust 		if (nn->nfsd4_minorversions[i])
233d3635ff0STrond Myklebust 			return;
234d3635ff0STrond Myklebust 	}
235e333f3bbSTrond Myklebust 	nfsd_vers(nn, 4, NFSD_CLEAR);
236d3635ff0STrond Myklebust }
237d3635ff0STrond Myklebust 
nfsd_minorversion(struct nfsd_net * nn,u32 minorversion,enum vers_op change)238e333f3bbSTrond Myklebust int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change)
2398daf220aSBenny Halevy {
240928c6fb3SNeilBrown 	if (minorversion > NFSD_SUPPORTED_MINOR_VERSION &&
241928c6fb3SNeilBrown 	    change != NFSD_AVAIL)
2428daf220aSBenny Halevy 		return -1;
243e333f3bbSTrond Myklebust 
2448daf220aSBenny Halevy 	switch(change) {
2458daf220aSBenny Halevy 	case NFSD_SET:
246e333f3bbSTrond Myklebust 		if (nn->nfsd4_minorversions) {
247e333f3bbSTrond Myklebust 			nfsd_vers(nn, 4, NFSD_SET);
248e333f3bbSTrond Myklebust 			nn->nfsd4_minorversions[minorversion] =
249e333f3bbSTrond Myklebust 				nfsd_vers(nn, 4, NFSD_TEST);
250e333f3bbSTrond Myklebust 		}
2518daf220aSBenny Halevy 		break;
2528daf220aSBenny Halevy 	case NFSD_CLEAR:
253e333f3bbSTrond Myklebust 		nfsd_netns_init_versions(nn);
254e333f3bbSTrond Myklebust 		if (nn->nfsd4_minorversions) {
255e333f3bbSTrond Myklebust 			nn->nfsd4_minorversions[minorversion] = false;
256e333f3bbSTrond Myklebust 			nfsd_adjust_nfsd_versions4(nn);
257e333f3bbSTrond Myklebust 		}
2588daf220aSBenny Halevy 		break;
2598daf220aSBenny Halevy 	case NFSD_TEST:
260e333f3bbSTrond Myklebust 		if (nn->nfsd4_minorversions)
261e333f3bbSTrond Myklebust 			return nn->nfsd4_minorversions[minorversion];
262e333f3bbSTrond Myklebust 		return nfsd_vers(nn, 4, NFSD_TEST);
2638daf220aSBenny Halevy 	case NFSD_AVAIL:
264e333f3bbSTrond Myklebust 		return minorversion <= NFSD_SUPPORTED_MINOR_VERSION &&
265e333f3bbSTrond Myklebust 			nfsd_vers(nn, 4, NFSD_AVAIL);
2668daf220aSBenny Halevy 	}
2678daf220aSBenny Halevy 	return 0;
2688daf220aSBenny Halevy }
2698daf220aSBenny Halevy 
2701da177e4SLinus Torvalds /*
2711da177e4SLinus Torvalds  * Maximum number of nfsd processes
2721da177e4SLinus Torvalds  */
2731da177e4SLinus Torvalds #define	NFSD_MAXSERVS		8192
2741da177e4SLinus Torvalds 
nfsd_nrthreads(struct net * net)2759dd9845fSStanislav Kinsbursky int nfsd_nrthreads(struct net *net)
2761da177e4SLinus Torvalds {
277c7d106c9SNeil Brown 	int rv = 0;
2789dd9845fSStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
2799dd9845fSStanislav Kinsbursky 
280c7d106c9SNeil Brown 	mutex_lock(&nfsd_mutex);
2819dd9845fSStanislav Kinsbursky 	if (nn->nfsd_serv)
2829dd9845fSStanislav Kinsbursky 		rv = nn->nfsd_serv->sv_nrthreads;
283c7d106c9SNeil Brown 	mutex_unlock(&nfsd_mutex);
284c7d106c9SNeil Brown 	return rv;
2851da177e4SLinus Torvalds }
2861da177e4SLinus Torvalds 
nfsd_init_socks(struct net * net,const struct cred * cred)2874df493a2STrond Myklebust static int nfsd_init_socks(struct net *net, const struct cred *cred)
28859db4a0cSJ. Bruce Fields {
28959db4a0cSJ. Bruce Fields 	int error;
2909dd9845fSStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
2919dd9845fSStanislav Kinsbursky 
2929dd9845fSStanislav Kinsbursky 	if (!list_empty(&nn->nfsd_serv->sv_permsocks))
29359db4a0cSJ. Bruce Fields 		return 0;
29459db4a0cSJ. Bruce Fields 
295352ad314SChuck Lever 	error = svc_xprt_create(nn->nfsd_serv, "udp", net, PF_INET, NFS_PORT,
2964df493a2STrond Myklebust 				SVC_SOCK_DEFAULTS, cred);
29759db4a0cSJ. Bruce Fields 	if (error < 0)
29859db4a0cSJ. Bruce Fields 		return error;
29959db4a0cSJ. Bruce Fields 
300352ad314SChuck Lever 	error = svc_xprt_create(nn->nfsd_serv, "tcp", net, PF_INET, NFS_PORT,
3014df493a2STrond Myklebust 				SVC_SOCK_DEFAULTS, cred);
30259db4a0cSJ. Bruce Fields 	if (error < 0)
30359db4a0cSJ. Bruce Fields 		return error;
30459db4a0cSJ. Bruce Fields 
30559db4a0cSJ. Bruce Fields 	return 0;
30659db4a0cSJ. Bruce Fields }
30759db4a0cSJ. Bruce Fields 
3084539f149SStanislav Kinsbursky static int nfsd_users = 0;
3094ad9a344SJeff Layton 
nfsd_startup_generic(void)31070c53075SVasily Averin static int nfsd_startup_generic(void)
311bda9cac1SStanislav Kinsbursky {
312bda9cac1SStanislav Kinsbursky 	int ret;
313bda9cac1SStanislav Kinsbursky 
3144539f149SStanislav Kinsbursky 	if (nfsd_users++)
315bda9cac1SStanislav Kinsbursky 		return 0;
316bda9cac1SStanislav Kinsbursky 
31765294c1fSJeff Layton 	ret = nfsd_file_cache_init();
31865294c1fSJeff Layton 	if (ret)
31965294c1fSJeff Layton 		goto dec_users;
320d9499a95SKinglong Mee 
321bda9cac1SStanislav Kinsbursky 	ret = nfs4_state_start();
322bda9cac1SStanislav Kinsbursky 	if (ret)
323501cb184SJeff Layton 		goto out_file_cache;
324bda9cac1SStanislav Kinsbursky 	return 0;
325bda9cac1SStanislav Kinsbursky 
32665294c1fSJeff Layton out_file_cache:
32765294c1fSJeff Layton 	nfsd_file_cache_shutdown();
328d9499a95SKinglong Mee dec_users:
329d9499a95SKinglong Mee 	nfsd_users--;
330bda9cac1SStanislav Kinsbursky 	return ret;
331bda9cac1SStanislav Kinsbursky }
332bda9cac1SStanislav Kinsbursky 
nfsd_shutdown_generic(void)333bda9cac1SStanislav Kinsbursky static void nfsd_shutdown_generic(void)
334bda9cac1SStanislav Kinsbursky {
3354539f149SStanislav Kinsbursky 	if (--nfsd_users)
3364539f149SStanislav Kinsbursky 		return;
3374539f149SStanislav Kinsbursky 
338bda9cac1SStanislav Kinsbursky 	nfs4_state_shutdown();
33965294c1fSJeff Layton 	nfsd_file_cache_shutdown();
340bda9cac1SStanislav Kinsbursky }
341bda9cac1SStanislav Kinsbursky 
nfsd_needs_lockd(struct nfsd_net * nn)342e333f3bbSTrond Myklebust static bool nfsd_needs_lockd(struct nfsd_net *nn)
3438ef66714SKinglong Mee {
344e333f3bbSTrond Myklebust 	return nfsd_vers(nn, 2, NFSD_TEST) || nfsd_vers(nn, 3, NFSD_TEST);
3458ef66714SKinglong Mee }
3468ef66714SKinglong Mee 
34791d2e9b5SChuck Lever /**
3483988a578SChuck Lever  * nfsd_copy_write_verifier - Atomically copy a write verifier
34991d2e9b5SChuck Lever  * @verf: buffer in which to receive the verifier cookie
35091d2e9b5SChuck Lever  * @nn: NFS net namespace
35191d2e9b5SChuck Lever  *
35291d2e9b5SChuck Lever  * This function provides a wait-free mechanism for copying the
3533988a578SChuck Lever  * namespace's write verifier without tearing it.
35491d2e9b5SChuck Lever  */
nfsd_copy_write_verifier(__be32 verf[2],struct nfsd_net * nn)3553988a578SChuck Lever void nfsd_copy_write_verifier(__be32 verf[2], struct nfsd_net *nn)
35627c438f5STrond Myklebust {
35727c438f5STrond Myklebust 	int seq = 0;
35827c438f5STrond Myklebust 
35927c438f5STrond Myklebust 	do {
36091d2e9b5SChuck Lever 		read_seqbegin_or_lock(&nn->writeverf_lock, &seq);
36190d21755SChuck Lever 		memcpy(verf, nn->writeverf, sizeof(nn->writeverf));
36291d2e9b5SChuck Lever 	} while (need_seqretry(&nn->writeverf_lock, seq));
36391d2e9b5SChuck Lever 	done_seqretry(&nn->writeverf_lock, seq);
36427c438f5STrond Myklebust }
36527c438f5STrond Myklebust 
nfsd_reset_write_verifier_locked(struct nfsd_net * nn)3663988a578SChuck Lever static void nfsd_reset_write_verifier_locked(struct nfsd_net *nn)
36727c438f5STrond Myklebust {
36891d2e9b5SChuck Lever 	struct timespec64 now;
36991d2e9b5SChuck Lever 	u64 verf;
37091d2e9b5SChuck Lever 
37191d2e9b5SChuck Lever 	/*
37291d2e9b5SChuck Lever 	 * Because the time value is hashed, y2038 time_t overflow
37391d2e9b5SChuck Lever 	 * is irrelevant in this usage.
37491d2e9b5SChuck Lever 	 */
37591d2e9b5SChuck Lever 	ktime_get_raw_ts64(&now);
37691d2e9b5SChuck Lever 	verf = siphash_2u64(now.tv_sec, now.tv_nsec, &nn->siphash_key);
37791d2e9b5SChuck Lever 	memcpy(nn->writeverf, &verf, sizeof(nn->writeverf));
37827c438f5STrond Myklebust }
37927c438f5STrond Myklebust 
38091d2e9b5SChuck Lever /**
3813988a578SChuck Lever  * nfsd_reset_write_verifier - Generate a new write verifier
38291d2e9b5SChuck Lever  * @nn: NFS net namespace
38391d2e9b5SChuck Lever  *
38491d2e9b5SChuck Lever  * This function updates the ->writeverf field of @nn. This field
38591d2e9b5SChuck Lever  * contains an opaque cookie that, according to Section 18.32.3 of
38691d2e9b5SChuck Lever  * RFC 8881, "the client can use to determine whether a server has
38791d2e9b5SChuck Lever  * changed instance state (e.g., server restart) between a call to
38891d2e9b5SChuck Lever  * WRITE and a subsequent call to either WRITE or COMMIT.  This
38991d2e9b5SChuck Lever  * cookie MUST be unchanged during a single instance of the NFSv4.1
39091d2e9b5SChuck Lever  * server and MUST be unique between instances of the NFSv4.1
39191d2e9b5SChuck Lever  * server."
39291d2e9b5SChuck Lever  */
nfsd_reset_write_verifier(struct nfsd_net * nn)3933988a578SChuck Lever void nfsd_reset_write_verifier(struct nfsd_net *nn)
39427c438f5STrond Myklebust {
39591d2e9b5SChuck Lever 	write_seqlock(&nn->writeverf_lock);
3963988a578SChuck Lever 	nfsd_reset_write_verifier_locked(nn);
39791d2e9b5SChuck Lever 	write_sequnlock(&nn->writeverf_lock);
39827c438f5STrond Myklebust }
39927c438f5STrond Myklebust 
4005e092be7SChuck Lever /*
4015e092be7SChuck Lever  * Crank up a set of per-namespace resources for a new NFSD instance,
4025e092be7SChuck Lever  * including lockd, a duplicate reply cache, an open file cache
4035e092be7SChuck Lever  * instance, and a cache of NFSv4 state objects.
4045e092be7SChuck Lever  */
nfsd_startup_net(struct net * net,const struct cred * cred)40570c53075SVasily Averin static int nfsd_startup_net(struct net *net, const struct cred *cred)
4066ff50b3dSStanislav Kinsbursky {
4072c2fe290SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
4086ff50b3dSStanislav Kinsbursky 	int ret;
4096ff50b3dSStanislav Kinsbursky 
4102c2fe290SStanislav Kinsbursky 	if (nn->nfsd_net_up)
4112c2fe290SStanislav Kinsbursky 		return 0;
4122c2fe290SStanislav Kinsbursky 
41370c53075SVasily Averin 	ret = nfsd_startup_generic();
414903d9bf0SStanislav Kinsbursky 	if (ret)
415903d9bf0SStanislav Kinsbursky 		return ret;
4164df493a2STrond Myklebust 	ret = nfsd_init_socks(net, cred);
4176ff50b3dSStanislav Kinsbursky 	if (ret)
418903d9bf0SStanislav Kinsbursky 		goto out_socks;
4198ef66714SKinglong Mee 
420e333f3bbSTrond Myklebust 	if (nfsd_needs_lockd(nn) && !nn->lockd_up) {
42140373b12STrond Myklebust 		ret = lockd_up(net, cred);
4226ff50b3dSStanislav Kinsbursky 		if (ret)
423903d9bf0SStanislav Kinsbursky 			goto out_socks;
424e44b4bf2Szhengbin 		nn->lockd_up = true;
4258ef66714SKinglong Mee 	}
4268ef66714SKinglong Mee 
4279542e6a6STrond Myklebust 	ret = nfsd_file_cache_start_net(net);
4286ff50b3dSStanislav Kinsbursky 	if (ret)
4296ff50b3dSStanislav Kinsbursky 		goto out_lockd;
430f5f9d4a3SJeff Layton 
431f5f9d4a3SJeff Layton 	ret = nfsd_reply_cache_init(nn);
4329542e6a6STrond Myklebust 	if (ret)
4339542e6a6STrond Myklebust 		goto out_filecache;
4346ff50b3dSStanislav Kinsbursky 
435f5f9d4a3SJeff Layton 	ret = nfs4_state_start_net(net);
436f5f9d4a3SJeff Layton 	if (ret)
437f5f9d4a3SJeff Layton 		goto out_reply_cache;
438f5f9d4a3SJeff Layton 
439f4e44b39SDai Ngo #ifdef CONFIG_NFSD_V4_2_INTER_SSC
440f4e44b39SDai Ngo 	nfsd4_ssc_init_umount_work(nn);
441f4e44b39SDai Ngo #endif
4422c2fe290SStanislav Kinsbursky 	nn->nfsd_net_up = true;
4436ff50b3dSStanislav Kinsbursky 	return 0;
4446ff50b3dSStanislav Kinsbursky 
445f5f9d4a3SJeff Layton out_reply_cache:
446f5f9d4a3SJeff Layton 	nfsd_reply_cache_shutdown(nn);
4479542e6a6STrond Myklebust out_filecache:
4489542e6a6STrond Myklebust 	nfsd_file_cache_shutdown_net(net);
4496ff50b3dSStanislav Kinsbursky out_lockd:
4508ef66714SKinglong Mee 	if (nn->lockd_up) {
4516ff50b3dSStanislav Kinsbursky 		lockd_down(net);
452e44b4bf2Szhengbin 		nn->lockd_up = false;
4538ef66714SKinglong Mee 	}
454903d9bf0SStanislav Kinsbursky out_socks:
455bda9cac1SStanislav Kinsbursky 	nfsd_shutdown_generic();
4564ad9a344SJeff Layton 	return ret;
4574ad9a344SJeff Layton }
4584ad9a344SJeff Layton 
nfsd_shutdown_net(struct net * net)4596ff50b3dSStanislav Kinsbursky static void nfsd_shutdown_net(struct net *net)
4606ff50b3dSStanislav Kinsbursky {
4612c2fe290SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
4622c2fe290SStanislav Kinsbursky 
4636ff50b3dSStanislav Kinsbursky 	nfs4_state_shutdown_net(net);
464f5f9d4a3SJeff Layton 	nfsd_reply_cache_shutdown(nn);
465789e1e10SJeff Layton 	nfsd_file_cache_shutdown_net(net);
4668ef66714SKinglong Mee 	if (nn->lockd_up) {
4676ff50b3dSStanislav Kinsbursky 		lockd_down(net);
468e44b4bf2Szhengbin 		nn->lockd_up = false;
4698ef66714SKinglong Mee 	}
4702c2fe290SStanislav Kinsbursky 	nn->nfsd_net_up = false;
471903d9bf0SStanislav Kinsbursky 	nfsd_shutdown_generic();
4726ff50b3dSStanislav Kinsbursky }
4736ff50b3dSStanislav Kinsbursky 
474d057cfecSNeilBrown static DEFINE_SPINLOCK(nfsd_notifier_lock);
nfsd_inetaddr_event(struct notifier_block * this,unsigned long event,void * ptr)47536684996SScott Mayhew static int nfsd_inetaddr_event(struct notifier_block *this, unsigned long event,
47636684996SScott Mayhew 	void *ptr)
47736684996SScott Mayhew {
47836684996SScott Mayhew 	struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
47936684996SScott Mayhew 	struct net_device *dev = ifa->ifa_dev->dev;
48036684996SScott Mayhew 	struct net *net = dev_net(dev);
48136684996SScott Mayhew 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
48236684996SScott Mayhew 	struct sockaddr_in sin;
48336684996SScott Mayhew 
484d057cfecSNeilBrown 	if (event != NETDEV_DOWN || !nn->nfsd_serv)
48536684996SScott Mayhew 		goto out;
48636684996SScott Mayhew 
487d057cfecSNeilBrown 	spin_lock(&nfsd_notifier_lock);
48836684996SScott Mayhew 	if (nn->nfsd_serv) {
48936684996SScott Mayhew 		dprintk("nfsd_inetaddr_event: removed %pI4\n", &ifa->ifa_local);
49036684996SScott Mayhew 		sin.sin_family = AF_INET;
49136684996SScott Mayhew 		sin.sin_addr.s_addr = ifa->ifa_local;
49236684996SScott Mayhew 		svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin);
49336684996SScott Mayhew 	}
494d057cfecSNeilBrown 	spin_unlock(&nfsd_notifier_lock);
49536684996SScott Mayhew 
49636684996SScott Mayhew out:
49736684996SScott Mayhew 	return NOTIFY_DONE;
49836684996SScott Mayhew }
49936684996SScott Mayhew 
50036684996SScott Mayhew static struct notifier_block nfsd_inetaddr_notifier = {
50136684996SScott Mayhew 	.notifier_call = nfsd_inetaddr_event,
50236684996SScott Mayhew };
50336684996SScott Mayhew 
50436684996SScott Mayhew #if IS_ENABLED(CONFIG_IPV6)
nfsd_inet6addr_event(struct notifier_block * this,unsigned long event,void * ptr)50536684996SScott Mayhew static int nfsd_inet6addr_event(struct notifier_block *this,
50636684996SScott Mayhew 	unsigned long event, void *ptr)
50736684996SScott Mayhew {
50836684996SScott Mayhew 	struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
50936684996SScott Mayhew 	struct net_device *dev = ifa->idev->dev;
51036684996SScott Mayhew 	struct net *net = dev_net(dev);
51136684996SScott Mayhew 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
51236684996SScott Mayhew 	struct sockaddr_in6 sin6;
51336684996SScott Mayhew 
514d057cfecSNeilBrown 	if (event != NETDEV_DOWN || !nn->nfsd_serv)
51536684996SScott Mayhew 		goto out;
51636684996SScott Mayhew 
517d057cfecSNeilBrown 	spin_lock(&nfsd_notifier_lock);
51836684996SScott Mayhew 	if (nn->nfsd_serv) {
51936684996SScott Mayhew 		dprintk("nfsd_inet6addr_event: removed %pI6\n", &ifa->addr);
52036684996SScott Mayhew 		sin6.sin6_family = AF_INET6;
52136684996SScott Mayhew 		sin6.sin6_addr = ifa->addr;
5227b19824dSScott Mayhew 		if (ipv6_addr_type(&sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
5237b19824dSScott Mayhew 			sin6.sin6_scope_id = ifa->idev->dev->ifindex;
52436684996SScott Mayhew 		svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin6);
52536684996SScott Mayhew 	}
526d057cfecSNeilBrown 	spin_unlock(&nfsd_notifier_lock);
527d057cfecSNeilBrown 
52836684996SScott Mayhew out:
52936684996SScott Mayhew 	return NOTIFY_DONE;
53036684996SScott Mayhew }
53136684996SScott Mayhew 
53236684996SScott Mayhew static struct notifier_block nfsd_inet6addr_notifier = {
53336684996SScott Mayhew 	.notifier_call = nfsd_inet6addr_event,
53436684996SScott Mayhew };
53536684996SScott Mayhew #endif
53636684996SScott Mayhew 
5371eca45f8SVasily Averin /* Only used under nfsd_mutex, so this atomic may be overkill: */
5381eca45f8SVasily Averin static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
5391eca45f8SVasily Averin 
nfsd_last_thread(struct net * net)540c21acd67SNeilBrown void nfsd_last_thread(struct net *net)
5414ad9a344SJeff Layton {
542903d9bf0SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
5439f28a971SNeilBrown 	struct svc_serv *serv = nn->nfsd_serv;
5449f28a971SNeilBrown 
5459f28a971SNeilBrown 	spin_lock(&nfsd_notifier_lock);
5469f28a971SNeilBrown 	nn->nfsd_serv = NULL;
5479f28a971SNeilBrown 	spin_unlock(&nfsd_notifier_lock);
548903d9bf0SStanislav Kinsbursky 
5491eca45f8SVasily Averin 	/* check if the notifier still has clients */
5501eca45f8SVasily Averin 	if (atomic_dec_return(&nfsd_notifier_refcount) == 0) {
55136684996SScott Mayhew 		unregister_inetaddr_notifier(&nfsd_inetaddr_notifier);
55236684996SScott Mayhew #if IS_ENABLED(CONFIG_IPV6)
55336684996SScott Mayhew 		unregister_inet6addr_notifier(&nfsd_inet6addr_notifier);
55436684996SScott Mayhew #endif
5551eca45f8SVasily Averin 	}
5561eca45f8SVasily Averin 
5579f28a971SNeilBrown 	svc_xprt_destroy_all(serv, net);
5589f28a971SNeilBrown 
5594ad9a344SJeff Layton 	/*
5604ad9a344SJeff Layton 	 * write_ports can create the server without actually starting
5614ad9a344SJeff Layton 	 * any threads--if we get shut down before any threads are
5624ad9a344SJeff Layton 	 * started, then nfsd_last_thread will be run before any of this
563691412b4SKinglong Mee 	 * other initialization has been done except the rpcb information.
5644ad9a344SJeff Layton 	 */
565691412b4SKinglong Mee 	svc_rpcb_cleanup(serv, net);
566903d9bf0SStanislav Kinsbursky 	if (!nn->nfsd_net_up)
5674ad9a344SJeff Layton 		return;
568691412b4SKinglong Mee 
5696ff50b3dSStanislav Kinsbursky 	nfsd_shutdown_net(net);
570b3853e0eSStanislav Kinsbursky 	nfsd_export_flush(net);
571bc591ccfSNeilBrown }
5726658d3a7SNeilBrown 
nfsd_reset_versions(struct nfsd_net * nn)573e333f3bbSTrond Myklebust void nfsd_reset_versions(struct nfsd_net *nn)
5746658d3a7SNeilBrown {
5756658d3a7SNeilBrown 	int i;
5766658d3a7SNeilBrown 
577800a938fSNeilBrown 	for (i = 0; i < NFSD_NRVERS; i++)
578e333f3bbSTrond Myklebust 		if (nfsd_vers(nn, i, NFSD_TEST))
579800a938fSNeilBrown 			return;
5806658d3a7SNeilBrown 
581800a938fSNeilBrown 	for (i = 0; i < NFSD_NRVERS; i++)
582800a938fSNeilBrown 		if (i != 4)
583e333f3bbSTrond Myklebust 			nfsd_vers(nn, i, NFSD_SET);
584800a938fSNeilBrown 		else {
585800a938fSNeilBrown 			int minor = 0;
586e333f3bbSTrond Myklebust 			while (nfsd_minorversion(nn, minor, NFSD_SET) >= 0)
587800a938fSNeilBrown 				minor++;
5886658d3a7SNeilBrown 		}
5896658d3a7SNeilBrown }
5906658d3a7SNeilBrown 
591c3d06f9cSAndy Adamson /*
592c3d06f9cSAndy Adamson  * Each session guarantees a negotiated per slot memory cache for replies
593c3d06f9cSAndy Adamson  * which in turn consumes memory beyond the v2/v3/v4.0 server. A dedicated
594c3d06f9cSAndy Adamson  * NFSv4.1 server might want to use more memory for a DRC than a machine
595c3d06f9cSAndy Adamson  * with mutiple services.
596c3d06f9cSAndy Adamson  *
597c3d06f9cSAndy Adamson  * Impose a hard limit on the number of pages for the DRC which varies
598c3d06f9cSAndy Adamson  * according to the machines free pages. This is of course only a default.
599c3d06f9cSAndy Adamson  *
600c3d06f9cSAndy Adamson  * For now this is a #defined shift which could be under admin control
601c3d06f9cSAndy Adamson  * in the future.
602c3d06f9cSAndy Adamson  */
set_max_drc(void)603c3d06f9cSAndy Adamson static void set_max_drc(void)
604c3d06f9cSAndy Adamson {
60544d8660dSJ. Bruce Fields 	#define NFSD_DRC_SIZE_SHIFT	7
6060c193054SAndy Adamson 	nfsd_drc_max_mem = (nr_free_buffer_pages()
6070c193054SAndy Adamson 					>> NFSD_DRC_SIZE_SHIFT) * PAGE_SIZE;
6080c193054SAndy Adamson 	nfsd_drc_mem_used = 0;
609697ce9beSZhang Yanfei 	dprintk("%s nfsd_drc_max_mem %lu \n", __func__, nfsd_drc_max_mem);
610c3d06f9cSAndy Adamson }
611bedbdd8bSNeil Brown 
nfsd_get_default_max_blksize(void)61287b0fc7dSJ. Bruce Fields static int nfsd_get_default_max_blksize(void)
61387b0fc7dSJ. Bruce Fields {
61487b0fc7dSJ. Bruce Fields 	struct sysinfo i;
61587b0fc7dSJ. Bruce Fields 	unsigned long long target;
61687b0fc7dSJ. Bruce Fields 	unsigned long ret;
61787b0fc7dSJ. Bruce Fields 
61887b0fc7dSJ. Bruce Fields 	si_meminfo(&i);
619508f9227SJ. Bruce Fields 	target = (i.totalram - i.totalhigh) << PAGE_SHIFT;
62087b0fc7dSJ. Bruce Fields 	/*
62187b0fc7dSJ. Bruce Fields 	 * Aim for 1/4096 of memory per thread This gives 1MB on 4Gig
62287b0fc7dSJ. Bruce Fields 	 * machines, but only uses 32K on 128M machines.  Bottom out at
62387b0fc7dSJ. Bruce Fields 	 * 8K on 32M and smaller.  Of course, this is only a default.
62487b0fc7dSJ. Bruce Fields 	 */
62587b0fc7dSJ. Bruce Fields 	target >>= 12;
62687b0fc7dSJ. Bruce Fields 
62787b0fc7dSJ. Bruce Fields 	ret = NFSSVC_MAXBLKSIZE;
62887b0fc7dSJ. Bruce Fields 	while (ret > target && ret >= 8*1024*2)
62987b0fc7dSJ. Bruce Fields 		ret /= 2;
63087b0fc7dSJ. Bruce Fields 	return ret;
63187b0fc7dSJ. Bruce Fields }
63287b0fc7dSJ. Bruce Fields 
nfsd_shutdown_threads(struct net * net)633c6c7f2a8STrond Myklebust void nfsd_shutdown_threads(struct net *net)
634c6c7f2a8STrond Myklebust {
635c6c7f2a8STrond Myklebust 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
636c6c7f2a8STrond Myklebust 	struct svc_serv *serv;
637c6c7f2a8STrond Myklebust 
638c6c7f2a8STrond Myklebust 	mutex_lock(&nfsd_mutex);
639c6c7f2a8STrond Myklebust 	serv = nn->nfsd_serv;
640c6c7f2a8STrond Myklebust 	if (serv == NULL) {
641c6c7f2a8STrond Myklebust 		mutex_unlock(&nfsd_mutex);
642c6c7f2a8STrond Myklebust 		return;
643c6c7f2a8STrond Myklebust 	}
644c6c7f2a8STrond Myklebust 
645c6c7f2a8STrond Myklebust 	svc_get(serv);
646c6c7f2a8STrond Myklebust 	/* Kill outstanding nfsd threads */
6473ebdbe52SNeilBrown 	svc_set_num_threads(serv, NULL, 0);
6489f28a971SNeilBrown 	nfsd_last_thread(net);
6499f28a971SNeilBrown 	svc_put(serv);
650c6c7f2a8STrond Myklebust 	mutex_unlock(&nfsd_mutex);
651c6c7f2a8STrond Myklebust }
652c6c7f2a8STrond Myklebust 
i_am_nfsd(void)65344fb26c6SMa Feng bool i_am_nfsd(void)
65428df3d15SJ. Bruce Fields {
65528df3d15SJ. Bruce Fields 	return kthread_func(current) == nfsd;
65628df3d15SJ. Bruce Fields }
65728df3d15SJ. Bruce Fields 
nfsd_create_serv(struct net * net)6586777436bSStanislav Kinsbursky int nfsd_create_serv(struct net *net)
65902a375f0SNeilBrown {
6609793f7c8SStanislav Kinsbursky 	int error;
661b9c0ef85SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
662d057cfecSNeilBrown 	struct svc_serv *serv;
6639793f7c8SStanislav Kinsbursky 
664bedbdd8bSNeil Brown 	WARN_ON(!mutex_is_locked(&nfsd_mutex));
6659dd9845fSStanislav Kinsbursky 	if (nn->nfsd_serv) {
6669dd9845fSStanislav Kinsbursky 		svc_get(nn->nfsd_serv);
66702a375f0SNeilBrown 		return 0;
66802a375f0SNeilBrown 	}
66987b0fc7dSJ. Bruce Fields 	if (nfsd_max_blksize == 0)
67087b0fc7dSJ. Bruce Fields 		nfsd_max_blksize = nfsd_get_default_max_blksize();
671e333f3bbSTrond Myklebust 	nfsd_reset_versions(nn);
6729eae1900SJosef Bacik 	serv = svc_create_pooled(&nfsd_program, &nn->nfsd_svcstats,
673465bb0f1SJosef Bacik 				 nfsd_max_blksize, nfsd);
674d057cfecSNeilBrown 	if (serv == NULL)
675628b3687SJeff Layton 		return -ENOMEM;
676bedbdd8bSNeil Brown 
677d057cfecSNeilBrown 	serv->sv_maxconn = nn->max_connections;
678d057cfecSNeilBrown 	error = svc_bind(serv, net);
6799793f7c8SStanislav Kinsbursky 	if (error < 0) {
680d057cfecSNeilBrown 		svc_put(serv);
6819793f7c8SStanislav Kinsbursky 		return error;
6829793f7c8SStanislav Kinsbursky 	}
683d057cfecSNeilBrown 	spin_lock(&nfsd_notifier_lock);
684d057cfecSNeilBrown 	nn->nfsd_serv = serv;
685d057cfecSNeilBrown 	spin_unlock(&nfsd_notifier_lock);
6869793f7c8SStanislav Kinsbursky 
687628b3687SJeff Layton 	set_max_drc();
6881eca45f8SVasily Averin 	/* check if the notifier is already set */
6891eca45f8SVasily Averin 	if (atomic_inc_return(&nfsd_notifier_refcount) == 1) {
69036684996SScott Mayhew 		register_inetaddr_notifier(&nfsd_inetaddr_notifier);
69136684996SScott Mayhew #if IS_ENABLED(CONFIG_IPV6)
69236684996SScott Mayhew 		register_inet6addr_notifier(&nfsd_inet6addr_notifier);
69336684996SScott Mayhew #endif
6941eca45f8SVasily Averin 	}
6953988a578SChuck Lever 	nfsd_reset_write_verifier(nn);
69687b0fc7dSJ. Bruce Fields 	return 0;
69702a375f0SNeilBrown }
69802a375f0SNeilBrown 
nfsd_nrpools(struct net * net)6999dd9845fSStanislav Kinsbursky int nfsd_nrpools(struct net *net)
700eed2965aSGreg Banks {
7019dd9845fSStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
7029dd9845fSStanislav Kinsbursky 
7039dd9845fSStanislav Kinsbursky 	if (nn->nfsd_serv == NULL)
704eed2965aSGreg Banks 		return 0;
705eed2965aSGreg Banks 	else
7069dd9845fSStanislav Kinsbursky 		return nn->nfsd_serv->sv_nrpools;
707eed2965aSGreg Banks }
708eed2965aSGreg Banks 
nfsd_get_nrthreads(int n,int * nthreads,struct net * net)7099dd9845fSStanislav Kinsbursky int nfsd_get_nrthreads(int n, int *nthreads, struct net *net)
710eed2965aSGreg Banks {
711eed2965aSGreg Banks 	int i = 0;
7129dd9845fSStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
713eed2965aSGreg Banks 
7149dd9845fSStanislav Kinsbursky 	if (nn->nfsd_serv != NULL) {
7159dd9845fSStanislav Kinsbursky 		for (i = 0; i < nn->nfsd_serv->sv_nrpools && i < n; i++)
7169dd9845fSStanislav Kinsbursky 			nthreads[i] = nn->nfsd_serv->sv_pools[i].sp_nrthreads;
717eed2965aSGreg Banks 	}
718eed2965aSGreg Banks 
719eed2965aSGreg Banks 	return 0;
720eed2965aSGreg Banks }
721eed2965aSGreg Banks 
nfsd_set_nrthreads(int n,int * nthreads,struct net * net)7223938a0d5SStanislav Kinsbursky int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
723eed2965aSGreg Banks {
724eed2965aSGreg Banks 	int i = 0;
725eed2965aSGreg Banks 	int tot = 0;
726eed2965aSGreg Banks 	int err = 0;
7279dd9845fSStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
728eed2965aSGreg Banks 
729bedbdd8bSNeil Brown 	WARN_ON(!mutex_is_locked(&nfsd_mutex));
730bedbdd8bSNeil Brown 
7319dd9845fSStanislav Kinsbursky 	if (nn->nfsd_serv == NULL || n <= 0)
732eed2965aSGreg Banks 		return 0;
733eed2965aSGreg Banks 
7349dd9845fSStanislav Kinsbursky 	if (n > nn->nfsd_serv->sv_nrpools)
7359dd9845fSStanislav Kinsbursky 		n = nn->nfsd_serv->sv_nrpools;
736eed2965aSGreg Banks 
737eed2965aSGreg Banks 	/* enforce a global maximum number of threads */
738eed2965aSGreg Banks 	tot = 0;
739eed2965aSGreg Banks 	for (i = 0; i < n; i++) {
7403c7aa15dSKinglong Mee 		nthreads[i] = min(nthreads[i], NFSD_MAXSERVS);
741eed2965aSGreg Banks 		tot += nthreads[i];
742eed2965aSGreg Banks 	}
743eed2965aSGreg Banks 	if (tot > NFSD_MAXSERVS) {
744eed2965aSGreg Banks 		/* total too large: scale down requested numbers */
745eed2965aSGreg Banks 		for (i = 0; i < n && tot > 0; i++) {
746eed2965aSGreg Banks 			int new = nthreads[i] * NFSD_MAXSERVS / tot;
747eed2965aSGreg Banks 			tot -= (nthreads[i] - new);
748eed2965aSGreg Banks 			nthreads[i] = new;
749eed2965aSGreg Banks 		}
750eed2965aSGreg Banks 		for (i = 0; i < n && tot > 0; i++) {
751eed2965aSGreg Banks 			nthreads[i]--;
752eed2965aSGreg Banks 			tot--;
753eed2965aSGreg Banks 		}
754eed2965aSGreg Banks 	}
755eed2965aSGreg Banks 
756eed2965aSGreg Banks 	/*
757eed2965aSGreg Banks 	 * There must always be a thread in pool 0; the admin
758eed2965aSGreg Banks 	 * can't shut down NFS completely using pool_threads.
759eed2965aSGreg Banks 	 */
760eed2965aSGreg Banks 	if (nthreads[0] == 0)
761eed2965aSGreg Banks 		nthreads[0] = 1;
762eed2965aSGreg Banks 
763eed2965aSGreg Banks 	/* apply the new numbers */
7649dd9845fSStanislav Kinsbursky 	svc_get(nn->nfsd_serv);
765eed2965aSGreg Banks 	for (i = 0; i < n; i++) {
7663ebdbe52SNeilBrown 		err = svc_set_num_threads(nn->nfsd_serv,
7673ebdbe52SNeilBrown 					  &nn->nfsd_serv->sv_pools[i],
7683ebdbe52SNeilBrown 					  nthreads[i]);
769eed2965aSGreg Banks 		if (err)
770eed2965aSGreg Banks 			break;
771eed2965aSGreg Banks 	}
7729f28a971SNeilBrown 	svc_put(nn->nfsd_serv);
773eed2965aSGreg Banks 	return err;
774eed2965aSGreg Banks }
775eed2965aSGreg Banks 
776ac77efbeSJeff Layton /*
777ac77efbeSJeff Layton  * Adjust the number of threads and return the new number of threads.
778ac77efbeSJeff Layton  * This is also the function that starts the server if necessary, if
779ac77efbeSJeff Layton  * this is the first time nrservs is nonzero.
780ac77efbeSJeff Layton  */
7811da177e4SLinus Torvalds int
nfsd_svc(int nrservs,struct net * net,const struct cred * cred)7824df493a2STrond Myklebust nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
7831da177e4SLinus Torvalds {
7841da177e4SLinus Torvalds 	int	error;
7859dd9845fSStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
7869f28a971SNeilBrown 	struct svc_serv *serv;
7871da177e4SLinus Torvalds 
788bedbdd8bSNeil Brown 	mutex_lock(&nfsd_mutex);
7896658d3a7SNeilBrown 	dprintk("nfsd: creating service\n");
7903c7aa15dSKinglong Mee 
7913c7aa15dSKinglong Mee 	nrservs = max(nrservs, 0);
7923c7aa15dSKinglong Mee 	nrservs = min(nrservs, NFSD_MAXSERVS);
793671e1fcfSNeilBrown 	error = 0;
7943c7aa15dSKinglong Mee 
7959dd9845fSStanislav Kinsbursky 	if (nrservs == 0 && nn->nfsd_serv == NULL)
796671e1fcfSNeilBrown 		goto out;
7971da177e4SLinus Torvalds 
79872f78ae0SWolfram Sang 	strscpy(nn->nfsd_name, utsname()->nodename,
7997627d7dcSScott Mayhew 		sizeof(nn->nfsd_name));
8007627d7dcSScott Mayhew 
8016777436bSStanislav Kinsbursky 	error = nfsd_create_serv(net);
80202a375f0SNeilBrown 	if (error)
803774f8bbdSJ. Bruce Fields 		goto out;
8049f28a971SNeilBrown 	serv = nn->nfsd_serv;
805774f8bbdSJ. Bruce Fields 
80670c53075SVasily Averin 	error = nfsd_startup_net(net, cred);
807af4718f3SJ. Bruce Fields 	if (error)
8088c62d127SNeilBrown 		goto out_put;
8099f28a971SNeilBrown 	error = svc_set_num_threads(serv, NULL, nrservs);
810774f8bbdSJ. Bruce Fields 	if (error)
811*e0aeb26bSNeilBrown 		goto out_put;
8129f28a971SNeilBrown 	error = serv->sv_nrthreads;
8138c62d127SNeilBrown out_put:
814ec52361dSNeilBrown 	/* Threads now hold service active */
815ec52361dSNeilBrown 	if (xchg(&nn->keep_active, 0))
8169f28a971SNeilBrown 		svc_put(serv);
817*e0aeb26bSNeilBrown 
818*e0aeb26bSNeilBrown 	if (serv->sv_nrthreads == 0)
819*e0aeb26bSNeilBrown 		nfsd_last_thread(net);
8209f28a971SNeilBrown 	svc_put(serv);
8211da177e4SLinus Torvalds out:
822bedbdd8bSNeil Brown 	mutex_unlock(&nfsd_mutex);
8231da177e4SLinus Torvalds 	return error;
8241da177e4SLinus Torvalds }
8251da177e4SLinus Torvalds 
826029be5d0STrond Myklebust #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
827029be5d0STrond Myklebust static bool
nfsd_support_acl_version(int vers)828029be5d0STrond Myklebust nfsd_support_acl_version(int vers)
829029be5d0STrond Myklebust {
830029be5d0STrond Myklebust 	if (vers >= NFSD_ACL_MINVERS && vers < NFSD_ACL_NRVERS)
831029be5d0STrond Myklebust 		return nfsd_acl_version[vers] != NULL;
832029be5d0STrond Myklebust 	return false;
833029be5d0STrond Myklebust }
834029be5d0STrond Myklebust 
835029be5d0STrond Myklebust static int
nfsd_acl_rpcbind_set(struct net * net,const struct svc_program * progp,u32 version,int family,unsigned short proto,unsigned short port)836029be5d0STrond Myklebust nfsd_acl_rpcbind_set(struct net *net, const struct svc_program *progp,
837029be5d0STrond Myklebust 		     u32 version, int family, unsigned short proto,
838029be5d0STrond Myklebust 		     unsigned short port)
839029be5d0STrond Myklebust {
840029be5d0STrond Myklebust 	if (!nfsd_support_acl_version(version) ||
841e333f3bbSTrond Myklebust 	    !nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST))
842029be5d0STrond Myklebust 		return 0;
843029be5d0STrond Myklebust 	return svc_generic_rpcbind_set(net, progp, version, family,
844029be5d0STrond Myklebust 			proto, port);
845029be5d0STrond Myklebust }
846e333f3bbSTrond Myklebust 
847e333f3bbSTrond Myklebust static __be32
nfsd_acl_init_request(struct svc_rqst * rqstp,const struct svc_program * progp,struct svc_process_info * ret)848e333f3bbSTrond Myklebust nfsd_acl_init_request(struct svc_rqst *rqstp,
849e333f3bbSTrond Myklebust 		      const struct svc_program *progp,
850e333f3bbSTrond Myklebust 		      struct svc_process_info *ret)
851e333f3bbSTrond Myklebust {
852e333f3bbSTrond Myklebust 	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
853e333f3bbSTrond Myklebust 	int i;
854e333f3bbSTrond Myklebust 
855e333f3bbSTrond Myklebust 	if (likely(nfsd_support_acl_version(rqstp->rq_vers) &&
856e333f3bbSTrond Myklebust 	    nfsd_vers(nn, rqstp->rq_vers, NFSD_TEST)))
857e333f3bbSTrond Myklebust 		return svc_generic_init_request(rqstp, progp, ret);
858e333f3bbSTrond Myklebust 
859e333f3bbSTrond Myklebust 	ret->mismatch.lovers = NFSD_ACL_NRVERS;
860e333f3bbSTrond Myklebust 	for (i = NFSD_ACL_MINVERS; i < NFSD_ACL_NRVERS; i++) {
861e333f3bbSTrond Myklebust 		if (nfsd_support_acl_version(rqstp->rq_vers) &&
862e333f3bbSTrond Myklebust 		    nfsd_vers(nn, i, NFSD_TEST)) {
863e333f3bbSTrond Myklebust 			ret->mismatch.lovers = i;
864e333f3bbSTrond Myklebust 			break;
865e333f3bbSTrond Myklebust 		}
866e333f3bbSTrond Myklebust 	}
867e333f3bbSTrond Myklebust 	if (ret->mismatch.lovers == NFSD_ACL_NRVERS)
868e333f3bbSTrond Myklebust 		return rpc_prog_unavail;
869e333f3bbSTrond Myklebust 	ret->mismatch.hivers = NFSD_ACL_MINVERS;
870e333f3bbSTrond Myklebust 	for (i = NFSD_ACL_NRVERS - 1; i >= NFSD_ACL_MINVERS; i--) {
871e333f3bbSTrond Myklebust 		if (nfsd_support_acl_version(rqstp->rq_vers) &&
872e333f3bbSTrond Myklebust 		    nfsd_vers(nn, i, NFSD_TEST)) {
873e333f3bbSTrond Myklebust 			ret->mismatch.hivers = i;
874e333f3bbSTrond Myklebust 			break;
875e333f3bbSTrond Myklebust 		}
876e333f3bbSTrond Myklebust 	}
877e333f3bbSTrond Myklebust 	return rpc_prog_mismatch;
878e333f3bbSTrond Myklebust }
879029be5d0STrond Myklebust #endif
880029be5d0STrond Myklebust 
881029be5d0STrond Myklebust static int
nfsd_rpcbind_set(struct net * net,const struct svc_program * progp,u32 version,int family,unsigned short proto,unsigned short port)882029be5d0STrond Myklebust nfsd_rpcbind_set(struct net *net, const struct svc_program *progp,
883029be5d0STrond Myklebust 		 u32 version, int family, unsigned short proto,
884029be5d0STrond Myklebust 		 unsigned short port)
885029be5d0STrond Myklebust {
886e333f3bbSTrond Myklebust 	if (!nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST))
887029be5d0STrond Myklebust 		return 0;
888029be5d0STrond Myklebust 	return svc_generic_rpcbind_set(net, progp, version, family,
889029be5d0STrond Myklebust 			proto, port);
890029be5d0STrond Myklebust }
8911da177e4SLinus Torvalds 
892e333f3bbSTrond Myklebust static __be32
nfsd_init_request(struct svc_rqst * rqstp,const struct svc_program * progp,struct svc_process_info * ret)893e333f3bbSTrond Myklebust nfsd_init_request(struct svc_rqst *rqstp,
894e333f3bbSTrond Myklebust 		  const struct svc_program *progp,
895e333f3bbSTrond Myklebust 		  struct svc_process_info *ret)
896e333f3bbSTrond Myklebust {
897e333f3bbSTrond Myklebust 	struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
898e333f3bbSTrond Myklebust 	int i;
899e333f3bbSTrond Myklebust 
900e333f3bbSTrond Myklebust 	if (likely(nfsd_vers(nn, rqstp->rq_vers, NFSD_TEST)))
901e333f3bbSTrond Myklebust 		return svc_generic_init_request(rqstp, progp, ret);
902e333f3bbSTrond Myklebust 
903e333f3bbSTrond Myklebust 	ret->mismatch.lovers = NFSD_NRVERS;
904e333f3bbSTrond Myklebust 	for (i = NFSD_MINVERS; i < NFSD_NRVERS; i++) {
905e333f3bbSTrond Myklebust 		if (nfsd_vers(nn, i, NFSD_TEST)) {
906e333f3bbSTrond Myklebust 			ret->mismatch.lovers = i;
907e333f3bbSTrond Myklebust 			break;
908e333f3bbSTrond Myklebust 		}
909e333f3bbSTrond Myklebust 	}
910e333f3bbSTrond Myklebust 	if (ret->mismatch.lovers == NFSD_NRVERS)
911e333f3bbSTrond Myklebust 		return rpc_prog_unavail;
912e333f3bbSTrond Myklebust 	ret->mismatch.hivers = NFSD_MINVERS;
913e333f3bbSTrond Myklebust 	for (i = NFSD_NRVERS - 1; i >= NFSD_MINVERS; i--) {
914e333f3bbSTrond Myklebust 		if (nfsd_vers(nn, i, NFSD_TEST)) {
915e333f3bbSTrond Myklebust 			ret->mismatch.hivers = i;
916e333f3bbSTrond Myklebust 			break;
917e333f3bbSTrond Myklebust 		}
918e333f3bbSTrond Myklebust 	}
919e333f3bbSTrond Myklebust 	return rpc_prog_mismatch;
920e333f3bbSTrond Myklebust }
921e333f3bbSTrond Myklebust 
9221da177e4SLinus Torvalds /*
9231da177e4SLinus Torvalds  * This is the NFS server kernel thread
9241da177e4SLinus Torvalds  */
9259867d76cSJeff Layton static int
nfsd(void * vrqstp)9269867d76cSJeff Layton nfsd(void *vrqstp)
9271da177e4SLinus Torvalds {
9289867d76cSJeff Layton 	struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp;
92988c47666SStanislav Kinsbursky 	struct svc_xprt *perm_sock = list_entry(rqstp->rq_server->sv_permsocks.next, typeof(struct svc_xprt), xpt_list);
93088c47666SStanislav Kinsbursky 	struct net *net = perm_sock->xpt_net;
9315b8db00bSJeff Layton 	struct nfsd_net *nn = net_generic(net, nfsd_net_id);
9321da177e4SLinus Torvalds 
9339867d76cSJeff Layton 	/* At this point, the thread shares current->fs
93447057abdSAndreas Gruenbacher 	 * with the init process. We need to create files with the
93547057abdSAndreas Gruenbacher 	 * umask as defined by the client instead of init's umask. */
9363e93cd67SAl Viro 	if (unshare_fs_struct() < 0) {
9371da177e4SLinus Torvalds 		printk("Unable to start nfsd thread: out of memory\n");
9381da177e4SLinus Torvalds 		goto out;
9391da177e4SLinus Torvalds 	}
9403e93cd67SAl Viro 
9411da177e4SLinus Torvalds 	current->fs->umask = 0;
9421da177e4SLinus Torvalds 
9439ae63aabSJosef Bacik 	atomic_inc(&nfsd_th_cnt);
944bedbdd8bSNeil Brown 
94583144186SRafael J. Wysocki 	set_freezable();
9461da177e4SLinus Torvalds 
9471da177e4SLinus Torvalds 	/*
9481da177e4SLinus Torvalds 	 * The main request loop
9491da177e4SLinus Torvalds 	 */
9507b719e2bSNeilBrown 	while (!kthread_should_stop()) {
9515b8db00bSJeff Layton 		/* Update sv_maxconn if it has changed */
9525b8db00bSJeff Layton 		rqstp->rq_server->sv_maxconn = nn->max_connections;
9535b8db00bSJeff Layton 
954c743b425SNeilBrown 		svc_recv(rqstp);
9551da177e4SLinus Torvalds 	}
9561da177e4SLinus Torvalds 
9579ae63aabSJosef Bacik 	atomic_dec(&nfsd_th_cnt);
9581da177e4SLinus Torvalds 
9591da177e4SLinus Torvalds out:
9601da177e4SLinus Torvalds 	/* Release the thread */
9611da177e4SLinus Torvalds 	svc_exit_thread(rqstp);
9629867d76cSJeff Layton 	return 0;
9631da177e4SLinus Torvalds }
9641da177e4SLinus Torvalds 
965383c440dSChuck Lever /**
966383c440dSChuck Lever  * nfsd_dispatch - Process an NFS or NFSACL Request
967383c440dSChuck Lever  * @rqstp: incoming request
968383c440dSChuck Lever  *
969383c440dSChuck Lever  * This RPC dispatcher integrates the NFS server's duplicate reply cache.
970383c440dSChuck Lever  *
971383c440dSChuck Lever  * Return values:
972383c440dSChuck Lever  *  %0: Processing complete; do not send a Reply
973383c440dSChuck Lever  *  %1: Processing complete; send Reply in rqstp->rq_res
974383c440dSChuck Lever  */
nfsd_dispatch(struct svc_rqst * rqstp)975cee4db19SChuck Lever int nfsd_dispatch(struct svc_rqst *rqstp)
9761da177e4SLinus Torvalds {
9774c96cb56SChuck Lever 	const struct svc_procedure *proc = rqstp->rq_procinfo;
978cee4db19SChuck Lever 	__be32 *statp = rqstp->rq_accept_statp;
979e7421ce7SChuck Lever 	struct nfsd_cacherep *rp;
980b79e3569SChuck Lever 	unsigned int start, len;
981ed1e0ea2SChuck Lever 	__be32 *nfs_reply;
9821da177e4SLinus Torvalds 
9831091006cSJ. Bruce Fields 	/*
9841091006cSJ. Bruce Fields 	 * Give the xdr decoder a chance to change this if it wants
9851091006cSJ. Bruce Fields 	 * (necessary in the NFSv4.0 compound case)
9861091006cSJ. Bruce Fields 	 */
9871091006cSJ. Bruce Fields 	rqstp->rq_cachetype = proc->pc_cachetype;
9885191955dSChuck Lever 
989b79e3569SChuck Lever 	/*
990b79e3569SChuck Lever 	 * ->pc_decode advances the argument stream past the NFS
991b79e3569SChuck Lever 	 * Call header, so grab the header's starting location and
992b79e3569SChuck Lever 	 * size now for the call to nfsd_cache_lookup().
993b79e3569SChuck Lever 	 */
994b79e3569SChuck Lever 	start = xdr_stream_pos(&rqstp->rq_arg_stream);
995b79e3569SChuck Lever 	len = xdr_stream_remaining(&rqstp->rq_arg_stream);
99616c66364SChuck Lever 	if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream))
99785085aacSChuck Lever 		goto out_decode_err;
9981091006cSJ. Bruce Fields 
999cb18eca4SChuck Lever 	rp = NULL;
1000b79e3569SChuck Lever 	switch (nfsd_cache_lookup(rqstp, start, len, &rp)) {
100184c138e7SChuck Lever 	case RC_DOIT:
100284c138e7SChuck Lever 		break;
10031da177e4SLinus Torvalds 	case RC_REPLY:
100485085aacSChuck Lever 		goto out_cached_reply;
100584c138e7SChuck Lever 	case RC_DROPIT:
100685085aacSChuck Lever 		goto out_dropit;
10071da177e4SLinus Torvalds 	}
10081da177e4SLinus Torvalds 
1009ed1e0ea2SChuck Lever 	nfs_reply = xdr_inline_decode(&rqstp->rq_res_stream, 0);
1010cc028a10SChuck Lever 	*statp = proc->pc_func(rqstp);
101193155647SChuck Lever 	if (test_bit(RQ_DROPME, &rqstp->rq_flags))
101285085aacSChuck Lever 		goto out_update_drop;
10131da177e4SLinus Torvalds 
1014fda49441SChuck Lever 	if (!proc->pc_encode(rqstp, &rqstp->rq_res_stream))
101585085aacSChuck Lever 		goto out_encode_err;
101685085aacSChuck Lever 
1017ed1e0ea2SChuck Lever 	nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, nfs_reply);
101885085aacSChuck Lever out_cached_reply:
101985085aacSChuck Lever 	return 1;
102085085aacSChuck Lever 
102185085aacSChuck Lever out_decode_err:
10220dfdad1cSChuck Lever 	trace_nfsd_garbage_args_err(rqstp);
102385085aacSChuck Lever 	*statp = rpc_garbage_args;
102485085aacSChuck Lever 	return 1;
102585085aacSChuck Lever 
102685085aacSChuck Lever out_update_drop:
1027cb18eca4SChuck Lever 	nfsd_cache_update(rqstp, rp, RC_NOCACHE, NULL);
102885085aacSChuck Lever out_dropit:
102985085aacSChuck Lever 	return 0;
103085085aacSChuck Lever 
103185085aacSChuck Lever out_encode_err:
10320dfdad1cSChuck Lever 	trace_nfsd_cant_encode_err(rqstp);
1033cb18eca4SChuck Lever 	nfsd_cache_update(rqstp, rp, RC_NOCACHE, NULL);
10341da177e4SLinus Torvalds 	*statp = rpc_system_err;
10351da177e4SLinus Torvalds 	return 1;
10361da177e4SLinus Torvalds }
103703cf6c9fSGreg Banks 
1038788f7183SChuck Lever /**
1039788f7183SChuck Lever  * nfssvc_decode_voidarg - Decode void arguments
1040788f7183SChuck Lever  * @rqstp: Server RPC transaction context
104116c66364SChuck Lever  * @xdr: XDR stream positioned at arguments to decode
1042788f7183SChuck Lever  *
1043788f7183SChuck Lever  * Return values:
1044c44b31c2SChuck Lever  *   %false: Arguments were not valid
1045c44b31c2SChuck Lever  *   %true: Decoding was successful
1046788f7183SChuck Lever  */
nfssvc_decode_voidarg(struct svc_rqst * rqstp,struct xdr_stream * xdr)1047c44b31c2SChuck Lever bool nfssvc_decode_voidarg(struct svc_rqst *rqstp, struct xdr_stream *xdr)
1048788f7183SChuck Lever {
1049291cd656SChangcheng Deng 	return true;
1050788f7183SChuck Lever }
1051788f7183SChuck Lever 
1052788f7183SChuck Lever /**
1053788f7183SChuck Lever  * nfssvc_encode_voidres - Encode void results
1054788f7183SChuck Lever  * @rqstp: Server RPC transaction context
1055fda49441SChuck Lever  * @xdr: XDR stream into which to encode results
1056788f7183SChuck Lever  *
1057788f7183SChuck Lever  * Return values:
1058130e2054SChuck Lever  *   %false: Local error while encoding
1059130e2054SChuck Lever  *   %true: Encoding was successful
1060788f7183SChuck Lever  */
nfssvc_encode_voidres(struct svc_rqst * rqstp,struct xdr_stream * xdr)1061130e2054SChuck Lever bool nfssvc_encode_voidres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
1062788f7183SChuck Lever {
1063130e2054SChuck Lever 	return true;
1064788f7183SChuck Lever }
1065788f7183SChuck Lever 
nfsd_pool_stats_open(struct inode * inode,struct file * file)106603cf6c9fSGreg Banks int nfsd_pool_stats_open(struct inode *inode, struct file *file)
106703cf6c9fSGreg Banks {
1068ed2d8aedSRyusei Yamaguchi 	int ret;
106911f77942SStanislav Kinsbursky 	struct nfsd_net *nn = net_generic(inode->i_sb->s_fs_info, nfsd_net_id);
10709dd9845fSStanislav Kinsbursky 
1071ed2d8aedSRyusei Yamaguchi 	mutex_lock(&nfsd_mutex);
10729dd9845fSStanislav Kinsbursky 	if (nn->nfsd_serv == NULL) {
1073ed2d8aedSRyusei Yamaguchi 		mutex_unlock(&nfsd_mutex);
107403cf6c9fSGreg Banks 		return -ENODEV;
1075ed2d8aedSRyusei Yamaguchi 	}
10769dd9845fSStanislav Kinsbursky 	svc_get(nn->nfsd_serv);
10779dd9845fSStanislav Kinsbursky 	ret = svc_pool_stats_open(nn->nfsd_serv, file);
1078ed2d8aedSRyusei Yamaguchi 	mutex_unlock(&nfsd_mutex);
1079ed2d8aedSRyusei Yamaguchi 	return ret;
1080ed2d8aedSRyusei Yamaguchi }
1081ed2d8aedSRyusei Yamaguchi 
nfsd_pool_stats_release(struct inode * inode,struct file * file)1082ed2d8aedSRyusei Yamaguchi int nfsd_pool_stats_release(struct inode *inode, struct file *file)
1083ed2d8aedSRyusei Yamaguchi {
108488956eabSNeilBrown 	struct seq_file *seq = file->private_data;
108588956eabSNeilBrown 	struct svc_serv *serv = seq->private;
1086ed2d8aedSRyusei Yamaguchi 	int ret = seq_release(inode, file);
1087786185b5SStanislav Kinsbursky 
1088ed2d8aedSRyusei Yamaguchi 	mutex_lock(&nfsd_mutex);
108988956eabSNeilBrown 	svc_put(serv);
1090ed2d8aedSRyusei Yamaguchi 	mutex_unlock(&nfsd_mutex);
1091ed2d8aedSRyusei Yamaguchi 	return ret;
109203cf6c9fSGreg Banks }
1093