Commit | Line | Data |
---|---|---|
09c434b8 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1da177e4 LT |
2 | /* |
3 | * linux/fs/lockd/svc.c | |
4 | * | |
5 | * This is the central lockd service. | |
6 | * | |
7 | * FIXME: Separate the lockd NFS server functionality from the lockd NFS | |
8 | * client functionality. Oh why didn't Sun create two separate | |
9 | * services in the first place? | |
10 | * | |
11 | * Authors: Olaf Kirch (okir@monad.swb.de) | |
12 | * | |
13 | * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> | |
14 | */ | |
15 | ||
1da177e4 LT |
16 | #include <linux/module.h> |
17 | #include <linux/init.h> | |
18 | #include <linux/sysctl.h> | |
19 | #include <linux/moduleparam.h> | |
20 | ||
3f07c014 | 21 | #include <linux/sched/signal.h> |
1da177e4 LT |
22 | #include <linux/errno.h> |
23 | #include <linux/in.h> | |
24 | #include <linux/uio.h> | |
1da177e4 | 25 | #include <linux/smp.h> |
353ab6e9 | 26 | #include <linux/mutex.h> |
d751a7cd | 27 | #include <linux/kthread.h> |
83144186 | 28 | #include <linux/freezer.h> |
0751ddf7 | 29 | #include <linux/inetdevice.h> |
1da177e4 LT |
30 | |
31 | #include <linux/sunrpc/types.h> | |
32 | #include <linux/sunrpc/stats.h> | |
33 | #include <linux/sunrpc/clnt.h> | |
34 | #include <linux/sunrpc/svc.h> | |
35 | #include <linux/sunrpc/svcsock.h> | |
0751ddf7 | 36 | #include <linux/sunrpc/svc_xprt.h> |
24e36663 | 37 | #include <net/ip.h> |
0751ddf7 SM |
38 | #include <net/addrconf.h> |
39 | #include <net/ipv6.h> | |
1da177e4 LT |
40 | #include <linux/lockd/lockd.h> |
41 | #include <linux/nfs.h> | |
42 | ||
a9c5d73a | 43 | #include "netns.h" |
d68e3c4a | 44 | #include "procfs.h" |
a9c5d73a | 45 | |
1da177e4 LT |
46 | #define NLMDBG_FACILITY NLMDBG_SVC |
47 | #define LOCKD_BUFSIZE (1024 + NLMSVC_XDRSIZE) | |
48 | #define ALLOWED_SIGS (sigmask(SIGKILL)) | |
49 | ||
50 | static struct svc_program nlmsvc_program; | |
51 | ||
2a297450 | 52 | const struct nlmsvc_binding *nlmsvc_ops; |
2de59872 | 53 | EXPORT_SYMBOL_GPL(nlmsvc_ops); |
1da177e4 | 54 | |
353ab6e9 | 55 | static DEFINE_MUTEX(nlmsvc_mutex); |
1da177e4 | 56 | static unsigned int nlmsvc_users; |
2840fe86 | 57 | static struct svc_serv *nlmsvc_serv; |
1da177e4 LT |
58 | unsigned long nlmsvc_timeout; |
59 | ||
c7d03a00 | 60 | unsigned int lockd_net_id; |
a9c5d73a | 61 | |
1da177e4 LT |
62 | /* |
63 | * These can be set at insmod time (useful for NFS as root filesystem), | |
64 | * and also changed through the sysctl interface. -- Jamie Lokier, Aug 2003 | |
65 | */ | |
66 | static unsigned long nlm_grace_period; | |
67 | static unsigned long nlm_timeout = LOCKD_DFLT_TIMEO; | |
68 | static int nlm_udpport, nlm_tcpport; | |
69 | ||
c72a476b JL |
70 | /* RLIM_NOFILE defaults to 1024. That seems like a reasonable default here. */ |
71 | static unsigned int nlm_max_connections = 1024; | |
72 | ||
1da177e4 LT |
73 | /* |
74 | * Constants needed for the sysctl interface. | |
75 | */ | |
76 | static const unsigned long nlm_grace_period_min = 0; | |
77 | static const unsigned long nlm_grace_period_max = 240; | |
78 | static const unsigned long nlm_timeout_min = 3; | |
79 | static const unsigned long nlm_timeout_max = 20; | |
80 | static const int nlm_port_min = 0, nlm_port_max = 65535; | |
81 | ||
90d5b180 | 82 | #ifdef CONFIG_SYSCTL |
1da177e4 | 83 | static struct ctl_table_header * nlm_sysctl_table; |
90d5b180 | 84 | #endif |
1da177e4 | 85 | |
9a8db97e | 86 | static unsigned long get_lockd_grace_period(void) |
1da177e4 | 87 | { |
1da177e4 LT |
88 | /* Note: nlm_timeout should always be nonzero */ |
89 | if (nlm_grace_period) | |
9a8db97e | 90 | return roundup(nlm_grace_period, nlm_timeout) * HZ; |
1da177e4 | 91 | else |
9a8db97e ME |
92 | return nlm_timeout * 5 * HZ; |
93 | } | |
94 | ||
08d44a35 | 95 | static void grace_ender(struct work_struct *grace) |
9a8db97e | 96 | { |
ea44463f | 97 | struct delayed_work *dwork = to_delayed_work(grace); |
08d44a35 SK |
98 | struct lockd_net *ln = container_of(dwork, struct lockd_net, |
99 | grace_period_end); | |
100 | ||
101 | locks_end_grace(&ln->lockd_manager); | |
1da177e4 LT |
102 | } |
103 | ||
5ccb0066 | 104 | static void set_grace_period(struct net *net) |
1da177e4 | 105 | { |
af558e33 | 106 | unsigned long grace_period = get_lockd_grace_period(); |
5ccb0066 | 107 | struct lockd_net *ln = net_generic(net, lockd_net_id); |
c8ab5f2a | 108 | |
5ccb0066 | 109 | locks_start_grace(net, &ln->lockd_manager); |
66547b02 SK |
110 | cancel_delayed_work_sync(&ln->grace_period_end); |
111 | schedule_delayed_work(&ln->grace_period_end, grace_period); | |
1da177e4 LT |
112 | } |
113 | ||
89996df4 BF |
114 | static void restart_grace(void) |
115 | { | |
116 | if (nlmsvc_ops) { | |
5ccb0066 SK |
117 | struct net *net = &init_net; |
118 | struct lockd_net *ln = net_generic(net, lockd_net_id); | |
66547b02 SK |
119 | |
120 | cancel_delayed_work_sync(&ln->grace_period_end); | |
08d44a35 | 121 | locks_end_grace(&ln->lockd_manager); |
89996df4 | 122 | nlmsvc_invalidate_all(); |
5ccb0066 | 123 | set_grace_period(net); |
89996df4 BF |
124 | } |
125 | } | |
126 | ||
1da177e4 LT |
127 | /* |
128 | * This is the lockd kernel thread | |
129 | */ | |
d751a7cd JL |
130 | static int |
131 | lockd(void *vrqstp) | |
1da177e4 | 132 | { |
5b444cc9 | 133 | int err = 0; |
d751a7cd | 134 | struct svc_rqst *rqstp = vrqstp; |
efda760f BF |
135 | struct net *net = &init_net; |
136 | struct lockd_net *ln = net_generic(net, lockd_net_id); | |
1da177e4 | 137 | |
d751a7cd | 138 | /* try_to_freeze() is called from svc_recv() */ |
83144186 | 139 | set_freezable(); |
1da177e4 | 140 | |
d751a7cd | 141 | /* Allow SIGKILL to tell lockd to drop all of its locks */ |
1da177e4 LT |
142 | allow_signal(SIGKILL); |
143 | ||
1da177e4 LT |
144 | dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n"); |
145 | ||
1da177e4 LT |
146 | /* |
147 | * The main request loop. We don't terminate until the last | |
d751a7cd | 148 | * NFS mount or NFS daemon has gone away. |
1da177e4 | 149 | */ |
d751a7cd | 150 | while (!kthread_should_stop()) { |
1da177e4 | 151 | long timeout = MAX_SCHEDULE_TIMEOUT; |
5216a8e7 | 152 | RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); |
1da177e4 | 153 | |
c72a476b JL |
154 | /* update sv_maxconn if it has changed */ |
155 | rqstp->rq_server->sv_maxconn = nlm_max_connections; | |
156 | ||
1da177e4 LT |
157 | if (signalled()) { |
158 | flush_signals(current); | |
89996df4 | 159 | restart_grace(); |
d751a7cd | 160 | continue; |
1da177e4 LT |
161 | } |
162 | ||
8fafa900 BF |
163 | timeout = nlmsvc_retry_blocked(); |
164 | ||
1da177e4 LT |
165 | /* |
166 | * Find a socket with data available and call its | |
167 | * recvfrom routine. | |
168 | */ | |
6fb2b47f | 169 | err = svc_recv(rqstp, timeout); |
5b444cc9 | 170 | if (err == -EAGAIN || err == -EINTR) |
1da177e4 | 171 | continue; |
ad06e4bd CL |
172 | dprintk("lockd: request from %s\n", |
173 | svc_print_addr(rqstp, buf, sizeof(buf))); | |
1da177e4 | 174 | |
6fb2b47f | 175 | svc_process(rqstp); |
1da177e4 | 176 | } |
2bd61579 | 177 | flush_signals(current); |
d751a7cd JL |
178 | if (nlmsvc_ops) |
179 | nlmsvc_invalidate_all(); | |
180 | nlm_shutdown_hosts(); | |
efda760f BF |
181 | cancel_delayed_work_sync(&ln->grace_period_end); |
182 | locks_end_grace(&ln->lockd_manager); | |
6a4e2527 N |
183 | |
184 | dprintk("lockd_down: service stopped\n"); | |
185 | ||
186 | svc_exit_thread(rqstp); | |
187 | ||
35ce8ae9 | 188 | module_put_and_kthread_exit(0); |
1da177e4 LT |
189 | } |
190 | ||
eb16e907 | 191 | static int create_lockd_listener(struct svc_serv *serv, const char *name, |
c228fa20 | 192 | struct net *net, const int family, |
40373b12 TM |
193 | const unsigned short port, |
194 | const struct cred *cred) | |
d3fe5ea7 CL |
195 | { |
196 | struct svc_xprt *xprt; | |
197 | ||
c228fa20 | 198 | xprt = svc_find_xprt(serv, name, net, family, 0); |
d3fe5ea7 | 199 | if (xprt == NULL) |
c228fa20 | 200 | return svc_create_xprt(serv, name, net, family, port, |
4df493a2 | 201 | SVC_SOCK_DEFAULTS, cred); |
d3fe5ea7 CL |
202 | svc_xprt_put(xprt); |
203 | return 0; | |
204 | } | |
205 | ||
c228fa20 | 206 | static int create_lockd_family(struct svc_serv *serv, struct net *net, |
40373b12 | 207 | const int family, const struct cred *cred) |
eb16e907 CL |
208 | { |
209 | int err; | |
210 | ||
40373b12 TM |
211 | err = create_lockd_listener(serv, "udp", net, family, nlm_udpport, |
212 | cred); | |
eb16e907 CL |
213 | if (err < 0) |
214 | return err; | |
215 | ||
40373b12 TM |
216 | return create_lockd_listener(serv, "tcp", net, family, nlm_tcpport, |
217 | cred); | |
eb16e907 CL |
218 | } |
219 | ||
482fb94e | 220 | /* |
8c3916f4 CL |
221 | * Ensure there are active UDP and TCP listeners for lockd. |
222 | * | |
223 | * Even if we have only TCP NFS mounts and/or TCP NFSDs, some | |
224 | * local services (such as rpc.statd) still require UDP, and | |
225 | * some NFS servers do not yet support NLM over TCP. | |
226 | * | |
227 | * Returns zero if all listeners are available; otherwise a | |
228 | * negative errno value is returned. | |
482fb94e | 229 | */ |
40373b12 TM |
230 | static int make_socks(struct svc_serv *serv, struct net *net, |
231 | const struct cred *cred) | |
24e36663 | 232 | { |
482fb94e | 233 | static int warned; |
0dba7c2a | 234 | int err; |
482fb94e | 235 | |
40373b12 | 236 | err = create_lockd_family(serv, net, PF_INET, cred); |
0dba7c2a CL |
237 | if (err < 0) |
238 | goto out_err; | |
239 | ||
40373b12 | 240 | err = create_lockd_family(serv, net, PF_INET6, cred); |
eb16e907 | 241 | if (err < 0 && err != -EAFNOSUPPORT) |
0dba7c2a CL |
242 | goto out_err; |
243 | ||
244 | warned = 0; | |
245 | return 0; | |
246 | ||
247 | out_err: | |
248 | if (warned++ == 0) | |
7dcf91ec | 249 | printk(KERN_WARNING |
0dba7c2a | 250 | "lockd_up: makesock failed, error=%d\n", err); |
679b033d | 251 | svc_shutdown_net(serv, net); |
87cdd864 | 252 | svc_rpcb_cleanup(serv, net); |
24e36663 N |
253 | return err; |
254 | } | |
255 | ||
40373b12 TM |
256 | static int lockd_up_net(struct svc_serv *serv, struct net *net, |
257 | const struct cred *cred) | |
bb2224df SK |
258 | { |
259 | struct lockd_net *ln = net_generic(net, lockd_net_id); | |
bb2224df SK |
260 | int error; |
261 | ||
786185b5 | 262 | if (ln->nlmsvc_users++) |
bb2224df SK |
263 | return 0; |
264 | ||
dbf9b5d7 | 265 | error = svc_bind(serv, net); |
bb2224df | 266 | if (error) |
dbf9b5d7 | 267 | goto err_bind; |
bb2224df | 268 | |
40373b12 | 269 | error = make_socks(serv, net, cred); |
bb2224df | 270 | if (error < 0) |
7c17705e | 271 | goto err_bind; |
5630f7fa | 272 | set_grace_period(net); |
e919b076 | 273 | dprintk("%s: per-net data created; net=%x\n", __func__, net->ns.inum); |
bb2224df SK |
274 | return 0; |
275 | ||
dbf9b5d7 | 276 | err_bind: |
786185b5 | 277 | ln->nlmsvc_users--; |
bb2224df SK |
278 | return error; |
279 | } | |
280 | ||
4db77695 | 281 | static void lockd_down_net(struct svc_serv *serv, struct net *net) |
bb2224df SK |
282 | { |
283 | struct lockd_net *ln = net_generic(net, lockd_net_id); | |
bb2224df SK |
284 | |
285 | if (ln->nlmsvc_users) { | |
3b64739f SK |
286 | if (--ln->nlmsvc_users == 0) { |
287 | nlm_shutdown_hosts_net(net); | |
3a2b19d1 VA |
288 | cancel_delayed_work_sync(&ln->grace_period_end); |
289 | locks_end_grace(&ln->lockd_manager); | |
bb2224df | 290 | svc_shutdown_net(serv, net); |
87cdd864 | 291 | svc_rpcb_cleanup(serv, net); |
3b64739f | 292 | } |
bb2224df | 293 | } else { |
6b044fba N |
294 | pr_err("%s: no users! net=%x\n", |
295 | __func__, net->ns.inum); | |
bb2224df SK |
296 | BUG(); |
297 | } | |
298 | } | |
299 | ||
0751ddf7 SM |
300 | static int lockd_inetaddr_event(struct notifier_block *this, |
301 | unsigned long event, void *ptr) | |
302 | { | |
303 | struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; | |
304 | struct sockaddr_in sin; | |
305 | ||
5a8a7ff5 | 306 | if (event != NETDEV_DOWN) |
0751ddf7 SM |
307 | goto out; |
308 | ||
2840fe86 | 309 | if (nlmsvc_serv) { |
0751ddf7 SM |
310 | dprintk("lockd_inetaddr_event: removed %pI4\n", |
311 | &ifa->ifa_local); | |
312 | sin.sin_family = AF_INET; | |
313 | sin.sin_addr.s_addr = ifa->ifa_local; | |
2840fe86 | 314 | svc_age_temp_xprts_now(nlmsvc_serv, (struct sockaddr *)&sin); |
0751ddf7 SM |
315 | } |
316 | ||
317 | out: | |
318 | return NOTIFY_DONE; | |
319 | } | |
320 | ||
321 | static struct notifier_block lockd_inetaddr_notifier = { | |
322 | .notifier_call = lockd_inetaddr_event, | |
323 | }; | |
324 | ||
325 | #if IS_ENABLED(CONFIG_IPV6) | |
326 | static int lockd_inet6addr_event(struct notifier_block *this, | |
327 | unsigned long event, void *ptr) | |
328 | { | |
329 | struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; | |
330 | struct sockaddr_in6 sin6; | |
331 | ||
5a8a7ff5 | 332 | if (event != NETDEV_DOWN) |
0751ddf7 SM |
333 | goto out; |
334 | ||
2840fe86 | 335 | if (nlmsvc_serv) { |
0751ddf7 SM |
336 | dprintk("lockd_inet6addr_event: removed %pI6\n", &ifa->addr); |
337 | sin6.sin6_family = AF_INET6; | |
338 | sin6.sin6_addr = ifa->addr; | |
c01410f7 SM |
339 | if (ipv6_addr_type(&sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL) |
340 | sin6.sin6_scope_id = ifa->idev->dev->ifindex; | |
2840fe86 | 341 | svc_age_temp_xprts_now(nlmsvc_serv, (struct sockaddr *)&sin6); |
0751ddf7 SM |
342 | } |
343 | ||
344 | out: | |
345 | return NOTIFY_DONE; | |
346 | } | |
347 | ||
348 | static struct notifier_block lockd_inet6addr_notifier = { | |
349 | .notifier_call = lockd_inet6addr_event, | |
350 | }; | |
351 | #endif | |
352 | ||
afea5657 | 353 | static const struct svc_serv_ops lockd_sv_ops = { |
6b044fba | 354 | .svo_function = lockd, |
6b044fba | 355 | .svo_module = THIS_MODULE, |
ea126e74 JL |
356 | }; |
357 | ||
ecd3ad68 | 358 | static int lockd_get(void) |
1da177e4 | 359 | { |
d751a7cd | 360 | struct svc_serv *serv; |
b73a2972 | 361 | int error; |
1da177e4 | 362 | |
2840fe86 N |
363 | if (nlmsvc_serv) { |
364 | svc_get(nlmsvc_serv); | |
ecd3ad68 | 365 | nlmsvc_users++; |
2840fe86 N |
366 | return 0; |
367 | } | |
1da177e4 LT |
368 | |
369 | /* | |
370 | * Sanity check: if there's no pid, | |
371 | * we should be the first user ... | |
372 | */ | |
4a3ae42d | 373 | if (nlmsvc_users) |
1da177e4 LT |
374 | printk(KERN_WARNING |
375 | "lockd_up: no pid, %d users??\n", nlmsvc_users); | |
376 | ||
06bed7d1 TM |
377 | if (!nlm_timeout) |
378 | nlm_timeout = LOCKD_DFLT_TIMEO; | |
379 | nlmsvc_timeout = nlm_timeout * HZ; | |
380 | ||
ea126e74 | 381 | serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, &lockd_sv_ops); |
1da177e4 LT |
382 | if (!serv) { |
383 | printk(KERN_WARNING "lockd_up: create service failed\n"); | |
2840fe86 | 384 | return -ENOMEM; |
24452239 | 385 | } |
b73a2972 | 386 | |
6b044fba N |
387 | serv->sv_maxconn = nlm_max_connections; |
388 | error = svc_set_num_threads(serv, NULL, 1); | |
b73a2972 N |
389 | /* The thread now holds the only reference */ |
390 | svc_put(serv); | |
391 | if (error < 0) | |
392 | return error; | |
393 | ||
2840fe86 | 394 | nlmsvc_serv = serv; |
0751ddf7 SM |
395 | register_inetaddr_notifier(&lockd_inetaddr_notifier); |
396 | #if IS_ENABLED(CONFIG_IPV6) | |
397 | register_inet6addr_notifier(&lockd_inet6addr_notifier); | |
398 | #endif | |
8dbf28e4 | 399 | dprintk("lockd_up: service created\n"); |
ecd3ad68 | 400 | nlmsvc_users++; |
2840fe86 | 401 | return 0; |
24452239 SK |
402 | } |
403 | ||
865b6740 N |
404 | static void lockd_put(void) |
405 | { | |
406 | if (WARN(nlmsvc_users <= 0, "lockd_down: no users!\n")) | |
407 | return; | |
408 | if (--nlmsvc_users) | |
409 | return; | |
410 | ||
411 | unregister_inetaddr_notifier(&lockd_inetaddr_notifier); | |
412 | #if IS_ENABLED(CONFIG_IPV6) | |
413 | unregister_inet6addr_notifier(&lockd_inet6addr_notifier); | |
414 | #endif | |
415 | ||
6b044fba | 416 | svc_set_num_threads(nlmsvc_serv, NULL, 0); |
865b6740 N |
417 | nlmsvc_serv = NULL; |
418 | dprintk("lockd_down: service destroyed\n"); | |
419 | } | |
420 | ||
24452239 SK |
421 | /* |
422 | * Bring up the lockd process if it's not already up. | |
423 | */ | |
40373b12 | 424 | int lockd_up(struct net *net, const struct cred *cred) |
24452239 | 425 | { |
7d13ec76 | 426 | int error; |
24452239 SK |
427 | |
428 | mutex_lock(&nlmsvc_mutex); | |
429 | ||
ecd3ad68 | 430 | error = lockd_get(); |
2840fe86 | 431 | if (error) |
865b6740 | 432 | goto err; |
1da177e4 | 433 | |
b73a2972 | 434 | error = lockd_up_net(nlmsvc_serv, net, cred); |
dc3033e1 | 435 | if (error < 0) { |
865b6740 N |
436 | lockd_put(); |
437 | goto err; | |
dc3033e1 | 438 | } |
1da177e4 | 439 | |
865b6740 | 440 | err: |
353ab6e9 | 441 | mutex_unlock(&nlmsvc_mutex); |
1da177e4 LT |
442 | return error; |
443 | } | |
2de59872 | 444 | EXPORT_SYMBOL_GPL(lockd_up); |
1da177e4 LT |
445 | |
446 | /* | |
447 | * Decrement the user count and bring down lockd if we're the last. | |
448 | */ | |
449 | void | |
e3f70ead | 450 | lockd_down(struct net *net) |
1da177e4 | 451 | { |
353ab6e9 | 452 | mutex_lock(&nlmsvc_mutex); |
2840fe86 | 453 | lockd_down_net(nlmsvc_serv, net); |
865b6740 | 454 | lockd_put(); |
353ab6e9 | 455 | mutex_unlock(&nlmsvc_mutex); |
1da177e4 | 456 | } |
2de59872 | 457 | EXPORT_SYMBOL_GPL(lockd_down); |
1da177e4 | 458 | |
90d5b180 CL |
459 | #ifdef CONFIG_SYSCTL |
460 | ||
1da177e4 LT |
461 | /* |
462 | * Sysctl parameters (same as module parameters, different interface). | |
463 | */ | |
464 | ||
7ac9fe57 | 465 | static struct ctl_table nlm_sysctls[] = { |
1da177e4 | 466 | { |
1da177e4 LT |
467 | .procname = "nlm_grace_period", |
468 | .data = &nlm_grace_period, | |
7ee91ec1 | 469 | .maxlen = sizeof(unsigned long), |
1da177e4 | 470 | .mode = 0644, |
6d456111 | 471 | .proc_handler = proc_doulongvec_minmax, |
1da177e4 LT |
472 | .extra1 = (unsigned long *) &nlm_grace_period_min, |
473 | .extra2 = (unsigned long *) &nlm_grace_period_max, | |
474 | }, | |
475 | { | |
1da177e4 LT |
476 | .procname = "nlm_timeout", |
477 | .data = &nlm_timeout, | |
7ee91ec1 | 478 | .maxlen = sizeof(unsigned long), |
1da177e4 | 479 | .mode = 0644, |
6d456111 | 480 | .proc_handler = proc_doulongvec_minmax, |
1da177e4 LT |
481 | .extra1 = (unsigned long *) &nlm_timeout_min, |
482 | .extra2 = (unsigned long *) &nlm_timeout_max, | |
483 | }, | |
484 | { | |
1da177e4 LT |
485 | .procname = "nlm_udpport", |
486 | .data = &nlm_udpport, | |
487 | .maxlen = sizeof(int), | |
488 | .mode = 0644, | |
6d456111 | 489 | .proc_handler = proc_dointvec_minmax, |
1da177e4 LT |
490 | .extra1 = (int *) &nlm_port_min, |
491 | .extra2 = (int *) &nlm_port_max, | |
492 | }, | |
493 | { | |
1da177e4 LT |
494 | .procname = "nlm_tcpport", |
495 | .data = &nlm_tcpport, | |
496 | .maxlen = sizeof(int), | |
497 | .mode = 0644, | |
6d456111 | 498 | .proc_handler = proc_dointvec_minmax, |
1da177e4 LT |
499 | .extra1 = (int *) &nlm_port_min, |
500 | .extra2 = (int *) &nlm_port_max, | |
501 | }, | |
abd1f500 | 502 | { |
abd1f500 OK |
503 | .procname = "nsm_use_hostnames", |
504 | .data = &nsm_use_hostnames, | |
505 | .maxlen = sizeof(int), | |
506 | .mode = 0644, | |
d02a3a2c | 507 | .proc_handler = proc_dobool, |
abd1f500 | 508 | }, |
460f5cac | 509 | { |
460f5cac OK |
510 | .procname = "nsm_local_state", |
511 | .data = &nsm_local_state, | |
512 | .maxlen = sizeof(int), | |
513 | .mode = 0644, | |
6d456111 | 514 | .proc_handler = proc_dointvec, |
460f5cac | 515 | }, |
ab09203e | 516 | { } |
1da177e4 LT |
517 | }; |
518 | ||
7ac9fe57 | 519 | static struct ctl_table nlm_sysctl_dir[] = { |
1da177e4 | 520 | { |
1da177e4 LT |
521 | .procname = "nfs", |
522 | .mode = 0555, | |
523 | .child = nlm_sysctls, | |
524 | }, | |
ab09203e | 525 | { } |
1da177e4 LT |
526 | }; |
527 | ||
7ac9fe57 | 528 | static struct ctl_table nlm_sysctl_root[] = { |
1da177e4 | 529 | { |
1da177e4 LT |
530 | .procname = "fs", |
531 | .mode = 0555, | |
532 | .child = nlm_sysctl_dir, | |
533 | }, | |
ab09203e | 534 | { } |
1da177e4 LT |
535 | }; |
536 | ||
90d5b180 CL |
537 | #endif /* CONFIG_SYSCTL */ |
538 | ||
1da177e4 | 539 | /* |
405ae7d3 | 540 | * Module (and sysfs) parameters. |
1da177e4 LT |
541 | */ |
542 | ||
543 | #define param_set_min_max(name, type, which_strtol, min, max) \ | |
e4dca7b7 | 544 | static int param_set_##name(const char *val, const struct kernel_param *kp) \ |
1da177e4 LT |
545 | { \ |
546 | char *endp; \ | |
547 | __typeof__(type) num = which_strtol(val, &endp, 0); \ | |
548 | if (endp == val || *endp || num < (min) || num > (max)) \ | |
549 | return -EINVAL; \ | |
de5b8e8e | 550 | *((type *) kp->arg) = num; \ |
1da177e4 LT |
551 | return 0; \ |
552 | } | |
553 | ||
554 | static inline int is_callback(u32 proc) | |
555 | { | |
556 | return proc == NLMPROC_GRANTED | |
557 | || proc == NLMPROC_GRANTED_MSG | |
558 | || proc == NLMPROC_TEST_RES | |
559 | || proc == NLMPROC_LOCK_RES | |
560 | || proc == NLMPROC_CANCEL_RES | |
561 | || proc == NLMPROC_UNLOCK_RES | |
562 | || proc == NLMPROC_NSM_NOTIFY; | |
563 | } | |
564 | ||
565 | ||
566 | static int lockd_authenticate(struct svc_rqst *rqstp) | |
567 | { | |
568 | rqstp->rq_client = NULL; | |
569 | switch (rqstp->rq_authop->flavour) { | |
570 | case RPC_AUTH_NULL: | |
571 | case RPC_AUTH_UNIX: | |
5c2465df | 572 | rqstp->rq_auth_stat = rpc_auth_ok; |
1da177e4 LT |
573 | if (rqstp->rq_proc == 0) |
574 | return SVC_OK; | |
575 | if (is_callback(rqstp->rq_proc)) { | |
576 | /* Leave it to individual procedures to | |
577 | * call nlmsvc_lookup_host(rqstp) | |
578 | */ | |
579 | return SVC_OK; | |
580 | } | |
581 | return svc_set_client(rqstp); | |
582 | } | |
5c2465df | 583 | rqstp->rq_auth_stat = rpc_autherr_badcred; |
1da177e4 LT |
584 | return SVC_DENIED; |
585 | } | |
586 | ||
587 | ||
588 | param_set_min_max(port, int, simple_strtol, 0, 65535) | |
589 | param_set_min_max(grace_period, unsigned long, simple_strtoul, | |
590 | nlm_grace_period_min, nlm_grace_period_max) | |
591 | param_set_min_max(timeout, unsigned long, simple_strtoul, | |
592 | nlm_timeout_min, nlm_timeout_max) | |
593 | ||
594 | MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); | |
595 | MODULE_DESCRIPTION("NFS file locking service version " LOCKD_VERSION "."); | |
596 | MODULE_LICENSE("GPL"); | |
597 | ||
598 | module_param_call(nlm_grace_period, param_set_grace_period, param_get_ulong, | |
599 | &nlm_grace_period, 0644); | |
600 | module_param_call(nlm_timeout, param_set_timeout, param_get_ulong, | |
601 | &nlm_timeout, 0644); | |
602 | module_param_call(nlm_udpport, param_set_port, param_get_int, | |
603 | &nlm_udpport, 0644); | |
604 | module_param_call(nlm_tcpport, param_set_port, param_get_int, | |
605 | &nlm_tcpport, 0644); | |
abd1f500 | 606 | module_param(nsm_use_hostnames, bool, 0644); |
c72a476b | 607 | module_param(nlm_max_connections, uint, 0644); |
1da177e4 | 608 | |
a9c5d73a SK |
609 | static int lockd_init_net(struct net *net) |
610 | { | |
66547b02 SK |
611 | struct lockd_net *ln = net_generic(net, lockd_net_id); |
612 | ||
613 | INIT_DELAYED_WORK(&ln->grace_period_end, grace_ender); | |
f7790029 | 614 | INIT_LIST_HEAD(&ln->lockd_manager.list); |
c87fb4a3 | 615 | ln->lockd_manager.block_opens = false; |
0ad95472 | 616 | INIT_LIST_HEAD(&ln->nsm_handles); |
a9c5d73a SK |
617 | return 0; |
618 | } | |
619 | ||
620 | static void lockd_exit_net(struct net *net) | |
621 | { | |
a3152f14 VA |
622 | struct lockd_net *ln = net_generic(net, lockd_net_id); |
623 | ||
624 | WARN_ONCE(!list_empty(&ln->lockd_manager.list), | |
625 | "net %x %s: lockd_manager.list is not empty\n", | |
626 | net->ns.inum, __func__); | |
627 | WARN_ONCE(!list_empty(&ln->nsm_handles), | |
628 | "net %x %s: nsm_handles list is not empty\n", | |
629 | net->ns.inum, __func__); | |
630 | WARN_ONCE(delayed_work_pending(&ln->grace_period_end), | |
631 | "net %x %s: grace_period_end was not cancelled\n", | |
632 | net->ns.inum, __func__); | |
a9c5d73a SK |
633 | } |
634 | ||
635 | static struct pernet_operations lockd_net_ops = { | |
636 | .init = lockd_init_net, | |
637 | .exit = lockd_exit_net, | |
638 | .id = &lockd_net_id, | |
639 | .size = sizeof(struct lockd_net), | |
640 | }; | |
641 | ||
642 | ||
1da177e4 LT |
643 | /* |
644 | * Initialising and terminating the module. | |
645 | */ | |
646 | ||
647 | static int __init init_nlm(void) | |
648 | { | |
a9c5d73a SK |
649 | int err; |
650 | ||
90d5b180 | 651 | #ifdef CONFIG_SYSCTL |
a9c5d73a | 652 | err = -ENOMEM; |
0b4d4147 | 653 | nlm_sysctl_table = register_sysctl_table(nlm_sysctl_root); |
a9c5d73a SK |
654 | if (nlm_sysctl_table == NULL) |
655 | goto err_sysctl; | |
656 | #endif | |
657 | err = register_pernet_subsys(&lockd_net_ops); | |
658 | if (err) | |
659 | goto err_pernet; | |
d68e3c4a JL |
660 | |
661 | err = lockd_create_procfs(); | |
662 | if (err) | |
663 | goto err_procfs; | |
664 | ||
90d5b180 | 665 | return 0; |
a9c5d73a | 666 | |
d68e3c4a JL |
667 | err_procfs: |
668 | unregister_pernet_subsys(&lockd_net_ops); | |
a9c5d73a SK |
669 | err_pernet: |
670 | #ifdef CONFIG_SYSCTL | |
671 | unregister_sysctl_table(nlm_sysctl_table); | |
a9c5d73a | 672 | err_sysctl: |
12dd7ecf | 673 | #endif |
a9c5d73a | 674 | return err; |
1da177e4 LT |
675 | } |
676 | ||
677 | static void __exit exit_nlm(void) | |
678 | { | |
679 | /* FIXME: delete all NLM clients */ | |
680 | nlm_shutdown_hosts(); | |
d68e3c4a | 681 | lockd_remove_procfs(); |
a9c5d73a | 682 | unregister_pernet_subsys(&lockd_net_ops); |
90d5b180 | 683 | #ifdef CONFIG_SYSCTL |
1da177e4 | 684 | unregister_sysctl_table(nlm_sysctl_table); |
90d5b180 | 685 | #endif |
1da177e4 LT |
686 | } |
687 | ||
688 | module_init(init_nlm); | |
689 | module_exit(exit_nlm); | |
690 | ||
a9ad1a80 CL |
691 | /** |
692 | * nlmsvc_dispatch - Process an NLM Request | |
693 | * @rqstp: incoming request | |
694 | * @statp: pointer to location of accept_stat field in RPC Reply buffer | |
695 | * | |
696 | * Return values: | |
697 | * %0: Processing complete; do not send a Reply | |
698 | * %1: Processing complete; send Reply in rqstp->rq_res | |
699 | */ | |
700 | static int nlmsvc_dispatch(struct svc_rqst *rqstp, __be32 *statp) | |
701 | { | |
702 | const struct svc_procedure *procp = rqstp->rq_procinfo; | |
a9ad1a80 CL |
703 | |
704 | svcxdr_init_decode(rqstp); | |
16c66364 | 705 | if (!procp->pc_decode(rqstp, &rqstp->rq_arg_stream)) |
a9ad1a80 CL |
706 | goto out_decode_err; |
707 | ||
708 | *statp = procp->pc_func(rqstp); | |
709 | if (*statp == rpc_drop_reply) | |
710 | return 0; | |
711 | if (*statp != rpc_success) | |
712 | return 1; | |
713 | ||
714 | svcxdr_init_encode(rqstp); | |
fda49441 | 715 | if (!procp->pc_encode(rqstp, &rqstp->rq_res_stream)) |
a9ad1a80 CL |
716 | goto out_encode_err; |
717 | ||
718 | return 1; | |
719 | ||
720 | out_decode_err: | |
721 | *statp = rpc_garbage_args; | |
722 | return 1; | |
723 | ||
724 | out_encode_err: | |
725 | *statp = rpc_system_err; | |
726 | return 1; | |
727 | } | |
728 | ||
1da177e4 LT |
729 | /* |
730 | * Define NLM program and procedures | |
731 | */ | |
7fd38af9 | 732 | static unsigned int nlmsvc_version1_count[17]; |
e9679189 CH |
733 | static const struct svc_version nlmsvc_version1 = { |
734 | .vs_vers = 1, | |
735 | .vs_nproc = 17, | |
736 | .vs_proc = nlmsvc_procedures, | |
737 | .vs_count = nlmsvc_version1_count, | |
a9ad1a80 | 738 | .vs_dispatch = nlmsvc_dispatch, |
e9679189 | 739 | .vs_xdrsize = NLMSVC_XDRSIZE, |
1da177e4 | 740 | }; |
7fd38af9 | 741 | static unsigned int nlmsvc_version3_count[24]; |
e9679189 CH |
742 | static const struct svc_version nlmsvc_version3 = { |
743 | .vs_vers = 3, | |
744 | .vs_nproc = 24, | |
745 | .vs_proc = nlmsvc_procedures, | |
746 | .vs_count = nlmsvc_version3_count, | |
a9ad1a80 | 747 | .vs_dispatch = nlmsvc_dispatch, |
e9679189 | 748 | .vs_xdrsize = NLMSVC_XDRSIZE, |
1da177e4 LT |
749 | }; |
750 | #ifdef CONFIG_LOCKD_V4 | |
7fd38af9 | 751 | static unsigned int nlmsvc_version4_count[24]; |
e9679189 CH |
752 | static const struct svc_version nlmsvc_version4 = { |
753 | .vs_vers = 4, | |
754 | .vs_nproc = 24, | |
755 | .vs_proc = nlmsvc_procedures4, | |
756 | .vs_count = nlmsvc_version4_count, | |
a9ad1a80 | 757 | .vs_dispatch = nlmsvc_dispatch, |
e9679189 | 758 | .vs_xdrsize = NLMSVC_XDRSIZE, |
1da177e4 LT |
759 | }; |
760 | #endif | |
e9679189 | 761 | static const struct svc_version *nlmsvc_version[] = { |
1da177e4 LT |
762 | [1] = &nlmsvc_version1, |
763 | [3] = &nlmsvc_version3, | |
764 | #ifdef CONFIG_LOCKD_V4 | |
765 | [4] = &nlmsvc_version4, | |
766 | #endif | |
767 | }; | |
768 | ||
769 | static struct svc_stat nlmsvc_stats; | |
770 | ||
e8c96f8c | 771 | #define NLM_NRVERS ARRAY_SIZE(nlmsvc_version) |
1da177e4 LT |
772 | static struct svc_program nlmsvc_program = { |
773 | .pg_prog = NLM_PROGRAM, /* program number */ | |
774 | .pg_nvers = NLM_NRVERS, /* number of entries in nlmsvc_version */ | |
775 | .pg_vers = nlmsvc_version, /* version table */ | |
776 | .pg_name = "lockd", /* service name */ | |
777 | .pg_class = "nfsd", /* share authentication with nfsd */ | |
778 | .pg_stats = &nlmsvc_stats, /* stats table */ | |
8e5b6773 TM |
779 | .pg_authenticate = &lockd_authenticate, /* export authentication */ |
780 | .pg_init_request = svc_generic_init_request, | |
642ee6b2 | 781 | .pg_rpcbind_set = svc_generic_rpcbind_set, |
1da177e4 | 782 | }; |