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