Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

SUNRPC: change how svc threads are asked to exit.

svc threads are currently stopped using kthread_stop(). This requires
identifying a specific thread. However we don't care which thread
stops, just as long as one does.

So instead, set a flag in the svc_pool to say that a thread needs to
die, and have each thread check this flag instead of calling
kthread_should_stop(). The first thread to find and clear this flag
then moves towards exiting.

This removes an explicit dependency on sp_all_threads which will make a
future patch simpler.

Signed-off-by: NeilBrown <neilb@suse.de>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>

authored by

NeilBrown and committed by
Chuck Lever
fa341560 f4578ba1

+61 -39
+2 -3
fs/lockd/svc.c
··· 24 24 #include <linux/uio.h> 25 25 #include <linux/smp.h> 26 26 #include <linux/mutex.h> 27 - #include <linux/kthread.h> 28 27 #include <linux/freezer.h> 29 28 #include <linux/inetdevice.h> 30 29 ··· 134 135 * The main request loop. We don't terminate until the last 135 136 * NFS mount or NFS daemon has gone away. 136 137 */ 137 - while (!kthread_should_stop()) { 138 + while (!svc_thread_should_stop(rqstp)) { 138 139 /* update sv_maxconn if it has changed */ 139 140 rqstp->rq_server->sv_maxconn = nlm_max_connections; 140 141 141 - nlmsvc_retry_blocked(); 142 + nlmsvc_retry_blocked(rqstp); 142 143 svc_recv(rqstp); 143 144 } 144 145 if (nlmsvc_ops)
+2 -3
fs/lockd/svclock.c
··· 30 30 #include <linux/sunrpc/svc_xprt.h> 31 31 #include <linux/lockd/nlm.h> 32 32 #include <linux/lockd/lockd.h> 33 - #include <linux/kthread.h> 34 33 #include <linux/exportfs.h> 35 34 36 35 #define NLMDBG_FACILITY NLMDBG_SVCLOCK ··· 1031 1032 * be retransmitted. 1032 1033 */ 1033 1034 void 1034 - nlmsvc_retry_blocked(void) 1035 + nlmsvc_retry_blocked(struct svc_rqst *rqstp) 1035 1036 { 1036 1037 unsigned long timeout = MAX_SCHEDULE_TIMEOUT; 1037 1038 struct nlm_block *block; 1038 1039 1039 1040 spin_lock(&nlm_blocked_lock); 1040 - while (!list_empty(&nlm_blocked) && !kthread_should_stop()) { 1041 + while (!list_empty(&nlm_blocked) && !svc_thread_should_stop(rqstp)) { 1041 1042 block = list_entry(nlm_blocked.next, struct nlm_block, b_list); 1042 1043 1043 1044 if (block->b_when == NLM_NEVER)
+1 -1
fs/nfs/callback.c
··· 78 78 79 79 set_freezable(); 80 80 81 - while (!kthread_should_stop()) 81 + while (!svc_thread_should_stop(rqstp)) 82 82 svc_recv(rqstp); 83 83 84 84 svc_exit_thread(rqstp);
+5 -3
fs/nfsd/nfs4proc.c
··· 1329 1329 * setup a work entry in the ssc delayed unmount list. 1330 1330 */ 1331 1331 static __be32 nfsd4_ssc_setup_dul(struct nfsd_net *nn, char *ipaddr, 1332 - struct nfsd4_ssc_umount_item **nsui) 1332 + struct nfsd4_ssc_umount_item **nsui, 1333 + struct svc_rqst *rqstp) 1333 1334 { 1334 1335 struct nfsd4_ssc_umount_item *ni = NULL; 1335 1336 struct nfsd4_ssc_umount_item *work = NULL; ··· 1352 1351 spin_unlock(&nn->nfsd_ssc_lock); 1353 1352 1354 1353 /* allow 20secs for mount/unmount for now - revisit */ 1355 - if (kthread_should_stop() || 1354 + if (svc_thread_should_stop(rqstp) || 1356 1355 (schedule_timeout(20*HZ) == 0)) { 1357 1356 finish_wait(&nn->nfsd_ssc_waitq, &wait); 1358 1357 kfree(work); ··· 1468 1467 goto out_free_rawdata; 1469 1468 snprintf(dev_name, len + 5, "%s%s%s:/", startsep, ipaddr, endsep); 1470 1469 1471 - status = nfsd4_ssc_setup_dul(nn, ipaddr, nsui); 1470 + status = nfsd4_ssc_setup_dul(nn, ipaddr, nsui, rqstp); 1472 1471 if (status) 1473 1472 goto out_free_devname; 1474 1473 if ((*nsui)->nsui_vfsmount) ··· 1643 1642 if (bytes_total == 0) 1644 1643 bytes_total = ULLONG_MAX; 1645 1644 do { 1645 + /* Only async copies can be stopped here */ 1646 1646 if (kthread_should_stop()) 1647 1647 break; 1648 1648 bytes_copied = nfsd_copy_file_range(src, src_pos, dst, dst_pos,
+1 -1
fs/nfsd/nfssvc.c
··· 957 957 /* 958 958 * The main request loop 959 959 */ 960 - while (!kthread_should_stop()) { 960 + while (!svc_thread_should_stop(rqstp)) { 961 961 /* Update sv_maxconn if it has changed */ 962 962 rqstp->rq_server->sv_maxconn = nn->max_connections; 963 963
+1 -1
include/linux/lockd/lockd.h
··· 282 282 struct nlm_host *, struct nlm_lock *, 283 283 struct nlm_lock *, struct nlm_cookie *); 284 284 __be32 nlmsvc_cancel_blocked(struct net *net, struct nlm_file *, struct nlm_lock *); 285 - void nlmsvc_retry_blocked(void); 285 + void nlmsvc_retry_blocked(struct svc_rqst *rqstp); 286 286 void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *, 287 287 nlm_host_match_fn_t match); 288 288 void nlmsvc_grant_reply(struct nlm_cookie *, __be32);
+25 -1
include/linux/sunrpc/svc.h
··· 50 50 enum { 51 51 SP_TASK_PENDING, /* still work to do even if no xprt is queued */ 52 52 SP_CONGESTED, /* all threads are busy, none idle */ 53 + SP_NEED_VICTIM, /* One thread needs to agree to exit */ 54 + SP_VICTIM_REMAINS, /* One thread needs to actually exit */ 53 55 }; 54 56 55 57 ··· 261 259 RQ_DROPME, /* drop current reply */ 262 260 RQ_SPLICE_OK, /* turned off in gss privacy to prevent 263 261 * encrypting page cache pages */ 264 - RQ_VICTIM, /* about to be shut down */ 262 + RQ_VICTIM, /* Have agreed to shut down */ 265 263 RQ_BUSY, /* request is busy */ 266 264 RQ_DATA, /* request has data */ 267 265 }; ··· 299 297 static inline struct sockaddr *svc_daddr(const struct svc_rqst *rqst) 300 298 { 301 299 return (struct sockaddr *) &rqst->rq_daddr; 300 + } 301 + 302 + /** 303 + * svc_thread_should_stop - check if this thread should stop 304 + * @rqstp: the thread that might need to stop 305 + * 306 + * To stop an svc thread, the pool flags SP_NEED_VICTIM and SP_VICTIM_REMAINS 307 + * are set. The first thread which sees SP_NEED_VICTIM clears it, becoming 308 + * the victim using this function. It should then promptly call 309 + * svc_exit_thread() to complete the process, clearing SP_VICTIM_REMAINS 310 + * so the task waiting for a thread to exit can wake and continue. 311 + * 312 + * Return values: 313 + * %true: caller should invoke svc_exit_thread() 314 + * %false: caller should do nothing 315 + */ 316 + static inline bool svc_thread_should_stop(struct svc_rqst *rqstp) 317 + { 318 + if (test_and_clear_bit(SP_NEED_VICTIM, &rqstp->rq_pool->sp_flags)) 319 + set_bit(RQ_VICTIM, &rqstp->rq_flags); 320 + 321 + return test_bit(RQ_VICTIM, &rqstp->rq_flags); 302 322 } 303 323 304 324 struct svc_deferred_req {
+21 -22
net/sunrpc/svc.c
··· 725 725 return pool ? pool : &serv->sv_pools[(*state)++ % serv->sv_nrpools]; 726 726 } 727 727 728 - static struct task_struct * 728 + static struct svc_pool * 729 729 svc_pool_victim(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state) 730 730 { 731 731 unsigned int i; 732 - struct task_struct *task = NULL; 733 732 734 733 if (pool != NULL) { 735 734 spin_lock_bh(&pool->sp_lock); 735 + if (pool->sp_nrthreads) 736 + goto found_pool; 737 + spin_unlock_bh(&pool->sp_lock); 738 + return NULL; 736 739 } else { 737 740 for (i = 0; i < serv->sv_nrpools; i++) { 738 741 pool = &serv->sv_pools[--(*state) % serv->sv_nrpools]; 739 742 spin_lock_bh(&pool->sp_lock); 740 - if (!list_empty(&pool->sp_all_threads)) 743 + if (pool->sp_nrthreads) 741 744 goto found_pool; 742 745 spin_unlock_bh(&pool->sp_lock); 743 746 } ··· 748 745 } 749 746 750 747 found_pool: 751 - if (!list_empty(&pool->sp_all_threads)) { 752 - struct svc_rqst *rqstp; 753 - 754 - rqstp = list_entry(pool->sp_all_threads.next, struct svc_rqst, rq_all); 755 - set_bit(RQ_VICTIM, &rqstp->rq_flags); 756 - list_del_rcu(&rqstp->rq_all); 757 - task = rqstp->rq_task; 758 - } 748 + set_bit(SP_VICTIM_REMAINS, &pool->sp_flags); 749 + set_bit(SP_NEED_VICTIM, &pool->sp_flags); 759 750 spin_unlock_bh(&pool->sp_lock); 760 - return task; 751 + return pool; 761 752 } 762 753 763 754 static int ··· 792 795 static int 793 796 svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) 794 797 { 795 - struct svc_rqst *rqstp; 796 - struct task_struct *task; 797 798 unsigned int state = serv->sv_nrthreads-1; 799 + struct svc_pool *victim; 798 800 799 801 do { 800 - task = svc_pool_victim(serv, pool, &state); 801 - if (task == NULL) 802 + victim = svc_pool_victim(serv, pool, &state); 803 + if (!victim) 802 804 break; 803 - rqstp = kthread_data(task); 804 - /* Did we lose a race to svo_function threadfn? */ 805 - if (kthread_stop(task) == -EINTR) 806 - svc_exit_thread(rqstp); 805 + svc_pool_wake_idle_thread(victim); 806 + wait_on_bit(&victim->sp_flags, SP_VICTIM_REMAINS, 807 + TASK_IDLE); 807 808 nrservs++; 808 809 } while (nrservs < 0); 809 810 return 0; ··· 921 926 922 927 spin_lock_bh(&pool->sp_lock); 923 928 pool->sp_nrthreads--; 924 - if (!test_and_set_bit(RQ_VICTIM, &rqstp->rq_flags)) 925 - list_del_rcu(&rqstp->rq_all); 929 + list_del_rcu(&rqstp->rq_all); 926 930 spin_unlock_bh(&pool->sp_lock); 927 931 928 932 spin_lock_bh(&serv->sv_lock); ··· 932 938 svc_rqst_free(rqstp); 933 939 934 940 svc_put(serv); 941 + /* That svc_put() cannot be the last, because the thread 942 + * waiting for SP_VICTIM_REMAINS to clear must hold 943 + * a reference. So it is still safe to access pool. 944 + */ 945 + clear_and_wake_up_bit(SP_VICTIM_REMAINS, &pool->sp_flags); 935 946 } 936 947 EXPORT_SYMBOL_GPL(svc_exit_thread); 937 948
+3 -4
net/sunrpc/svc_xprt.c
··· 9 9 #include <linux/sched/mm.h> 10 10 #include <linux/errno.h> 11 11 #include <linux/freezer.h> 12 - #include <linux/kthread.h> 13 12 #include <linux/slab.h> 14 13 #include <net/sock.h> 15 14 #include <linux/sunrpc/addr.h> ··· 674 675 continue; 675 676 676 677 set_current_state(TASK_IDLE); 677 - if (kthread_should_stop()) { 678 + if (svc_thread_should_stop(rqstp)) { 678 679 set_current_state(TASK_RUNNING); 679 680 return false; 680 681 } ··· 712 713 return false; 713 714 714 715 /* are we shutting down? */ 715 - if (kthread_should_stop()) 716 + if (svc_thread_should_stop(rqstp)) 716 717 return false; 717 718 718 719 /* are we freezing? */ ··· 857 858 858 859 clear_bit(SP_TASK_PENDING, &pool->sp_flags); 859 860 860 - if (kthread_should_stop()) 861 + if (svc_thread_should_stop(rqstp)) 861 862 return; 862 863 863 864 rqstp->rq_xprt = svc_xprt_dequeue(pool);