X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=fs%2Flockd%2Fsvc.c;h=64f1c31b58533317d65a256b79e1d5c474620421;hb=0dba7c2a9ed3d4a1e58f5d94fffa9f44dbe012e6;hp=fd56c8872f3444297aebef53fe3272db18189dba;hpb=353ab6e97b8f209dbecc9f650f1f84e3da2a7bb1;p=safe%2Fjmp%2Flinux-2.6 diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index fd56c88..64f1c31 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -12,7 +12,6 @@ * Copyright (C) 1995, 1996 Olaf Kirch */ -#include #include #include #include @@ -26,12 +25,15 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include #include @@ -42,16 +44,24 @@ static struct svc_program nlmsvc_program; struct nlmsvc_binding * nlmsvc_ops; -EXPORT_SYMBOL(nlmsvc_ops); +EXPORT_SYMBOL_GPL(nlmsvc_ops); static DEFINE_MUTEX(nlmsvc_mutex); static unsigned int nlmsvc_users; -static pid_t nlmsvc_pid; -int nlmsvc_grace_period; +static struct task_struct *nlmsvc_task; +static struct svc_rqst *nlmsvc_rqst; unsigned long nlmsvc_timeout; -static DECLARE_COMPLETION(lockd_start_done); -static DECLARE_WAIT_QUEUE_HEAD(lockd_exit); +/* + * If the kernel has IPv6 support available, always listen for + * both AF_INET and AF_INET6 requests. + */ +#if (defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)) && \ + defined(CONFIG_SUNRPC_REGISTER_V4) +static const sa_family_t nlmsvc_family = AF_INET6; +#else /* (CONFIG_IPV6 || CONFIG_IPV6_MODULE) && CONFIG_SUNRPC_REGISTER_V4 */ +static const sa_family_t nlmsvc_family = AF_INET; +#endif /* (CONFIG_IPV6 || CONFIG_IPV6_MODULE) && CONFIG_SUNRPC_REGISTER_V4 */ /* * These can be set at insmod time (useful for NFS as root filesystem), @@ -61,6 +71,9 @@ static unsigned long nlm_grace_period; static unsigned long nlm_timeout = LOCKD_DFLT_TIMEO; static int nlm_udpport, nlm_tcpport; +/* RLIM_NOFILE defaults to 1024. That seems like a reasonable default here. */ +static unsigned int nlm_max_connections = 1024; + /* * Constants needed for the sysctl interface. */ @@ -70,201 +83,233 @@ static const unsigned long nlm_timeout_min = 3; static const unsigned long nlm_timeout_max = 20; static const int nlm_port_min = 0, nlm_port_max = 65535; +#ifdef CONFIG_SYSCTL static struct ctl_table_header * nlm_sysctl_table; +#endif -static unsigned long set_grace_period(void) +static unsigned long get_lockd_grace_period(void) { - unsigned long grace_period; - /* Note: nlm_timeout should always be nonzero */ if (nlm_grace_period) - grace_period = ((nlm_grace_period + nlm_timeout - 1) - / nlm_timeout) * nlm_timeout * HZ; + return roundup(nlm_grace_period, nlm_timeout) * HZ; else - grace_period = nlm_timeout * 5 * HZ; - nlmsvc_grace_period = 1; - return grace_period + jiffies; + return nlm_timeout * 5 * HZ; } -static inline void clear_grace_period(void) +static struct lock_manager lockd_manager = { +}; + +static void grace_ender(struct work_struct *not_used) +{ + locks_end_grace(&lockd_manager); +} + +static DECLARE_DELAYED_WORK(grace_period_end, grace_ender); + +static void set_grace_period(void) { - nlmsvc_grace_period = 0; + unsigned long grace_period = get_lockd_grace_period(); + + locks_start_grace(&lockd_manager); + cancel_delayed_work_sync(&grace_period_end); + schedule_delayed_work(&grace_period_end, grace_period); } /* * This is the lockd kernel thread */ -static void -lockd(struct svc_rqst *rqstp) +static int +lockd(void *vrqstp) { - struct svc_serv *serv = rqstp->rq_server; - int err = 0; - unsigned long grace_period_expire; - - /* Lock module and set up kernel thread */ - /* lockd_up is waiting for us to startup, so will - * be holding a reference to this module, so it - * is safe to just claim another reference - */ - __module_get(THIS_MODULE); - lock_kernel(); - - /* - * Let our maker know we're running. - */ - nlmsvc_pid = current->pid; - complete(&lockd_start_done); + int err = 0, preverr = 0; + struct svc_rqst *rqstp = vrqstp; - daemonize("lockd"); + /* try_to_freeze() is called from svc_recv() */ + set_freezable(); - /* Process request with signals blocked, but allow SIGKILL. */ + /* Allow SIGKILL to tell lockd to drop all of its locks */ allow_signal(SIGKILL); - /* kick rpciod */ - rpciod_up(); - dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n"); + /* + * FIXME: it would be nice if lockd didn't spend its entire life + * running under the BKL. At the very least, it would be good to + * have someone clarify what it's intended to protect here. I've + * seen some handwavy posts about posix locking needing to be + * done under the BKL, but it's far from clear. + */ + lock_kernel(); + if (!nlm_timeout) nlm_timeout = LOCKD_DFLT_TIMEO; nlmsvc_timeout = nlm_timeout * HZ; - grace_period_expire = set_grace_period(); + set_grace_period(); /* * The main request loop. We don't terminate until the last - * NFS mount or NFS daemon has gone away, and we've been sent a - * signal, or else another process has taken over our job. + * NFS mount or NFS daemon has gone away. */ - while ((nlmsvc_users || !signalled()) && nlmsvc_pid == current->pid) { + while (!kthread_should_stop()) { long timeout = MAX_SCHEDULE_TIMEOUT; + RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); + + /* update sv_maxconn if it has changed */ + rqstp->rq_server->sv_maxconn = nlm_max_connections; if (signalled()) { flush_signals(current); if (nlmsvc_ops) { nlmsvc_invalidate_all(); - grace_period_expire = set_grace_period(); + set_grace_period(); } + continue; } - /* - * Retry any blocked locks that have been notified by - * the VFS. Don't do this during grace period. - * (Theoretically, there shouldn't even be blocked locks - * during grace period). - */ - if (!nlmsvc_grace_period) { - timeout = nlmsvc_retry_blocked(); - } else if (time_before(grace_period_expire, jiffies)) - clear_grace_period(); + timeout = nlmsvc_retry_blocked(); /* * Find a socket with data available and call its * recvfrom routine. */ - err = svc_recv(serv, rqstp, timeout); - if (err == -EAGAIN || err == -EINTR) + err = svc_recv(rqstp, timeout); + if (err == -EAGAIN || err == -EINTR) { + preverr = err; continue; + } if (err < 0) { - printk(KERN_WARNING - "lockd: terminating on error %d\n", - -err); - break; + if (err != preverr) { + printk(KERN_WARNING "%s: unexpected error " + "from svc_recv (%d)\n", __func__, err); + preverr = err; + } + schedule_timeout_interruptible(HZ); + continue; } + preverr = err; - dprintk("lockd: request from %08x\n", - (unsigned)ntohl(rqstp->rq_addr.sin_addr.s_addr)); - - svc_process(serv, rqstp); + dprintk("lockd: request from %s\n", + svc_print_addr(rqstp, buf, sizeof(buf))); + svc_process(rqstp); } - flush_signals(current); - - /* - * Check whether there's a new lockd process before - * shutting down the hosts and clearing the slot. - */ - if (!nlmsvc_pid || current->pid == nlmsvc_pid) { - if (nlmsvc_ops) - nlmsvc_invalidate_all(); - nlm_shutdown_hosts(); - nlmsvc_pid = 0; - } else - printk(KERN_DEBUG - "lockd: new process, skipping host shutdown\n"); - wake_up(&lockd_exit); - - /* Exit the RPC thread */ - svc_exit_thread(rqstp); - - /* release rpciod */ - rpciod_down(); - - /* Release module */ + cancel_delayed_work_sync(&grace_period_end); + locks_end_grace(&lockd_manager); + if (nlmsvc_ops) + nlmsvc_invalidate_all(); + nlm_shutdown_hosts(); unlock_kernel(); - module_put_and_exit(0); + return 0; +} + +static int create_lockd_listener(struct svc_serv *serv, char *name, + unsigned short port) +{ + struct svc_xprt *xprt; + + xprt = svc_find_xprt(serv, name, 0, 0); + if (xprt == NULL) + return svc_create_xprt(serv, name, port, SVC_SOCK_DEFAULTS); + + svc_xprt_put(xprt); + return 0; +} + +/* + * Ensure there are active UDP and TCP listeners for lockd. + * + * Even if we have only TCP NFS mounts and/or TCP NFSDs, some + * local services (such as rpc.statd) still require UDP, and + * some NFS servers do not yet support NLM over TCP. + * + * Returns zero if all listeners are available; otherwise a + * negative errno value is returned. + */ +static int make_socks(struct svc_serv *serv) +{ + static int warned; + int err; + + err = create_lockd_listener(serv, "udp", nlm_udpport); + if (err < 0) + goto out_err; + + err = create_lockd_listener(serv, "tcp", nlm_tcpport); + if (err < 0) + goto out_err; + + warned = 0; + return 0; + +out_err: + if (warned++ == 0) + printk(KERN_WARNING + "lockd_up: makesock failed, error=%d\n", err); + return err; } /* * Bring up the lockd process if it's not already up. */ -int -lockd_up(void) +int lockd_up(void) { - static int warned; - struct svc_serv * serv; - int error = 0; + struct svc_serv *serv; + int error = 0; mutex_lock(&nlmsvc_mutex); /* - * Unconditionally increment the user count ... this is - * the number of clients who _want_ a lockd process. - */ - nlmsvc_users++; - /* * Check whether we're already up and running. */ - if (nlmsvc_pid) + if (nlmsvc_rqst) goto out; /* * Sanity check: if there's no pid, * we should be the first user ... */ - if (nlmsvc_users > 1) + if (nlmsvc_users) printk(KERN_WARNING "lockd_up: no pid, %d users??\n", nlmsvc_users); error = -ENOMEM; - serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE); + serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, nlmsvc_family, NULL); if (!serv) { printk(KERN_WARNING "lockd_up: create service failed\n"); goto out; } - if ((error = svc_makesock(serv, IPPROTO_UDP, nlm_udpport)) < 0 -#ifdef CONFIG_NFSD_TCP - || (error = svc_makesock(serv, IPPROTO_TCP, nlm_tcpport)) < 0 -#endif - ) { - if (warned++ == 0) - printk(KERN_WARNING - "lockd_up: makesock failed, error=%d\n", error); + error = make_socks(serv); + if (error < 0) goto destroy_and_out; - } - warned = 0; /* * Create the kernel thread and wait for it to start. */ - error = svc_create_thread(lockd, serv); - if (error) { + nlmsvc_rqst = svc_prepare_thread(serv, &serv->sv_pools[0]); + if (IS_ERR(nlmsvc_rqst)) { + error = PTR_ERR(nlmsvc_rqst); + nlmsvc_rqst = NULL; + printk(KERN_WARNING + "lockd_up: svc_rqst allocation failed, error=%d\n", + error); + goto destroy_and_out; + } + + svc_sock_update_bufs(serv); + serv->sv_maxconn = nlm_max_connections; + + nlmsvc_task = kthread_run(lockd, nlmsvc_rqst, serv->sv_name); + if (IS_ERR(nlmsvc_task)) { + error = PTR_ERR(nlmsvc_task); + svc_exit_thread(nlmsvc_rqst); + nlmsvc_task = NULL; + nlmsvc_rqst = NULL; printk(KERN_WARNING - "lockd_up: create thread failed, error=%d\n", error); + "lockd_up: kthread_run failed, error=%d\n", error); goto destroy_and_out; } - wait_for_completion(&lockd_start_done); /* * Note: svc_serv structures have an initial use count of 1, @@ -273,10 +318,12 @@ lockd_up(void) destroy_and_out: svc_destroy(serv); out: + if (!error) + nlmsvc_users++; mutex_unlock(&nlmsvc_mutex); return error; } -EXPORT_SYMBOL(lockd_up); +EXPORT_SYMBOL_GPL(lockd_up); /* * Decrement the user count and bring down lockd if we're the last. @@ -284,49 +331,35 @@ EXPORT_SYMBOL(lockd_up); void lockd_down(void) { - static int warned; - mutex_lock(&nlmsvc_mutex); if (nlmsvc_users) { if (--nlmsvc_users) goto out; - } else - printk(KERN_WARNING "lockd_down: no users! pid=%d\n", nlmsvc_pid); - - if (!nlmsvc_pid) { - if (warned++ == 0) - printk(KERN_WARNING "lockd_down: no lockd running.\n"); - goto out; + } else { + printk(KERN_ERR "lockd_down: no users! task=%p\n", + nlmsvc_task); + BUG(); } - warned = 0; - kill_proc(nlmsvc_pid, SIGKILL, 1); - /* - * Wait for the lockd process to exit, but since we're holding - * the lockd semaphore, we can't wait around forever ... - */ - clear_thread_flag(TIF_SIGPENDING); - interruptible_sleep_on_timeout(&lockd_exit, HZ); - if (nlmsvc_pid) { - printk(KERN_WARNING - "lockd_down: lockd failed to exit, clearing pid\n"); - nlmsvc_pid = 0; + if (!nlmsvc_task) { + printk(KERN_ERR "lockd_down: no lockd running.\n"); + BUG(); } - spin_lock_irq(¤t->sighand->siglock); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + kthread_stop(nlmsvc_task); + svc_exit_thread(nlmsvc_rqst); + nlmsvc_task = NULL; + nlmsvc_rqst = NULL; out: mutex_unlock(&nlmsvc_mutex); } -EXPORT_SYMBOL(lockd_down); +EXPORT_SYMBOL_GPL(lockd_down); + +#ifdef CONFIG_SYSCTL /* * Sysctl parameters (same as module parameters, different interface). */ -/* Something that isn't CTL_ANY, CTL_NONE or a value that may clash. */ -#define CTL_UNNUMBERED -2 - static ctl_table nlm_sysctls[] = { { .ctl_name = CTL_UNNUMBERED, @@ -368,6 +401,22 @@ static ctl_table nlm_sysctls[] = { .extra1 = (int *) &nlm_port_min, .extra2 = (int *) &nlm_port_max, }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "nsm_use_hostnames", + .data = &nsm_use_hostnames, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "nsm_local_state", + .data = &nsm_local_state, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, { .ctl_name = 0 } }; @@ -391,8 +440,10 @@ static ctl_table nlm_sysctl_root[] = { { .ctl_name = 0 } }; +#endif /* CONFIG_SYSCTL */ + /* - * Module (and driverfs) parameters. + * Module (and sysfs) parameters. */ #define param_set_min_max(name, type, which_strtol, min, max) \ @@ -456,6 +507,8 @@ module_param_call(nlm_udpport, param_set_port, param_get_int, &nlm_udpport, 0644); module_param_call(nlm_tcpport, param_set_port, param_get_int, &nlm_tcpport, 0644); +module_param(nsm_use_hostnames, bool, 0644); +module_param(nlm_max_connections, uint, 0644); /* * Initialising and terminating the module. @@ -463,15 +516,21 @@ module_param_call(nlm_tcpport, param_set_port, param_get_int, static int __init init_nlm(void) { - nlm_sysctl_table = register_sysctl_table(nlm_sysctl_root, 0); +#ifdef CONFIG_SYSCTL + nlm_sysctl_table = register_sysctl_table(nlm_sysctl_root); return nlm_sysctl_table ? 0 : -ENOMEM; +#else + return 0; +#endif } static void __exit exit_nlm(void) { /* FIXME: delete all NLM clients */ nlm_shutdown_hosts(); +#ifdef CONFIG_SYSCTL unregister_sysctl_table(nlm_sysctl_table); +#endif } module_init(init_nlm);