diff options
author | Trond Myklebust <trond.myklebust@primarydata.com> | 2017-04-26 11:55:27 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2017-04-27 18:00:16 -0400 |
commit | ed6473ddc704a2005b9900ca08e236ebb2d8540a (patch) | |
tree | 2d4a3fa182fcd7cef1fac00517d797b0ac6fa099 | |
parent | 9e0d87680d689f1758185851c3da6eafb16e71e1 (diff) |
NFSv4: Fix callback server shutdown
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>
-rw-r--r-- | fs/nfs/callback.c | 24 | ||||
-rw-r--r-- | include/linux/sunrpc/svc.h | 1 | ||||
-rw-r--r-- | net/sunrpc/svc.c | 38 |
3 files changed, 55 insertions, 8 deletions
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index c5e27ebd8da8..73a1f928226c 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c | |||
@@ -76,7 +76,10 @@ nfs4_callback_svc(void *vrqstp) | |||
76 | 76 | ||
77 | set_freezable(); | 77 | set_freezable(); |
78 | 78 | ||
79 | while (!kthread_should_stop()) { | 79 | while (!kthread_freezable_should_stop(NULL)) { |
80 | |||
81 | if (signal_pending(current)) | ||
82 | flush_signals(current); | ||
80 | /* | 83 | /* |
81 | * Listen for a request on the socket | 84 | * Listen for a request on the socket |
82 | */ | 85 | */ |
@@ -85,6 +88,8 @@ nfs4_callback_svc(void *vrqstp) | |||
85 | continue; | 88 | continue; |
86 | svc_process(rqstp); | 89 | svc_process(rqstp); |
87 | } | 90 | } |
91 | svc_exit_thread(rqstp); | ||
92 | module_put_and_exit(0); | ||
88 | return 0; | 93 | return 0; |
89 | } | 94 | } |
90 | 95 | ||
@@ -103,9 +108,10 @@ nfs41_callback_svc(void *vrqstp) | |||
103 | 108 | ||
104 | set_freezable(); | 109 | set_freezable(); |
105 | 110 | ||
106 | while (!kthread_should_stop()) { | 111 | while (!kthread_freezable_should_stop(NULL)) { |
107 | if (try_to_freeze()) | 112 | |
108 | continue; | 113 | if (signal_pending(current)) |
114 | flush_signals(current); | ||
109 | 115 | ||
110 | prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE); | 116 | prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE); |
111 | spin_lock_bh(&serv->sv_cb_lock); | 117 | spin_lock_bh(&serv->sv_cb_lock); |
@@ -121,11 +127,13 @@ nfs41_callback_svc(void *vrqstp) | |||
121 | error); | 127 | error); |
122 | } else { | 128 | } else { |
123 | spin_unlock_bh(&serv->sv_cb_lock); | 129 | spin_unlock_bh(&serv->sv_cb_lock); |
124 | schedule(); | 130 | if (!kthread_should_stop()) |
131 | schedule(); | ||
125 | finish_wait(&serv->sv_cb_waitq, &wq); | 132 | finish_wait(&serv->sv_cb_waitq, &wq); |
126 | } | 133 | } |
127 | flush_signals(current); | ||
128 | } | 134 | } |
135 | svc_exit_thread(rqstp); | ||
136 | module_put_and_exit(0); | ||
129 | return 0; | 137 | return 0; |
130 | } | 138 | } |
131 | 139 | ||
@@ -221,14 +229,14 @@ err_bind: | |||
221 | static struct svc_serv_ops nfs40_cb_sv_ops = { | 229 | static struct svc_serv_ops nfs40_cb_sv_ops = { |
222 | .svo_function = nfs4_callback_svc, | 230 | .svo_function = nfs4_callback_svc, |
223 | .svo_enqueue_xprt = svc_xprt_do_enqueue, | 231 | .svo_enqueue_xprt = svc_xprt_do_enqueue, |
224 | .svo_setup = svc_set_num_threads, | 232 | .svo_setup = svc_set_num_threads_sync, |
225 | .svo_module = THIS_MODULE, | 233 | .svo_module = THIS_MODULE, |
226 | }; | 234 | }; |
227 | #if defined(CONFIG_NFS_V4_1) | 235 | #if defined(CONFIG_NFS_V4_1) |
228 | static struct svc_serv_ops nfs41_cb_sv_ops = { | 236 | static struct svc_serv_ops nfs41_cb_sv_ops = { |
229 | .svo_function = nfs41_callback_svc, | 237 | .svo_function = nfs41_callback_svc, |
230 | .svo_enqueue_xprt = svc_xprt_do_enqueue, | 238 | .svo_enqueue_xprt = svc_xprt_do_enqueue, |
231 | .svo_setup = svc_set_num_threads, | 239 | .svo_setup = svc_set_num_threads_sync, |
232 | .svo_module = THIS_MODULE, | 240 | .svo_module = THIS_MODULE, |
233 | }; | 241 | }; |
234 | 242 | ||
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 6ef19cf658b4..94631026f79c 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h | |||
@@ -473,6 +473,7 @@ void svc_pool_map_put(void); | |||
473 | struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int, | 473 | struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int, |
474 | struct svc_serv_ops *); | 474 | struct svc_serv_ops *); |
475 | int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); | 475 | int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); |
476 | int svc_set_num_threads_sync(struct svc_serv *, struct svc_pool *, int); | ||
476 | int svc_pool_stats_open(struct svc_serv *serv, struct file *file); | 477 | int svc_pool_stats_open(struct svc_serv *serv, struct file *file); |
477 | void svc_destroy(struct svc_serv *); | 478 | void svc_destroy(struct svc_serv *); |
478 | void svc_shutdown_net(struct svc_serv *, struct net *); | 479 | void svc_shutdown_net(struct svc_serv *, struct net *); |
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 98dc33ae738b..bc0f5a0ecbdc 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c | |||
@@ -795,6 +795,44 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) | |||
795 | } | 795 | } |
796 | EXPORT_SYMBOL_GPL(svc_set_num_threads); | 796 | EXPORT_SYMBOL_GPL(svc_set_num_threads); |
797 | 797 | ||
798 | /* destroy old threads */ | ||
799 | static int | ||
800 | svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) | ||
801 | { | ||
802 | struct task_struct *task; | ||
803 | unsigned int state = serv->sv_nrthreads-1; | ||
804 | |||
805 | /* destroy old threads */ | ||
806 | do { | ||
807 | task = choose_victim(serv, pool, &state); | ||
808 | if (task == NULL) | ||
809 | break; | ||
810 | kthread_stop(task); | ||
811 | nrservs++; | ||
812 | } while (nrservs < 0); | ||
813 | return 0; | ||
814 | } | ||
815 | |||
816 | int | ||
817 | svc_set_num_threads_sync(struct svc_serv *serv, struct svc_pool *pool, int nrservs) | ||
818 | { | ||
819 | if (pool == NULL) { | ||
820 | /* The -1 assumes caller has done a svc_get() */ | ||
821 | nrservs -= (serv->sv_nrthreads-1); | ||
822 | } else { | ||
823 | spin_lock_bh(&pool->sp_lock); | ||
824 | nrservs -= pool->sp_nrthreads; | ||
825 | spin_unlock_bh(&pool->sp_lock); | ||
826 | } | ||
827 | |||
828 | if (nrservs > 0) | ||
829 | return svc_start_kthreads(serv, pool, nrservs); | ||
830 | if (nrservs < 0) | ||
831 | return svc_stop_kthreads(serv, pool, nrservs); | ||
832 | return 0; | ||
833 | } | ||
834 | EXPORT_SYMBOL_GPL(svc_set_num_threads_sync); | ||
835 | |||
798 | /* | 836 | /* |
799 | * Called from a server thread as it's exiting. Caller must hold the "service | 837 | * Called from a server thread as it's exiting. Caller must hold the "service |
800 | * mutex" for the service. | 838 | * mutex" for the service. |