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/nfslocalio.h>
2336684996SScott Mayhew #include <linux/seq_file.h>
2436684996SScott Mayhew #include <linux/inetdevice.h>
2536684996SScott Mayhew #include <net/addrconf.h>
26fc5d00b0SPavel Emelyanov #include <net/ipv6.h>
279a74af21SBoaz Harrosh #include <net/net_namespace.h>
289a74af21SBoaz Harrosh #include "nfsd.h"
290a3adadeSJ. Bruce Fields #include "cache.h"
302c2fe290SStanislav Kinsbursky #include "vfs.h"
3165294c1fSJeff Layton #include "netns.h"
321da177e4SLinus Torvalds #include "filecache.h"
330dfdad1cSChuck Lever
340dfdad1cSChuck Lever #include "trace.h"
351da177e4SLinus Torvalds
361da177e4SLinus Torvalds #define NFSDDBG_FACILITY NFSDDBG_SVC
37e41ee44cSJosef Bacik
381da177e4SLinus Torvalds atomic_t nfsd_th_cnt = ATOMIC_INIT(0);
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 *
633dd98a3bSJeff Layton * Finally, the nfsd_mutex also protects some of the global variables that are
643dd98a3bSJeff Layton * accessed when nfsd starts and that are settable via the write_* routines in
653dd98a3bSJeff Layton * nfsctl.c. In particular:
663dd98a3bSJeff Layton *
673dd98a3bSJeff Layton * user_recovery_dirname
683dd98a3bSJeff Layton * user_lease_time
693dd98a3bSJeff Layton * nfsd_versions
70bedbdd8bSNeil Brown */
71bedbdd8bSNeil Brown DEFINE_MUTEX(nfsd_mutex);
72bedbdd8bSNeil Brown
734bd9b0f4SAndy Adamson #if IS_ENABLED(CONFIG_NFS_LOCALIO)
744bd9b0f4SAndy Adamson static const struct svc_version *localio_versions[] = {
754bd9b0f4SAndy Adamson [1] = &localio_version1,
764bd9b0f4SAndy Adamson };
774bd9b0f4SAndy Adamson
784bd9b0f4SAndy Adamson #define NFSD_LOCALIO_NRVERS ARRAY_SIZE(localio_versions)
79b73ac680SGuobin Huang
80697ce9beSZhang Yanfei #endif /* CONFIG_NFS_LOCALIO */
81697ce9beSZhang Yanfei
824bd9b0f4SAndy Adamson #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
833fb803a9SAndreas Gruenbacher static const struct svc_version *nfsd_acl_version[] = {
84e9679189SChristoph Hellwig # if defined(CONFIG_NFSD_V2_ACL)
852f3a4b2aSJeff Layton [2] = &nfsd_acl_version2,
863fb803a9SAndreas Gruenbacher # endif
872f3a4b2aSJeff Layton # if defined(CONFIG_NFSD_V3_ACL)
882f3a4b2aSJeff Layton [3] = &nfsd_acl_version3,
893fb803a9SAndreas Gruenbacher # endif
902f3a4b2aSJeff Layton };
913fb803a9SAndreas Gruenbacher
923fb803a9SAndreas Gruenbacher #define NFSD_ACL_MINVERS 2
933fb803a9SAndreas Gruenbacher #define NFSD_ACL_NRVERS ARRAY_SIZE(nfsd_acl_version)
94e8c96f8cSTobias Klauser
953fb803a9SAndreas Gruenbacher #endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
963fb803a9SAndreas Gruenbacher
973fb803a9SAndreas Gruenbacher static const struct svc_version *nfsd_version[NFSD_MAXVERS+1] = {
983fb803a9SAndreas Gruenbacher #if defined(CONFIG_NFSD_V2)
997c149057SJ. Bruce Fields [2] = &nfsd_version2,
1001a8eff6dSNeilBrown #endif
1013fb803a9SAndreas Gruenbacher [3] = &nfsd_version3,
1023fb803a9SAndreas Gruenbacher #if defined(CONFIG_NFSD_V4)
103e333f3bbSTrond Myklebust [4] = &nfsd_version4,
104029be5d0STrond Myklebust #endif
1053fb803a9SAndreas Gruenbacher };
1063fb803a9SAndreas Gruenbacher
1073fb803a9SAndreas Gruenbacher struct svc_program nfsd_programs[] = {
1083fb803a9SAndreas Gruenbacher {
109e9679189SChristoph Hellwig .pg_prog = NFS_PROGRAM, /* program number */
1102f3a4b2aSJeff Layton .pg_nvers = NFSD_MAXVERS+1, /* nr of entries in nfsd_version */
11170c3b76cSNeilBrown .pg_vers = nfsd_version, /* version table */
1122f3a4b2aSJeff Layton .pg_name = "nfsd", /* program name */
11370c3b76cSNeilBrown .pg_class = "nfsd", /* authentication class */
11470c3b76cSNeilBrown .pg_authenticate = svc_set_client, /* export authentication */
11570c3b76cSNeilBrown .pg_init_request = nfsd_init_request,
11670c3b76cSNeilBrown .pg_rpcbind_set = nfsd_rpcbind_set,
11770c3b76cSNeilBrown },
11870c3b76cSNeilBrown #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
11970c3b76cSNeilBrown {
120e8c96f8cSTobias Klauser .pg_prog = NFS_ACL_PROGRAM,
12170c3b76cSNeilBrown .pg_nvers = NFSD_ACL_NRVERS,
12270c3b76cSNeilBrown .pg_vers = nfsd_acl_version,
1233fb803a9SAndreas Gruenbacher .pg_name = "nfsacl",
1243fb803a9SAndreas Gruenbacher .pg_class = "nfsd",
1253fb803a9SAndreas Gruenbacher .pg_authenticate = svc_set_client,
12670c3b76cSNeilBrown .pg_init_request = nfsd_acl_init_request,
12770c3b76cSNeilBrown .pg_rpcbind_set = nfsd_acl_rpcbind_set,
128e333f3bbSTrond Myklebust },
12970c3b76cSNeilBrown #endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */
13070c3b76cSNeilBrown #if IS_ENABLED(CONFIG_NFS_LOCALIO)
13170c3b76cSNeilBrown {
132e333f3bbSTrond Myklebust .pg_prog = NFS_LOCALIO_PROGRAM,
133029be5d0STrond Myklebust .pg_nvers = NFSD_LOCALIO_NRVERS,
13470c3b76cSNeilBrown .pg_vers = localio_versions,
13570c3b76cSNeilBrown .pg_name = "nfslocalio",
136e333f3bbSTrond Myklebust .pg_class = "nfsd",
137e333f3bbSTrond Myklebust .pg_authenticate = svc_set_client,
138e333f3bbSTrond Myklebust .pg_init_request = svc_generic_init_request,
139e333f3bbSTrond Myklebust .pg_rpcbind_set = svc_generic_rpcbind_set,
140e333f3bbSTrond Myklebust }
141e333f3bbSTrond Myklebust #endif /* CONFIG_NFS_LOCALIO */
142e333f3bbSTrond Myklebust };
1438daf220aSBenny Halevy
nfsd_support_version(int vers)144e333f3bbSTrond Myklebust bool nfsd_support_version(int vers)
145e333f3bbSTrond Myklebust {
146e333f3bbSTrond Myklebust if (vers >= NFSD_MINVERS && vers <= NFSD_MAXVERS)
147e333f3bbSTrond Myklebust return nfsd_version[vers] != NULL;
148e333f3bbSTrond Myklebust return false;
149e333f3bbSTrond Myklebust }
150e333f3bbSTrond Myklebust
nfsd_vers(struct nfsd_net * nn,int vers,enum vers_op change)151e333f3bbSTrond Myklebust int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change)
152e333f3bbSTrond Myklebust {
153e333f3bbSTrond Myklebust if (vers < NFSD_MINVERS || vers > NFSD_MAXVERS)
154e333f3bbSTrond Myklebust return 0;
155e333f3bbSTrond Myklebust switch(change) {
156e333f3bbSTrond Myklebust case NFSD_SET:
157e333f3bbSTrond Myklebust nn->nfsd_versions[vers] = nfsd_support_version(vers);
158e333f3bbSTrond Myklebust break;
159e333f3bbSTrond Myklebust case NFSD_CLEAR:
160e333f3bbSTrond Myklebust nn->nfsd_versions[vers] = false;
161e333f3bbSTrond Myklebust break;
162e333f3bbSTrond Myklebust case NFSD_TEST:
163e333f3bbSTrond Myklebust return nn->nfsd_versions[vers];
164e333f3bbSTrond Myklebust case NFSD_AVAIL:
165e333f3bbSTrond Myklebust return nfsd_support_version(vers);
166e333f3bbSTrond Myklebust }
167e333f3bbSTrond Myklebust return 0;
168e333f3bbSTrond Myklebust }
169e333f3bbSTrond Myklebust
170e333f3bbSTrond Myklebust static void
nfsd_adjust_nfsd_versions4(struct nfsd_net * nn)171e333f3bbSTrond Myklebust nfsd_adjust_nfsd_versions4(struct nfsd_net *nn)
172e333f3bbSTrond Myklebust {
173e333f3bbSTrond Myklebust unsigned i;
174e333f3bbSTrond Myklebust
175e333f3bbSTrond Myklebust for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++) {
176e333f3bbSTrond Myklebust if (nn->nfsd4_minorversions[i])
177e333f3bbSTrond Myklebust return;
178e333f3bbSTrond Myklebust }
179e333f3bbSTrond Myklebust nfsd_vers(nn, 4, NFSD_CLEAR);
180e333f3bbSTrond Myklebust }
181e333f3bbSTrond Myklebust
nfsd_minorversion(struct nfsd_net * nn,u32 minorversion,enum vers_op change)182e333f3bbSTrond Myklebust int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change)
183e333f3bbSTrond Myklebust {
184e333f3bbSTrond Myklebust if (minorversion > NFSD_SUPPORTED_MINOR_VERSION &&
185e333f3bbSTrond Myklebust change != NFSD_AVAIL)
186e333f3bbSTrond Myklebust return -1;
187e333f3bbSTrond Myklebust
188e333f3bbSTrond Myklebust switch(change) {
189e333f3bbSTrond Myklebust case NFSD_SET:
190e333f3bbSTrond Myklebust nfsd_vers(nn, 4, NFSD_SET);
191e333f3bbSTrond Myklebust nn->nfsd4_minorversions[minorversion] =
192e333f3bbSTrond Myklebust nfsd_vers(nn, 4, NFSD_TEST);
193e333f3bbSTrond Myklebust break;
1946658d3a7SNeilBrown case NFSD_CLEAR:
1956658d3a7SNeilBrown nn->nfsd4_minorversions[minorversion] = false;
19615ddb4aeSPavel Emelyanov nfsd_adjust_nfsd_versions4(nn);
1976658d3a7SNeilBrown break;
1986658d3a7SNeilBrown case NFSD_TEST:
199e333f3bbSTrond Myklebust return nn->nfsd4_minorversions[minorversion];
200e333f3bbSTrond Myklebust case NFSD_AVAIL:
2011a8eff6dSNeilBrown return minorversion <= NFSD_SUPPORTED_MINOR_VERSION &&
2026658d3a7SNeilBrown nfsd_vers(nn, 4, NFSD_AVAIL);
203e333f3bbSTrond Myklebust }
204e333f3bbSTrond Myklebust return 0;
205e333f3bbSTrond Myklebust }
2066658d3a7SNeilBrown
nfsd_net_try_get(struct net * net)2076658d3a7SNeilBrown bool nfsd_net_try_get(struct net *net) __must_hold(rcu)
208e333f3bbSTrond Myklebust {
209e333f3bbSTrond Myklebust struct nfsd_net *nn = net_generic(net, nfsd_net_id);
210df561f66SGustavo A. R. Silva
2116658d3a7SNeilBrown return (nn && percpu_ref_tryget_live(&nn->nfsd_net_ref));
212e333f3bbSTrond Myklebust }
2136658d3a7SNeilBrown
nfsd_net_put(struct net * net)2146658d3a7SNeilBrown void nfsd_net_put(struct net *net) __must_hold(rcu)
2156658d3a7SNeilBrown {
2168daf220aSBenny Halevy struct nfsd_net *nn = net_generic(net, nfsd_net_id);
217d3635ff0STrond Myklebust
218e333f3bbSTrond Myklebust percpu_ref_put(&nn->nfsd_net_ref);
219d3635ff0STrond Myklebust }
220d3635ff0STrond Myklebust
nfsd_net_done(struct percpu_ref * ref)221d3635ff0STrond Myklebust static void nfsd_net_done(struct percpu_ref *ref)
222d3635ff0STrond Myklebust {
223e333f3bbSTrond Myklebust struct nfsd_net *nn = container_of(ref, struct nfsd_net, nfsd_net_ref);
224d3635ff0STrond Myklebust
225d3635ff0STrond Myklebust complete(&nn->nfsd_net_confirm_done);
226e333f3bbSTrond Myklebust }
227d3635ff0STrond Myklebust
nfsd_net_free(struct percpu_ref * ref)228d3635ff0STrond Myklebust static void nfsd_net_free(struct percpu_ref *ref)
229e333f3bbSTrond Myklebust {
2308daf220aSBenny Halevy struct nfsd_net *nn = container_of(ref, struct nfsd_net, nfsd_net_ref);
231928c6fb3SNeilBrown
232928c6fb3SNeilBrown complete(&nn->nfsd_net_free_done);
2338daf220aSBenny Halevy }
234e333f3bbSTrond Myklebust
2358daf220aSBenny Halevy /*
2368daf220aSBenny Halevy * Maximum number of nfsd processes
237e333f3bbSTrond Myklebust */
238e333f3bbSTrond Myklebust #define NFSD_MAXSERVS 8192
239e333f3bbSTrond Myklebust
nfsd_nrthreads(struct net * net)240e333f3bbSTrond Myklebust int nfsd_nrthreads(struct net *net)
241e333f3bbSTrond Myklebust {
2428daf220aSBenny Halevy int rv = 0;
2438daf220aSBenny Halevy struct nfsd_net *nn = net_generic(net, nfsd_net_id);
244e333f3bbSTrond Myklebust
245e333f3bbSTrond Myklebust mutex_lock(&nfsd_mutex);
246e333f3bbSTrond Myklebust if (nn->nfsd_serv)
247e333f3bbSTrond Myklebust rv = nn->nfsd_serv->sv_nrthreads;
248e333f3bbSTrond Myklebust mutex_unlock(&nfsd_mutex);
2498daf220aSBenny Halevy return rv;
2508daf220aSBenny Halevy }
251e333f3bbSTrond Myklebust
nfsd_init_socks(struct net * net,const struct cred * cred)252e333f3bbSTrond Myklebust static int nfsd_init_socks(struct net *net, const struct cred *cred)
253e333f3bbSTrond Myklebust {
2548daf220aSBenny Halevy int error;
255e333f3bbSTrond Myklebust struct nfsd_net *nn = net_generic(net, nfsd_net_id);
256e333f3bbSTrond Myklebust
2578daf220aSBenny Halevy if (!list_empty(&nn->nfsd_serv->sv_permsocks))
2588daf220aSBenny Halevy return 0;
2598daf220aSBenny Halevy
2608daf220aSBenny Halevy error = svc_xprt_create(nn->nfsd_serv, "udp", net, PF_INET, NFS_PORT,
2611da177e4SLinus Torvalds SVC_SOCK_DEFAULTS, cred);
2621da177e4SLinus Torvalds if (error < 0)
2631da177e4SLinus Torvalds return error;
2641da177e4SLinus Torvalds
2651da177e4SLinus Torvalds error = svc_xprt_create(nn->nfsd_serv, "tcp", net, PF_INET, NFS_PORT,
2669dd9845fSStanislav Kinsbursky SVC_SOCK_DEFAULTS, cred);
2671da177e4SLinus Torvalds if (error < 0)
268c7d106c9SNeil Brown return error;
2699dd9845fSStanislav Kinsbursky
2709dd9845fSStanislav Kinsbursky return 0;
271c7d106c9SNeil Brown }
2729dd9845fSStanislav Kinsbursky
2739dd9845fSStanislav Kinsbursky static int nfsd_users = 0;
274c7d106c9SNeil Brown
nfsd_startup_generic(void)275c7d106c9SNeil Brown static int nfsd_startup_generic(void)
2761da177e4SLinus Torvalds {
2771da177e4SLinus Torvalds int ret;
2784df493a2STrond Myklebust
27959db4a0cSJ. Bruce Fields if (nfsd_users++)
28059db4a0cSJ. Bruce Fields return 0;
2819dd9845fSStanislav Kinsbursky
2829dd9845fSStanislav Kinsbursky ret = nfsd_file_cache_init();
2839dd9845fSStanislav Kinsbursky if (ret)
28459db4a0cSJ. Bruce Fields goto dec_users;
28559db4a0cSJ. Bruce Fields
286352ad314SChuck Lever ret = nfs4_state_start();
2874df493a2STrond Myklebust if (ret)
28859db4a0cSJ. Bruce Fields goto out_file_cache;
28959db4a0cSJ. Bruce Fields return 0;
29059db4a0cSJ. Bruce Fields
291352ad314SChuck Lever out_file_cache:
2924df493a2STrond Myklebust nfsd_file_cache_shutdown();
29359db4a0cSJ. Bruce Fields dec_users:
29459db4a0cSJ. Bruce Fields nfsd_users--;
29559db4a0cSJ. Bruce Fields return ret;
29659db4a0cSJ. Bruce Fields }
29759db4a0cSJ. Bruce Fields
nfsd_shutdown_generic(void)29859db4a0cSJ. Bruce Fields static void nfsd_shutdown_generic(void)
2994539f149SStanislav Kinsbursky {
3004ad9a344SJeff Layton if (--nfsd_users)
30170c53075SVasily Averin return;
302bda9cac1SStanislav Kinsbursky
303bda9cac1SStanislav Kinsbursky nfs4_state_shutdown();
304bda9cac1SStanislav Kinsbursky nfsd_file_cache_shutdown();
3054539f149SStanislav Kinsbursky }
306bda9cac1SStanislav Kinsbursky
nfsd_needs_lockd(struct nfsd_net * nn)307bda9cac1SStanislav Kinsbursky static bool nfsd_needs_lockd(struct nfsd_net *nn)
30865294c1fSJeff Layton {
30965294c1fSJeff Layton return nfsd_vers(nn, 2, NFSD_TEST) || nfsd_vers(nn, 3, NFSD_TEST);
31065294c1fSJeff Layton }
311d9499a95SKinglong Mee
312bda9cac1SStanislav Kinsbursky /**
313bda9cac1SStanislav Kinsbursky * nfsd_copy_write_verifier - Atomically copy a write verifier
314501cb184SJeff Layton * @verf: buffer in which to receive the verifier cookie
315bda9cac1SStanislav Kinsbursky * @nn: NFS net namespace
316bda9cac1SStanislav Kinsbursky *
31765294c1fSJeff Layton * This function provides a wait-free mechanism for copying the
31865294c1fSJeff Layton * namespace's write verifier without tearing it.
319d9499a95SKinglong Mee */
nfsd_copy_write_verifier(__be32 verf[2],struct nfsd_net * nn)320d9499a95SKinglong Mee void nfsd_copy_write_verifier(__be32 verf[2], struct nfsd_net *nn)
321bda9cac1SStanislav Kinsbursky {
322bda9cac1SStanislav Kinsbursky unsigned int seq;
323bda9cac1SStanislav Kinsbursky
324bda9cac1SStanislav Kinsbursky do {
325bda9cac1SStanislav Kinsbursky seq = read_seqbegin(&nn->writeverf_lock);
3264539f149SStanislav Kinsbursky memcpy(verf, nn->writeverf, sizeof(nn->writeverf));
3274539f149SStanislav Kinsbursky } while (read_seqretry(&nn->writeverf_lock, seq));
3284539f149SStanislav Kinsbursky }
329bda9cac1SStanislav Kinsbursky
nfsd_reset_write_verifier_locked(struct nfsd_net * nn)33065294c1fSJeff Layton static void nfsd_reset_write_verifier_locked(struct nfsd_net *nn)
331bda9cac1SStanislav Kinsbursky {
332bda9cac1SStanislav Kinsbursky struct timespec64 now;
333e333f3bbSTrond Myklebust u64 verf;
3348ef66714SKinglong Mee
335e333f3bbSTrond Myklebust /*
3368ef66714SKinglong Mee * Because the time value is hashed, y2038 time_t overflow
3378ef66714SKinglong Mee * is irrelevant in this usage.
33891d2e9b5SChuck Lever */
3393988a578SChuck Lever ktime_get_raw_ts64(&now);
34091d2e9b5SChuck Lever verf = siphash_2u64(now.tv_sec, now.tv_nsec, &nn->siphash_key);
34191d2e9b5SChuck Lever memcpy(nn->writeverf, &verf, sizeof(nn->writeverf));
34291d2e9b5SChuck Lever }
34391d2e9b5SChuck Lever
3443988a578SChuck Lever /**
34591d2e9b5SChuck Lever * nfsd_reset_write_verifier - Generate a new write verifier
3463988a578SChuck Lever * @nn: NFS net namespace
34727c438f5STrond Myklebust *
348f3734cc4SOleg Nesterov * This function updates the ->writeverf field of @nn. This field
34927c438f5STrond Myklebust * contains an opaque cookie that, according to Section 18.32.3 of
35027c438f5STrond Myklebust * RFC 8881, "the client can use to determine whether a server has
351f3734cc4SOleg Nesterov * changed instance state (e.g., server restart) between a call to
35290d21755SChuck Lever * WRITE and a subsequent call to either WRITE or COMMIT. This
353f3734cc4SOleg Nesterov * cookie MUST be unchanged during a single instance of the NFSv4.1
35427c438f5STrond Myklebust * server and MUST be unique between instances of the NFSv4.1
35527c438f5STrond Myklebust * server."
3563988a578SChuck Lever */
nfsd_reset_write_verifier(struct nfsd_net * nn)35727c438f5STrond Myklebust void nfsd_reset_write_verifier(struct nfsd_net *nn)
35891d2e9b5SChuck Lever {
35991d2e9b5SChuck Lever write_seqlock(&nn->writeverf_lock);
36091d2e9b5SChuck Lever nfsd_reset_write_verifier_locked(nn);
36191d2e9b5SChuck Lever write_sequnlock(&nn->writeverf_lock);
36291d2e9b5SChuck Lever }
36391d2e9b5SChuck Lever
36491d2e9b5SChuck Lever /*
36591d2e9b5SChuck Lever * Crank up a set of per-namespace resources for a new NFSD instance,
36691d2e9b5SChuck Lever * including lockd, a duplicate reply cache, an open file cache
36791d2e9b5SChuck Lever * instance, and a cache of NFSv4 state objects.
36827c438f5STrond Myklebust */
nfsd_startup_net(struct net * net,const struct cred * cred)36927c438f5STrond Myklebust static int nfsd_startup_net(struct net *net, const struct cred *cred)
37091d2e9b5SChuck Lever {
3713988a578SChuck Lever struct nfsd_net *nn = net_generic(net, nfsd_net_id);
37291d2e9b5SChuck Lever int ret;
37391d2e9b5SChuck Lever
37491d2e9b5SChuck Lever if (nn->nfsd_net_up)
37591d2e9b5SChuck Lever return 0;
37691d2e9b5SChuck Lever
37791d2e9b5SChuck Lever ret = nfsd_startup_generic();
37891d2e9b5SChuck Lever if (ret)
37991d2e9b5SChuck Lever return ret;
38091d2e9b5SChuck Lever ret = nfsd_init_socks(net, cred);
38191d2e9b5SChuck Lever if (ret)
38291d2e9b5SChuck Lever goto out_socks;
3833988a578SChuck Lever
38427c438f5STrond Myklebust if (nfsd_needs_lockd(nn) && !nn->lockd_up) {
38591d2e9b5SChuck Lever ret = lockd_up(net, cred);
3863988a578SChuck Lever if (ret)
38791d2e9b5SChuck Lever goto out_socks;
38827c438f5STrond Myklebust nn->lockd_up = true;
38927c438f5STrond Myklebust }
3905e092be7SChuck Lever
3915e092be7SChuck Lever ret = nfsd_file_cache_start_net(net);
3925e092be7SChuck Lever if (ret)
3935e092be7SChuck Lever goto out_lockd;
3945e092be7SChuck Lever
39570c53075SVasily Averin ret = nfsd_reply_cache_init(nn);
3966ff50b3dSStanislav Kinsbursky if (ret)
3972c2fe290SStanislav Kinsbursky goto out_filecache;
3986ff50b3dSStanislav Kinsbursky
3996ff50b3dSStanislav Kinsbursky #ifdef CONFIG_NFSD_V4_2_INTER_SSC
4002c2fe290SStanislav Kinsbursky nfsd4_ssc_init_umount_work(nn);
4012c2fe290SStanislav Kinsbursky #endif
4022c2fe290SStanislav Kinsbursky ret = nfs4_state_start_net(net);
40370c53075SVasily Averin if (ret)
404903d9bf0SStanislav Kinsbursky goto out_reply_cache;
405903d9bf0SStanislav Kinsbursky
4064df493a2STrond Myklebust nn->nfsd_net_up = true;
4076ff50b3dSStanislav Kinsbursky return 0;
408903d9bf0SStanislav Kinsbursky
4098ef66714SKinglong Mee out_reply_cache:
410e333f3bbSTrond Myklebust nfsd_reply_cache_shutdown(nn);
41140373b12STrond Myklebust out_filecache:
4126ff50b3dSStanislav Kinsbursky nfsd_file_cache_shutdown_net(net);
413903d9bf0SStanislav Kinsbursky out_lockd:
414e44b4bf2Szhengbin if (nn->lockd_up) {
4158ef66714SKinglong Mee lockd_down(net);
4168ef66714SKinglong Mee nn->lockd_up = false;
4179542e6a6STrond Myklebust }
4186ff50b3dSStanislav Kinsbursky out_socks:
4196ff50b3dSStanislav Kinsbursky nfsd_shutdown_generic();
420f5f9d4a3SJeff Layton return ret;
421f5f9d4a3SJeff Layton }
4229542e6a6STrond Myklebust
nfsd_shutdown_net(struct net * net)4239542e6a6STrond Myklebust static void nfsd_shutdown_net(struct net *net)
4246ff50b3dSStanislav Kinsbursky {
425f5f9d4a3SJeff Layton struct nfsd_net *nn = net_generic(net, nfsd_net_id);
426f5f9d4a3SJeff Layton
427f5f9d4a3SJeff Layton if (!nn->nfsd_net_up)
428f5f9d4a3SJeff Layton return;
429f4e44b39SDai Ngo
430f4e44b39SDai Ngo percpu_ref_kill_and_confirm(&nn->nfsd_net_ref, nfsd_net_done);
431f4e44b39SDai Ngo wait_for_completion(&nn->nfsd_net_confirm_done);
4322c2fe290SStanislav Kinsbursky
4336ff50b3dSStanislav Kinsbursky nfsd_export_flush(net);
4346ff50b3dSStanislav Kinsbursky nfs4_state_shutdown_net(net);
435f5f9d4a3SJeff Layton nfsd_reply_cache_shutdown(nn);
436f5f9d4a3SJeff Layton nfsd_file_cache_shutdown_net(net);
4379542e6a6STrond Myklebust if (nn->lockd_up) {
4389542e6a6STrond Myklebust lockd_down(net);
4396ff50b3dSStanislav Kinsbursky nn->lockd_up = false;
4408ef66714SKinglong Mee }
4416ff50b3dSStanislav Kinsbursky
442e44b4bf2Szhengbin wait_for_completion(&nn->nfsd_net_free_done);
4438ef66714SKinglong Mee percpu_ref_exit(&nn->nfsd_net_ref);
444903d9bf0SStanislav Kinsbursky
445bda9cac1SStanislav Kinsbursky nn->nfsd_net_up = false;
4464ad9a344SJeff Layton nfsd_shutdown_generic();
4474ad9a344SJeff Layton }
4484ad9a344SJeff Layton
4496ff50b3dSStanislav Kinsbursky static DEFINE_SPINLOCK(nfsd_notifier_lock);
nfsd_inetaddr_event(struct notifier_block * this,unsigned long event,void * ptr)4506ff50b3dSStanislav Kinsbursky static int nfsd_inetaddr_event(struct notifier_block *this, unsigned long event,
4512c2fe290SStanislav Kinsbursky void *ptr)
4522c2fe290SStanislav Kinsbursky {
4536ff50b3dSStanislav Kinsbursky struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
454f5f9d4a3SJeff Layton struct net_device *dev = ifa->ifa_dev->dev;
455789e1e10SJeff Layton struct net *net = dev_net(dev);
4568ef66714SKinglong Mee struct nfsd_net *nn = net_generic(net, nfsd_net_id);
4576ff50b3dSStanislav Kinsbursky struct sockaddr_in sin;
458e44b4bf2Szhengbin
4598ef66714SKinglong Mee if (event != NETDEV_DOWN || !nn->nfsd_serv)
4602c2fe290SStanislav Kinsbursky goto out;
461903d9bf0SStanislav Kinsbursky
4626ff50b3dSStanislav Kinsbursky spin_lock(&nfsd_notifier_lock);
4636ff50b3dSStanislav Kinsbursky if (nn->nfsd_serv) {
464d057cfecSNeilBrown dprintk("nfsd_inetaddr_event: removed %pI4\n", &ifa->ifa_local);
46536684996SScott Mayhew sin.sin_family = AF_INET;
46636684996SScott Mayhew sin.sin_addr.s_addr = ifa->ifa_local;
46736684996SScott Mayhew svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin);
46836684996SScott Mayhew }
46936684996SScott Mayhew spin_unlock(&nfsd_notifier_lock);
47036684996SScott Mayhew
47136684996SScott Mayhew out:
47236684996SScott Mayhew return NOTIFY_DONE;
47336684996SScott Mayhew }
474d057cfecSNeilBrown
47536684996SScott Mayhew static struct notifier_block nfsd_inetaddr_notifier = {
47636684996SScott Mayhew .notifier_call = nfsd_inetaddr_event,
477d057cfecSNeilBrown };
47836684996SScott Mayhew
47936684996SScott Mayhew #if IS_ENABLED(CONFIG_IPV6)
nfsd_inet6addr_event(struct notifier_block * this,unsigned long event,void * ptr)48036684996SScott Mayhew static int nfsd_inet6addr_event(struct notifier_block *this,
48136684996SScott Mayhew unsigned long event, void *ptr)
48236684996SScott Mayhew {
48336684996SScott Mayhew struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
484d057cfecSNeilBrown struct net_device *dev = ifa->idev->dev;
48536684996SScott Mayhew struct net *net = dev_net(dev);
48636684996SScott Mayhew struct nfsd_net *nn = net_generic(net, nfsd_net_id);
48736684996SScott Mayhew struct sockaddr_in6 sin6;
48836684996SScott Mayhew
48936684996SScott Mayhew if (event != NETDEV_DOWN || !nn->nfsd_serv)
49036684996SScott Mayhew goto out;
49136684996SScott Mayhew
49236684996SScott Mayhew spin_lock(&nfsd_notifier_lock);
49336684996SScott Mayhew if (nn->nfsd_serv) {
49436684996SScott Mayhew dprintk("nfsd_inet6addr_event: removed %pI6\n", &ifa->addr);
49536684996SScott Mayhew sin6.sin6_family = AF_INET6;
49636684996SScott Mayhew sin6.sin6_addr = ifa->addr;
49736684996SScott Mayhew if (ipv6_addr_type(&sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
49836684996SScott Mayhew sin6.sin6_scope_id = ifa->idev->dev->ifindex;
49936684996SScott Mayhew svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin6);
50036684996SScott Mayhew }
50136684996SScott Mayhew spin_unlock(&nfsd_notifier_lock);
50236684996SScott Mayhew
50336684996SScott Mayhew out:
504d057cfecSNeilBrown return NOTIFY_DONE;
50536684996SScott Mayhew }
50636684996SScott Mayhew
507d057cfecSNeilBrown static struct notifier_block nfsd_inet6addr_notifier = {
50836684996SScott Mayhew .notifier_call = nfsd_inet6addr_event,
50936684996SScott Mayhew };
51036684996SScott Mayhew #endif
51136684996SScott Mayhew
5127b19824dSScott Mayhew /* Only used under nfsd_mutex, so this atomic may be overkill: */
5137b19824dSScott Mayhew static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
51436684996SScott Mayhew
51536684996SScott Mayhew /**
516d057cfecSNeilBrown * nfsd_destroy_serv - tear down NFSD's svc_serv for a namespace
517d057cfecSNeilBrown * @net: network namespace the NFS service is associated with
51836684996SScott Mayhew */
nfsd_destroy_serv(struct net * net)51936684996SScott Mayhew void nfsd_destroy_serv(struct net *net)
52036684996SScott Mayhew {
52136684996SScott Mayhew struct nfsd_net *nn = net_generic(net, nfsd_net_id);
52236684996SScott Mayhew struct svc_serv *serv = nn->nfsd_serv;
52336684996SScott Mayhew
52436684996SScott Mayhew lockdep_assert_held(&nfsd_mutex);
52536684996SScott Mayhew
52636684996SScott Mayhew spin_lock(&nfsd_notifier_lock);
5271eca45f8SVasily Averin nn->nfsd_serv = NULL;
5281eca45f8SVasily Averin spin_unlock(&nfsd_notifier_lock);
5291eca45f8SVasily Averin
53017419aefSNeilBrown /* check if the notifier still has clients */
53117419aefSNeilBrown if (atomic_dec_return(&nfsd_notifier_refcount) == 0) {
53217419aefSNeilBrown unregister_inetaddr_notifier(&nfsd_inetaddr_notifier);
53317419aefSNeilBrown #if IS_ENABLED(CONFIG_IPV6)
53417419aefSNeilBrown unregister_inet6addr_notifier(&nfsd_inet6addr_notifier);
5354ad9a344SJeff Layton #endif
536903d9bf0SStanislav Kinsbursky }
5379f28a971SNeilBrown
5389f28a971SNeilBrown /*
5399f28a971SNeilBrown * write_ports can create the server without actually starting
5409f28a971SNeilBrown * any threads. If we get shut down before any threads are
5419f28a971SNeilBrown * started, then nfsd_destroy_serv will be run before any of this
542903d9bf0SStanislav Kinsbursky * other initialization has been done except the rpcb information.
5431eca45f8SVasily Averin */
5441eca45f8SVasily Averin svc_xprt_destroy_all(serv, net, true);
54536684996SScott Mayhew nfsd_shutdown_net(net);
54636684996SScott Mayhew svc_destroy(&serv);
54736684996SScott Mayhew }
54836684996SScott Mayhew
nfsd_reset_versions(struct nfsd_net * nn)5491eca45f8SVasily Averin void nfsd_reset_versions(struct nfsd_net *nn)
5501eca45f8SVasily Averin {
5519f28a971SNeilBrown int i;
5529f28a971SNeilBrown
5534ad9a344SJeff Layton for (i = 0; i <= NFSD_MAXVERS; i++)
5544ad9a344SJeff Layton if (nfsd_vers(nn, i, NFSD_TEST))
5554ad9a344SJeff Layton return;
55617419aefSNeilBrown
557691412b4SKinglong Mee for (i = 0; i <= NFSD_MAXVERS; i++)
5584ad9a344SJeff Layton if (i != 4)
559691412b4SKinglong Mee nfsd_vers(nn, i, NFSD_SET);
560903d9bf0SStanislav Kinsbursky else {
5614ad9a344SJeff Layton int minor = 0;
562691412b4SKinglong Mee while (nfsd_minorversion(nn, minor, NFSD_SET) >= 0)
5636ff50b3dSStanislav Kinsbursky minor++;
564b3853e0eSStanislav Kinsbursky }
5651e3577a4SNeilBrown }
566bc591ccfSNeilBrown
nfsd_get_default_max_blksize(void)5676658d3a7SNeilBrown static int nfsd_get_default_max_blksize(void)
568e333f3bbSTrond Myklebust {
5696658d3a7SNeilBrown struct sysinfo i;
5706658d3a7SNeilBrown unsigned long long target;
5716658d3a7SNeilBrown unsigned long ret;
572800a938fSNeilBrown
573e333f3bbSTrond Myklebust si_meminfo(&i);
574800a938fSNeilBrown target = (i.totalram - i.totalhigh) << PAGE_SHIFT;
5756658d3a7SNeilBrown /*
576800a938fSNeilBrown * Aim for 1/4096 of memory per thread This gives 1MB on 4Gig
577800a938fSNeilBrown * machines, but only uses 32K on 128M machines. Bottom out at
578e333f3bbSTrond Myklebust * 8K on 32M and smaller. Of course, this is only a default.
579800a938fSNeilBrown */
580800a938fSNeilBrown target >>= 12;
581e333f3bbSTrond Myklebust
582800a938fSNeilBrown ret = NFSSVC_DEFBLKSIZE;
5836658d3a7SNeilBrown while (ret > target && ret >= 8*1024*2)
5846658d3a7SNeilBrown ret /= 2;
5856658d3a7SNeilBrown return ret;
586c3d06f9cSAndy Adamson }
587c3d06f9cSAndy Adamson
nfsd_shutdown_threads(struct net * net)588c3d06f9cSAndy Adamson void nfsd_shutdown_threads(struct net *net)
589c3d06f9cSAndy Adamson {
590c3d06f9cSAndy Adamson struct nfsd_net *nn = net_generic(net, nfsd_net_id);
591c3d06f9cSAndy Adamson struct svc_serv *serv;
592c3d06f9cSAndy Adamson
593c3d06f9cSAndy Adamson mutex_lock(&nfsd_mutex);
594c3d06f9cSAndy Adamson serv = nn->nfsd_serv;
595c3d06f9cSAndy Adamson if (serv == NULL) {
596c3d06f9cSAndy Adamson mutex_unlock(&nfsd_mutex);
597c3d06f9cSAndy Adamson return;
598c3d06f9cSAndy Adamson }
599c3d06f9cSAndy Adamson
60044d8660dSJ. Bruce Fields /* Kill outstanding nfsd threads */
6010c193054SAndy Adamson svc_set_num_threads(serv, NULL, 0);
6020c193054SAndy Adamson nfsd_destroy_serv(net);
6030c193054SAndy Adamson mutex_unlock(&nfsd_mutex);
604697ce9beSZhang Yanfei }
605c3d06f9cSAndy Adamson
nfsd_current_rqst(void)606bedbdd8bSNeil Brown struct svc_rqst *nfsd_current_rqst(void)
60787b0fc7dSJ. Bruce Fields {
60887b0fc7dSJ. Bruce Fields if (kthread_func(current) == nfsd)
60987b0fc7dSJ. Bruce Fields return kthread_data(current);
61087b0fc7dSJ. Bruce Fields return NULL;
61187b0fc7dSJ. Bruce Fields }
61287b0fc7dSJ. Bruce Fields
nfsd_create_serv(struct net * net)61387b0fc7dSJ. Bruce Fields int nfsd_create_serv(struct net *net)
614508f9227SJ. Bruce Fields {
61587b0fc7dSJ. Bruce Fields int error;
61687b0fc7dSJ. Bruce Fields struct nfsd_net *nn = net_generic(net, nfsd_net_id);
61787b0fc7dSJ. Bruce Fields struct svc_serv *serv;
61887b0fc7dSJ. Bruce Fields
61987b0fc7dSJ. Bruce Fields WARN_ON(!mutex_is_locked(&nfsd_mutex));
62087b0fc7dSJ. Bruce Fields if (nn->nfsd_serv)
62187b0fc7dSJ. Bruce Fields return 0;
62287b0fc7dSJ. Bruce Fields
62387b0fc7dSJ. Bruce Fields error = percpu_ref_init(&nn->nfsd_net_ref, nfsd_net_free,
62487b0fc7dSJ. Bruce Fields 0, GFP_KERNEL);
62587b0fc7dSJ. Bruce Fields if (error)
62687b0fc7dSJ. Bruce Fields return error;
62787b0fc7dSJ. Bruce Fields init_completion(&nn->nfsd_net_free_done);
628c6c7f2a8STrond Myklebust init_completion(&nn->nfsd_net_confirm_done);
629c6c7f2a8STrond Myklebust
630c6c7f2a8STrond Myklebust if (nfsd_max_blksize == 0)
631c6c7f2a8STrond Myklebust nfsd_max_blksize = nfsd_get_default_max_blksize();
632c6c7f2a8STrond Myklebust nfsd_reset_versions(nn);
633c6c7f2a8STrond Myklebust serv = svc_create_pooled(nfsd_programs, ARRAY_SIZE(nfsd_programs),
634c6c7f2a8STrond Myklebust &nn->nfsd_svcstats,
635c6c7f2a8STrond Myklebust nfsd_max_blksize, nfsd);
636c6c7f2a8STrond Myklebust if (serv == NULL)
637c6c7f2a8STrond Myklebust return -ENOMEM;
638c6c7f2a8STrond Myklebust
639c6c7f2a8STrond Myklebust error = svc_bind(serv, net);
640c6c7f2a8STrond Myklebust if (error < 0) {
6413ebdbe52SNeilBrown svc_destroy(&serv);
64217419aefSNeilBrown return error;
643c6c7f2a8STrond Myklebust }
644c6c7f2a8STrond Myklebust spin_lock(&nfsd_notifier_lock);
645c6c7f2a8STrond Myklebust nn->nfsd_serv = serv;
64644fb26c6SMa Feng spin_unlock(&nfsd_notifier_lock);
64728df3d15SJ. Bruce Fields
64828df3d15SJ. Bruce Fields /* check if the notifier is already set */
64928df3d15SJ. Bruce Fields if (atomic_inc_return(&nfsd_notifier_refcount) == 1) {
65028df3d15SJ. Bruce Fields register_inetaddr_notifier(&nfsd_inetaddr_notifier);
6516777436bSStanislav Kinsbursky #if IS_ENABLED(CONFIG_IPV6)
65202a375f0SNeilBrown register_inet6addr_notifier(&nfsd_inet6addr_notifier);
6539793f7c8SStanislav Kinsbursky #endif
654b9c0ef85SStanislav Kinsbursky }
655d057cfecSNeilBrown nfsd_reset_write_verifier(nn);
6569793f7c8SStanislav Kinsbursky return 0;
657bedbdd8bSNeil Brown }
6581e3577a4SNeilBrown
nfsd_nrpools(struct net * net)65902a375f0SNeilBrown int nfsd_nrpools(struct net *net)
6601e3577a4SNeilBrown {
66187b0fc7dSJ. Bruce Fields struct nfsd_net *nn = net_generic(net, nfsd_net_id);
66287b0fc7dSJ. Bruce Fields
663e333f3bbSTrond Myklebust if (nn->nfsd_serv == NULL)
664*16fb9808SJosef Bacik return 0;
665f0943238SJosef Bacik else
666d057cfecSNeilBrown return nn->nfsd_serv->sv_nrpools;
667628b3687SJeff Layton }
668bedbdd8bSNeil Brown
nfsd_get_nrthreads(int n,int * nthreads,struct net * net)669d057cfecSNeilBrown int nfsd_get_nrthreads(int n, int *nthreads, struct net *net)
670d057cfecSNeilBrown {
6719793f7c8SStanislav Kinsbursky struct nfsd_net *nn = net_generic(net, nfsd_net_id);
6721e3577a4SNeilBrown struct svc_serv *serv = nn->nfsd_serv;
6739793f7c8SStanislav Kinsbursky int i;
6749793f7c8SStanislav Kinsbursky
675d057cfecSNeilBrown if (serv)
6767b207ccdSNeilBrown for (i = 0; i < serv->sv_nrpools && i < n; i++)
677d057cfecSNeilBrown nthreads[i] = serv->sv_pools[i].sp_nrthreads;
678d057cfecSNeilBrown return 0;
6799793f7c8SStanislav Kinsbursky }
680628b3687SJeff Layton
6811eca45f8SVasily Averin /**
6821eca45f8SVasily Averin * nfsd_set_nrthreads - set the number of running threads in the net's service
68336684996SScott Mayhew * @n: number of array members in @nthreads
68436684996SScott Mayhew * @nthreads: array of thread counts for each pool
68536684996SScott Mayhew * @net: network namespace to operate within
68636684996SScott Mayhew *
6871eca45f8SVasily Averin * This function alters the number of running threads for the given network
6883988a578SChuck Lever * namespace in each pool. If passed an array longer then the number of pools
68987b0fc7dSJ. Bruce Fields * the extra pool settings are ignored. If passed an array shorter than the
69002a375f0SNeilBrown * number of pools, the missing values are interpreted as 0's.
69102a375f0SNeilBrown *
6929dd9845fSStanislav Kinsbursky * Returns 0 on success or a negative errno on error.
693eed2965aSGreg Banks */
nfsd_set_nrthreads(int n,int * nthreads,struct net * net)6949dd9845fSStanislav Kinsbursky int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
6959dd9845fSStanislav Kinsbursky {
6969dd9845fSStanislav Kinsbursky int i = 0;
697eed2965aSGreg Banks int tot = 0;
698eed2965aSGreg Banks int err = 0;
6999dd9845fSStanislav Kinsbursky struct nfsd_net *nn = net_generic(net, nfsd_net_id);
700eed2965aSGreg Banks
701eed2965aSGreg Banks lockdep_assert_held(&nfsd_mutex);
7029dd9845fSStanislav Kinsbursky
703eed2965aSGreg Banks if (nn->nfsd_serv == NULL || n <= 0)
7049dd9845fSStanislav Kinsbursky return 0;
7052e8fc923SNeilBrown
7062e8fc923SNeilBrown /*
707eed2965aSGreg Banks * Special case: When n == 1, pass in NULL for the pool, so that the
7082e8fc923SNeilBrown * change is distributed equally among them.
7092e8fc923SNeilBrown */
7102e8fc923SNeilBrown if (n == 1)
711eed2965aSGreg Banks return svc_set_num_threads(nn->nfsd_serv, NULL, nthreads[0]);
712eed2965aSGreg Banks
713eed2965aSGreg Banks if (n > nn->nfsd_serv->sv_nrpools)
7143938a0d5SStanislav Kinsbursky n = nn->nfsd_serv->sv_nrpools;
715eed2965aSGreg Banks
716eed2965aSGreg Banks /* enforce a global maximum number of threads */
717eed2965aSGreg Banks tot = 0;
718eed2965aSGreg Banks for (i = 0; i < n; i++) {
7199dd9845fSStanislav Kinsbursky nthreads[i] = min(nthreads[i], NFSD_MAXSERVS);
720eed2965aSGreg Banks tot += nthreads[i];
721bedbdd8bSNeil Brown }
722bedbdd8bSNeil Brown if (tot > NFSD_MAXSERVS) {
7239dd9845fSStanislav Kinsbursky /* total too large: scale down requested numbers */
724eed2965aSGreg Banks for (i = 0; i < n && tot > 0; i++) {
725eed2965aSGreg Banks int new = nthreads[i] * NFSD_MAXSERVS / tot;
7269dd9845fSStanislav Kinsbursky tot -= (nthreads[i] - new);
7279dd9845fSStanislav Kinsbursky nthreads[i] = new;
728eed2965aSGreg Banks }
729eed2965aSGreg Banks for (i = 0; i < n && tot > 0; i++) {
730eed2965aSGreg Banks nthreads[i]--;
731eed2965aSGreg Banks tot--;
7323c7aa15dSKinglong Mee }
733eed2965aSGreg Banks }
734eed2965aSGreg Banks
735eed2965aSGreg Banks /* apply the new numbers */
736eed2965aSGreg Banks for (i = 0; i < n; i++) {
737eed2965aSGreg Banks err = svc_set_num_threads(nn->nfsd_serv,
738eed2965aSGreg Banks &nn->nfsd_serv->sv_pools[i],
739eed2965aSGreg Banks nthreads[i]);
740eed2965aSGreg Banks if (err)
741eed2965aSGreg Banks goto out;
742eed2965aSGreg Banks }
743eed2965aSGreg Banks
744eed2965aSGreg Banks /* Anything undefined in array is considered to be 0 */
745eed2965aSGreg Banks for (i = n; i < nn->nfsd_serv->sv_nrpools; ++i) {
746eed2965aSGreg Banks err = svc_set_num_threads(nn->nfsd_serv,
747eed2965aSGreg Banks &nn->nfsd_serv->sv_pools[i],
748eed2965aSGreg Banks 0);
749eed2965aSGreg Banks if (err)
750eed2965aSGreg Banks goto out;
751eed2965aSGreg Banks }
752eed2965aSGreg Banks out:
753eed2965aSGreg Banks return err;
754eed2965aSGreg Banks }
755eed2965aSGreg Banks
756eed2965aSGreg Banks /**
7573ebdbe52SNeilBrown * nfsd_svc: start up or shut down the nfsd server
7583ebdbe52SNeilBrown * @n: number of array members in @nthreads
7593ebdbe52SNeilBrown * @nthreads: array of thread counts for each pool
760eed2965aSGreg Banks * @net: network namespace to operate within
761eed2965aSGreg Banks * @cred: credentials to use for xprt creation
762eed2965aSGreg Banks * @scope: server scope value (defaults to nodename)
763eed2965aSGreg Banks *
764eed2965aSGreg Banks * Adjust the number of threads in each pool and return the new
765eed2965aSGreg Banks * total number of threads in the service.
766ac77efbeSJeff Layton */
767ac77efbeSJeff Layton int
nfsd_svc(int n,int * nthreads,struct net * net,const struct cred * cred,const char * scope)768ac77efbeSJeff Layton nfsd_svc(int n, int *nthreads, struct net *net, const struct cred *cred, const char *scope)
769ac77efbeSJeff Layton {
770ac77efbeSJeff Layton int error;
7711da177e4SLinus Torvalds struct nfsd_net *nn = net_generic(net, nfsd_net_id);
7724df493a2STrond Myklebust struct svc_serv *serv;
7731da177e4SLinus Torvalds
7741da177e4SLinus Torvalds lockdep_assert_held(&nfsd_mutex);
7759dd9845fSStanislav Kinsbursky
7769f28a971SNeilBrown dprintk("nfsd: creating service\n");
7771da177e4SLinus Torvalds
778bedbdd8bSNeil Brown strscpy(nn->nfsd_name, scope ? scope : utsname()->nodename,
7796658d3a7SNeilBrown sizeof(nn->nfsd_name));
7803c7aa15dSKinglong Mee
7813c7aa15dSKinglong Mee error = nfsd_create_serv(net);
7823c7aa15dSKinglong Mee if (error)
783671e1fcfSNeilBrown goto out;
7843c7aa15dSKinglong Mee serv = nn->nfsd_serv;
7859dd9845fSStanislav Kinsbursky
786671e1fcfSNeilBrown error = nfsd_startup_net(net, cred);
7871da177e4SLinus Torvalds if (error)
78872f78ae0SWolfram Sang goto out_put;
7897627d7dcSScott Mayhew error = nfsd_set_nrthreads(n, nthreads, net);
7907627d7dcSScott Mayhew if (error)
7916777436bSStanislav Kinsbursky goto out_put;
79202a375f0SNeilBrown error = serv->sv_nrthreads;
793774f8bbdSJ. Bruce Fields out_put:
7949f28a971SNeilBrown if (serv->sv_nrthreads == 0)
795774f8bbdSJ. Bruce Fields nfsd_destroy_serv(net);
79670c53075SVasily Averin out:
797af4718f3SJ. Bruce Fields return error;
7988c62d127SNeilBrown }
7999f28a971SNeilBrown
800774f8bbdSJ. Bruce Fields #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
801bf320752SNeilBrown static bool
nfsd_support_acl_version(int vers)8029f28a971SNeilBrown nfsd_support_acl_version(int vers)
8038c62d127SNeilBrown {
804bf320752SNeilBrown if (vers >= NFSD_ACL_MINVERS && vers < NFSD_ACL_NRVERS)
80517419aefSNeilBrown return nfsd_acl_version[vers] != NULL;
8061da177e4SLinus Torvalds return false;
807bedbdd8bSNeil Brown }
8081da177e4SLinus Torvalds
8091da177e4SLinus Torvalds static int
nfsd_acl_rpcbind_set(struct net * net,const struct svc_program * progp,u32 version,int family,unsigned short proto,unsigned short port)8101da177e4SLinus Torvalds nfsd_acl_rpcbind_set(struct net *net, const struct svc_program *progp,
811029be5d0STrond Myklebust u32 version, int family, unsigned short proto,
812029be5d0STrond Myklebust unsigned short port)
813029be5d0STrond Myklebust {
814029be5d0STrond Myklebust if (!nfsd_support_acl_version(version) ||
815029be5d0STrond Myklebust !nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST))
816029be5d0STrond Myklebust return 0;
817029be5d0STrond Myklebust return svc_generic_rpcbind_set(net, progp, version, family,
818029be5d0STrond Myklebust proto, port);
819029be5d0STrond Myklebust }
820029be5d0STrond Myklebust
821029be5d0STrond Myklebust static __be32
nfsd_acl_init_request(struct svc_rqst * rqstp,const struct svc_program * progp,struct svc_process_info * ret)822029be5d0STrond Myklebust nfsd_acl_init_request(struct svc_rqst *rqstp,
823029be5d0STrond Myklebust const struct svc_program *progp,
824029be5d0STrond Myklebust struct svc_process_info *ret)
825029be5d0STrond Myklebust {
826e333f3bbSTrond Myklebust struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
827029be5d0STrond Myklebust int i;
828029be5d0STrond Myklebust
829029be5d0STrond Myklebust if (likely(nfsd_support_acl_version(rqstp->rq_vers) &&
830029be5d0STrond Myklebust nfsd_vers(nn, rqstp->rq_vers, NFSD_TEST)))
831e333f3bbSTrond Myklebust return svc_generic_init_request(rqstp, progp, ret);
832e333f3bbSTrond Myklebust
833e333f3bbSTrond Myklebust ret->mismatch.lovers = NFSD_ACL_NRVERS;
834e333f3bbSTrond Myklebust for (i = NFSD_ACL_MINVERS; i < NFSD_ACL_NRVERS; i++) {
835e333f3bbSTrond Myklebust if (nfsd_support_acl_version(rqstp->rq_vers) &&
836e333f3bbSTrond Myklebust nfsd_vers(nn, i, NFSD_TEST)) {
837e333f3bbSTrond Myklebust ret->mismatch.lovers = i;
838e333f3bbSTrond Myklebust break;
839e333f3bbSTrond Myklebust }
840e333f3bbSTrond Myklebust }
841e333f3bbSTrond Myklebust if (ret->mismatch.lovers == NFSD_ACL_NRVERS)
842e333f3bbSTrond Myklebust return rpc_prog_unavail;
843e333f3bbSTrond Myklebust ret->mismatch.hivers = NFSD_ACL_MINVERS;
844e333f3bbSTrond Myklebust for (i = NFSD_ACL_NRVERS - 1; i >= NFSD_ACL_MINVERS; i--) {
845e333f3bbSTrond Myklebust if (nfsd_support_acl_version(rqstp->rq_vers) &&
846e333f3bbSTrond Myklebust nfsd_vers(nn, i, NFSD_TEST)) {
847e333f3bbSTrond Myklebust ret->mismatch.hivers = i;
848e333f3bbSTrond Myklebust break;
849e333f3bbSTrond Myklebust }
850e333f3bbSTrond Myklebust }
851e333f3bbSTrond Myklebust return rpc_prog_mismatch;
852e333f3bbSTrond Myklebust }
853e333f3bbSTrond Myklebust #endif
854e333f3bbSTrond Myklebust
855e333f3bbSTrond Myklebust static int
nfsd_rpcbind_set(struct net * net,const struct svc_program * progp,u32 version,int family,unsigned short proto,unsigned short port)856e333f3bbSTrond Myklebust nfsd_rpcbind_set(struct net *net, const struct svc_program *progp,
857e333f3bbSTrond Myklebust u32 version, int family, unsigned short proto,
858e333f3bbSTrond Myklebust unsigned short port)
859e333f3bbSTrond Myklebust {
860e333f3bbSTrond Myklebust if (!nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST))
861e333f3bbSTrond Myklebust return 0;
862e333f3bbSTrond Myklebust return svc_generic_rpcbind_set(net, progp, version, family,
863e333f3bbSTrond Myklebust proto, port);
864029be5d0STrond Myklebust }
865029be5d0STrond Myklebust
866029be5d0STrond Myklebust static __be32
nfsd_init_request(struct svc_rqst * rqstp,const struct svc_program * progp,struct svc_process_info * ret)867029be5d0STrond Myklebust nfsd_init_request(struct svc_rqst *rqstp,
868029be5d0STrond Myklebust const struct svc_program *progp,
869029be5d0STrond Myklebust struct svc_process_info *ret)
870029be5d0STrond Myklebust {
871e333f3bbSTrond Myklebust struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
872029be5d0STrond Myklebust int i;
873029be5d0STrond Myklebust
874029be5d0STrond Myklebust if (likely(nfsd_vers(nn, rqstp->rq_vers, NFSD_TEST)))
875029be5d0STrond Myklebust return svc_generic_init_request(rqstp, progp, ret);
8761da177e4SLinus Torvalds
877e333f3bbSTrond Myklebust ret->mismatch.lovers = NFSD_MAXVERS + 1;
878e333f3bbSTrond Myklebust for (i = NFSD_MINVERS; i <= NFSD_MAXVERS; i++) {
879e333f3bbSTrond Myklebust if (nfsd_vers(nn, i, NFSD_TEST)) {
880e333f3bbSTrond Myklebust ret->mismatch.lovers = i;
881e333f3bbSTrond Myklebust break;
882e333f3bbSTrond Myklebust }
883e333f3bbSTrond Myklebust }
884e333f3bbSTrond Myklebust if (ret->mismatch.lovers > NFSD_MAXVERS)
885e333f3bbSTrond Myklebust return rpc_prog_unavail;
886e333f3bbSTrond Myklebust ret->mismatch.hivers = NFSD_MINVERS;
887e333f3bbSTrond Myklebust for (i = NFSD_MAXVERS; i >= NFSD_MINVERS; i--) {
888e333f3bbSTrond Myklebust if (nfsd_vers(nn, i, NFSD_TEST)) {
889e333f3bbSTrond Myklebust ret->mismatch.hivers = i;
890e333f3bbSTrond Myklebust break;
891e333f3bbSTrond Myklebust }
892e333f3bbSTrond Myklebust }
893e333f3bbSTrond Myklebust return rpc_prog_mismatch;
894e333f3bbSTrond Myklebust }
895e333f3bbSTrond Myklebust
896e333f3bbSTrond Myklebust /*
897e333f3bbSTrond Myklebust * This is the NFS server kernel thread
898e333f3bbSTrond Myklebust */
899e333f3bbSTrond Myklebust static int
nfsd(void * vrqstp)900e333f3bbSTrond Myklebust nfsd(void *vrqstp)
901e333f3bbSTrond Myklebust {
902e333f3bbSTrond Myklebust struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp;
903e333f3bbSTrond Myklebust struct svc_xprt *perm_sock = list_entry(rqstp->rq_server->sv_permsocks.next, typeof(struct svc_xprt), xpt_list);
904e333f3bbSTrond Myklebust struct net *net = perm_sock->xpt_net;
905e333f3bbSTrond Myklebust struct nfsd_net *nn = net_generic(net, nfsd_net_id);
906e333f3bbSTrond Myklebust
9071da177e4SLinus Torvalds /* At this point, the thread shares current->fs
9081da177e4SLinus Torvalds * with the init process. We need to create files with the
9091da177e4SLinus Torvalds * umask as defined by the client instead of init's umask.
9109867d76cSJeff Layton */
9119867d76cSJeff Layton svc_thread_init_status(rqstp, unshare_fs_struct());
9121da177e4SLinus Torvalds
9139867d76cSJeff Layton current->fs->umask = 0;
91488c47666SStanislav Kinsbursky
91588c47666SStanislav Kinsbursky atomic_inc(&nfsd_th_cnt);
9165b8db00bSJeff Layton
9171da177e4SLinus Torvalds set_freezable();
9189867d76cSJeff Layton
91947057abdSAndreas Gruenbacher /*
92047057abdSAndreas Gruenbacher * The main request loop
9213e93cd67SAl Viro */
9221da177e4SLinus Torvalds while (!svc_thread_should_stop(rqstp)) {
9231da177e4SLinus Torvalds svc_recv(rqstp);
9241da177e4SLinus Torvalds nfsd_file_net_dispose(nn);
9253e93cd67SAl Viro }
9261da177e4SLinus Torvalds
9271da177e4SLinus Torvalds atomic_dec(&nfsd_th_cnt);
928e41ee44cSJosef Bacik
929bedbdd8bSNeil Brown /* Release the thread */
93083144186SRafael J. Wysocki svc_exit_thread(rqstp);
9311da177e4SLinus Torvalds return 0;
9321da177e4SLinus Torvalds }
9331da177e4SLinus Torvalds
9341da177e4SLinus Torvalds /**
935fa341560SNeilBrown * nfsd_dispatch - Process an NFS or NFSACL or LOCALIO Request
9365b8db00bSJeff Layton * @rqstp: incoming request
9375b8db00bSJeff Layton *
9385b8db00bSJeff Layton * This RPC dispatcher integrates the NFS server's duplicate reply cache.
939c743b425SNeilBrown *
940ffb40259SNeilBrown * Return values:
941ffb40259SNeilBrown * %0: Processing complete; do not send a Reply
9421da177e4SLinus Torvalds * %1: Processing complete; send Reply in rqstp->rq_res
9431da177e4SLinus Torvalds */
nfsd_dispatch(struct svc_rqst * rqstp)944e41ee44cSJosef Bacik int nfsd_dispatch(struct svc_rqst *rqstp)
9451da177e4SLinus Torvalds {
9461da177e4SLinus Torvalds const struct svc_procedure *proc = rqstp->rq_procinfo;
9471da177e4SLinus Torvalds __be32 *statp = rqstp->rq_accept_statp;
9481da177e4SLinus Torvalds struct nfsd_cacherep *rp;
9499867d76cSJeff Layton unsigned int start, len;
9501da177e4SLinus Torvalds __be32 *nfs_reply;
9511da177e4SLinus Torvalds
952383c440dSChuck Lever /*
953383c440dSChuck Lever * Give the xdr decoder a chance to change this if it wants
954383c440dSChuck Lever * (necessary in the NFSv4.0 compound case)
955383c440dSChuck Lever */
956383c440dSChuck Lever rqstp->rq_cachetype = proc->pc_cachetype;
957383c440dSChuck Lever
958383c440dSChuck Lever /*
959383c440dSChuck Lever * ->pc_decode advances the argument stream past the NFS
960383c440dSChuck Lever * Call header, so grab the header's starting location and
961383c440dSChuck Lever * size now for the call to nfsd_cache_lookup().
962cee4db19SChuck Lever */
9631da177e4SLinus Torvalds start = xdr_stream_pos(&rqstp->rq_arg_stream);
9644c96cb56SChuck Lever len = xdr_stream_remaining(&rqstp->rq_arg_stream);
965cee4db19SChuck Lever if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream))
966e7421ce7SChuck Lever goto out_decode_err;
967bf51c52aSChuck Lever
9681caf5f61SChuck Lever /*
9691da177e4SLinus Torvalds * Release rq_status_counter setting it to an odd value after the rpc
9701091006cSJ. Bruce Fields * request has been properly parsed. rq_status_counter is used to
9711091006cSJ. Bruce Fields * notify the consumers if the rqstp fields are stable
9721091006cSJ. Bruce Fields * (rq_status_counter is odd) or not meaningful (rq_status_counter
9731091006cSJ. Bruce Fields * is even).
9741091006cSJ. Bruce Fields */
9755191955dSChuck Lever smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter | 1);
976bf51c52aSChuck Lever
977bf51c52aSChuck Lever rp = NULL;
978bf51c52aSChuck Lever switch (nfsd_cache_lookup(rqstp, start, len, &rp)) {
979bf51c52aSChuck Lever case RC_DOIT:
980bf51c52aSChuck Lever break;
981bf51c52aSChuck Lever case RC_REPLY:
982bf51c52aSChuck Lever goto out_cached_reply;
98316c66364SChuck Lever case RC_DROPIT:
98485085aacSChuck Lever goto out_dropit;
9851091006cSJ. Bruce Fields }
986bd9d6a3eSLorenzo Bianconi
987bd9d6a3eSLorenzo Bianconi nfs_reply = xdr_inline_decode(&rqstp->rq_res_stream, 0);
988bd9d6a3eSLorenzo Bianconi *statp = proc->pc_func(rqstp);
989bd9d6a3eSLorenzo Bianconi if (test_bit(RQ_DROPME, &rqstp->rq_flags))
990bd9d6a3eSLorenzo Bianconi goto out_update_drop;
991bd9d6a3eSLorenzo Bianconi
992bd9d6a3eSLorenzo Bianconi if (!proc->pc_encode(rqstp, &rqstp->rq_res_stream))
993bd9d6a3eSLorenzo Bianconi goto out_encode_err;
994bd9d6a3eSLorenzo Bianconi
995cb18eca4SChuck Lever /*
996bf51c52aSChuck Lever * Release rq_status_counter setting it to an even value after the rpc
99784c138e7SChuck Lever * request has been properly processed.
99884c138e7SChuck Lever */
9991da177e4SLinus Torvalds smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter + 1);
100085085aacSChuck Lever
100184c138e7SChuck Lever nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, nfs_reply);
100285085aacSChuck Lever out_cached_reply:
10031da177e4SLinus Torvalds return 1;
10041da177e4SLinus Torvalds
10051caf5f61SChuck Lever out_decode_err:
1006cc028a10SChuck Lever trace_nfsd_garbage_args_err(rqstp);
100793155647SChuck Lever *statp = rpc_garbage_args;
100885085aacSChuck Lever return 1;
10091da177e4SLinus Torvalds
1010fda49441SChuck Lever out_update_drop:
101185085aacSChuck Lever nfsd_cache_update(rqstp, rp, RC_NOCACHE, NULL);
101285085aacSChuck Lever out_dropit:
1013bd9d6a3eSLorenzo Bianconi return 0;
1014bd9d6a3eSLorenzo Bianconi
1015bd9d6a3eSLorenzo Bianconi out_encode_err:
1016bd9d6a3eSLorenzo Bianconi trace_nfsd_cant_encode_err(rqstp);
1017bd9d6a3eSLorenzo Bianconi nfsd_cache_update(rqstp, rp, RC_NOCACHE, NULL);
1018bd9d6a3eSLorenzo Bianconi *statp = rpc_system_err;
10191caf5f61SChuck Lever return 1;
102085085aacSChuck Lever }
102185085aacSChuck Lever
102285085aacSChuck Lever /**
102385085aacSChuck Lever * nfssvc_decode_voidarg - Decode void arguments
10240dfdad1cSChuck Lever * @rqstp: Server RPC transaction context
102585085aacSChuck Lever * @xdr: XDR stream positioned at arguments to decode
102685085aacSChuck Lever *
102785085aacSChuck Lever * Return values:
102885085aacSChuck Lever * %false: Arguments were not valid
1029cb18eca4SChuck Lever * %true: Decoding was successful
103085085aacSChuck Lever */
nfssvc_decode_voidarg(struct svc_rqst * rqstp,struct xdr_stream * xdr)103185085aacSChuck Lever bool nfssvc_decode_voidarg(struct svc_rqst *rqstp, struct xdr_stream *xdr)
103285085aacSChuck Lever {
103385085aacSChuck Lever return true;
10340dfdad1cSChuck Lever }
1035cb18eca4SChuck Lever
10361da177e4SLinus Torvalds /**
10371da177e4SLinus Torvalds * nfssvc_encode_voidres - Encode void results
10381da177e4SLinus Torvalds * @rqstp: Server RPC transaction context
103903cf6c9fSGreg Banks * @xdr: XDR stream into which to encode results
1040788f7183SChuck Lever *
1041788f7183SChuck Lever * Return values:
1042788f7183SChuck Lever * %false: Local error while encoding
104316c66364SChuck Lever * %true: Encoding was successful
1044788f7183SChuck Lever */
nfssvc_encode_voidres(struct svc_rqst * rqstp,struct xdr_stream * xdr)1045788f7183SChuck Lever bool nfssvc_encode_voidres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
1046c44b31c2SChuck Lever {
1047c44b31c2SChuck Lever return true;
1048788f7183SChuck Lever }
1049c44b31c2SChuck Lever