LKML Archive on lore.kernel.org help / color / mirror / Atom feed
* [PATCH 0/4] Intro: convert lockd to kthread and fix use-after-free (try #8) @ 2008-01-14 14:05 Jeff Layton 2008-01-14 14:05 ` [PATCH 1/4] SUNRPC: spin svc_rqst initialization to its own function Jeff Layton 0 siblings, 1 reply; 10+ messages in thread From: Jeff Layton @ 2008-01-14 14:05 UTC (permalink / raw) To: neilb; +Cc: linux-nfs, linux-kernel This is the eighth patchset to fix the use-after-free problem in lockd which we originally discussed back in October. Along the way, Christoph Hellwig mentioned that it would be advantageous to convert lockd to use the kthread API. This patch set first makes that change and then patches it to actually fix the use after free problem. The main changes from the last patchset are: - dropped the try_to_freeze() patch. svc_recv calls try_to_freeze() too, so that wasn't really necessary. I added a comment to clarify that for future reference. - fixed a race whereby make_socks() could get called with a null nlmsvc_serv pointer and cause an oops. Thanks to Christoph Hellwig for the original patch. - moved lock_kernel() down slightly in the lockd() function, and unlock_kernel() up. It's not much of a change, but it's a start. I also added a FIXME comment that we should fix lockd to not run under the BKL. Perhaps someone can clarify why lockd() does this. I've done some very basic testing and everything seems to work as expected. I've also tested this against the reproducer that I have for the use-after-free problem and this does fix it. I've tried to make this cleanly bisectable, but have only really tested the final result. Many thanks to Trond Myklebust, Chuck Lever, Neil Brown and Christoph Hellwig for their guidance on this. Signed-off-by: Jeff Layton <jlayton@redhat.com> ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/4] SUNRPC: spin svc_rqst initialization to its own function 2008-01-14 14:05 [PATCH 0/4] Intro: convert lockd to kthread and fix use-after-free (try #8) Jeff Layton @ 2008-01-14 14:05 ` Jeff Layton 2008-01-14 14:05 ` [PATCH 2/4] SUNRPC: export svc_sock_update_bufs Jeff Layton 2008-01-18 20:59 ` [PATCH 1/4] SUNRPC: spin svc_rqst initialization to its own function J. Bruce Fields 0 siblings, 2 replies; 10+ messages in thread From: Jeff Layton @ 2008-01-14 14:05 UTC (permalink / raw) To: neilb; +Cc: linux-nfs, linux-kernel Move the initialzation in __svc_create_thread that happens prior to thread creation to a new function. Export the function to allow services to have better control over the svc_rqst structs. Also rearrange the rqstp initialization to prevent NULL pointer dereferences in svc_exit_thread in case allocations fail. Signed-off-by: Jeff Layton <jlayton@redhat.com> --- include/linux/sunrpc/svc.h | 2 + net/sunrpc/svc.c | 59 +++++++++++++++++++++++++++++++------------ 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 8531a70..5f07300 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -382,6 +382,8 @@ struct svc_procedure { */ struct svc_serv * svc_create(struct svc_program *, unsigned int, void (*shutdown)(struct svc_serv*)); +struct svc_rqst *svc_prepare_thread(struct svc_serv *serv, + struct svc_pool *pool); int svc_create_thread(svc_thread_fn, struct svc_serv *); void svc_exit_thread(struct svc_rqst *); struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int, diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index fca17d0..f9636bf 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -538,31 +538,17 @@ svc_release_buffer(struct svc_rqst *rqstp) put_page(rqstp->rq_pages[i]); } -/* - * Create a thread in the given pool. Caller must hold BKL. - * On a NUMA or SMP machine, with a multi-pool serv, the thread - * will be restricted to run on the cpus belonging to the pool. - */ -static int -__svc_create_thread(svc_thread_fn func, struct svc_serv *serv, - struct svc_pool *pool) +struct svc_rqst * +svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool) { struct svc_rqst *rqstp; - int error = -ENOMEM; - int have_oldmask = 0; - cpumask_t oldmask; rqstp = kzalloc(sizeof(*rqstp), GFP_KERNEL); if (!rqstp) - goto out; + goto out_enomem; init_waitqueue_head(&rqstp->rq_wait); - if (!(rqstp->rq_argp = kmalloc(serv->sv_xdrsize, GFP_KERNEL)) - || !(rqstp->rq_resp = kmalloc(serv->sv_xdrsize, GFP_KERNEL)) - || !svc_init_buffer(rqstp, serv->sv_max_mesg)) - goto out_thread; - serv->sv_nrthreads++; spin_lock_bh(&pool->sp_lock); pool->sp_nrthreads++; @@ -571,6 +557,45 @@ __svc_create_thread(svc_thread_fn func, struct svc_serv *serv, rqstp->rq_server = serv; rqstp->rq_pool = pool; + rqstp->rq_argp = kmalloc(serv->sv_xdrsize, GFP_KERNEL); + if (!rqstp->rq_argp) + goto out_thread; + + rqstp->rq_resp = kmalloc(serv->sv_xdrsize, GFP_KERNEL); + if (!rqstp->rq_resp) + goto out_thread; + + if (!svc_init_buffer(rqstp, serv->sv_max_mesg)) + goto out_thread; + + return rqstp; +out_thread: + svc_exit_thread(rqstp); +out_enomem: + return ERR_PTR(-ENOMEM); +} +EXPORT_SYMBOL(svc_prepare_thread); + +/* + * Create a thread in the given pool. Caller must hold BKL. + * On a NUMA or SMP machine, with a multi-pool serv, the thread + * will be restricted to run on the cpus belonging to the pool. + */ +static int +__svc_create_thread(svc_thread_fn func, struct svc_serv *serv, + struct svc_pool *pool) +{ + struct svc_rqst *rqstp; + int error = -ENOMEM; + int have_oldmask = 0; + cpumask_t oldmask; + + rqstp = svc_prepare_thread(serv, pool); + if (IS_ERR(rqstp)) { + error = PTR_ERR(rqstp); + goto out; + } + if (serv->sv_nrpools > 1) have_oldmask = svc_pool_map_set_cpumask(pool->sp_id, &oldmask); -- 1.5.3.7 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 2/4] SUNRPC: export svc_sock_update_bufs 2008-01-14 14:05 ` [PATCH 1/4] SUNRPC: spin svc_rqst initialization to its own function Jeff Layton @ 2008-01-14 14:05 ` Jeff Layton 2008-01-14 14:05 ` [PATCH 3/4] NLM: Convert lockd to use kthreads Jeff Layton 2008-01-18 20:59 ` [PATCH 1/4] SUNRPC: spin svc_rqst initialization to its own function J. Bruce Fields 1 sibling, 1 reply; 10+ messages in thread From: Jeff Layton @ 2008-01-14 14:05 UTC (permalink / raw) To: neilb; +Cc: linux-nfs, linux-kernel Needed since the plan is to not have a svc_create_thread helper and to have current users of that function just call kthread_run directly. Signed-off-by: Jeff Layton <jlayton@redhat.com> --- net/sunrpc/svcsock.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 057c870..f2bef16 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -1407,6 +1407,7 @@ svc_sock_update_bufs(struct svc_serv *serv) } spin_unlock_bh(&serv->sv_lock); } +EXPORT_SYMBOL(svc_sock_update_bufs); /* * Receive the next request on any socket. This code is carefully -- 1.5.3.7 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 3/4] NLM: Convert lockd to use kthreads 2008-01-14 14:05 ` [PATCH 2/4] SUNRPC: export svc_sock_update_bufs Jeff Layton @ 2008-01-14 14:05 ` Jeff Layton 2008-01-14 14:05 ` [PATCH 4/4] NLM: have nlm_shutdown_hosts kill off all NLM RPC tasks Jeff Layton 0 siblings, 1 reply; 10+ messages in thread From: Jeff Layton @ 2008-01-14 14:05 UTC (permalink / raw) To: neilb; +Cc: linux-nfs, linux-kernel Have lockd_up start lockd using kthread_run. With this change, lockd_down now blocks until lockd actually exits, so there's no longer need for the waitqueue code at the end of lockd_down. This also means that only one lockd can be running at a time which simplifies the code within lockd's main loop. Signed-off-by: Jeff Layton <jlayton@redhat.com> --- fs/lockd/svc.c | 131 ++++++++++++++++++++++++------------------------------- 1 files changed, 57 insertions(+), 74 deletions(-) diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 82e2192..55fdd97 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -25,6 +25,7 @@ #include <linux/smp.h> #include <linux/smp_lock.h> #include <linux/mutex.h> +#include <linux/kthread.h> #include <linux/freezer.h> #include <linux/sunrpc/types.h> @@ -48,14 +49,11 @@ EXPORT_SYMBOL(nlmsvc_ops); static DEFINE_MUTEX(nlmsvc_mutex); static unsigned int nlmsvc_users; -static pid_t nlmsvc_pid; +static struct task_struct *nlmsvc_task; static struct svc_serv *nlmsvc_serv; int nlmsvc_grace_period; unsigned long nlmsvc_timeout; -static DECLARE_COMPLETION(lockd_start_done); -static DECLARE_WAIT_QUEUE_HEAD(lockd_exit); - /* * These can be set at insmod time (useful for NFS as root filesystem), * and also changed through the sysctl interface. -- Jamie Lokier, Aug 2003 @@ -111,35 +109,30 @@ static inline void clear_grace_period(void) /* * This is the lockd kernel thread */ -static void -lockd(struct svc_rqst *rqstp) +static int +lockd(void *vrqstp) { int err = 0; + struct svc_rqst *rqstp = vrqstp; 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; - nlmsvc_serv = rqstp->rq_server; - complete(&lockd_start_done); - - 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); 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; @@ -148,10 +141,9 @@ lockd(struct svc_rqst *rqstp) /* * 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; char buf[RPC_MAX_ADDRBUFLEN]; @@ -195,28 +187,19 @@ lockd(struct svc_rqst *rqstp) } flush_signals(current); + if (nlmsvc_ops) + nlmsvc_invalidate_all(); + nlm_shutdown_hosts(); - /* - * 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; - nlmsvc_serv = NULL; - } else - printk(KERN_DEBUG - "lockd: new process, skipping host shutdown\n"); - wake_up(&lockd_exit); + unlock_kernel(); + + nlmsvc_task = NULL; + nlmsvc_serv = NULL; /* Exit the RPC thread */ svc_exit_thread(rqstp); - /* Release module */ - unlock_kernel(); - module_put_and_exit(0); + return 0; } @@ -266,14 +249,15 @@ static int make_socks(struct svc_serv *serv, int proto) int lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */ { - struct svc_serv * serv; - int error = 0; + struct svc_serv *serv; + struct svc_rqst *rqstp; + int error = 0; mutex_lock(&nlmsvc_mutex); /* * Check whether we're already up and running. */ - if (nlmsvc_pid) { + if (nlmsvc_serv) { if (proto) error = make_socks(nlmsvc_serv, proto); goto out; @@ -300,13 +284,28 @@ lockd_up(int proto) /* Maybe add a 'family' option when IPv6 is supported ?? */ /* * Create the kernel thread and wait for it to start. */ - error = svc_create_thread(lockd, serv); - if (error) { + rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]); + if (IS_ERR(rqstp)) { + error = PTR_ERR(rqstp); + printk(KERN_WARNING + "lockd_up: svc_rqst allocation failed, error=%d\n", + error); + goto destroy_and_out; + } + + svc_sock_update_bufs(serv); + nlmsvc_serv = rqstp->rq_server; + + nlmsvc_task = kthread_run(lockd, rqstp, serv->sv_name); + if (IS_ERR(nlmsvc_task)) { + error = PTR_ERR(nlmsvc_task); + nlmsvc_task = NULL; + nlmsvc_serv = NULL; printk(KERN_WARNING - "lockd_up: create thread failed, error=%d\n", error); + "lockd_up: kthread_run failed, error=%d\n", error); + svc_exit_thread(rqstp); goto destroy_and_out; } - wait_for_completion(&lockd_start_done); /* * Note: svc_serv structures have an initial use count of 1, @@ -328,37 +327,21 @@ 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); out: mutex_unlock(&nlmsvc_mutex); } -- 1.5.3.7 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 4/4] NLM: have nlm_shutdown_hosts kill off all NLM RPC tasks 2008-01-14 14:05 ` [PATCH 3/4] NLM: Convert lockd to use kthreads Jeff Layton @ 2008-01-14 14:05 ` Jeff Layton 2008-01-18 21:43 ` J. Bruce Fields 0 siblings, 1 reply; 10+ messages in thread From: Jeff Layton @ 2008-01-14 14:05 UTC (permalink / raw) To: neilb; +Cc: linux-nfs, linux-kernel If we're shutting down all the nlm_hosts anyway, then it doesn't make sense to allow RPC calls to linger. Allowing them to do so can mean that the RPC calls can outlive the currently running lockd and can lead to a use after free situation. Signed-off-by: Jeff Layton <jlayton@redhat.com> --- fs/lockd/host.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 572601e..8771484 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -377,8 +377,10 @@ nlm_shutdown_hosts(void) /* First, make all hosts eligible for gc */ dprintk("lockd: nuking all hosts...\n"); for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { - hlist_for_each_entry(host, pos, chain, h_hash) + hlist_for_each_entry(host, pos, chain, h_hash) { host->h_expires = jiffies - 1; + rpc_killall_tasks(host->h_rpcclnt); + } } /* Then, perform a garbage collection pass */ -- 1.5.3.7 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 4/4] NLM: have nlm_shutdown_hosts kill off all NLM RPC tasks 2008-01-14 14:05 ` [PATCH 4/4] NLM: have nlm_shutdown_hosts kill off all NLM RPC tasks Jeff Layton @ 2008-01-18 21:43 ` J. Bruce Fields 2008-01-18 22:07 ` Jeff Layton 0 siblings, 1 reply; 10+ messages in thread From: J. Bruce Fields @ 2008-01-18 21:43 UTC (permalink / raw) To: Jeff Layton; +Cc: neilb, linux-nfs, linux-kernel On Mon, Jan 14, 2008 at 09:05:18AM -0500, Jeff Layton wrote: > If we're shutting down all the nlm_hosts anyway, then it doesn't make > sense to allow RPC calls to linger. Allowing them to do so can mean > that the RPC calls can outlive the currently running lockd and can lead > to a use after free situation. I assume that all new rpc calls are created by the lockd thread itself (which also calls nlm_shutdown_hosts(), which guarantees that there can't be someone about to make an rpc call using the clnt we're destroying here? By the way, any idea what the nlm_shutdown_hosts() call in exit_nlm() is doing? --b. > > Signed-off-by: Jeff Layton <jlayton@redhat.com> > --- > fs/lockd/host.c | 4 +++- > 1 files changed, 3 insertions(+), 1 deletions(-) > > diff --git a/fs/lockd/host.c b/fs/lockd/host.c > index 572601e..8771484 100644 > --- a/fs/lockd/host.c > +++ b/fs/lockd/host.c > @@ -377,8 +377,10 @@ nlm_shutdown_hosts(void) > /* First, make all hosts eligible for gc */ > dprintk("lockd: nuking all hosts...\n"); > for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) { > - hlist_for_each_entry(host, pos, chain, h_hash) > + hlist_for_each_entry(host, pos, chain, h_hash) { > host->h_expires = jiffies - 1; > + rpc_killall_tasks(host->h_rpcclnt); > + } > } > > /* Then, perform a garbage collection pass */ > -- > 1.5.3.7 > > - > To unsubscribe from this list: send the line "unsubscribe linux-nfs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 4/4] NLM: have nlm_shutdown_hosts kill off all NLM RPC tasks 2008-01-18 21:43 ` J. Bruce Fields @ 2008-01-18 22:07 ` Jeff Layton 0 siblings, 0 replies; 10+ messages in thread From: Jeff Layton @ 2008-01-18 22:07 UTC (permalink / raw) To: J. Bruce Fields; +Cc: neilb, linux-nfs, linux-kernel On Fri, 18 Jan 2008 16:43:45 -0500 "J. Bruce Fields" <bfields@fieldses.org> wrote: > On Mon, Jan 14, 2008 at 09:05:18AM -0500, Jeff Layton wrote: > > If we're shutting down all the nlm_hosts anyway, then it doesn't > > make sense to allow RPC calls to linger. Allowing them to do so can > > mean that the RPC calls can outlive the currently running lockd and > > can lead to a use after free situation. > > I assume that all new rpc calls are created by the lockd thread itself > (which also calls nlm_shutdown_hosts(), which guarantees that there > can't be someone about to make an rpc call using the clnt we're > destroying here? > I believe that's correct. > By the way, any idea what the nlm_shutdown_hosts() call in exit_nlm() > is doing? > Before this patchset, it was possible for more than one lockd to be up at a time, and I suppose there could have been races that would cause both to exit without ever calling nlm_shutdown_hosts. With this patchset, we may be able to remove that. I suspect that it's probably a noop now. That said, even after spending a fair bit of time in this code, I'm not entirely comfortable with it. I suggest that we take the incremental approach to lockd changes unless someone here is sure :-). Cheers, -- Jeff Layton <jlayton@redhat.com> ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] SUNRPC: spin svc_rqst initialization to its own function 2008-01-14 14:05 ` [PATCH 1/4] SUNRPC: spin svc_rqst initialization to its own function Jeff Layton 2008-01-14 14:05 ` [PATCH 2/4] SUNRPC: export svc_sock_update_bufs Jeff Layton @ 2008-01-18 20:59 ` J. Bruce Fields 2008-01-18 21:48 ` Jeff Layton 1 sibling, 1 reply; 10+ messages in thread From: J. Bruce Fields @ 2008-01-18 20:59 UTC (permalink / raw) To: Jeff Layton; +Cc: neilb, linux-nfs, linux-kernel On Mon, Jan 14, 2008 at 09:05:15AM -0500, Jeff Layton wrote: > Move the initialzation in __svc_create_thread that happens prior to > thread creation to a new function. Export the function to allow > services to have better control over the svc_rqst structs. > > Also rearrange the rqstp initialization to prevent NULL pointer > dereferences in svc_exit_thread in case allocations fail. Those NULL dereferences are from the list_del(&rqstp->rq_all); ? OK, make sense. Thanks! --b. > > Signed-off-by: Jeff Layton <jlayton@redhat.com> > --- > include/linux/sunrpc/svc.h | 2 + > net/sunrpc/svc.c | 59 +++++++++++++++++++++++++++++++------------ > 2 files changed, 44 insertions(+), 17 deletions(-) > > diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h > index 8531a70..5f07300 100644 > --- a/include/linux/sunrpc/svc.h > +++ b/include/linux/sunrpc/svc.h > @@ -382,6 +382,8 @@ struct svc_procedure { > */ > struct svc_serv * svc_create(struct svc_program *, unsigned int, > void (*shutdown)(struct svc_serv*)); > +struct svc_rqst *svc_prepare_thread(struct svc_serv *serv, > + struct svc_pool *pool); > int svc_create_thread(svc_thread_fn, struct svc_serv *); > void svc_exit_thread(struct svc_rqst *); > struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int, > diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c > index fca17d0..f9636bf 100644 > --- a/net/sunrpc/svc.c > +++ b/net/sunrpc/svc.c > @@ -538,31 +538,17 @@ svc_release_buffer(struct svc_rqst *rqstp) > put_page(rqstp->rq_pages[i]); > } > > -/* > - * Create a thread in the given pool. Caller must hold BKL. > - * On a NUMA or SMP machine, with a multi-pool serv, the thread > - * will be restricted to run on the cpus belonging to the pool. > - */ > -static int > -__svc_create_thread(svc_thread_fn func, struct svc_serv *serv, > - struct svc_pool *pool) > +struct svc_rqst * > +svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool) > { > struct svc_rqst *rqstp; > - int error = -ENOMEM; > - int have_oldmask = 0; > - cpumask_t oldmask; > > rqstp = kzalloc(sizeof(*rqstp), GFP_KERNEL); > if (!rqstp) > - goto out; > + goto out_enomem; > > init_waitqueue_head(&rqstp->rq_wait); > > - if (!(rqstp->rq_argp = kmalloc(serv->sv_xdrsize, GFP_KERNEL)) > - || !(rqstp->rq_resp = kmalloc(serv->sv_xdrsize, GFP_KERNEL)) > - || !svc_init_buffer(rqstp, serv->sv_max_mesg)) > - goto out_thread; > - > serv->sv_nrthreads++; > spin_lock_bh(&pool->sp_lock); > pool->sp_nrthreads++; > @@ -571,6 +557,45 @@ __svc_create_thread(svc_thread_fn func, struct svc_serv *serv, > rqstp->rq_server = serv; > rqstp->rq_pool = pool; > > + rqstp->rq_argp = kmalloc(serv->sv_xdrsize, GFP_KERNEL); > + if (!rqstp->rq_argp) > + goto out_thread; > + > + rqstp->rq_resp = kmalloc(serv->sv_xdrsize, GFP_KERNEL); > + if (!rqstp->rq_resp) > + goto out_thread; > + > + if (!svc_init_buffer(rqstp, serv->sv_max_mesg)) > + goto out_thread; > + > + return rqstp; > +out_thread: > + svc_exit_thread(rqstp); > +out_enomem: > + return ERR_PTR(-ENOMEM); > +} > +EXPORT_SYMBOL(svc_prepare_thread); > + > +/* > + * Create a thread in the given pool. Caller must hold BKL. > + * On a NUMA or SMP machine, with a multi-pool serv, the thread > + * will be restricted to run on the cpus belonging to the pool. > + */ > +static int > +__svc_create_thread(svc_thread_fn func, struct svc_serv *serv, > + struct svc_pool *pool) > +{ > + struct svc_rqst *rqstp; > + int error = -ENOMEM; > + int have_oldmask = 0; > + cpumask_t oldmask; > + > + rqstp = svc_prepare_thread(serv, pool); > + if (IS_ERR(rqstp)) { > + error = PTR_ERR(rqstp); > + goto out; > + } > + > if (serv->sv_nrpools > 1) > have_oldmask = svc_pool_map_set_cpumask(pool->sp_id, &oldmask); > > -- > 1.5.3.7 > > - > To unsubscribe from this list: send the line "unsubscribe linux-nfs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] SUNRPC: spin svc_rqst initialization to its own function 2008-01-18 20:59 ` [PATCH 1/4] SUNRPC: spin svc_rqst initialization to its own function J. Bruce Fields @ 2008-01-18 21:48 ` Jeff Layton 2008-01-18 21:51 ` J. Bruce Fields 0 siblings, 1 reply; 10+ messages in thread From: Jeff Layton @ 2008-01-18 21:48 UTC (permalink / raw) To: J. Bruce Fields; +Cc: neilb, linux-nfs, linux-kernel On Fri, 18 Jan 2008 15:59:43 -0500 "J. Bruce Fields" <bfields@fieldses.org> wrote: > On Mon, Jan 14, 2008 at 09:05:15AM -0500, Jeff Layton wrote: > > Move the initialzation in __svc_create_thread that happens prior to > > thread creation to a new function. Export the function to allow > > services to have better control over the svc_rqst structs. > > > > Also rearrange the rqstp initialization to prevent NULL pointer > > dereferences in svc_exit_thread in case allocations fail. > > Those NULL dereferences are from the > > list_del(&rqstp->rq_all); > > ? OK, make sense. Thanks! > > --b. > Sorry, I didn't explain that well... This was the problem that Neil pointed out with the existing code. If the rqstp kzalloc succeeds, but the later kmallocs in __svc_create_thread fail, we goto here: out_thread: svc_exit_thread(rqstp); svc_exit_thread does this: struct svc_pool *pool = rqstp->rq_pool; ...and then later: spin_lock_bh(&pool->sp_lock); ...but rq_pool is set after the kmallocs, so if they fail rq_pool will be NULL, and we'll oops in that spin_lock_bh(). The fix is to move the kmallocs closer to the bottom in the new svc_prepare_thread function. Thanks, -- Jeff Layton <jlayton@redhat.com> ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 1/4] SUNRPC: spin svc_rqst initialization to its own function 2008-01-18 21:48 ` Jeff Layton @ 2008-01-18 21:51 ` J. Bruce Fields 0 siblings, 0 replies; 10+ messages in thread From: J. Bruce Fields @ 2008-01-18 21:51 UTC (permalink / raw) To: Jeff Layton; +Cc: neilb, linux-nfs, linux-kernel On Fri, Jan 18, 2008 at 04:48:44PM -0500, Jeff Layton wrote: > On Fri, 18 Jan 2008 15:59:43 -0500 > "J. Bruce Fields" <bfields@fieldses.org> wrote: > > > On Mon, Jan 14, 2008 at 09:05:15AM -0500, Jeff Layton wrote: > > > Move the initialzation in __svc_create_thread that happens prior to > > > thread creation to a new function. Export the function to allow > > > services to have better control over the svc_rqst structs. > > > > > > Also rearrange the rqstp initialization to prevent NULL pointer > > > dereferences in svc_exit_thread in case allocations fail. > > > > Those NULL dereferences are from the > > > > list_del(&rqstp->rq_all); > > > > ? OK, make sense. Thanks! > > > > --b. > > > > Sorry, I didn't explain that well... > > This was the problem that Neil pointed out with the existing code. If > the rqstp kzalloc succeeds, but the later kmallocs in > __svc_create_thread fail, we goto here: > > out_thread: > svc_exit_thread(rqstp); > > svc_exit_thread does this: > > struct svc_pool *pool = rqstp->rq_pool; > > ...and then later: > > spin_lock_bh(&pool->sp_lock); > > ...but rq_pool is set after the kmallocs, so if they fail rq_pool will > be NULL, and we'll oops in that spin_lock_bh(). > > The fix is to move the kmallocs closer to the bottom in the new > svc_prepare_thread function. OK, got it, thanks. --b. ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2008-01-18 22:07 UTC | newest] Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2008-01-14 14:05 [PATCH 0/4] Intro: convert lockd to kthread and fix use-after-free (try #8) Jeff Layton 2008-01-14 14:05 ` [PATCH 1/4] SUNRPC: spin svc_rqst initialization to its own function Jeff Layton 2008-01-14 14:05 ` [PATCH 2/4] SUNRPC: export svc_sock_update_bufs Jeff Layton 2008-01-14 14:05 ` [PATCH 3/4] NLM: Convert lockd to use kthreads Jeff Layton 2008-01-14 14:05 ` [PATCH 4/4] NLM: have nlm_shutdown_hosts kill off all NLM RPC tasks Jeff Layton 2008-01-18 21:43 ` J. Bruce Fields 2008-01-18 22:07 ` Jeff Layton 2008-01-18 20:59 ` [PATCH 1/4] SUNRPC: spin svc_rqst initialization to its own function J. Bruce Fields 2008-01-18 21:48 ` Jeff Layton 2008-01-18 21:51 ` J. Bruce Fields
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).