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(&current->sigmask_lock);
85     	siginitsetinv(&current->blocked, sigmask(SIGKILL));
86     	recalc_sigpending(current);
87     	spin_unlock_irq(&current->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(&current->sigmask_lock);
121     			flush_signals(current);
122     			spin_unlock_irq(&current->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(&current->sigmask_lock);
312     	recalc_sigpending(current);
313     	spin_unlock_irq(&current->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