Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 | 2 | /* |
1da177e4 LT |
3 | * Central processing for nfsd. |
4 | * | |
5 | * Authors: Olaf Kirch (okir@monad.swb.de) | |
6 | * | |
7 | * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de> | |
8 | */ | |
9 | ||
3f07c014 | 10 | #include <linux/sched/signal.h> |
83144186 | 11 | #include <linux/freezer.h> |
143cb494 | 12 | #include <linux/module.h> |
1da177e4 | 13 | #include <linux/fs_struct.h> |
c3d06f9c | 14 | #include <linux/swap.h> |
91d2e9b5 | 15 | #include <linux/siphash.h> |
1da177e4 | 16 | |
1da177e4 | 17 | #include <linux/sunrpc/stats.h> |
1da177e4 | 18 | #include <linux/sunrpc/svcsock.h> |
36684996 | 19 | #include <linux/sunrpc/svc_xprt.h> |
1da177e4 | 20 | #include <linux/lockd/bind.h> |
a257cdd0 | 21 | #include <linux/nfsacl.h> |
a61e147e | 22 | #include <linux/nfslocalio.h> |
ed2d8aed | 23 | #include <linux/seq_file.h> |
36684996 SM |
24 | #include <linux/inetdevice.h> |
25 | #include <net/addrconf.h> | |
26 | #include <net/ipv6.h> | |
fc5d00b0 | 27 | #include <net/net_namespace.h> |
9a74af21 BH |
28 | #include "nfsd.h" |
29 | #include "cache.h" | |
0a3adade | 30 | #include "vfs.h" |
2c2fe290 | 31 | #include "netns.h" |
65294c1f | 32 | #include "filecache.h" |
1da177e4 | 33 | |
0dfdad1c CL |
34 | #include "trace.h" |
35 | ||
1da177e4 LT |
36 | #define NFSDDBG_FACILITY NFSDDBG_SVC |
37 | ||
e41ee44c | 38 | atomic_t nfsd_th_cnt = ATOMIC_INIT(0); |
9867d76c | 39 | static int nfsd(void *vrqstp); |
029be5d0 TM |
40 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) |
41 | static int nfsd_acl_rpcbind_set(struct net *, | |
42 | const struct svc_program *, | |
43 | u32, int, | |
44 | unsigned short, | |
45 | unsigned short); | |
e333f3bb TM |
46 | static __be32 nfsd_acl_init_request(struct svc_rqst *, |
47 | const struct svc_program *, | |
48 | struct svc_process_info *); | |
029be5d0 TM |
49 | #endif |
50 | static int nfsd_rpcbind_set(struct net *, | |
51 | const struct svc_program *, | |
52 | u32, int, | |
53 | unsigned short, | |
54 | unsigned short); | |
e333f3bb TM |
55 | static __be32 nfsd_init_request(struct svc_rqst *, |
56 | const struct svc_program *, | |
57 | struct svc_process_info *); | |
1da177e4 | 58 | |
bedbdd8b | 59 | /* |
2a36395f N |
60 | * nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and some members |
61 | * of the svc_serv struct such as ->sv_temp_socks and ->sv_permsocks. | |
bedbdd8b | 62 | * |
3dd98a3b JL |
63 | * Finally, the nfsd_mutex also protects some of the global variables that are |
64 | * accessed when nfsd starts and that are settable via the write_* routines in | |
65 | * nfsctl.c. In particular: | |
66 | * | |
67 | * user_recovery_dirname | |
68 | * user_lease_time | |
69 | * nfsd_versions | |
bedbdd8b NB |
70 | */ |
71 | DEFINE_MUTEX(nfsd_mutex); | |
bedbdd8b | 72 | |
946af9b3 MS |
73 | #if IS_ENABLED(CONFIG_NFS_LOCALIO) |
74 | static const struct svc_version *localio_versions[] = { | |
75 | [1] = &localio_version1, | |
76 | }; | |
77 | ||
78 | #define NFSD_LOCALIO_NRVERS ARRAY_SIZE(localio_versions) | |
79 | ||
80 | #endif /* CONFIG_NFS_LOCALIO */ | |
81 | ||
3fb803a9 | 82 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) |
e9679189 | 83 | static const struct svc_version *nfsd_acl_version[] = { |
2f3a4b2a | 84 | # if defined(CONFIG_NFSD_V2_ACL) |
3fb803a9 | 85 | [2] = &nfsd_acl_version2, |
2f3a4b2a JL |
86 | # endif |
87 | # if defined(CONFIG_NFSD_V3_ACL) | |
3fb803a9 | 88 | [3] = &nfsd_acl_version3, |
2f3a4b2a | 89 | # endif |
3fb803a9 AG |
90 | }; |
91 | ||
86ab08be | 92 | #define NFSD_ACL_MINVERS 2 |
e8c96f8c | 93 | #define NFSD_ACL_NRVERS ARRAY_SIZE(nfsd_acl_version) |
3fb803a9 | 94 | |
3fb803a9 AG |
95 | #endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */ |
96 | ||
73598a0c | 97 | static const struct svc_version *nfsd_version[NFSD_MAXVERS+1] = { |
2f3a4b2a | 98 | #if defined(CONFIG_NFSD_V2) |
70c3b76c | 99 | [2] = &nfsd_version2, |
2f3a4b2a | 100 | #endif |
70c3b76c | 101 | [3] = &nfsd_version3, |
70c3b76c N |
102 | #if defined(CONFIG_NFSD_V4) |
103 | [4] = &nfsd_version4, | |
104 | #endif | |
105 | }; | |
106 | ||
86ab08be N |
107 | struct svc_program nfsd_programs[] = { |
108 | { | |
70c3b76c | 109 | .pg_prog = NFS_PROGRAM, /* program number */ |
73598a0c | 110 | .pg_nvers = NFSD_MAXVERS+1, /* nr of entries in nfsd_version */ |
e333f3bb | 111 | .pg_vers = nfsd_version, /* version table */ |
70c3b76c N |
112 | .pg_name = "nfsd", /* program name */ |
113 | .pg_class = "nfsd", /* authentication class */ | |
86ab08be | 114 | .pg_authenticate = svc_set_client, /* export authentication */ |
e333f3bb | 115 | .pg_init_request = nfsd_init_request, |
029be5d0 | 116 | .pg_rpcbind_set = nfsd_rpcbind_set, |
86ab08be N |
117 | }, |
118 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) | |
119 | { | |
120 | .pg_prog = NFS_ACL_PROGRAM, | |
121 | .pg_nvers = NFSD_ACL_NRVERS, | |
122 | .pg_vers = nfsd_acl_version, | |
123 | .pg_name = "nfsacl", | |
124 | .pg_class = "nfsd", | |
125 | .pg_authenticate = svc_set_client, | |
126 | .pg_init_request = nfsd_acl_init_request, | |
127 | .pg_rpcbind_set = nfsd_acl_rpcbind_set, | |
128 | }, | |
129 | #endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */ | |
946af9b3 MS |
130 | #if IS_ENABLED(CONFIG_NFS_LOCALIO) |
131 | { | |
132 | .pg_prog = NFS_LOCALIO_PROGRAM, | |
133 | .pg_nvers = NFSD_LOCALIO_NRVERS, | |
134 | .pg_vers = localio_versions, | |
135 | .pg_name = "nfslocalio", | |
136 | .pg_class = "nfsd", | |
137 | .pg_authenticate = svc_set_client, | |
138 | .pg_init_request = svc_generic_init_request, | |
139 | .pg_rpcbind_set = svc_generic_rpcbind_set, | |
140 | } | |
141 | #endif /* CONFIG_NFS_LOCALIO */ | |
70c3b76c N |
142 | }; |
143 | ||
5a939bea | 144 | bool nfsd_support_version(int vers) |
e333f3bb | 145 | { |
73598a0c | 146 | if (vers >= NFSD_MINVERS && vers <= NFSD_MAXVERS) |
e333f3bb TM |
147 | return nfsd_version[vers] != NULL; |
148 | return false; | |
149 | } | |
150 | ||
e333f3bb | 151 | int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change) |
6658d3a7 | 152 | { |
73598a0c | 153 | if (vers < NFSD_MINVERS || vers > NFSD_MAXVERS) |
15ddb4ae | 154 | return 0; |
6658d3a7 N |
155 | switch(change) { |
156 | case NFSD_SET: | |
73598a0c | 157 | nn->nfsd_versions[vers] = nfsd_support_version(vers); |
1a8eff6d | 158 | break; |
6658d3a7 | 159 | case NFSD_CLEAR: |
73598a0c | 160 | nn->nfsd_versions[vers] = false; |
6658d3a7 N |
161 | break; |
162 | case NFSD_TEST: | |
73598a0c | 163 | return nn->nfsd_versions[vers]; |
6658d3a7 | 164 | case NFSD_AVAIL: |
e333f3bb | 165 | return nfsd_support_version(vers); |
6658d3a7 N |
166 | } |
167 | return 0; | |
168 | } | |
8daf220a | 169 | |
d3635ff0 | 170 | static void |
e333f3bb | 171 | nfsd_adjust_nfsd_versions4(struct nfsd_net *nn) |
d3635ff0 TM |
172 | { |
173 | unsigned i; | |
174 | ||
175 | for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++) { | |
e333f3bb | 176 | if (nn->nfsd4_minorversions[i]) |
d3635ff0 TM |
177 | return; |
178 | } | |
e333f3bb | 179 | nfsd_vers(nn, 4, NFSD_CLEAR); |
d3635ff0 TM |
180 | } |
181 | ||
e333f3bb | 182 | int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change) |
8daf220a | 183 | { |
928c6fb3 N |
184 | if (minorversion > NFSD_SUPPORTED_MINOR_VERSION && |
185 | change != NFSD_AVAIL) | |
8daf220a | 186 | return -1; |
e333f3bb | 187 | |
8daf220a BH |
188 | switch(change) { |
189 | case NFSD_SET: | |
73598a0c N |
190 | nfsd_vers(nn, 4, NFSD_SET); |
191 | nn->nfsd4_minorversions[minorversion] = | |
192 | nfsd_vers(nn, 4, NFSD_TEST); | |
8daf220a BH |
193 | break; |
194 | case NFSD_CLEAR: | |
73598a0c N |
195 | nn->nfsd4_minorversions[minorversion] = false; |
196 | nfsd_adjust_nfsd_versions4(nn); | |
8daf220a BH |
197 | break; |
198 | case NFSD_TEST: | |
73598a0c | 199 | return nn->nfsd4_minorversions[minorversion]; |
8daf220a | 200 | case NFSD_AVAIL: |
e333f3bb TM |
201 | return minorversion <= NFSD_SUPPORTED_MINOR_VERSION && |
202 | nfsd_vers(nn, 4, NFSD_AVAIL); | |
8daf220a BH |
203 | } |
204 | return 0; | |
205 | } | |
206 | ||
b33f7dec | 207 | bool nfsd_net_try_get(struct net *net) __must_hold(rcu) |
47e98814 MS |
208 | { |
209 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | |
210 | ||
b33f7dec | 211 | return (nn && percpu_ref_tryget_live(&nn->nfsd_net_ref)); |
47e98814 MS |
212 | } |
213 | ||
b33f7dec | 214 | void nfsd_net_put(struct net *net) __must_hold(rcu) |
47e98814 MS |
215 | { |
216 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | |
217 | ||
b33f7dec | 218 | percpu_ref_put(&nn->nfsd_net_ref); |
47e98814 MS |
219 | } |
220 | ||
b33f7dec | 221 | static void nfsd_net_done(struct percpu_ref *ref) |
47e98814 | 222 | { |
b33f7dec | 223 | struct nfsd_net *nn = container_of(ref, struct nfsd_net, nfsd_net_ref); |
47e98814 | 224 | |
b33f7dec | 225 | complete(&nn->nfsd_net_confirm_done); |
47e98814 MS |
226 | } |
227 | ||
b33f7dec | 228 | static void nfsd_net_free(struct percpu_ref *ref) |
47e98814 | 229 | { |
b33f7dec | 230 | struct nfsd_net *nn = container_of(ref, struct nfsd_net, nfsd_net_ref); |
47e98814 | 231 | |
b33f7dec | 232 | complete(&nn->nfsd_net_free_done); |
47e98814 MS |
233 | } |
234 | ||
1da177e4 LT |
235 | /* |
236 | * Maximum number of nfsd processes | |
237 | */ | |
238 | #define NFSD_MAXSERVS 8192 | |
239 | ||
9dd9845f | 240 | int nfsd_nrthreads(struct net *net) |
1da177e4 | 241 | { |
c7d106c9 | 242 | int rv = 0; |
9dd9845f SK |
243 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
244 | ||
c7d106c9 | 245 | mutex_lock(&nfsd_mutex); |
9dd9845f SK |
246 | if (nn->nfsd_serv) |
247 | rv = nn->nfsd_serv->sv_nrthreads; | |
c7d106c9 NB |
248 | mutex_unlock(&nfsd_mutex); |
249 | return rv; | |
1da177e4 LT |
250 | } |
251 | ||
4df493a2 | 252 | static int nfsd_init_socks(struct net *net, const struct cred *cred) |
59db4a0c BF |
253 | { |
254 | int error; | |
9dd9845f SK |
255 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
256 | ||
257 | if (!list_empty(&nn->nfsd_serv->sv_permsocks)) | |
59db4a0c BF |
258 | return 0; |
259 | ||
352ad314 CL |
260 | error = svc_xprt_create(nn->nfsd_serv, "udp", net, PF_INET, NFS_PORT, |
261 | SVC_SOCK_DEFAULTS, cred); | |
59db4a0c BF |
262 | if (error < 0) |
263 | return error; | |
264 | ||
352ad314 CL |
265 | error = svc_xprt_create(nn->nfsd_serv, "tcp", net, PF_INET, NFS_PORT, |
266 | SVC_SOCK_DEFAULTS, cred); | |
59db4a0c BF |
267 | if (error < 0) |
268 | return error; | |
269 | ||
270 | return 0; | |
271 | } | |
272 | ||
4539f149 | 273 | static int nfsd_users = 0; |
4ad9a344 | 274 | |
70c53075 | 275 | static int nfsd_startup_generic(void) |
bda9cac1 SK |
276 | { |
277 | int ret; | |
278 | ||
4539f149 | 279 | if (nfsd_users++) |
bda9cac1 SK |
280 | return 0; |
281 | ||
65294c1f JL |
282 | ret = nfsd_file_cache_init(); |
283 | if (ret) | |
284 | goto dec_users; | |
d9499a95 | 285 | |
bda9cac1 SK |
286 | ret = nfs4_state_start(); |
287 | if (ret) | |
501cb184 | 288 | goto out_file_cache; |
bda9cac1 SK |
289 | return 0; |
290 | ||
65294c1f JL |
291 | out_file_cache: |
292 | nfsd_file_cache_shutdown(); | |
d9499a95 KM |
293 | dec_users: |
294 | nfsd_users--; | |
bda9cac1 SK |
295 | return ret; |
296 | } | |
297 | ||
298 | static void nfsd_shutdown_generic(void) | |
299 | { | |
4539f149 SK |
300 | if (--nfsd_users) |
301 | return; | |
302 | ||
bda9cac1 | 303 | nfs4_state_shutdown(); |
65294c1f | 304 | nfsd_file_cache_shutdown(); |
bda9cac1 SK |
305 | } |
306 | ||
e333f3bb | 307 | static bool nfsd_needs_lockd(struct nfsd_net *nn) |
8ef66714 | 308 | { |
e333f3bb | 309 | return nfsd_vers(nn, 2, NFSD_TEST) || nfsd_vers(nn, 3, NFSD_TEST); |
8ef66714 KM |
310 | } |
311 | ||
91d2e9b5 | 312 | /** |
3988a578 | 313 | * nfsd_copy_write_verifier - Atomically copy a write verifier |
91d2e9b5 CL |
314 | * @verf: buffer in which to receive the verifier cookie |
315 | * @nn: NFS net namespace | |
316 | * | |
317 | * This function provides a wait-free mechanism for copying the | |
3988a578 | 318 | * namespace's write verifier without tearing it. |
91d2e9b5 | 319 | */ |
3988a578 | 320 | void nfsd_copy_write_verifier(__be32 verf[2], struct nfsd_net *nn) |
27c438f5 | 321 | { |
f3734cc4 | 322 | unsigned int seq; |
27c438f5 TM |
323 | |
324 | do { | |
f3734cc4 | 325 | seq = read_seqbegin(&nn->writeverf_lock); |
90d21755 | 326 | memcpy(verf, nn->writeverf, sizeof(nn->writeverf)); |
f3734cc4 | 327 | } while (read_seqretry(&nn->writeverf_lock, seq)); |
27c438f5 TM |
328 | } |
329 | ||
3988a578 | 330 | static void nfsd_reset_write_verifier_locked(struct nfsd_net *nn) |
27c438f5 | 331 | { |
91d2e9b5 CL |
332 | struct timespec64 now; |
333 | u64 verf; | |
334 | ||
335 | /* | |
336 | * Because the time value is hashed, y2038 time_t overflow | |
337 | * is irrelevant in this usage. | |
338 | */ | |
339 | ktime_get_raw_ts64(&now); | |
340 | verf = siphash_2u64(now.tv_sec, now.tv_nsec, &nn->siphash_key); | |
341 | memcpy(nn->writeverf, &verf, sizeof(nn->writeverf)); | |
27c438f5 TM |
342 | } |
343 | ||
91d2e9b5 | 344 | /** |
3988a578 | 345 | * nfsd_reset_write_verifier - Generate a new write verifier |
91d2e9b5 CL |
346 | * @nn: NFS net namespace |
347 | * | |
348 | * This function updates the ->writeverf field of @nn. This field | |
349 | * contains an opaque cookie that, according to Section 18.32.3 of | |
350 | * RFC 8881, "the client can use to determine whether a server has | |
351 | * changed instance state (e.g., server restart) between a call to | |
352 | * WRITE and a subsequent call to either WRITE or COMMIT. This | |
353 | * cookie MUST be unchanged during a single instance of the NFSv4.1 | |
354 | * server and MUST be unique between instances of the NFSv4.1 | |
355 | * server." | |
356 | */ | |
3988a578 | 357 | void nfsd_reset_write_verifier(struct nfsd_net *nn) |
27c438f5 | 358 | { |
91d2e9b5 | 359 | write_seqlock(&nn->writeverf_lock); |
3988a578 | 360 | nfsd_reset_write_verifier_locked(nn); |
91d2e9b5 | 361 | write_sequnlock(&nn->writeverf_lock); |
27c438f5 TM |
362 | } |
363 | ||
5e092be7 CL |
364 | /* |
365 | * Crank up a set of per-namespace resources for a new NFSD instance, | |
366 | * including lockd, a duplicate reply cache, an open file cache | |
367 | * instance, and a cache of NFSv4 state objects. | |
368 | */ | |
70c53075 | 369 | static int nfsd_startup_net(struct net *net, const struct cred *cred) |
6ff50b3d | 370 | { |
2c2fe290 | 371 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
6ff50b3d SK |
372 | int ret; |
373 | ||
2c2fe290 SK |
374 | if (nn->nfsd_net_up) |
375 | return 0; | |
376 | ||
70c53075 | 377 | ret = nfsd_startup_generic(); |
6ff50b3d SK |
378 | if (ret) |
379 | return ret; | |
4df493a2 | 380 | ret = nfsd_init_socks(net, cred); |
903d9bf0 SK |
381 | if (ret) |
382 | goto out_socks; | |
8ef66714 | 383 | |
e333f3bb | 384 | if (nfsd_needs_lockd(nn) && !nn->lockd_up) { |
40373b12 | 385 | ret = lockd_up(net, cred); |
8ef66714 KM |
386 | if (ret) |
387 | goto out_socks; | |
e44b4bf2 | 388 | nn->lockd_up = true; |
8ef66714 KM |
389 | } |
390 | ||
9542e6a6 | 391 | ret = nfsd_file_cache_start_net(net); |
6ff50b3d SK |
392 | if (ret) |
393 | goto out_lockd; | |
f5f9d4a3 JL |
394 | |
395 | ret = nfsd_reply_cache_init(nn); | |
9542e6a6 TM |
396 | if (ret) |
397 | goto out_filecache; | |
6ff50b3d | 398 | |
b31da628 LL |
399 | #ifdef CONFIG_NFSD_V4_2_INTER_SSC |
400 | nfsd4_ssc_init_umount_work(nn); | |
401 | #endif | |
f5f9d4a3 JL |
402 | ret = nfs4_state_start_net(net); |
403 | if (ret) | |
404 | goto out_reply_cache; | |
405 | ||
2c2fe290 | 406 | nn->nfsd_net_up = true; |
6ff50b3d SK |
407 | return 0; |
408 | ||
f5f9d4a3 JL |
409 | out_reply_cache: |
410 | nfsd_reply_cache_shutdown(nn); | |
9542e6a6 TM |
411 | out_filecache: |
412 | nfsd_file_cache_shutdown_net(net); | |
6ff50b3d | 413 | out_lockd: |
8ef66714 KM |
414 | if (nn->lockd_up) { |
415 | lockd_down(net); | |
e44b4bf2 | 416 | nn->lockd_up = false; |
8ef66714 | 417 | } |
903d9bf0 | 418 | out_socks: |
bda9cac1 | 419 | nfsd_shutdown_generic(); |
4ad9a344 JL |
420 | return ret; |
421 | } | |
422 | ||
6ff50b3d SK |
423 | static void nfsd_shutdown_net(struct net *net) |
424 | { | |
2c2fe290 SK |
425 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
426 | ||
53e4e175 N |
427 | if (!nn->nfsd_net_up) |
428 | return; | |
39972494 | 429 | |
b33f7dec MS |
430 | percpu_ref_kill_and_confirm(&nn->nfsd_net_ref, nfsd_net_done); |
431 | wait_for_completion(&nn->nfsd_net_confirm_done); | |
39972494 | 432 | |
53e4e175 | 433 | nfsd_export_flush(net); |
6ff50b3d | 434 | nfs4_state_shutdown_net(net); |
f5f9d4a3 | 435 | nfsd_reply_cache_shutdown(nn); |
789e1e10 | 436 | nfsd_file_cache_shutdown_net(net); |
8ef66714 KM |
437 | if (nn->lockd_up) { |
438 | lockd_down(net); | |
e44b4bf2 | 439 | nn->lockd_up = false; |
8ef66714 | 440 | } |
39972494 | 441 | |
b33f7dec MS |
442 | wait_for_completion(&nn->nfsd_net_free_done); |
443 | percpu_ref_exit(&nn->nfsd_net_ref); | |
39972494 | 444 | |
2c2fe290 | 445 | nn->nfsd_net_up = false; |
903d9bf0 | 446 | nfsd_shutdown_generic(); |
6ff50b3d SK |
447 | } |
448 | ||
d057cfec | 449 | static DEFINE_SPINLOCK(nfsd_notifier_lock); |
36684996 SM |
450 | static int nfsd_inetaddr_event(struct notifier_block *this, unsigned long event, |
451 | void *ptr) | |
452 | { | |
453 | struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; | |
454 | struct net_device *dev = ifa->ifa_dev->dev; | |
455 | struct net *net = dev_net(dev); | |
456 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | |
457 | struct sockaddr_in sin; | |
458 | ||
d057cfec | 459 | if (event != NETDEV_DOWN || !nn->nfsd_serv) |
36684996 SM |
460 | goto out; |
461 | ||
d057cfec | 462 | spin_lock(&nfsd_notifier_lock); |
36684996 SM |
463 | if (nn->nfsd_serv) { |
464 | dprintk("nfsd_inetaddr_event: removed %pI4\n", &ifa->ifa_local); | |
465 | sin.sin_family = AF_INET; | |
466 | sin.sin_addr.s_addr = ifa->ifa_local; | |
467 | svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin); | |
468 | } | |
d057cfec | 469 | spin_unlock(&nfsd_notifier_lock); |
36684996 SM |
470 | |
471 | out: | |
472 | return NOTIFY_DONE; | |
473 | } | |
474 | ||
475 | static struct notifier_block nfsd_inetaddr_notifier = { | |
476 | .notifier_call = nfsd_inetaddr_event, | |
477 | }; | |
478 | ||
479 | #if IS_ENABLED(CONFIG_IPV6) | |
480 | static int nfsd_inet6addr_event(struct notifier_block *this, | |
481 | unsigned long event, void *ptr) | |
482 | { | |
483 | struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; | |
484 | struct net_device *dev = ifa->idev->dev; | |
485 | struct net *net = dev_net(dev); | |
486 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | |
487 | struct sockaddr_in6 sin6; | |
488 | ||
d057cfec | 489 | if (event != NETDEV_DOWN || !nn->nfsd_serv) |
36684996 SM |
490 | goto out; |
491 | ||
d057cfec | 492 | spin_lock(&nfsd_notifier_lock); |
36684996 SM |
493 | if (nn->nfsd_serv) { |
494 | dprintk("nfsd_inet6addr_event: removed %pI6\n", &ifa->addr); | |
495 | sin6.sin6_family = AF_INET6; | |
496 | sin6.sin6_addr = ifa->addr; | |
7b19824d SM |
497 | if (ipv6_addr_type(&sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL) |
498 | sin6.sin6_scope_id = ifa->idev->dev->ifindex; | |
36684996 SM |
499 | svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin6); |
500 | } | |
d057cfec N |
501 | spin_unlock(&nfsd_notifier_lock); |
502 | ||
36684996 SM |
503 | out: |
504 | return NOTIFY_DONE; | |
505 | } | |
506 | ||
507 | static struct notifier_block nfsd_inet6addr_notifier = { | |
508 | .notifier_call = nfsd_inet6addr_event, | |
509 | }; | |
510 | #endif | |
511 | ||
1eca45f8 VA |
512 | /* Only used under nfsd_mutex, so this atomic may be overkill: */ |
513 | static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0); | |
514 | ||
17419aef N |
515 | /** |
516 | * nfsd_destroy_serv - tear down NFSD's svc_serv for a namespace | |
517 | * @net: network namespace the NFS service is associated with | |
518 | */ | |
519 | void nfsd_destroy_serv(struct net *net) | |
4ad9a344 | 520 | { |
903d9bf0 | 521 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
9f28a971 N |
522 | struct svc_serv *serv = nn->nfsd_serv; |
523 | ||
47e98814 MS |
524 | lockdep_assert_held(&nfsd_mutex); |
525 | ||
9f28a971 N |
526 | spin_lock(&nfsd_notifier_lock); |
527 | nn->nfsd_serv = NULL; | |
528 | spin_unlock(&nfsd_notifier_lock); | |
903d9bf0 | 529 | |
1eca45f8 VA |
530 | /* check if the notifier still has clients */ |
531 | if (atomic_dec_return(&nfsd_notifier_refcount) == 0) { | |
532 | unregister_inetaddr_notifier(&nfsd_inetaddr_notifier); | |
36684996 | 533 | #if IS_ENABLED(CONFIG_IPV6) |
1eca45f8 | 534 | unregister_inet6addr_notifier(&nfsd_inet6addr_notifier); |
36684996 | 535 | #endif |
1eca45f8 VA |
536 | } |
537 | ||
9f28a971 N |
538 | svc_xprt_destroy_all(serv, net); |
539 | ||
4ad9a344 JL |
540 | /* |
541 | * write_ports can create the server without actually starting | |
542 | * any threads--if we get shut down before any threads are | |
17419aef | 543 | * started, then nfsd_destroy_serv will be run before any of this |
691412b4 | 544 | * other initialization has been done except the rpcb information. |
4ad9a344 | 545 | */ |
691412b4 | 546 | svc_rpcb_cleanup(serv, net); |
16d05870 | 547 | |
691412b4 | 548 | nfsd_shutdown_net(net); |
1e3577a4 | 549 | svc_destroy(&serv); |
bc591ccf | 550 | } |
6658d3a7 | 551 | |
e333f3bb | 552 | void nfsd_reset_versions(struct nfsd_net *nn) |
6658d3a7 | 553 | { |
6658d3a7 N |
554 | int i; |
555 | ||
73598a0c | 556 | for (i = 0; i <= NFSD_MAXVERS; i++) |
e333f3bb | 557 | if (nfsd_vers(nn, i, NFSD_TEST)) |
800a938f | 558 | return; |
6658d3a7 | 559 | |
73598a0c | 560 | for (i = 0; i <= NFSD_MAXVERS; i++) |
800a938f | 561 | if (i != 4) |
e333f3bb | 562 | nfsd_vers(nn, i, NFSD_SET); |
800a938f N |
563 | else { |
564 | int minor = 0; | |
e333f3bb | 565 | while (nfsd_minorversion(nn, minor, NFSD_SET) >= 0) |
800a938f N |
566 | minor++; |
567 | } | |
6658d3a7 N |
568 | } |
569 | ||
87b0fc7d | 570 | static int nfsd_get_default_max_blksize(void) |
02a375f0 | 571 | { |
87b0fc7d BF |
572 | struct sysinfo i; |
573 | unsigned long long target; | |
574 | unsigned long ret; | |
bedbdd8b | 575 | |
87b0fc7d | 576 | si_meminfo(&i); |
508f9227 | 577 | target = (i.totalram - i.totalhigh) << PAGE_SHIFT; |
87b0fc7d BF |
578 | /* |
579 | * Aim for 1/4096 of memory per thread This gives 1MB on 4Gig | |
580 | * machines, but only uses 32K on 128M machines. Bottom out at | |
581 | * 8K on 32M and smaller. Of course, this is only a default. | |
582 | */ | |
583 | target >>= 12; | |
584 | ||
8a81f16d | 585 | ret = NFSSVC_DEFBLKSIZE; |
87b0fc7d BF |
586 | while (ret > target && ret >= 8*1024*2) |
587 | ret /= 2; | |
588 | return ret; | |
589 | } | |
590 | ||
c6c7f2a8 TM |
591 | void nfsd_shutdown_threads(struct net *net) |
592 | { | |
593 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | |
594 | struct svc_serv *serv; | |
595 | ||
596 | mutex_lock(&nfsd_mutex); | |
597 | serv = nn->nfsd_serv; | |
598 | if (serv == NULL) { | |
599 | mutex_unlock(&nfsd_mutex); | |
600 | return; | |
601 | } | |
602 | ||
c6c7f2a8 | 603 | /* Kill outstanding nfsd threads */ |
3ebdbe52 | 604 | svc_set_num_threads(serv, NULL, 0); |
17419aef | 605 | nfsd_destroy_serv(net); |
c6c7f2a8 | 606 | mutex_unlock(&nfsd_mutex); |
c6c7f2a8 TM |
607 | } |
608 | ||
4f67d24f | 609 | struct svc_rqst *nfsd_current_rqst(void) |
28df3d15 | 610 | { |
4f67d24f N |
611 | if (kthread_func(current) == nfsd) |
612 | return kthread_data(current); | |
613 | return NULL; | |
28df3d15 BF |
614 | } |
615 | ||
6777436b | 616 | int nfsd_create_serv(struct net *net) |
87b0fc7d | 617 | { |
9793f7c8 | 618 | int error; |
b9c0ef85 | 619 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
d057cfec | 620 | struct svc_serv *serv; |
9793f7c8 | 621 | |
bedbdd8b | 622 | WARN_ON(!mutex_is_locked(&nfsd_mutex)); |
1e3577a4 | 623 | if (nn->nfsd_serv) |
02a375f0 | 624 | return 0; |
1e3577a4 | 625 | |
b33f7dec | 626 | error = percpu_ref_init(&nn->nfsd_net_ref, nfsd_net_free, |
47e98814 MS |
627 | 0, GFP_KERNEL); |
628 | if (error) | |
629 | return error; | |
b33f7dec MS |
630 | init_completion(&nn->nfsd_net_free_done); |
631 | init_completion(&nn->nfsd_net_confirm_done); | |
47e98814 | 632 | |
87b0fc7d BF |
633 | if (nfsd_max_blksize == 0) |
634 | nfsd_max_blksize = nfsd_get_default_max_blksize(); | |
e333f3bb | 635 | nfsd_reset_versions(nn); |
86ab08be N |
636 | serv = svc_create_pooled(nfsd_programs, ARRAY_SIZE(nfsd_programs), |
637 | &nn->nfsd_svcstats, | |
f0943238 | 638 | nfsd_max_blksize, nfsd); |
d057cfec | 639 | if (serv == NULL) |
628b3687 | 640 | return -ENOMEM; |
bedbdd8b | 641 | |
d057cfec | 642 | error = svc_bind(serv, net); |
9793f7c8 | 643 | if (error < 0) { |
1e3577a4 | 644 | svc_destroy(&serv); |
9793f7c8 SK |
645 | return error; |
646 | } | |
d057cfec N |
647 | spin_lock(&nfsd_notifier_lock); |
648 | nn->nfsd_serv = serv; | |
649 | spin_unlock(&nfsd_notifier_lock); | |
9793f7c8 | 650 | |
1eca45f8 VA |
651 | /* check if the notifier is already set */ |
652 | if (atomic_inc_return(&nfsd_notifier_refcount) == 1) { | |
653 | register_inetaddr_notifier(&nfsd_inetaddr_notifier); | |
36684996 | 654 | #if IS_ENABLED(CONFIG_IPV6) |
1eca45f8 | 655 | register_inet6addr_notifier(&nfsd_inet6addr_notifier); |
36684996 | 656 | #endif |
1eca45f8 | 657 | } |
3988a578 | 658 | nfsd_reset_write_verifier(nn); |
87b0fc7d | 659 | return 0; |
02a375f0 N |
660 | } |
661 | ||
9dd9845f | 662 | int nfsd_nrpools(struct net *net) |
eed2965a | 663 | { |
9dd9845f SK |
664 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
665 | ||
666 | if (nn->nfsd_serv == NULL) | |
eed2965a GB |
667 | return 0; |
668 | else | |
9dd9845f | 669 | return nn->nfsd_serv->sv_nrpools; |
eed2965a GB |
670 | } |
671 | ||
9dd9845f | 672 | int nfsd_get_nrthreads(int n, int *nthreads, struct net *net) |
eed2965a | 673 | { |
9dd9845f | 674 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
2e8fc923 N |
675 | struct svc_serv *serv = nn->nfsd_serv; |
676 | int i; | |
eed2965a | 677 | |
2e8fc923 N |
678 | if (serv) |
679 | for (i = 0; i < serv->sv_nrpools && i < n; i++) | |
60749cbe | 680 | nthreads[i] = serv->sv_pools[i].sp_nrthreads; |
eed2965a GB |
681 | return 0; |
682 | } | |
683 | ||
b4d8f228 JL |
684 | /** |
685 | * nfsd_set_nrthreads - set the number of running threads in the net's service | |
686 | * @n: number of array members in @nthreads | |
687 | * @nthreads: array of thread counts for each pool | |
688 | * @net: network namespace to operate within | |
689 | * | |
690 | * This function alters the number of running threads for the given network | |
691 | * namespace in each pool. If passed an array longer then the number of pools | |
692 | * the extra pool settings are ignored. If passed an array shorter than the | |
693 | * number of pools, the missing values are interpreted as 0's. | |
694 | * | |
695 | * Returns 0 on success or a negative errno on error. | |
696 | */ | |
3938a0d5 | 697 | int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) |
eed2965a GB |
698 | { |
699 | int i = 0; | |
700 | int tot = 0; | |
701 | int err = 0; | |
9dd9845f | 702 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
eed2965a | 703 | |
b4d8f228 | 704 | lockdep_assert_held(&nfsd_mutex); |
bedbdd8b | 705 | |
9dd9845f | 706 | if (nn->nfsd_serv == NULL || n <= 0) |
eed2965a GB |
707 | return 0; |
708 | ||
b4d8f228 JL |
709 | /* |
710 | * Special case: When n == 1, pass in NULL for the pool, so that the | |
711 | * change is distributed equally among them. | |
712 | */ | |
713 | if (n == 1) | |
714 | return svc_set_num_threads(nn->nfsd_serv, NULL, nthreads[0]); | |
715 | ||
9dd9845f SK |
716 | if (n > nn->nfsd_serv->sv_nrpools) |
717 | n = nn->nfsd_serv->sv_nrpools; | |
eed2965a GB |
718 | |
719 | /* enforce a global maximum number of threads */ | |
720 | tot = 0; | |
721 | for (i = 0; i < n; i++) { | |
3c7aa15d | 722 | nthreads[i] = min(nthreads[i], NFSD_MAXSERVS); |
eed2965a GB |
723 | tot += nthreads[i]; |
724 | } | |
725 | if (tot > NFSD_MAXSERVS) { | |
726 | /* total too large: scale down requested numbers */ | |
727 | for (i = 0; i < n && tot > 0; i++) { | |
3409e4f1 | 728 | int new = nthreads[i] * NFSD_MAXSERVS / tot; |
eed2965a GB |
729 | tot -= (nthreads[i] - new); |
730 | nthreads[i] = new; | |
731 | } | |
732 | for (i = 0; i < n && tot > 0; i++) { | |
733 | nthreads[i]--; | |
734 | tot--; | |
735 | } | |
736 | } | |
737 | ||
eed2965a | 738 | /* apply the new numbers */ |
eed2965a | 739 | for (i = 0; i < n; i++) { |
3ebdbe52 N |
740 | err = svc_set_num_threads(nn->nfsd_serv, |
741 | &nn->nfsd_serv->sv_pools[i], | |
742 | nthreads[i]); | |
eed2965a | 743 | if (err) |
7f5c330b | 744 | goto out; |
eed2965a | 745 | } |
7f5c330b JL |
746 | |
747 | /* Anything undefined in array is considered to be 0 */ | |
748 | for (i = n; i < nn->nfsd_serv->sv_nrpools; ++i) { | |
749 | err = svc_set_num_threads(nn->nfsd_serv, | |
750 | &nn->nfsd_serv->sv_pools[i], | |
751 | 0); | |
752 | if (err) | |
753 | goto out; | |
754 | } | |
755 | out: | |
eed2965a GB |
756 | return err; |
757 | } | |
758 | ||
b4d8f228 JL |
759 | /** |
760 | * nfsd_svc: start up or shut down the nfsd server | |
761 | * @n: number of array members in @nthreads | |
762 | * @nthreads: array of thread counts for each pool | |
763 | * @net: network namespace to operate within | |
764 | * @cred: credentials to use for xprt creation | |
765 | * @scope: server scope value (defaults to nodename) | |
766 | * | |
767 | * Adjust the number of threads in each pool and return the new | |
768 | * total number of threads in the service. | |
ac77efbe | 769 | */ |
1da177e4 | 770 | int |
b4d8f228 | 771 | nfsd_svc(int n, int *nthreads, struct net *net, const struct cred *cred, const char *scope) |
1da177e4 LT |
772 | { |
773 | int error; | |
9dd9845f | 774 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
9f28a971 | 775 | struct svc_serv *serv; |
bedbdd8b | 776 | |
0842b4c8 JL |
777 | lockdep_assert_held(&nfsd_mutex); |
778 | ||
6658d3a7 | 779 | dprintk("nfsd: creating service\n"); |
3c7aa15d | 780 | |
9077d598 | 781 | strscpy(nn->nfsd_name, scope ? scope : utsname()->nodename, |
7627d7dc SM |
782 | sizeof(nn->nfsd_name)); |
783 | ||
6777436b | 784 | error = nfsd_create_serv(net); |
02a375f0 | 785 | if (error) |
774f8bbd | 786 | goto out; |
9f28a971 | 787 | serv = nn->nfsd_serv; |
774f8bbd | 788 | |
70c53075 | 789 | error = nfsd_startup_net(net, cred); |
af4718f3 | 790 | if (error) |
8c62d127 | 791 | goto out_put; |
b4d8f228 | 792 | error = nfsd_set_nrthreads(n, nthreads, net); |
774f8bbd | 793 | if (error) |
bf320752 | 794 | goto out_put; |
9f28a971 | 795 | error = serv->sv_nrthreads; |
8c62d127 | 796 | out_put: |
bf320752 | 797 | if (serv->sv_nrthreads == 0) |
17419aef | 798 | nfsd_destroy_serv(net); |
4ad9a344 | 799 | out: |
1da177e4 LT |
800 | return error; |
801 | } | |
802 | ||
029be5d0 TM |
803 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) |
804 | static bool | |
805 | nfsd_support_acl_version(int vers) | |
806 | { | |
807 | if (vers >= NFSD_ACL_MINVERS && vers < NFSD_ACL_NRVERS) | |
808 | return nfsd_acl_version[vers] != NULL; | |
809 | return false; | |
810 | } | |
811 | ||
812 | static int | |
813 | nfsd_acl_rpcbind_set(struct net *net, const struct svc_program *progp, | |
814 | u32 version, int family, unsigned short proto, | |
815 | unsigned short port) | |
816 | { | |
817 | if (!nfsd_support_acl_version(version) || | |
e333f3bb | 818 | !nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST)) |
029be5d0 TM |
819 | return 0; |
820 | return svc_generic_rpcbind_set(net, progp, version, family, | |
821 | proto, port); | |
822 | } | |
e333f3bb TM |
823 | |
824 | static __be32 | |
825 | nfsd_acl_init_request(struct svc_rqst *rqstp, | |
826 | const struct svc_program *progp, | |
827 | struct svc_process_info *ret) | |
828 | { | |
829 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | |
830 | int i; | |
831 | ||
832 | if (likely(nfsd_support_acl_version(rqstp->rq_vers) && | |
833 | nfsd_vers(nn, rqstp->rq_vers, NFSD_TEST))) | |
834 | return svc_generic_init_request(rqstp, progp, ret); | |
835 | ||
836 | ret->mismatch.lovers = NFSD_ACL_NRVERS; | |
837 | for (i = NFSD_ACL_MINVERS; i < NFSD_ACL_NRVERS; i++) { | |
838 | if (nfsd_support_acl_version(rqstp->rq_vers) && | |
839 | nfsd_vers(nn, i, NFSD_TEST)) { | |
840 | ret->mismatch.lovers = i; | |
841 | break; | |
842 | } | |
843 | } | |
844 | if (ret->mismatch.lovers == NFSD_ACL_NRVERS) | |
845 | return rpc_prog_unavail; | |
846 | ret->mismatch.hivers = NFSD_ACL_MINVERS; | |
847 | for (i = NFSD_ACL_NRVERS - 1; i >= NFSD_ACL_MINVERS; i--) { | |
848 | if (nfsd_support_acl_version(rqstp->rq_vers) && | |
849 | nfsd_vers(nn, i, NFSD_TEST)) { | |
850 | ret->mismatch.hivers = i; | |
851 | break; | |
852 | } | |
853 | } | |
854 | return rpc_prog_mismatch; | |
855 | } | |
029be5d0 TM |
856 | #endif |
857 | ||
858 | static int | |
859 | nfsd_rpcbind_set(struct net *net, const struct svc_program *progp, | |
860 | u32 version, int family, unsigned short proto, | |
861 | unsigned short port) | |
862 | { | |
e333f3bb | 863 | if (!nfsd_vers(net_generic(net, nfsd_net_id), version, NFSD_TEST)) |
029be5d0 TM |
864 | return 0; |
865 | return svc_generic_rpcbind_set(net, progp, version, family, | |
866 | proto, port); | |
867 | } | |
1da177e4 | 868 | |
e333f3bb TM |
869 | static __be32 |
870 | nfsd_init_request(struct svc_rqst *rqstp, | |
871 | const struct svc_program *progp, | |
872 | struct svc_process_info *ret) | |
873 | { | |
874 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | |
875 | int i; | |
876 | ||
877 | if (likely(nfsd_vers(nn, rqstp->rq_vers, NFSD_TEST))) | |
878 | return svc_generic_init_request(rqstp, progp, ret); | |
879 | ||
73598a0c N |
880 | ret->mismatch.lovers = NFSD_MAXVERS + 1; |
881 | for (i = NFSD_MINVERS; i <= NFSD_MAXVERS; i++) { | |
e333f3bb TM |
882 | if (nfsd_vers(nn, i, NFSD_TEST)) { |
883 | ret->mismatch.lovers = i; | |
884 | break; | |
885 | } | |
886 | } | |
73598a0c | 887 | if (ret->mismatch.lovers > NFSD_MAXVERS) |
e333f3bb TM |
888 | return rpc_prog_unavail; |
889 | ret->mismatch.hivers = NFSD_MINVERS; | |
73598a0c | 890 | for (i = NFSD_MAXVERS; i >= NFSD_MINVERS; i--) { |
e333f3bb TM |
891 | if (nfsd_vers(nn, i, NFSD_TEST)) { |
892 | ret->mismatch.hivers = i; | |
893 | break; | |
894 | } | |
895 | } | |
896 | return rpc_prog_mismatch; | |
897 | } | |
898 | ||
1da177e4 LT |
899 | /* |
900 | * This is the NFS server kernel thread | |
901 | */ | |
9867d76c JL |
902 | static int |
903 | nfsd(void *vrqstp) | |
1da177e4 | 904 | { |
9867d76c | 905 | struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp; |
88c47666 SK |
906 | struct svc_xprt *perm_sock = list_entry(rqstp->rq_server->sv_permsocks.next, typeof(struct svc_xprt), xpt_list); |
907 | struct net *net = perm_sock->xpt_net; | |
5b8db00b | 908 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
1da177e4 | 909 | |
9867d76c | 910 | /* At this point, the thread shares current->fs |
47057abd | 911 | * with the init process. We need to create files with the |
3391fc92 N |
912 | * umask as defined by the client instead of init's umask. |
913 | */ | |
914 | svc_thread_init_status(rqstp, unshare_fs_struct()); | |
3e93cd67 | 915 | |
1da177e4 LT |
916 | current->fs->umask = 0; |
917 | ||
e41ee44c | 918 | atomic_inc(&nfsd_th_cnt); |
bedbdd8b | 919 | |
83144186 | 920 | set_freezable(); |
1da177e4 LT |
921 | |
922 | /* | |
923 | * The main request loop | |
924 | */ | |
fa341560 | 925 | while (!svc_thread_should_stop(rqstp)) { |
c743b425 | 926 | svc_recv(rqstp); |
ffb40259 | 927 | nfsd_file_net_dispose(nn); |
1da177e4 LT |
928 | } |
929 | ||
e41ee44c | 930 | atomic_dec(&nfsd_th_cnt); |
1da177e4 | 931 | |
1da177e4 LT |
932 | /* Release the thread */ |
933 | svc_exit_thread(rqstp); | |
9867d76c | 934 | return 0; |
1da177e4 LT |
935 | } |
936 | ||
383c440d | 937 | /** |
946af9b3 | 938 | * nfsd_dispatch - Process an NFS or NFSACL or LOCALIO Request |
383c440d | 939 | * @rqstp: incoming request |
383c440d CL |
940 | * |
941 | * This RPC dispatcher integrates the NFS server's duplicate reply cache. | |
942 | * | |
943 | * Return values: | |
944 | * %0: Processing complete; do not send a Reply | |
945 | * %1: Processing complete; send Reply in rqstp->rq_res | |
946 | */ | |
cee4db19 | 947 | int nfsd_dispatch(struct svc_rqst *rqstp) |
1da177e4 | 948 | { |
4c96cb56 | 949 | const struct svc_procedure *proc = rqstp->rq_procinfo; |
cee4db19 | 950 | __be32 *statp = rqstp->rq_accept_statp; |
e7421ce7 | 951 | struct nfsd_cacherep *rp; |
bf51c52a | 952 | unsigned int start, len; |
1caf5f61 | 953 | __be32 *nfs_reply; |
1da177e4 | 954 | |
1091006c BF |
955 | /* |
956 | * Give the xdr decoder a chance to change this if it wants | |
957 | * (necessary in the NFSv4.0 compound case) | |
958 | */ | |
959 | rqstp->rq_cachetype = proc->pc_cachetype; | |
5191955d | 960 | |
bf51c52a CL |
961 | /* |
962 | * ->pc_decode advances the argument stream past the NFS | |
963 | * Call header, so grab the header's starting location and | |
964 | * size now for the call to nfsd_cache_lookup(). | |
965 | */ | |
966 | start = xdr_stream_pos(&rqstp->rq_arg_stream); | |
967 | len = xdr_stream_remaining(&rqstp->rq_arg_stream); | |
16c66364 | 968 | if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream)) |
85085aac | 969 | goto out_decode_err; |
1091006c | 970 | |
bd9d6a3e LB |
971 | /* |
972 | * Release rq_status_counter setting it to an odd value after the rpc | |
973 | * request has been properly parsed. rq_status_counter is used to | |
974 | * notify the consumers if the rqstp fields are stable | |
975 | * (rq_status_counter is odd) or not meaningful (rq_status_counter | |
976 | * is even). | |
977 | */ | |
978 | smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter | 1); | |
979 | ||
cb18eca4 | 980 | rp = NULL; |
bf51c52a | 981 | switch (nfsd_cache_lookup(rqstp, start, len, &rp)) { |
84c138e7 CL |
982 | case RC_DOIT: |
983 | break; | |
1da177e4 | 984 | case RC_REPLY: |
85085aac | 985 | goto out_cached_reply; |
84c138e7 | 986 | case RC_DROPIT: |
85085aac | 987 | goto out_dropit; |
1da177e4 LT |
988 | } |
989 | ||
1caf5f61 | 990 | nfs_reply = xdr_inline_decode(&rqstp->rq_res_stream, 0); |
cc028a10 | 991 | *statp = proc->pc_func(rqstp); |
93155647 | 992 | if (test_bit(RQ_DROPME, &rqstp->rq_flags)) |
85085aac | 993 | goto out_update_drop; |
1da177e4 | 994 | |
fda49441 | 995 | if (!proc->pc_encode(rqstp, &rqstp->rq_res_stream)) |
f0af2210 | 996 | goto out_encode_err; |
1da177e4 | 997 | |
bd9d6a3e LB |
998 | /* |
999 | * Release rq_status_counter setting it to an even value after the rpc | |
1000 | * request has been properly processed. | |
1001 | */ | |
1002 | smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter + 1); | |
1003 | ||
1caf5f61 | 1004 | nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, nfs_reply); |
85085aac CL |
1005 | out_cached_reply: |
1006 | return 1; | |
1007 | ||
85085aac | 1008 | out_decode_err: |
0dfdad1c | 1009 | trace_nfsd_garbage_args_err(rqstp); |
85085aac CL |
1010 | *statp = rpc_garbage_args; |
1011 | return 1; | |
1012 | ||
1013 | out_update_drop: | |
cb18eca4 | 1014 | nfsd_cache_update(rqstp, rp, RC_NOCACHE, NULL); |
85085aac CL |
1015 | out_dropit: |
1016 | return 0; | |
1017 | ||
1018 | out_encode_err: | |
0dfdad1c | 1019 | trace_nfsd_cant_encode_err(rqstp); |
cb18eca4 | 1020 | nfsd_cache_update(rqstp, rp, RC_NOCACHE, NULL); |
85085aac | 1021 | *statp = rpc_system_err; |
1da177e4 LT |
1022 | return 1; |
1023 | } | |
03cf6c9f | 1024 | |
788f7183 CL |
1025 | /** |
1026 | * nfssvc_decode_voidarg - Decode void arguments | |
1027 | * @rqstp: Server RPC transaction context | |
16c66364 | 1028 | * @xdr: XDR stream positioned at arguments to decode |
788f7183 CL |
1029 | * |
1030 | * Return values: | |
c44b31c2 CL |
1031 | * %false: Arguments were not valid |
1032 | * %true: Decoding was successful | |
788f7183 | 1033 | */ |
c44b31c2 | 1034 | bool nfssvc_decode_voidarg(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
788f7183 | 1035 | { |
291cd656 | 1036 | return true; |
788f7183 CL |
1037 | } |
1038 | ||
1039 | /** | |
1040 | * nfssvc_encode_voidres - Encode void results | |
1041 | * @rqstp: Server RPC transaction context | |
fda49441 | 1042 | * @xdr: XDR stream into which to encode results |
788f7183 CL |
1043 | * |
1044 | * Return values: | |
130e2054 CL |
1045 | * %false: Local error while encoding |
1046 | * %true: Encoding was successful | |
788f7183 | 1047 | */ |
130e2054 | 1048 | bool nfssvc_encode_voidres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
788f7183 | 1049 | { |
130e2054 | 1050 | return true; |
788f7183 | 1051 | } |