From a277e33cbe3fdfb9a77b448ea3043be22f000dfd Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 20 Feb 2008 08:55:30 -0500 Subject: NFS: convert nfs4 callback thread to kthread API There's a general push to convert kernel threads to use the (much cleaner) kthread API. This patch converts the NFSv4 callback kernel thread to the kthread API. In addition to being generally cleaner this also removes the dependency on signals when shutting down the thread. Note that this patch depends on the recent patches to svc_recv() to make it check kthread_should_stop() periodically. Those patches are in Bruce's tree at the moment and are slated for 2.6.26 along with the lockd conversion, so this conversion is probably also appropriate for 2.6.26. Signed-off-by: Jeff Layton Acked-by: Trond Myklebust Signed-off-by: J. Bruce Fields --- fs/nfs/callback.c | 73 ++++++++++++++++++++++++++----------------------------- 1 file changed, 35 insertions(+), 38 deletions(-) (limited to 'fs/nfs/callback.c') diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 66648dd92d97..2e5de77ff030 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -27,9 +28,7 @@ struct nfs_callback_data { unsigned int users; struct svc_serv *serv; - pid_t pid; - struct completion started; - struct completion stopped; + struct task_struct *task; }; static struct nfs_callback_data nfs_callback_info; @@ -57,27 +56,20 @@ module_param_call(callback_tcpport, param_set_port, param_get_int, /* * This is the callback kernel thread. */ -static void nfs_callback_svc(struct svc_rqst *rqstp) +static int +nfs_callback_svc(void *vrqstp) { int err; + struct svc_rqst *rqstp = vrqstp; - __module_get(THIS_MODULE); - lock_kernel(); - - nfs_callback_info.pid = current->pid; - daemonize("nfsv4-svc"); - /* Process request with signals blocked, but allow SIGKILL. */ - allow_signal(SIGKILL); set_freezable(); - complete(&nfs_callback_info.started); - - for(;;) { - if (signalled()) { - if (nfs_callback_info.users == 0) - break; - flush_signals(current); - } + /* + * FIXME: do we really need to run this under the BKL? If so, please + * add a comment about what it's intended to protect. + */ + lock_kernel(); + while (!kthread_should_stop()) { /* * Listen for a request on the socket */ @@ -92,13 +84,10 @@ static void nfs_callback_svc(struct svc_rqst *rqstp) } svc_process(rqstp); } - - flush_signals(current); - svc_exit_thread(rqstp); - nfs_callback_info.pid = 0; - complete(&nfs_callback_info.stopped); unlock_kernel(); - module_put_and_exit(0); + nfs_callback_info.task = NULL; + svc_exit_thread(rqstp); + return 0; } /* @@ -107,14 +96,13 @@ static void nfs_callback_svc(struct svc_rqst *rqstp) int nfs_callback_up(void) { struct svc_serv *serv = NULL; + struct svc_rqst *rqstp; int ret = 0; lock_kernel(); mutex_lock(&nfs_callback_mutex); - if (nfs_callback_info.users++ || nfs_callback_info.pid != 0) + if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) goto out; - init_completion(&nfs_callback_info.started); - init_completion(&nfs_callback_info.stopped); serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); ret = -ENOMEM; if (!serv) @@ -127,15 +115,28 @@ int nfs_callback_up(void) nfs_callback_tcpport = ret; dprintk("Callback port = 0x%x\n", nfs_callback_tcpport); - ret = svc_create_thread(nfs_callback_svc, serv); - if (ret < 0) + rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]); + if (IS_ERR(rqstp)) { + ret = PTR_ERR(rqstp); goto out_err; + } + + svc_sock_update_bufs(serv); nfs_callback_info.serv = serv; - wait_for_completion(&nfs_callback_info.started); + + nfs_callback_info.task = kthread_run(nfs_callback_svc, rqstp, + "nfsv4-svc"); + if (IS_ERR(nfs_callback_info.task)) { + ret = PTR_ERR(nfs_callback_info.task); + nfs_callback_info.serv = NULL; + nfs_callback_info.task = NULL; + svc_exit_thread(rqstp); + goto out_err; + } out: /* * svc_create creates the svc_serv with sv_nrthreads == 1, and then - * svc_create_thread increments that. So we need to call svc_destroy + * svc_prepare_thread increments that. So we need to call svc_destroy * on both success and failure so that the refcount is 1 when the * thread exits. */ @@ -159,12 +160,8 @@ void nfs_callback_down(void) lock_kernel(); mutex_lock(&nfs_callback_mutex); nfs_callback_info.users--; - do { - if (nfs_callback_info.users != 0 || nfs_callback_info.pid == 0) - break; - if (kill_proc(nfs_callback_info.pid, SIGKILL, 1) < 0) - break; - } while (wait_for_completion_timeout(&nfs_callback_info.stopped, 5*HZ) == 0); + if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) + kthread_stop(nfs_callback_info.task); mutex_unlock(&nfs_callback_mutex); unlock_kernel(); } -- cgit v1.2.3 From e1ba1ab76e68de9f4a93fae8406627924efaed99 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 7 Apr 2008 13:09:47 -0400 Subject: nfsd: fix comment Obvious comment nit. Signed-off-by: J. Bruce Fields --- fs/nfs/callback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/nfs/callback.c') diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 2e5de77ff030..a9f153867554 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -153,7 +153,7 @@ out_err: } /* - * Kill the server process if it is not already up. + * Kill the server process if it is not already down. */ void nfs_callback_down(void) { -- cgit v1.2.3 From 06e02d66fa0055230efc2443c43ee4f3ab5eb0b6 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 8 Apr 2008 15:40:07 -0400 Subject: NFS: don't let nfs_callback_svc exit on unexpected svc_recv errors (try #2) When svc_recv returns an unexpected error, nfs_callback_svc will print a warning and exit. This problematic for several reasons. In particular, it will cause the reference counts for the thread to be wrong, and no new thread will be started until all nfs4 mounts are unmounted. Rather than exiting on error from svc_recv, have the thread do a 1s sleep and then retry the loop. This is unlikely to cause any harm, and if the error turns out to be something temporary then it may be able to recover. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfs/callback.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'fs/nfs/callback.c') diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index a9f153867554..5606ae3d72d3 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -59,7 +59,7 @@ module_param_call(callback_tcpport, param_set_port, param_get_int, static int nfs_callback_svc(void *vrqstp) { - int err; + int err, preverr = 0; struct svc_rqst *rqstp = vrqstp; set_freezable(); @@ -74,14 +74,20 @@ nfs_callback_svc(void *vrqstp) * Listen for a request on the socket */ err = svc_recv(rqstp, MAX_SCHEDULE_TIMEOUT); - if (err == -EAGAIN || err == -EINTR) + if (err == -EAGAIN || err == -EINTR) { + preverr = err; continue; + } if (err < 0) { - printk(KERN_WARNING - "%s: terminating on error %d\n", - __FUNCTION__, -err); - break; + if (err != preverr) { + printk(KERN_WARNING "%s: unexpected error " + "from svc_recv (%d)\n", __func__, err); + preverr = err; + } + schedule_timeout_uninterruptible(HZ); + continue; } + preverr = err; svc_process(rqstp); } unlock_kernel(); -- cgit v1.2.3