summaryrefslogtreecommitdiff
path: root/net/sunrpc/svc_xprt.c
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2023-09-11 17:39:30 +0300
committerChuck Lever <chuck.lever@oracle.com>2023-10-16 19:44:05 +0300
commit9bd4161c591710f152a8cd3ed85ea928c61e26ca (patch)
tree28f92ebc977aff7156a9470489c60672c5142df9 /net/sunrpc/svc_xprt.c
parentd6b3358a2813bb14791259a2227d9af1e7019ca0 (diff)
downloadlinux-9bd4161c591710f152a8cd3ed85ea928c61e26ca.tar.xz
SUNRPC: change service idle list to be an llist
With an llist we don't need to take a lock to add a thread to the list, though we still need a lock to remove it. That will go in the next patch. Unlike double-linked lists, a thread cannot reliably remove itself from the list. Only the first thread can be removed, and that can change asynchronously. So some care is needed. We already check if there is pending work to do, so we are unlikely to add ourselves to the idle list and then want to remove ourselves again. If we DO find something needs to be done after adding ourselves to the list, we simply wake up the first thread on the list. If that was us, we successfully removed ourselves and can continue. If it was some other thread, they will do the work that needs to be done. We can safely sleep until woken. We also remove the test on freezing() from rqst_should_sleep(). Instead we set TASK_FREEZABLE before scheduling. This makes is safe to schedule() when a freeze is pending. As we now loop waiting to be removed from the idle queue, this is a cleaner way to handle freezing. Signed-off-by: NeilBrown <neilb@suse.de> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Diffstat (limited to 'net/sunrpc/svc_xprt.c')
-rw-r--r--net/sunrpc/svc_xprt.c45
1 files changed, 19 insertions, 26 deletions
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index fa0d854a5596..17c43bde35c9 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -715,10 +715,6 @@ rqst_should_sleep(struct svc_rqst *rqstp)
if (svc_thread_should_stop(rqstp))
return false;
- /* are we freezing? */
- if (freezing(current))
- return false;
-
#if defined(CONFIG_SUNRPC_BACKCHANNEL)
if (svc_is_backchannel(rqstp)) {
if (!list_empty(&rqstp->rq_server->sv_cb_list))
@@ -734,30 +730,26 @@ static void svc_rqst_wait_for_work(struct svc_rqst *rqstp)
struct svc_pool *pool = rqstp->rq_pool;
if (rqst_should_sleep(rqstp)) {
- set_current_state(TASK_IDLE);
- spin_lock_bh(&pool->sp_lock);
- list_add(&rqstp->rq_idle, &pool->sp_idle_threads);
- spin_unlock_bh(&pool->sp_lock);
+ set_current_state(TASK_IDLE | TASK_FREEZABLE);
+ llist_add(&rqstp->rq_idle, &pool->sp_idle_threads);
+
+ if (unlikely(!rqst_should_sleep(rqstp)))
+ /* Work just became available. This thread cannot simply
+ * choose not to sleep as it *must* wait until removed.
+ * So wake the first waiter - whether it is this
+ * thread or some other, it will get the work done.
+ */
+ svc_pool_wake_idle_thread(pool);
- /* Need to check should_sleep() again after
- * setting task state in case a wakeup happened
- * between testing and setting.
+ /* Since a thread cannot remove itself from an llist,
+ * schedule until someone else removes @rqstp from
+ * the idle list.
*/
- if (rqst_should_sleep(rqstp)) {
+ while (!svc_thread_busy(rqstp)) {
schedule();
- } else {
- __set_current_state(TASK_RUNNING);
- cond_resched();
- }
-
- /* We *must* be removed from the list before we can continue.
- * If we were woken, this is already done
- */
- if (!svc_thread_busy(rqstp)) {
- spin_lock_bh(&pool->sp_lock);
- list_del_init(&rqstp->rq_idle);
- spin_unlock_bh(&pool->sp_lock);
+ set_current_state(TASK_IDLE | TASK_FREEZABLE);
}
+ __set_current_state(TASK_RUNNING);
} else {
cond_resched();
}
@@ -870,9 +862,10 @@ void svc_recv(struct svc_rqst *rqstp)
struct svc_xprt *xprt = rqstp->rq_xprt;
/* Normally we will wait up to 5 seconds for any required
- * cache information to be provided.
+ * cache information to be provided. When there are no
+ * idle threads, we reduce the wait time.
*/
- if (!list_empty(&pool->sp_idle_threads))
+ if (pool->sp_idle_threads.first)
rqstp->rq_chandle.thread_wait = 5 * HZ;
else
rqstp->rq_chandle.thread_wait = 1 * HZ;