1 | // SPDX-License-Identifier: GPL-2.0 |
---|---|
2 | /* |
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 | |
10 | #include <linux/sched/signal.h> |
11 | #include <linux/freezer.h> |
12 | #include <linux/module.h> |
13 | #include <linux/fs_struct.h> |
14 | #include <linux/swap.h> |
15 | #include <linux/siphash.h> |
16 | |
17 | #include <linux/sunrpc/stats.h> |
18 | #include <linux/sunrpc/svcsock.h> |
19 | #include <linux/sunrpc/svc_xprt.h> |
20 | #include <linux/lockd/bind.h> |
21 | #include <linux/nfsacl.h> |
22 | #include <linux/seq_file.h> |
23 | #include <linux/inetdevice.h> |
24 | #include <net/addrconf.h> |
25 | #include <net/ipv6.h> |
26 | #include <net/net_namespace.h> |
27 | #include "nfsd.h" |
28 | #include "cache.h" |
29 | #include "vfs.h" |
30 | #include "netns.h" |
31 | #include "filecache.h" |
32 | |
33 | #include "trace.h" |
34 | |
35 | #define NFSDDBG_FACILITY NFSDDBG_SVC |
36 | |
37 | atomic_t nfsd_th_cnt = ATOMIC_INIT(0); |
38 | extern struct svc_program nfsd_program; |
39 | static int nfsd(void *vrqstp); |
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); |
46 | static __be32 nfsd_acl_init_request(struct svc_rqst *, |
47 | const struct svc_program *, |
48 | struct svc_process_info *); |
49 | #endif |
50 | static int nfsd_rpcbind_set(struct net *, |
51 | const struct svc_program *, |
52 | u32, int, |
53 | unsigned short, |
54 | unsigned short); |
55 | static __be32 nfsd_init_request(struct svc_rqst *, |
56 | const struct svc_program *, |
57 | struct svc_process_info *); |
58 | |
59 | /* |
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. |
62 | * |
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 |
70 | */ |
71 | DEFINE_MUTEX(nfsd_mutex); |
72 | |
73 | /* |
74 | * nfsd_drc_lock protects nfsd_drc_max_pages and nfsd_drc_pages_used. |
75 | * nfsd_drc_max_pages limits the total amount of memory available for |
76 | * version 4.1 DRC caches. |
77 | * nfsd_drc_pages_used tracks the current version 4.1 DRC memory usage. |
78 | */ |
79 | DEFINE_SPINLOCK(nfsd_drc_lock); |
80 | unsigned long nfsd_drc_max_mem; |
81 | unsigned long nfsd_drc_mem_used; |
82 | |
83 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) |
84 | static const struct svc_version *nfsd_acl_version[] = { |
85 | # if defined(CONFIG_NFSD_V2_ACL) |
86 | [2] = &nfsd_acl_version2, |
87 | # endif |
88 | # if defined(CONFIG_NFSD_V3_ACL) |
89 | [3] = &nfsd_acl_version3, |
90 | # endif |
91 | }; |
92 | |
93 | #define NFSD_ACL_MINVERS 2 |
94 | #define NFSD_ACL_NRVERS ARRAY_SIZE(nfsd_acl_version) |
95 | |
96 | static struct svc_program nfsd_acl_program = { |
97 | .pg_prog = NFS_ACL_PROGRAM, |
98 | .pg_nvers = NFSD_ACL_NRVERS, |
99 | .pg_vers = nfsd_acl_version, |
100 | .pg_name = "nfsacl", |
101 | .pg_class = "nfsd", |
102 | .pg_authenticate = &svc_set_client, |
103 | .pg_init_request = nfsd_acl_init_request, |
104 | .pg_rpcbind_set = nfsd_acl_rpcbind_set, |
105 | }; |
106 | |
107 | #endif /* defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) */ |
108 | |
109 | static const struct svc_version *nfsd_version[] = { |
110 | #if defined(CONFIG_NFSD_V2) |
111 | [2] = &nfsd_version2, |
112 | #endif |
113 | [3] = &nfsd_version3, |
114 | #if defined(CONFIG_NFSD_V4) |
115 | [4] = &nfsd_version4, |
116 | #endif |
117 | }; |
118 | |
119 | #define NFSD_MINVERS 2 |
120 | #define NFSD_NRVERS ARRAY_SIZE(nfsd_version) |
121 | |
122 | struct svc_program nfsd_program = { |
123 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) |
124 | .pg_next = &nfsd_acl_program, |
125 | #endif |
126 | .pg_prog = NFS_PROGRAM, /* program number */ |
127 | .pg_nvers = NFSD_NRVERS, /* nr of entries in nfsd_version */ |
128 | .pg_vers = nfsd_version, /* version table */ |
129 | .pg_name = "nfsd", /* program name */ |
130 | .pg_class = "nfsd", /* authentication class */ |
131 | .pg_authenticate = &svc_set_client, /* export authentication */ |
132 | .pg_init_request = nfsd_init_request, |
133 | .pg_rpcbind_set = nfsd_rpcbind_set, |
134 | }; |
135 | |
136 | static bool |
137 | nfsd_support_version(int vers) |
138 | { |
139 | if (vers >= NFSD_MINVERS && vers < NFSD_NRVERS) |
140 | return nfsd_version[vers] != NULL; |
141 | return false; |
142 | } |
143 | |
144 | static bool * |
145 | nfsd_alloc_versions(void) |
146 | { |
147 | bool *vers = kmalloc_array(NFSD_NRVERS, size: sizeof(bool), GFP_KERNEL); |
148 | unsigned i; |
149 | |
150 | if (vers) { |
151 | /* All compiled versions are enabled by default */ |
152 | for (i = 0; i < NFSD_NRVERS; i++) |
153 | vers[i] = nfsd_support_version(vers: i); |
154 | } |
155 | return vers; |
156 | } |
157 | |
158 | static bool * |
159 | nfsd_alloc_minorversions(void) |
160 | { |
161 | bool *vers = kmalloc_array(NFSD_SUPPORTED_MINOR_VERSION + 1, |
162 | size: sizeof(bool), GFP_KERNEL); |
163 | unsigned i; |
164 | |
165 | if (vers) { |
166 | /* All minor versions are enabled by default */ |
167 | for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++) |
168 | vers[i] = nfsd_support_version(vers: 4); |
169 | } |
170 | return vers; |
171 | } |
172 | |
173 | void |
174 | nfsd_netns_free_versions(struct nfsd_net *nn) |
175 | { |
176 | kfree(objp: nn->nfsd_versions); |
177 | kfree(objp: nn->nfsd4_minorversions); |
178 | nn->nfsd_versions = NULL; |
179 | nn->nfsd4_minorversions = NULL; |
180 | } |
181 | |
182 | static void |
183 | nfsd_netns_init_versions(struct nfsd_net *nn) |
184 | { |
185 | if (!nn->nfsd_versions) { |
186 | nn->nfsd_versions = nfsd_alloc_versions(); |
187 | nn->nfsd4_minorversions = nfsd_alloc_minorversions(); |
188 | if (!nn->nfsd_versions || !nn->nfsd4_minorversions) |
189 | nfsd_netns_free_versions(nn); |
190 | } |
191 | } |
192 | |
193 | int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change) |
194 | { |
195 | if (vers < NFSD_MINVERS || vers >= NFSD_NRVERS) |
196 | return 0; |
197 | switch(change) { |
198 | case NFSD_SET: |
199 | if (nn->nfsd_versions) |
200 | nn->nfsd_versions[vers] = nfsd_support_version(vers); |
201 | break; |
202 | case NFSD_CLEAR: |
203 | nfsd_netns_init_versions(nn); |
204 | if (nn->nfsd_versions) |
205 | nn->nfsd_versions[vers] = false; |
206 | break; |
207 | case NFSD_TEST: |
208 | if (nn->nfsd_versions) |
209 | return nn->nfsd_versions[vers]; |
210 | fallthrough; |
211 | case NFSD_AVAIL: |
212 | return nfsd_support_version(vers); |
213 | } |
214 | return 0; |
215 | } |
216 | |
217 | static void |
218 | nfsd_adjust_nfsd_versions4(struct nfsd_net *nn) |
219 | { |
220 | unsigned i; |
221 | |
222 | for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++) { |
223 | if (nn->nfsd4_minorversions[i]) |
224 | return; |
225 | } |
226 | nfsd_vers(nn, vers: 4, change: NFSD_CLEAR); |
227 | } |
228 | |
229 | int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change) |
230 | { |
231 | if (minorversion > NFSD_SUPPORTED_MINOR_VERSION && |
232 | change != NFSD_AVAIL) |
233 | return -1; |
234 | |
235 | switch(change) { |
236 | case NFSD_SET: |
237 | if (nn->nfsd4_minorversions) { |
238 | nfsd_vers(nn, vers: 4, change: NFSD_SET); |
239 | nn->nfsd4_minorversions[minorversion] = |
240 | nfsd_vers(nn, vers: 4, change: NFSD_TEST); |
241 | } |
242 | break; |
243 | case NFSD_CLEAR: |
244 | nfsd_netns_init_versions(nn); |
245 | if (nn->nfsd4_minorversions) { |
246 | nn->nfsd4_minorversions[minorversion] = false; |
247 | nfsd_adjust_nfsd_versions4(nn); |
248 | } |
249 | break; |
250 | case NFSD_TEST: |
251 | if (nn->nfsd4_minorversions) |
252 | return nn->nfsd4_minorversions[minorversion]; |
253 | return nfsd_vers(nn, vers: 4, change: NFSD_TEST); |
254 | case NFSD_AVAIL: |
255 | return minorversion <= NFSD_SUPPORTED_MINOR_VERSION && |
256 | nfsd_vers(nn, vers: 4, change: NFSD_AVAIL); |
257 | } |
258 | return 0; |
259 | } |
260 | |
261 | /* |
262 | * Maximum number of nfsd processes |
263 | */ |
264 | #define NFSD_MAXSERVS 8192 |
265 | |
266 | int nfsd_nrthreads(struct net *net) |
267 | { |
268 | int rv = 0; |
269 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
270 | |
271 | mutex_lock(&nfsd_mutex); |
272 | if (nn->nfsd_serv) |
273 | rv = nn->nfsd_serv->sv_nrthreads; |
274 | mutex_unlock(lock: &nfsd_mutex); |
275 | return rv; |
276 | } |
277 | |
278 | static int nfsd_init_socks(struct net *net, const struct cred *cred) |
279 | { |
280 | int error; |
281 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
282 | |
283 | if (!list_empty(head: &nn->nfsd_serv->sv_permsocks)) |
284 | return 0; |
285 | |
286 | error = svc_xprt_create(serv: nn->nfsd_serv, xprt_name: "udp", net, PF_INET, NFS_PORT, |
287 | SVC_SOCK_DEFAULTS, cred); |
288 | if (error < 0) |
289 | return error; |
290 | |
291 | error = svc_xprt_create(serv: nn->nfsd_serv, xprt_name: "tcp", net, PF_INET, NFS_PORT, |
292 | SVC_SOCK_DEFAULTS, cred); |
293 | if (error < 0) |
294 | return error; |
295 | |
296 | return 0; |
297 | } |
298 | |
299 | static int nfsd_users = 0; |
300 | |
301 | static int nfsd_startup_generic(void) |
302 | { |
303 | int ret; |
304 | |
305 | if (nfsd_users++) |
306 | return 0; |
307 | |
308 | ret = nfsd_file_cache_init(); |
309 | if (ret) |
310 | goto dec_users; |
311 | |
312 | ret = nfs4_state_start(); |
313 | if (ret) |
314 | goto out_file_cache; |
315 | return 0; |
316 | |
317 | out_file_cache: |
318 | nfsd_file_cache_shutdown(); |
319 | dec_users: |
320 | nfsd_users--; |
321 | return ret; |
322 | } |
323 | |
324 | static void nfsd_shutdown_generic(void) |
325 | { |
326 | if (--nfsd_users) |
327 | return; |
328 | |
329 | nfs4_state_shutdown(); |
330 | nfsd_file_cache_shutdown(); |
331 | } |
332 | |
333 | static bool nfsd_needs_lockd(struct nfsd_net *nn) |
334 | { |
335 | return nfsd_vers(nn, vers: 2, change: NFSD_TEST) || nfsd_vers(nn, vers: 3, change: NFSD_TEST); |
336 | } |
337 | |
338 | /** |
339 | * nfsd_copy_write_verifier - Atomically copy a write verifier |
340 | * @verf: buffer in which to receive the verifier cookie |
341 | * @nn: NFS net namespace |
342 | * |
343 | * This function provides a wait-free mechanism for copying the |
344 | * namespace's write verifier without tearing it. |
345 | */ |
346 | void nfsd_copy_write_verifier(__be32 verf[2], struct nfsd_net *nn) |
347 | { |
348 | unsigned int seq; |
349 | |
350 | do { |
351 | seq = read_seqbegin(sl: &nn->writeverf_lock); |
352 | memcpy(verf, nn->writeverf, sizeof(nn->writeverf)); |
353 | } while (read_seqretry(sl: &nn->writeverf_lock, start: seq)); |
354 | } |
355 | |
356 | static void nfsd_reset_write_verifier_locked(struct nfsd_net *nn) |
357 | { |
358 | struct timespec64 now; |
359 | u64 verf; |
360 | |
361 | /* |
362 | * Because the time value is hashed, y2038 time_t overflow |
363 | * is irrelevant in this usage. |
364 | */ |
365 | ktime_get_raw_ts64(ts: &now); |
366 | verf = siphash_2u64(a: now.tv_sec, b: now.tv_nsec, key: &nn->siphash_key); |
367 | memcpy(nn->writeverf, &verf, sizeof(nn->writeverf)); |
368 | } |
369 | |
370 | /** |
371 | * nfsd_reset_write_verifier - Generate a new write verifier |
372 | * @nn: NFS net namespace |
373 | * |
374 | * This function updates the ->writeverf field of @nn. This field |
375 | * contains an opaque cookie that, according to Section 18.32.3 of |
376 | * RFC 8881, "the client can use to determine whether a server has |
377 | * changed instance state (e.g., server restart) between a call to |
378 | * WRITE and a subsequent call to either WRITE or COMMIT. This |
379 | * cookie MUST be unchanged during a single instance of the NFSv4.1 |
380 | * server and MUST be unique between instances of the NFSv4.1 |
381 | * server." |
382 | */ |
383 | void nfsd_reset_write_verifier(struct nfsd_net *nn) |
384 | { |
385 | write_seqlock(sl: &nn->writeverf_lock); |
386 | nfsd_reset_write_verifier_locked(nn); |
387 | write_sequnlock(sl: &nn->writeverf_lock); |
388 | } |
389 | |
390 | /* |
391 | * Crank up a set of per-namespace resources for a new NFSD instance, |
392 | * including lockd, a duplicate reply cache, an open file cache |
393 | * instance, and a cache of NFSv4 state objects. |
394 | */ |
395 | static int nfsd_startup_net(struct net *net, const struct cred *cred) |
396 | { |
397 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
398 | int ret; |
399 | |
400 | if (nn->nfsd_net_up) |
401 | return 0; |
402 | |
403 | ret = nfsd_startup_generic(); |
404 | if (ret) |
405 | return ret; |
406 | ret = nfsd_init_socks(net, cred); |
407 | if (ret) |
408 | goto out_socks; |
409 | |
410 | if (nfsd_needs_lockd(nn) && !nn->lockd_up) { |
411 | ret = lockd_up(net, cred); |
412 | if (ret) |
413 | goto out_socks; |
414 | nn->lockd_up = true; |
415 | } |
416 | |
417 | ret = nfsd_file_cache_start_net(net); |
418 | if (ret) |
419 | goto out_lockd; |
420 | |
421 | ret = nfsd_reply_cache_init(nn); |
422 | if (ret) |
423 | goto out_filecache; |
424 | |
425 | ret = nfs4_state_start_net(net); |
426 | if (ret) |
427 | goto out_reply_cache; |
428 | |
429 | #ifdef CONFIG_NFSD_V4_2_INTER_SSC |
430 | nfsd4_ssc_init_umount_work(nn); |
431 | #endif |
432 | nn->nfsd_net_up = true; |
433 | return 0; |
434 | |
435 | out_reply_cache: |
436 | nfsd_reply_cache_shutdown(nn); |
437 | out_filecache: |
438 | nfsd_file_cache_shutdown_net(net); |
439 | out_lockd: |
440 | if (nn->lockd_up) { |
441 | lockd_down(net); |
442 | nn->lockd_up = false; |
443 | } |
444 | out_socks: |
445 | nfsd_shutdown_generic(); |
446 | return ret; |
447 | } |
448 | |
449 | static void nfsd_shutdown_net(struct net *net) |
450 | { |
451 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
452 | |
453 | nfs4_state_shutdown_net(net); |
454 | nfsd_reply_cache_shutdown(nn); |
455 | nfsd_file_cache_shutdown_net(net); |
456 | if (nn->lockd_up) { |
457 | lockd_down(net); |
458 | nn->lockd_up = false; |
459 | } |
460 | nn->nfsd_net_up = false; |
461 | nfsd_shutdown_generic(); |
462 | } |
463 | |
464 | static DEFINE_SPINLOCK(nfsd_notifier_lock); |
465 | static int nfsd_inetaddr_event(struct notifier_block *this, unsigned long event, |
466 | void *ptr) |
467 | { |
468 | struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; |
469 | struct net_device *dev = ifa->ifa_dev->dev; |
470 | struct net *net = dev_net(dev); |
471 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
472 | struct sockaddr_in sin; |
473 | |
474 | if (event != NETDEV_DOWN || !nn->nfsd_serv) |
475 | goto out; |
476 | |
477 | spin_lock(lock: &nfsd_notifier_lock); |
478 | if (nn->nfsd_serv) { |
479 | dprintk("nfsd_inetaddr_event: removed %pI4\n", &ifa->ifa_local); |
480 | sin.sin_family = AF_INET; |
481 | sin.sin_addr.s_addr = ifa->ifa_local; |
482 | svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin); |
483 | } |
484 | spin_unlock(lock: &nfsd_notifier_lock); |
485 | |
486 | out: |
487 | return NOTIFY_DONE; |
488 | } |
489 | |
490 | static struct notifier_block nfsd_inetaddr_notifier = { |
491 | .notifier_call = nfsd_inetaddr_event, |
492 | }; |
493 | |
494 | #if IS_ENABLED(CONFIG_IPV6) |
495 | static int nfsd_inet6addr_event(struct notifier_block *this, |
496 | unsigned long event, void *ptr) |
497 | { |
498 | struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; |
499 | struct net_device *dev = ifa->idev->dev; |
500 | struct net *net = dev_net(dev); |
501 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
502 | struct sockaddr_in6 sin6; |
503 | |
504 | if (event != NETDEV_DOWN || !nn->nfsd_serv) |
505 | goto out; |
506 | |
507 | spin_lock(lock: &nfsd_notifier_lock); |
508 | if (nn->nfsd_serv) { |
509 | dprintk("nfsd_inet6addr_event: removed %pI6\n", &ifa->addr); |
510 | sin6.sin6_family = AF_INET6; |
511 | sin6.sin6_addr = ifa->addr; |
512 | if (ipv6_addr_type(addr: &sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL) |
513 | sin6.sin6_scope_id = ifa->idev->dev->ifindex; |
514 | svc_age_temp_xprts_now(nn->nfsd_serv, (struct sockaddr *)&sin6); |
515 | } |
516 | spin_unlock(lock: &nfsd_notifier_lock); |
517 | |
518 | out: |
519 | return NOTIFY_DONE; |
520 | } |
521 | |
522 | static struct notifier_block nfsd_inet6addr_notifier = { |
523 | .notifier_call = nfsd_inet6addr_event, |
524 | }; |
525 | #endif |
526 | |
527 | /* Only used under nfsd_mutex, so this atomic may be overkill: */ |
528 | static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0); |
529 | |
530 | /** |
531 | * nfsd_destroy_serv - tear down NFSD's svc_serv for a namespace |
532 | * @net: network namespace the NFS service is associated with |
533 | */ |
534 | void nfsd_destroy_serv(struct net *net) |
535 | { |
536 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
537 | struct svc_serv *serv = nn->nfsd_serv; |
538 | |
539 | spin_lock(lock: &nfsd_notifier_lock); |
540 | nn->nfsd_serv = NULL; |
541 | spin_unlock(lock: &nfsd_notifier_lock); |
542 | |
543 | /* check if the notifier still has clients */ |
544 | if (atomic_dec_return(v: &nfsd_notifier_refcount) == 0) { |
545 | unregister_inetaddr_notifier(nb: &nfsd_inetaddr_notifier); |
546 | #if IS_ENABLED(CONFIG_IPV6) |
547 | unregister_inet6addr_notifier(nb: &nfsd_inet6addr_notifier); |
548 | #endif |
549 | } |
550 | |
551 | svc_xprt_destroy_all(serv, net); |
552 | |
553 | /* |
554 | * write_ports can create the server without actually starting |
555 | * any threads--if we get shut down before any threads are |
556 | * started, then nfsd_destroy_serv will be run before any of this |
557 | * other initialization has been done except the rpcb information. |
558 | */ |
559 | svc_rpcb_cleanup(serv, net); |
560 | if (!nn->nfsd_net_up) |
561 | return; |
562 | |
563 | nfsd_shutdown_net(net); |
564 | nfsd_export_flush(net); |
565 | svc_destroy(svcp: &serv); |
566 | } |
567 | |
568 | void nfsd_reset_versions(struct nfsd_net *nn) |
569 | { |
570 | int i; |
571 | |
572 | for (i = 0; i < NFSD_NRVERS; i++) |
573 | if (nfsd_vers(nn, vers: i, change: NFSD_TEST)) |
574 | return; |
575 | |
576 | for (i = 0; i < NFSD_NRVERS; i++) |
577 | if (i != 4) |
578 | nfsd_vers(nn, vers: i, change: NFSD_SET); |
579 | else { |
580 | int minor = 0; |
581 | while (nfsd_minorversion(nn, minorversion: minor, change: NFSD_SET) >= 0) |
582 | minor++; |
583 | } |
584 | } |
585 | |
586 | /* |
587 | * Each session guarantees a negotiated per slot memory cache for replies |
588 | * which in turn consumes memory beyond the v2/v3/v4.0 server. A dedicated |
589 | * NFSv4.1 server might want to use more memory for a DRC than a machine |
590 | * with mutiple services. |
591 | * |
592 | * Impose a hard limit on the number of pages for the DRC which varies |
593 | * according to the machines free pages. This is of course only a default. |
594 | * |
595 | * For now this is a #defined shift which could be under admin control |
596 | * in the future. |
597 | */ |
598 | static void set_max_drc(void) |
599 | { |
600 | #define NFSD_DRC_SIZE_SHIFT 7 |
601 | nfsd_drc_max_mem = (nr_free_buffer_pages() |
602 | >> NFSD_DRC_SIZE_SHIFT) * PAGE_SIZE; |
603 | nfsd_drc_mem_used = 0; |
604 | dprintk("%s nfsd_drc_max_mem %lu \n", __func__, nfsd_drc_max_mem); |
605 | } |
606 | |
607 | static int nfsd_get_default_max_blksize(void) |
608 | { |
609 | struct sysinfo i; |
610 | unsigned long long target; |
611 | unsigned long ret; |
612 | |
613 | si_meminfo(val: &i); |
614 | target = (i.totalram - i.totalhigh) << PAGE_SHIFT; |
615 | /* |
616 | * Aim for 1/4096 of memory per thread This gives 1MB on 4Gig |
617 | * machines, but only uses 32K on 128M machines. Bottom out at |
618 | * 8K on 32M and smaller. Of course, this is only a default. |
619 | */ |
620 | target >>= 12; |
621 | |
622 | ret = NFSSVC_MAXBLKSIZE; |
623 | while (ret > target && ret >= 8*1024*2) |
624 | ret /= 2; |
625 | return ret; |
626 | } |
627 | |
628 | void nfsd_shutdown_threads(struct net *net) |
629 | { |
630 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
631 | struct svc_serv *serv; |
632 | |
633 | mutex_lock(&nfsd_mutex); |
634 | serv = nn->nfsd_serv; |
635 | if (serv == NULL) { |
636 | mutex_unlock(lock: &nfsd_mutex); |
637 | return; |
638 | } |
639 | |
640 | /* Kill outstanding nfsd threads */ |
641 | svc_set_num_threads(serv, NULL, 0); |
642 | nfsd_destroy_serv(net); |
643 | mutex_unlock(lock: &nfsd_mutex); |
644 | } |
645 | |
646 | bool i_am_nfsd(void) |
647 | { |
648 | return kthread_func(current) == nfsd; |
649 | } |
650 | |
651 | int nfsd_create_serv(struct net *net) |
652 | { |
653 | int error; |
654 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
655 | struct svc_serv *serv; |
656 | |
657 | WARN_ON(!mutex_is_locked(&nfsd_mutex)); |
658 | if (nn->nfsd_serv) |
659 | return 0; |
660 | |
661 | if (nfsd_max_blksize == 0) |
662 | nfsd_max_blksize = nfsd_get_default_max_blksize(); |
663 | nfsd_reset_versions(nn); |
664 | serv = svc_create_pooled(prog: &nfsd_program, stats: &nn->nfsd_svcstats, |
665 | bufsize: nfsd_max_blksize, threadfn: nfsd); |
666 | if (serv == NULL) |
667 | return -ENOMEM; |
668 | |
669 | serv->sv_maxconn = nn->max_connections; |
670 | error = svc_bind(serv, net); |
671 | if (error < 0) { |
672 | svc_destroy(svcp: &serv); |
673 | return error; |
674 | } |
675 | spin_lock(lock: &nfsd_notifier_lock); |
676 | nn->nfsd_info.mutex = &nfsd_mutex; |
677 | nn->nfsd_serv = serv; |
678 | spin_unlock(lock: &nfsd_notifier_lock); |
679 | |
680 | set_max_drc(); |
681 | /* check if the notifier is already set */ |
682 | if (atomic_inc_return(v: &nfsd_notifier_refcount) == 1) { |
683 | register_inetaddr_notifier(nb: &nfsd_inetaddr_notifier); |
684 | #if IS_ENABLED(CONFIG_IPV6) |
685 | register_inet6addr_notifier(nb: &nfsd_inet6addr_notifier); |
686 | #endif |
687 | } |
688 | nfsd_reset_write_verifier(nn); |
689 | return 0; |
690 | } |
691 | |
692 | int nfsd_nrpools(struct net *net) |
693 | { |
694 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
695 | |
696 | if (nn->nfsd_serv == NULL) |
697 | return 0; |
698 | else |
699 | return nn->nfsd_serv->sv_nrpools; |
700 | } |
701 | |
702 | int nfsd_get_nrthreads(int n, int *nthreads, struct net *net) |
703 | { |
704 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
705 | struct svc_serv *serv = nn->nfsd_serv; |
706 | int i; |
707 | |
708 | if (serv) |
709 | for (i = 0; i < serv->sv_nrpools && i < n; i++) |
710 | nthreads[i] = atomic_read(v: &serv->sv_pools[i].sp_nrthreads); |
711 | return 0; |
712 | } |
713 | |
714 | int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) |
715 | { |
716 | int i = 0; |
717 | int tot = 0; |
718 | int err = 0; |
719 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
720 | |
721 | WARN_ON(!mutex_is_locked(&nfsd_mutex)); |
722 | |
723 | if (nn->nfsd_serv == NULL || n <= 0) |
724 | return 0; |
725 | |
726 | if (n > nn->nfsd_serv->sv_nrpools) |
727 | n = nn->nfsd_serv->sv_nrpools; |
728 | |
729 | /* enforce a global maximum number of threads */ |
730 | tot = 0; |
731 | for (i = 0; i < n; i++) { |
732 | nthreads[i] = min(nthreads[i], NFSD_MAXSERVS); |
733 | tot += nthreads[i]; |
734 | } |
735 | if (tot > NFSD_MAXSERVS) { |
736 | /* total too large: scale down requested numbers */ |
737 | for (i = 0; i < n && tot > 0; i++) { |
738 | int new = nthreads[i] * NFSD_MAXSERVS / tot; |
739 | tot -= (nthreads[i] - new); |
740 | nthreads[i] = new; |
741 | } |
742 | for (i = 0; i < n && tot > 0; i++) { |
743 | nthreads[i]--; |
744 | tot--; |
745 | } |
746 | } |
747 | |
748 | /* |
749 | * There must always be a thread in pool 0; the admin |
750 | * can't shut down NFS completely using pool_threads. |
751 | */ |
752 | if (nthreads[0] == 0) |
753 | nthreads[0] = 1; |
754 | |
755 | /* apply the new numbers */ |
756 | for (i = 0; i < n; i++) { |
757 | err = svc_set_num_threads(nn->nfsd_serv, |
758 | &nn->nfsd_serv->sv_pools[i], |
759 | nthreads[i]); |
760 | if (err) |
761 | break; |
762 | } |
763 | return err; |
764 | } |
765 | |
766 | /* |
767 | * Adjust the number of threads and return the new number of threads. |
768 | * This is also the function that starts the server if necessary, if |
769 | * this is the first time nrservs is nonzero. |
770 | */ |
771 | int |
772 | nfsd_svc(int nrservs, struct net *net, const struct cred *cred) |
773 | { |
774 | int error; |
775 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
776 | struct svc_serv *serv; |
777 | |
778 | mutex_lock(&nfsd_mutex); |
779 | dprintk("nfsd: creating service\n"); |
780 | |
781 | nrservs = max(nrservs, 0); |
782 | nrservs = min(nrservs, NFSD_MAXSERVS); |
783 | error = 0; |
784 | |
785 | if (nrservs == 0 && nn->nfsd_serv == NULL) |
786 | goto out; |
787 | |
788 | strscpy(nn->nfsd_name, utsname()->nodename, |
789 | sizeof(nn->nfsd_name)); |
790 | |
791 | error = nfsd_create_serv(net); |
792 | if (error) |
793 | goto out; |
794 | serv = nn->nfsd_serv; |
795 | |
796 | error = nfsd_startup_net(net, cred); |
797 | if (error) |
798 | goto out_put; |
799 | error = svc_set_num_threads(serv, NULL, nrservs); |
800 | if (error) |
801 | goto out_put; |
802 | error = serv->sv_nrthreads; |
803 | out_put: |
804 | if (serv->sv_nrthreads == 0) |
805 | nfsd_destroy_serv(net); |
806 | out: |
807 | mutex_unlock(lock: &nfsd_mutex); |
808 | return error; |
809 | } |
810 | |
811 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) |
812 | static bool |
813 | nfsd_support_acl_version(int vers) |
814 | { |
815 | if (vers >= NFSD_ACL_MINVERS && vers < NFSD_ACL_NRVERS) |
816 | return nfsd_acl_version[vers] != NULL; |
817 | return false; |
818 | } |
819 | |
820 | static int |
821 | nfsd_acl_rpcbind_set(struct net *net, const struct svc_program *progp, |
822 | u32 version, int family, unsigned short proto, |
823 | unsigned short port) |
824 | { |
825 | if (!nfsd_support_acl_version(vers: version) || |
826 | !nfsd_vers(nn: net_generic(net, id: nfsd_net_id), vers: version, change: NFSD_TEST)) |
827 | return 0; |
828 | return svc_generic_rpcbind_set(net, progp, version, family, |
829 | proto, port); |
830 | } |
831 | |
832 | static __be32 |
833 | nfsd_acl_init_request(struct svc_rqst *rqstp, |
834 | const struct svc_program *progp, |
835 | struct svc_process_info *ret) |
836 | { |
837 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), id: nfsd_net_id); |
838 | int i; |
839 | |
840 | if (likely(nfsd_support_acl_version(rqstp->rq_vers) && |
841 | nfsd_vers(nn, rqstp->rq_vers, NFSD_TEST))) |
842 | return svc_generic_init_request(rqstp, progp, procinfo: ret); |
843 | |
844 | ret->mismatch.lovers = NFSD_ACL_NRVERS; |
845 | for (i = NFSD_ACL_MINVERS; i < NFSD_ACL_NRVERS; i++) { |
846 | if (nfsd_support_acl_version(vers: rqstp->rq_vers) && |
847 | nfsd_vers(nn, vers: i, change: NFSD_TEST)) { |
848 | ret->mismatch.lovers = i; |
849 | break; |
850 | } |
851 | } |
852 | if (ret->mismatch.lovers == NFSD_ACL_NRVERS) |
853 | return rpc_prog_unavail; |
854 | ret->mismatch.hivers = NFSD_ACL_MINVERS; |
855 | for (i = NFSD_ACL_NRVERS - 1; i >= NFSD_ACL_MINVERS; i--) { |
856 | if (nfsd_support_acl_version(vers: rqstp->rq_vers) && |
857 | nfsd_vers(nn, vers: i, change: NFSD_TEST)) { |
858 | ret->mismatch.hivers = i; |
859 | break; |
860 | } |
861 | } |
862 | return rpc_prog_mismatch; |
863 | } |
864 | #endif |
865 | |
866 | static int |
867 | nfsd_rpcbind_set(struct net *net, const struct svc_program *progp, |
868 | u32 version, int family, unsigned short proto, |
869 | unsigned short port) |
870 | { |
871 | if (!nfsd_vers(nn: net_generic(net, id: nfsd_net_id), vers: version, change: NFSD_TEST)) |
872 | return 0; |
873 | return svc_generic_rpcbind_set(net, progp, version, family, |
874 | proto, port); |
875 | } |
876 | |
877 | static __be32 |
878 | nfsd_init_request(struct svc_rqst *rqstp, |
879 | const struct svc_program *progp, |
880 | struct svc_process_info *ret) |
881 | { |
882 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), id: nfsd_net_id); |
883 | int i; |
884 | |
885 | if (likely(nfsd_vers(nn, rqstp->rq_vers, NFSD_TEST))) |
886 | return svc_generic_init_request(rqstp, progp, procinfo: ret); |
887 | |
888 | ret->mismatch.lovers = NFSD_NRVERS; |
889 | for (i = NFSD_MINVERS; i < NFSD_NRVERS; i++) { |
890 | if (nfsd_vers(nn, vers: i, change: NFSD_TEST)) { |
891 | ret->mismatch.lovers = i; |
892 | break; |
893 | } |
894 | } |
895 | if (ret->mismatch.lovers == NFSD_NRVERS) |
896 | return rpc_prog_unavail; |
897 | ret->mismatch.hivers = NFSD_MINVERS; |
898 | for (i = NFSD_NRVERS - 1; i >= NFSD_MINVERS; i--) { |
899 | if (nfsd_vers(nn, vers: i, change: NFSD_TEST)) { |
900 | ret->mismatch.hivers = i; |
901 | break; |
902 | } |
903 | } |
904 | return rpc_prog_mismatch; |
905 | } |
906 | |
907 | /* |
908 | * This is the NFS server kernel thread |
909 | */ |
910 | static int |
911 | nfsd(void *vrqstp) |
912 | { |
913 | struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp; |
914 | struct svc_xprt *perm_sock = list_entry(rqstp->rq_server->sv_permsocks.next, typeof(struct svc_xprt), xpt_list); |
915 | struct net *net = perm_sock->xpt_net; |
916 | struct nfsd_net *nn = net_generic(net, id: nfsd_net_id); |
917 | |
918 | /* At this point, the thread shares current->fs |
919 | * with the init process. We need to create files with the |
920 | * umask as defined by the client instead of init's umask. */ |
921 | if (unshare_fs_struct() < 0) { |
922 | printk("Unable to start nfsd thread: out of memory\n"); |
923 | goto out; |
924 | } |
925 | |
926 | current->fs->umask = 0; |
927 | |
928 | atomic_inc(v: &nfsd_th_cnt); |
929 | |
930 | set_freezable(); |
931 | |
932 | /* |
933 | * The main request loop |
934 | */ |
935 | while (!svc_thread_should_stop(rqstp)) { |
936 | /* Update sv_maxconn if it has changed */ |
937 | rqstp->rq_server->sv_maxconn = nn->max_connections; |
938 | |
939 | svc_recv(rqstp); |
940 | |
941 | nfsd_file_net_dispose(nn); |
942 | } |
943 | |
944 | atomic_dec(v: &nfsd_th_cnt); |
945 | |
946 | out: |
947 | /* Release the thread */ |
948 | svc_exit_thread(rqstp); |
949 | return 0; |
950 | } |
951 | |
952 | /** |
953 | * nfsd_dispatch - Process an NFS or NFSACL Request |
954 | * @rqstp: incoming request |
955 | * |
956 | * This RPC dispatcher integrates the NFS server's duplicate reply cache. |
957 | * |
958 | * Return values: |
959 | * %0: Processing complete; do not send a Reply |
960 | * %1: Processing complete; send Reply in rqstp->rq_res |
961 | */ |
962 | int nfsd_dispatch(struct svc_rqst *rqstp) |
963 | { |
964 | const struct svc_procedure *proc = rqstp->rq_procinfo; |
965 | __be32 *statp = rqstp->rq_accept_statp; |
966 | struct nfsd_cacherep *rp; |
967 | unsigned int start, len; |
968 | __be32 *nfs_reply; |
969 | |
970 | /* |
971 | * Give the xdr decoder a chance to change this if it wants |
972 | * (necessary in the NFSv4.0 compound case) |
973 | */ |
974 | rqstp->rq_cachetype = proc->pc_cachetype; |
975 | |
976 | /* |
977 | * ->pc_decode advances the argument stream past the NFS |
978 | * Call header, so grab the header's starting location and |
979 | * size now for the call to nfsd_cache_lookup(). |
980 | */ |
981 | start = xdr_stream_pos(xdr: &rqstp->rq_arg_stream); |
982 | len = xdr_stream_remaining(xdr: &rqstp->rq_arg_stream); |
983 | if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream)) |
984 | goto out_decode_err; |
985 | |
986 | /* |
987 | * Release rq_status_counter setting it to an odd value after the rpc |
988 | * request has been properly parsed. rq_status_counter is used to |
989 | * notify the consumers if the rqstp fields are stable |
990 | * (rq_status_counter is odd) or not meaningful (rq_status_counter |
991 | * is even). |
992 | */ |
993 | smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter | 1); |
994 | |
995 | rp = NULL; |
996 | switch (nfsd_cache_lookup(rqstp, start, len, cacherep: &rp)) { |
997 | case RC_DOIT: |
998 | break; |
999 | case RC_REPLY: |
1000 | goto out_cached_reply; |
1001 | case RC_DROPIT: |
1002 | goto out_dropit; |
1003 | } |
1004 | |
1005 | nfs_reply = xdr_inline_decode(xdr: &rqstp->rq_res_stream, nbytes: 0); |
1006 | *statp = proc->pc_func(rqstp); |
1007 | if (test_bit(RQ_DROPME, &rqstp->rq_flags)) |
1008 | goto out_update_drop; |
1009 | |
1010 | if (!proc->pc_encode(rqstp, &rqstp->rq_res_stream)) |
1011 | goto out_encode_err; |
1012 | |
1013 | /* |
1014 | * Release rq_status_counter setting it to an even value after the rpc |
1015 | * request has been properly processed. |
1016 | */ |
1017 | smp_store_release(&rqstp->rq_status_counter, rqstp->rq_status_counter + 1); |
1018 | |
1019 | nfsd_cache_update(rqstp, rp, cachetype: rqstp->rq_cachetype, statp: nfs_reply); |
1020 | out_cached_reply: |
1021 | return 1; |
1022 | |
1023 | out_decode_err: |
1024 | trace_nfsd_garbage_args_err(rqstp); |
1025 | *statp = rpc_garbage_args; |
1026 | return 1; |
1027 | |
1028 | out_update_drop: |
1029 | nfsd_cache_update(rqstp, rp, cachetype: RC_NOCACHE, NULL); |
1030 | out_dropit: |
1031 | return 0; |
1032 | |
1033 | out_encode_err: |
1034 | trace_nfsd_cant_encode_err(rqstp); |
1035 | nfsd_cache_update(rqstp, rp, cachetype: RC_NOCACHE, NULL); |
1036 | *statp = rpc_system_err; |
1037 | return 1; |
1038 | } |
1039 | |
1040 | /** |
1041 | * nfssvc_decode_voidarg - Decode void arguments |
1042 | * @rqstp: Server RPC transaction context |
1043 | * @xdr: XDR stream positioned at arguments to decode |
1044 | * |
1045 | * Return values: |
1046 | * %false: Arguments were not valid |
1047 | * %true: Decoding was successful |
1048 | */ |
1049 | bool nfssvc_decode_voidarg(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
1050 | { |
1051 | return true; |
1052 | } |
1053 | |
1054 | /** |
1055 | * nfssvc_encode_voidres - Encode void results |
1056 | * @rqstp: Server RPC transaction context |
1057 | * @xdr: XDR stream into which to encode results |
1058 | * |
1059 | * Return values: |
1060 | * %false: Local error while encoding |
1061 | * %true: Encoding was successful |
1062 | */ |
1063 | bool nfssvc_encode_voidres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
1064 | { |
1065 | return true; |
1066 | } |
1067 | |
1068 | int nfsd_pool_stats_open(struct inode *inode, struct file *file) |
1069 | { |
1070 | struct nfsd_net *nn = net_generic(net: inode->i_sb->s_fs_info, id: nfsd_net_id); |
1071 | |
1072 | return svc_pool_stats_open(si: &nn->nfsd_info, file); |
1073 | } |
1074 |
Definitions
- nfsd_th_cnt
- nfsd_mutex
- nfsd_drc_lock
- nfsd_drc_max_mem
- nfsd_drc_mem_used
- nfsd_acl_version
- nfsd_acl_program
- nfsd_version
- nfsd_program
- nfsd_support_version
- nfsd_alloc_versions
- nfsd_alloc_minorversions
- nfsd_netns_free_versions
- nfsd_netns_init_versions
- nfsd_vers
- nfsd_adjust_nfsd_versions4
- nfsd_minorversion
- nfsd_nrthreads
- nfsd_init_socks
- nfsd_users
- nfsd_startup_generic
- nfsd_shutdown_generic
- nfsd_needs_lockd
- nfsd_copy_write_verifier
- nfsd_reset_write_verifier_locked
- nfsd_reset_write_verifier
- nfsd_startup_net
- nfsd_shutdown_net
- nfsd_notifier_lock
- nfsd_inetaddr_event
- nfsd_inetaddr_notifier
- nfsd_inet6addr_event
- nfsd_inet6addr_notifier
- nfsd_notifier_refcount
- nfsd_destroy_serv
- nfsd_reset_versions
- set_max_drc
- nfsd_get_default_max_blksize
- nfsd_shutdown_threads
- i_am_nfsd
- nfsd_create_serv
- nfsd_nrpools
- nfsd_get_nrthreads
- nfsd_set_nrthreads
- nfsd_svc
- nfsd_support_acl_version
- nfsd_acl_rpcbind_set
- nfsd_acl_init_request
- nfsd_rpcbind_set
- nfsd_init_request
- nfsd
- nfsd_dispatch
- nfssvc_decode_voidarg
- nfssvc_encode_voidres
Improve your Profiling and Debugging skills
Find out more