| From ed6473ddc704a2005b9900ca08e236ebb2d8540a Mon Sep 17 00:00:00 2001 |
| From: Trond Myklebust <trond.myklebust@primarydata.com> |
| Date: Wed, 26 Apr 2017 11:55:27 -0400 |
| Subject: NFSv4: Fix callback server shutdown |
| |
| From: Trond Myklebust <trond.myklebust@primarydata.com> |
| |
| commit ed6473ddc704a2005b9900ca08e236ebb2d8540a upstream. |
| |
| We want to use kthread_stop() in order to ensure the threads are |
| shut down before we tear down the nfs_callback_info in nfs_callback_down. |
| |
| Tested-and-reviewed-by: Kinglong Mee <kinglongmee@gmail.com> |
| Reported-by: Kinglong Mee <kinglongmee@gmail.com> |
| Fixes: bb6aeba736ba9 ("NFSv4.x: Switch to using svc_set_num_threads()...") |
| Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com> |
| Signed-off-by: J. Bruce Fields <bfields@redhat.com> |
| Cc: Jan Hudoba <kernel@jahu.sk> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/nfs/callback.c | 24 ++++++++++++++++-------- |
| include/linux/sunrpc/svc.h | 1 + |
| net/sunrpc/svc.c | 38 ++++++++++++++++++++++++++++++++++++++ |
| 3 files changed, 55 insertions(+), 8 deletions(-) |
| |
| --- a/fs/nfs/callback.c |
| +++ b/fs/nfs/callback.c |
| @@ -75,7 +75,10 @@ nfs4_callback_svc(void *vrqstp) |
| |
| set_freezable(); |
| |
| - while (!kthread_should_stop()) { |
| + while (!kthread_freezable_should_stop(NULL)) { |
| + |
| + if (signal_pending(current)) |
| + flush_signals(current); |
| /* |
| * Listen for a request on the socket |
| */ |
| @@ -84,6 +87,8 @@ nfs4_callback_svc(void *vrqstp) |
| continue; |
| svc_process(rqstp); |
| } |
| + svc_exit_thread(rqstp); |
| + module_put_and_exit(0); |
| return 0; |
| } |
| |
| @@ -102,9 +107,10 @@ nfs41_callback_svc(void *vrqstp) |
| |
| set_freezable(); |
| |
| - while (!kthread_should_stop()) { |
| - if (try_to_freeze()) |
| - continue; |
| + while (!kthread_freezable_should_stop(NULL)) { |
| + |
| + if (signal_pending(current)) |
| + flush_signals(current); |
| |
| prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE); |
| spin_lock_bh(&serv->sv_cb_lock); |
| @@ -120,11 +126,13 @@ nfs41_callback_svc(void *vrqstp) |
| error); |
| } else { |
| spin_unlock_bh(&serv->sv_cb_lock); |
| - schedule(); |
| + if (!kthread_should_stop()) |
| + schedule(); |
| finish_wait(&serv->sv_cb_waitq, &wq); |
| } |
| - flush_signals(current); |
| } |
| + svc_exit_thread(rqstp); |
| + module_put_and_exit(0); |
| return 0; |
| } |
| |
| @@ -220,14 +228,14 @@ err_bind: |
| static struct svc_serv_ops nfs40_cb_sv_ops = { |
| .svo_function = nfs4_callback_svc, |
| .svo_enqueue_xprt = svc_xprt_do_enqueue, |
| - .svo_setup = svc_set_num_threads, |
| + .svo_setup = svc_set_num_threads_sync, |
| .svo_module = THIS_MODULE, |
| }; |
| #if defined(CONFIG_NFS_V4_1) |
| static struct svc_serv_ops nfs41_cb_sv_ops = { |
| .svo_function = nfs41_callback_svc, |
| .svo_enqueue_xprt = svc_xprt_do_enqueue, |
| - .svo_setup = svc_set_num_threads, |
| + .svo_setup = svc_set_num_threads_sync, |
| .svo_module = THIS_MODULE, |
| }; |
| |
| --- a/include/linux/sunrpc/svc.h |
| +++ b/include/linux/sunrpc/svc.h |
| @@ -470,6 +470,7 @@ void svc_pool_map_put(void); |
| struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int, |
| struct svc_serv_ops *); |
| int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); |
| +int svc_set_num_threads_sync(struct svc_serv *, struct svc_pool *, int); |
| int svc_pool_stats_open(struct svc_serv *serv, struct file *file); |
| void svc_destroy(struct svc_serv *); |
| void svc_shutdown_net(struct svc_serv *, struct net *); |
| --- a/net/sunrpc/svc.c |
| +++ b/net/sunrpc/svc.c |
| @@ -795,6 +795,44 @@ svc_set_num_threads(struct svc_serv *ser |
| } |
| EXPORT_SYMBOL_GPL(svc_set_num_threads); |
| |
| +/* destroy old threads */ |
| +static int |
| +svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) |
| +{ |
| + struct task_struct *task; |
| + unsigned int state = serv->sv_nrthreads-1; |
| + |
| + /* destroy old threads */ |
| + do { |
| + task = choose_victim(serv, pool, &state); |
| + if (task == NULL) |
| + break; |
| + kthread_stop(task); |
| + nrservs++; |
| + } while (nrservs < 0); |
| + return 0; |
| +} |
| + |
| +int |
| +svc_set_num_threads_sync(struct svc_serv *serv, struct svc_pool *pool, int nrservs) |
| +{ |
| + if (pool == NULL) { |
| + /* The -1 assumes caller has done a svc_get() */ |
| + nrservs -= (serv->sv_nrthreads-1); |
| + } else { |
| + spin_lock_bh(&pool->sp_lock); |
| + nrservs -= pool->sp_nrthreads; |
| + spin_unlock_bh(&pool->sp_lock); |
| + } |
| + |
| + if (nrservs > 0) |
| + return svc_start_kthreads(serv, pool, nrservs); |
| + if (nrservs < 0) |
| + return svc_stop_kthreads(serv, pool, nrservs); |
| + return 0; |
| +} |
| +EXPORT_SYMBOL_GPL(svc_set_num_threads_sync); |
| + |
| /* |
| * Called from a server thread as it's exiting. Caller must hold the "service |
| * mutex" for the service. |