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(&current->sigmask_lock);
101 	siginitsetinv(&current->blocked, sigmask(SIGKILL));
102 	recalc_sigpending(current);
103 	spin_unlock_irq(&current->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(&current->sigmask_lock);
126 			flush_signals(current);
127 			spin_unlock_irq(&current->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(&current->sigmask_lock);
311 	recalc_sigpending(current);
312 	spin_unlock_irq(&current->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