]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
SUNRPC: change how svc threads are asked to exit.
authorNeilBrown <neilb@suse.de>
Mon, 11 Sep 2023 14:39:04 +0000 (10:39 -0400)
committerChuck Lever <chuck.lever@oracle.com>
Mon, 16 Oct 2023 16:44:04 +0000 (12:44 -0400)
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>
fs/lockd/svc.c
fs/lockd/svclock.c
fs/nfs/callback.c
fs/nfsd/nfs4proc.c
fs/nfsd/nfssvc.c
include/linux/lockd/lockd.h
include/linux/sunrpc/svc.h
net/sunrpc/svc.c
net/sunrpc/svc_xprt.c

index 365cc7adff66ea6650cc2629b75ef5943bf58f25..81be07c1d3d156abe7f7ff1d5802601d7c5dfa38 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/uio.h>
 #include <linux/smp.h>
 #include <linux/mutex.h>
-#include <linux/kthread.h>
 #include <linux/freezer.h>
 #include <linux/inetdevice.h>
 
@@ -135,11 +134,11 @@ lockd(void *vrqstp)
         * The main request loop. We don't terminate until the last
         * NFS mount or NFS daemon has gone away.
         */
-       while (!kthread_should_stop()) {
+       while (!svc_thread_should_stop(rqstp)) {
                /* update sv_maxconn if it has changed */
                rqstp->rq_server->sv_maxconn = nlm_max_connections;
 
-               nlmsvc_retry_blocked();
+               nlmsvc_retry_blocked(rqstp);
                svc_recv(rqstp);
        }
        if (nlmsvc_ops)
index 993999297e3142a3a3b92b836781b880749af63f..2dc10900ad1c335415e1ee43f9310d18a79dacc7 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/sunrpc/svc_xprt.h>
 #include <linux/lockd/nlm.h>
 #include <linux/lockd/lockd.h>
-#include <linux/kthread.h>
 #include <linux/exportfs.h>
 
 #define NLMDBG_FACILITY                NLMDBG_SVCLOCK
@@ -1032,13 +1031,13 @@ retry_deferred_block(struct nlm_block *block)
  * be retransmitted.
  */
 void
-nlmsvc_retry_blocked(void)
+nlmsvc_retry_blocked(struct svc_rqst *rqstp)
 {
        unsigned long   timeout = MAX_SCHEDULE_TIMEOUT;
        struct nlm_block *block;
 
        spin_lock(&nlm_blocked_lock);
-       while (!list_empty(&nlm_blocked) && !kthread_should_stop()) {
+       while (!list_empty(&nlm_blocked) && !svc_thread_should_stop(rqstp)) {
                block = list_entry(nlm_blocked.next, struct nlm_block, b_list);
 
                if (block->b_when == NLM_NEVER)
index 42a0c2f1e78565bc31b3faedf96bcaa9b0f7a585..4ffa1f469e90a954c28b9e562722bd4df78fd69a 100644 (file)
@@ -78,7 +78,7 @@ nfs4_callback_svc(void *vrqstp)
 
        set_freezable();
 
-       while (!kthread_should_stop())
+       while (!svc_thread_should_stop(rqstp))
                svc_recv(rqstp);
 
        svc_exit_thread(rqstp);
index 580c5f5924081a457101f0c2720ce8a5c0b267e9..d7e88c7beba32e02c738e53384abf018ea756429 100644 (file)
@@ -1329,7 +1329,8 @@ extern void nfs_sb_deactive(struct super_block *sb);
  * setup a work entry in the ssc delayed unmount list.
  */
 static __be32 nfsd4_ssc_setup_dul(struct nfsd_net *nn, char *ipaddr,
-                                 struct nfsd4_ssc_umount_item **nsui)
+                                 struct nfsd4_ssc_umount_item **nsui,
+                                 struct svc_rqst *rqstp)
 {
        struct nfsd4_ssc_umount_item *ni = NULL;
        struct nfsd4_ssc_umount_item *work = NULL;
@@ -1351,7 +1352,7 @@ try_again:
                        spin_unlock(&nn->nfsd_ssc_lock);
 
                        /* allow 20secs for mount/unmount for now - revisit */
-                       if (kthread_should_stop() ||
+                       if (svc_thread_should_stop(rqstp) ||
                                        (schedule_timeout(20*HZ) == 0)) {
                                finish_wait(&nn->nfsd_ssc_waitq, &wait);
                                kfree(work);
@@ -1467,7 +1468,7 @@ nfsd4_interssc_connect(struct nl4_server *nss, struct svc_rqst *rqstp,
                goto out_free_rawdata;
        snprintf(dev_name, len + 5, "%s%s%s:/", startsep, ipaddr, endsep);
 
-       status = nfsd4_ssc_setup_dul(nn, ipaddr, nsui);
+       status = nfsd4_ssc_setup_dul(nn, ipaddr, nsui, rqstp);
        if (status)
                goto out_free_devname;
        if ((*nsui)->nsui_vfsmount)
@@ -1642,6 +1643,7 @@ static ssize_t _nfsd_copy_file_range(struct nfsd4_copy *copy,
        if (bytes_total == 0)
                bytes_total = ULLONG_MAX;
        do {
+               /* Only async copies can be stopped here */
                if (kthread_should_stop())
                        break;
                bytes_copied = nfsd_copy_file_range(src, src_pos, dst, dst_pos,
index c7af1095f6b549f67e1e04b341b013bdedf6eee2..0b03a2e50deeaf1c4030856305d83bb32ff8f4ae 100644 (file)
@@ -957,7 +957,7 @@ nfsd(void *vrqstp)
        /*
         * The main request loop
         */
-       while (!kthread_should_stop()) {
+       while (!svc_thread_should_stop(rqstp)) {
                /* Update sv_maxconn if it has changed */
                rqstp->rq_server->sv_maxconn = nn->max_connections;
 
index 0f016d69c996e54809be92d61b852f994d55c8f8..9f565416d18671e110e1fb99e44b96e241b519d4 100644 (file)
@@ -282,7 +282,7 @@ __be32                nlmsvc_testlock(struct svc_rqst *, struct nlm_file *,
                        struct nlm_host *, struct nlm_lock *,
                        struct nlm_lock *, struct nlm_cookie *);
 __be32           nlmsvc_cancel_blocked(struct net *net, struct nlm_file *, struct nlm_lock *);
-void             nlmsvc_retry_blocked(void);
+void             nlmsvc_retry_blocked(struct svc_rqst *rqstp);
 void             nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *,
                                        nlm_host_match_fn_t match);
 void             nlmsvc_grant_reply(struct nlm_cookie *, __be32);
index acbe1314febd3221d80c6eb0d1869e2446cf4c19..0ec691070e276f62ec0a23cb6430c3efcf71c1c6 100644 (file)
@@ -50,6 +50,8 @@ struct svc_pool {
 enum {
        SP_TASK_PENDING,        /* still work to do even if no xprt is queued */
        SP_CONGESTED,           /* all threads are busy, none idle */
+       SP_NEED_VICTIM,         /* One thread needs to agree to exit */
+       SP_VICTIM_REMAINS,      /* One thread needs to actually exit */
 };
 
 
@@ -259,7 +261,7 @@ enum {
        RQ_DROPME,              /* drop current reply */
        RQ_SPLICE_OK,           /* turned off in gss privacy to prevent
                                 * encrypting page cache pages */
-       RQ_VICTIM,              /* about to be shut down */
+       RQ_VICTIM,              /* Have agreed to shut down */
        RQ_BUSY,                /* request is busy */
        RQ_DATA,                /* request has data */
 };
@@ -299,6 +301,28 @@ static inline struct sockaddr *svc_daddr(const struct svc_rqst *rqst)
        return (struct sockaddr *) &rqst->rq_daddr;
 }
 
+/**
+ * svc_thread_should_stop - check if this thread should stop
+ * @rqstp: the thread that might need to stop
+ *
+ * To stop an svc thread, the pool flags SP_NEED_VICTIM and SP_VICTIM_REMAINS
+ * are set.  The first thread which sees SP_NEED_VICTIM clears it, becoming
+ * the victim using this function.  It should then promptly call
+ * svc_exit_thread() to complete the process, clearing SP_VICTIM_REMAINS
+ * so the task waiting for a thread to exit can wake and continue.
+ *
+ * Return values:
+ *   %true: caller should invoke svc_exit_thread()
+ *   %false: caller should do nothing
+ */
+static inline bool svc_thread_should_stop(struct svc_rqst *rqstp)
+{
+       if (test_and_clear_bit(SP_NEED_VICTIM, &rqstp->rq_pool->sp_flags))
+               set_bit(RQ_VICTIM, &rqstp->rq_flags);
+
+       return test_bit(RQ_VICTIM, &rqstp->rq_flags);
+}
+
 struct svc_deferred_req {
        u32                     prot;   /* protocol (UDP or TCP) */
        struct svc_xprt         *xprt;
index b98a159eb17ffb58abf684f160a3b2dfa2dbf1cc..db579bbc0a0a8df1197f17c62293dfda6dece256 100644 (file)
@@ -725,19 +725,22 @@ svc_pool_next(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state)
        return pool ? pool : &serv->sv_pools[(*state)++ % serv->sv_nrpools];
 }
 
-static struct task_struct *
+static struct svc_pool *
 svc_pool_victim(struct svc_serv *serv, struct svc_pool *pool, unsigned int *state)
 {
        unsigned int i;
-       struct task_struct *task = NULL;
 
        if (pool != NULL) {
                spin_lock_bh(&pool->sp_lock);
+               if (pool->sp_nrthreads)
+                       goto found_pool;
+               spin_unlock_bh(&pool->sp_lock);
+               return NULL;
        } else {
                for (i = 0; i < serv->sv_nrpools; i++) {
                        pool = &serv->sv_pools[--(*state) % serv->sv_nrpools];
                        spin_lock_bh(&pool->sp_lock);
-                       if (!list_empty(&pool->sp_all_threads))
+                       if (pool->sp_nrthreads)
                                goto found_pool;
                        spin_unlock_bh(&pool->sp_lock);
                }
@@ -745,16 +748,10 @@ svc_pool_victim(struct svc_serv *serv, struct svc_pool *pool, unsigned int *stat
        }
 
 found_pool:
-       if (!list_empty(&pool->sp_all_threads)) {
-               struct svc_rqst *rqstp;
-
-               rqstp = list_entry(pool->sp_all_threads.next, struct svc_rqst, rq_all);
-               set_bit(RQ_VICTIM, &rqstp->rq_flags);
-               list_del_rcu(&rqstp->rq_all);
-               task = rqstp->rq_task;
-       }
+       set_bit(SP_VICTIM_REMAINS, &pool->sp_flags);
+       set_bit(SP_NEED_VICTIM, &pool->sp_flags);
        spin_unlock_bh(&pool->sp_lock);
-       return task;
+       return pool;
 }
 
 static int
@@ -795,18 +792,16 @@ svc_start_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
 static int
 svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
 {
-       struct svc_rqst *rqstp;
-       struct task_struct *task;
        unsigned int state = serv->sv_nrthreads-1;
+       struct svc_pool *victim;
 
        do {
-               task = svc_pool_victim(serv, pool, &state);
-               if (task == NULL)
+               victim = svc_pool_victim(serv, pool, &state);
+               if (!victim)
                        break;
-               rqstp = kthread_data(task);
-               /* Did we lose a race to svo_function threadfn? */
-               if (kthread_stop(task) == -EINTR)
-                       svc_exit_thread(rqstp);
+               svc_pool_wake_idle_thread(victim);
+               wait_on_bit(&victim->sp_flags, SP_VICTIM_REMAINS,
+                           TASK_IDLE);
                nrservs++;
        } while (nrservs < 0);
        return 0;
@@ -926,8 +921,7 @@ svc_exit_thread(struct svc_rqst *rqstp)
 
        spin_lock_bh(&pool->sp_lock);
        pool->sp_nrthreads--;
-       if (!test_and_set_bit(RQ_VICTIM, &rqstp->rq_flags))
-               list_del_rcu(&rqstp->rq_all);
+       list_del_rcu(&rqstp->rq_all);
        spin_unlock_bh(&pool->sp_lock);
 
        spin_lock_bh(&serv->sv_lock);
@@ -938,6 +932,11 @@ svc_exit_thread(struct svc_rqst *rqstp)
        svc_rqst_free(rqstp);
 
        svc_put(serv);
+       /* That svc_put() cannot be the last, because the thread
+        * waiting for SP_VICTIM_REMAINS to clear must hold
+        * a reference. So it is still safe to access pool.
+        */
+       clear_and_wake_up_bit(SP_VICTIM_REMAINS, &pool->sp_flags);
 }
 EXPORT_SYMBOL_GPL(svc_exit_thread);
 
index b057f1cbe7a1bb5b3e4479001f158a315639147e..b8539545fefdb1f203d87ce54cd49507bf1cb6a5 100644 (file)
@@ -9,7 +9,6 @@
 #include <linux/sched/mm.h>
 #include <linux/errno.h>
 #include <linux/freezer.h>
-#include <linux/kthread.h>
 #include <linux/slab.h>
 #include <net/sock.h>
 #include <linux/sunrpc/addr.h>
@@ -675,7 +674,7 @@ static bool svc_alloc_arg(struct svc_rqst *rqstp)
                        continue;
 
                set_current_state(TASK_IDLE);
-               if (kthread_should_stop()) {
+               if (svc_thread_should_stop(rqstp)) {
                        set_current_state(TASK_RUNNING);
                        return false;
                }
@@ -713,7 +712,7 @@ rqst_should_sleep(struct svc_rqst *rqstp)
                return false;
 
        /* are we shutting down? */
-       if (kthread_should_stop())
+       if (svc_thread_should_stop(rqstp))
                return false;
 
        /* are we freezing? */
@@ -858,7 +857,7 @@ void svc_recv(struct svc_rqst *rqstp)
 
        clear_bit(SP_TASK_PENDING, &pool->sp_flags);
 
-       if (kthread_should_stop())
+       if (svc_thread_should_stop(rqstp))
                return;
 
        rqstp->rq_xprt = svc_xprt_dequeue(pool);