1 /*
2 * linux/fs/lockd/svc.c
3 *
4 * This is the central lockd service.
5 *
6 * FIXME: Separate the lockd NFS server functionality from the lockd NFS
7 * client functionality. Oh why didn't Sun create two separate
8 * services in the first place?
9 *
10 * Authors: Olaf Kirch (okir@monad.swb.de)
11 *
12 * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
13 */
14
15 #define __KERNEL_SYSCALLS__
16 #include <linux/config.h>
17 #include <linux/module.h>
18 #include <linux/init.h>
19
20 #include <linux/sched.h>
21 #include <linux/errno.h>
22 #include <linux/in.h>
23 #include <linux/uio.h>
24 #include <linux/version.h>
25 #include <linux/unistd.h>
26 #include <linux/slab.h>
27 #include <linux/smp.h>
28 #include <linux/smp_lock.h>
29
30 #include <linux/sunrpc/types.h>
31 #include <linux/sunrpc/stats.h>
32 #include <linux/sunrpc/clnt.h>
33 #include <linux/sunrpc/svc.h>
34 #include <linux/sunrpc/svcsock.h>
35 #include <linux/lockd/lockd.h>
36 #include <linux/nfs.h>
37
38 #define NLMDBG_FACILITY NLMDBG_SVC
39 #define LOCKD_BUFSIZE (1024 + NLMSSVC_XDRSIZE)
40 #define ALLOWED_SIGS (sigmask(SIGKILL))
41
42 extern struct svc_program nlmsvc_program;
43 struct nlmsvc_binding * nlmsvc_ops;
44 static DECLARE_MUTEX(nlmsvc_sema);
45 static unsigned int nlmsvc_users;
46 static pid_t nlmsvc_pid;
47 int nlmsvc_grace_period;
48 unsigned long nlmsvc_timeout;
49
50 static DECLARE_MUTEX_LOCKED(lockd_start);
51 static DECLARE_WAIT_QUEUE_HEAD(lockd_exit);
52
53 /*
54 * Currently the following can be set only at insmod time.
55 * Ideally, they would be accessible through the sysctl interface.
56 */
57 unsigned long nlm_grace_period;
58 unsigned long nlm_timeout = LOCKD_DFLT_TIMEO;
59 unsigned long nlm_udpport, nlm_tcpport;
60
set_grace_period(void)61 static unsigned long set_grace_period(void)
62 {
63 unsigned long grace_period;
64
65 /* Note: nlm_timeout should always be nonzero */
66 if (nlm_grace_period)
67 grace_period = ((nlm_grace_period + nlm_timeout - 1)
68 / nlm_timeout) * nlm_timeout * HZ;
69 else
70 grace_period = nlm_timeout * 5 * HZ;
71 nlmsvc_grace_period = 1;
72 return grace_period + jiffies;
73 }
74
75 /*
76 * This is the lockd kernel thread
77 */
78 static void
lockd(struct svc_rqst * rqstp)79 lockd(struct svc_rqst *rqstp)
80 {
81 struct svc_serv *serv = rqstp->rq_server;
82 int err = 0;
83 unsigned long grace_period_expire;
84
85 /* Lock module and set up kernel thread */
86 MOD_INC_USE_COUNT;
87 lock_kernel();
88
89 /*
90 * Let our maker know we're running.
91 */
92 nlmsvc_pid = current->pid;
93 up(&lockd_start);
94
95 daemonize();
96 reparent_to_init();
97 sprintf(current->comm, "lockd");
98
99 /* Process request with signals blocked. */
100 spin_lock_irq(¤t->sigmask_lock);
101 siginitsetinv(¤t->blocked, sigmask(SIGKILL));
102 recalc_sigpending(current);
103 spin_unlock_irq(¤t->sigmask_lock);
104
105 /* kick rpciod */
106 rpciod_up();
107
108 dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n");
109
110 if (!nlm_timeout)
111 nlm_timeout = LOCKD_DFLT_TIMEO;
112 nlmsvc_timeout = nlm_timeout * HZ;
113
114 grace_period_expire = set_grace_period();
115
116 /*
117 * The main request loop. We don't terminate until the last
118 * NFS mount or NFS daemon has gone away, and we've been sent a
119 * signal, or else another process has taken over our job.
120 */
121 while ((nlmsvc_users || !signalled()) && nlmsvc_pid == current->pid)
122 {
123 long timeout = MAX_SCHEDULE_TIMEOUT;
124 if (signalled()) {
125 spin_lock_irq(¤t->sigmask_lock);
126 flush_signals(current);
127 spin_unlock_irq(¤t->sigmask_lock);
128 if (nlmsvc_ops) {
129 nlmsvc_ops->detach();
130 grace_period_expire = set_grace_period();
131 }
132 }
133
134 /*
135 * Retry any blocked locks that have been notified by
136 * the VFS. Don't do this during grace period.
137 * (Theoretically, there shouldn't even be blocked locks
138 * during grace period).
139 */
140 if (!nlmsvc_grace_period)
141 timeout = nlmsvc_retry_blocked();
142
143 /*
144 * Find a socket with data available and call its
145 * recvfrom routine.
146 */
147 err = svc_recv(serv, rqstp, timeout);
148 if (err == -EAGAIN || err == -EINTR)
149 continue;
150 if (err < 0) {
151 printk(KERN_WARNING
152 "lockd: terminating on error %d\n",
153 -err);
154 break;
155 }
156
157 dprintk("lockd: request from %08x\n",
158 (unsigned)ntohl(rqstp->rq_addr.sin_addr.s_addr));
159
160 /*
161 * Look up the NFS client handle. The handle is needed for
162 * all but the GRANTED callback RPCs.
163 */
164 rqstp->rq_client = NULL;
165 if (nlmsvc_ops) {
166 nlmsvc_ops->exp_readlock();
167 rqstp->rq_client =
168 nlmsvc_ops->exp_getclient(&rqstp->rq_addr);
169 }
170
171 if (nlmsvc_grace_period &&
172 time_before(grace_period_expire, jiffies))
173 nlmsvc_grace_period = 0;
174 svc_process(serv, rqstp);
175
176 /* Unlock export hash tables */
177 if (nlmsvc_ops)
178 nlmsvc_ops->exp_unlock();
179 }
180
181 /*
182 * Check whether there's a new lockd process before
183 * shutting down the hosts and clearing the slot.
184 */
185 if (!nlmsvc_pid || current->pid == nlmsvc_pid) {
186 if (nlmsvc_ops)
187 nlmsvc_ops->detach();
188 nlm_shutdown_hosts();
189 nlmsvc_pid = 0;
190 } else
191 printk(KERN_DEBUG
192 "lockd: new process, skipping host shutdown\n");
193 wake_up(&lockd_exit);
194
195 /* Exit the RPC thread */
196 svc_exit_thread(rqstp);
197
198 /* release rpciod */
199 rpciod_down();
200
201 /* Release module */
202 MOD_DEC_USE_COUNT;
203 }
204
205 /*
206 * Bring up the lockd process if it's not already up.
207 */
208 int
lockd_up(void)209 lockd_up(void)
210 {
211 static int warned = 0;
212 struct svc_serv * serv;
213 int error = 0;
214
215 down(&nlmsvc_sema);
216 /*
217 * Unconditionally increment the user count ... this is
218 * the number of clients who _want_ a lockd process.
219 */
220 nlmsvc_users++;
221 /*
222 * Check whether we're already up and running.
223 */
224 if (nlmsvc_pid)
225 goto out;
226
227 /*
228 * Sanity check: if there's no pid,
229 * we should be the first user ...
230 */
231 if (nlmsvc_users > 1)
232 printk(KERN_WARNING
233 "lockd_up: no pid, %d users??\n", nlmsvc_users);
234
235 error = -ENOMEM;
236 serv = svc_create(&nlmsvc_program, 0, NLMSVC_XDRSIZE);
237 if (!serv) {
238 printk(KERN_WARNING "lockd_up: create service failed\n");
239 goto out;
240 }
241
242 if ((error = svc_makesock(serv, IPPROTO_UDP, nlm_udpport)) < 0
243 #ifdef CONFIG_NFSD_TCP
244 || (error = svc_makesock(serv, IPPROTO_TCP, nlm_tcpport)) < 0
245 #endif
246 ) {
247 if (warned++ == 0)
248 printk(KERN_WARNING
249 "lockd_up: makesock failed, error=%d\n", error);
250 goto destroy_and_out;
251 }
252 warned = 0;
253
254 /*
255 * Create the kernel thread and wait for it to start.
256 */
257 error = svc_create_thread(lockd, serv);
258 if (error) {
259 printk(KERN_WARNING
260 "lockd_up: create thread failed, error=%d\n", error);
261 goto destroy_and_out;
262 }
263 down(&lockd_start);
264
265 /*
266 * Note: svc_serv structures have an initial use count of 1,
267 * so we exit through here on both success and failure.
268 */
269 destroy_and_out:
270 svc_destroy(serv);
271 out:
272 up(&nlmsvc_sema);
273 return error;
274 }
275
276 /*
277 * Decrement the user count and bring down lockd if we're the last.
278 */
279 void
lockd_down(void)280 lockd_down(void)
281 {
282 static int warned = 0;
283
284 down(&nlmsvc_sema);
285 if (nlmsvc_users) {
286 if (--nlmsvc_users)
287 goto out;
288 } else
289 printk(KERN_WARNING "lockd_down: no users! pid=%d\n", nlmsvc_pid);
290
291 if (!nlmsvc_pid) {
292 if (warned++ == 0)
293 printk(KERN_WARNING "lockd_down: no lockd running.\n");
294 goto out;
295 }
296 warned = 0;
297
298 kill_proc(nlmsvc_pid, SIGKILL, 1);
299 /*
300 * Wait for the lockd process to exit, but since we're holding
301 * the lockd semaphore, we can't wait around forever ...
302 */
303 current->sigpending = 0;
304 interruptible_sleep_on_timeout(&lockd_exit, HZ);
305 if (nlmsvc_pid) {
306 printk(KERN_WARNING
307 "lockd_down: lockd failed to exit, clearing pid\n");
308 nlmsvc_pid = 0;
309 }
310 spin_lock_irq(¤t->sigmask_lock);
311 recalc_sigpending(current);
312 spin_unlock_irq(¤t->sigmask_lock);
313 out:
314 up(&nlmsvc_sema);
315 }
316
317 #ifdef MODULE
318 /* New module support in 2.1.18 */
319
320 MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
321 MODULE_DESCRIPTION("NFS file locking service version " LOCKD_VERSION ".");
322 MODULE_LICENSE("GPL");
323 MODULE_PARM(nlm_grace_period, "10-240l");
324 MODULE_PARM(nlm_timeout, "3-20l");
325 MODULE_PARM(nlm_udpport, "0-65535l");
326 MODULE_PARM(nlm_tcpport, "0-65535l");
327
328 int
init_module(void)329 init_module(void)
330 {
331 /* Init the static variables */
332 init_MUTEX(&nlmsvc_sema);
333 nlmsvc_users = 0;
334 nlmsvc_pid = 0;
335 return 0;
336 }
337
338 void
cleanup_module(void)339 cleanup_module(void)
340 {
341 /* FIXME: delete all NLM clients */
342 nlm_shutdown_hosts();
343 }
344 #else
345 /* not a module, so process bootargs
346 * lockd.udpport and lockd.tcpport
347 */
348
udpport_set(char * str)349 static int __init udpport_set(char *str)
350 {
351 nlm_udpport = simple_strtoul(str, NULL, 0);
352 return 1;
353 }
tcpport_set(char * str)354 static int __init tcpport_set(char *str)
355 {
356 nlm_tcpport = simple_strtoul(str, NULL, 0);
357 return 1;
358 }
359 __setup("lockd.udpport=", udpport_set);
360 __setup("lockd.tcpport=", tcpport_set);
361
362 #endif
363
364 /*
365 * Define NLM program and procedures
366 */
367 static struct svc_version nlmsvc_version1 = {
368 1, 17, nlmsvc_procedures, NULL
369 };
370 static struct svc_version nlmsvc_version3 = {
371 3, 24, nlmsvc_procedures, NULL
372 };
373 #ifdef CONFIG_LOCKD_V4
374 static struct svc_version nlmsvc_version4 = {
375 4, 24, nlmsvc_procedures4, NULL
376 };
377 #endif
378 static struct svc_version * nlmsvc_version[] = {
379 NULL,
380 &nlmsvc_version1,
381 NULL,
382 &nlmsvc_version3,
383 #ifdef CONFIG_LOCKD_V4
384 &nlmsvc_version4,
385 #endif
386 };
387
388 static struct svc_stat nlmsvc_stats;
389
390 #define NLM_NRVERS (sizeof(nlmsvc_version)/sizeof(nlmsvc_version[0]))
391 struct svc_program nlmsvc_program = {
392 NLM_PROGRAM, /* program number */
393 1, NLM_NRVERS-1, /* version range */
394 NLM_NRVERS, /* number of entries in nlmsvc_version */
395 nlmsvc_version, /* version table */
396 "lockd", /* service name */
397 &nlmsvc_stats, /* stats table */
398 };
399