Skip to content

Commit 850bac3

Browse files
committed
SUNRPC: Deduplicate thread wake-up code
Refactor: Extract the loop that finds an idle service thread from svc_xprt_enqueue() and svc_wake_up(). Both functions do just about the same thing. Note that svc_wake_up() currently does not hold the RCU read lock while waking the target thread. It indeed should hold the lock, just as svc_xprt_enqueue() does, to ensure the rqstp does not vanish during the wake-up. This patch adds the RCU lock for svc_wake_up(). Note that shrinking the pool thread count is rare, and calls to svc_wake_up() are also quite infrequent. In practice, this race is very unlikely to be hit, so we are not marking the lock fix for stable backport at this time. Reviewed-by: Jeff Layton <jlayton@redhat.com> Reviewed-by: NeilBrown <neilb@suse.de> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
1 parent 82e5d82 commit 850bac3

File tree

3 files changed

+43
-36
lines changed

3 files changed

+43
-36
lines changed

include/linux/sunrpc/svc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ int svc_register(const struct svc_serv *, struct net *, const int,
419419

420420
void svc_wake_up(struct svc_serv *);
421421
void svc_reserve(struct svc_rqst *rqstp, int space);
422+
bool svc_pool_wake_idle_thread(struct svc_pool *pool);
422423
struct svc_pool *svc_pool_for_cpu(struct svc_serv *serv);
423424
char * svc_print_addr(struct svc_rqst *, char *, size_t);
424425
const char * svc_proc_name(const struct svc_rqst *rqstp);

net/sunrpc/svc.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,40 @@ svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool, int node)
687687
return rqstp;
688688
}
689689

690+
/**
691+
* svc_pool_wake_idle_thread - Awaken an idle thread in @pool
692+
* @pool: service thread pool
693+
*
694+
* Can be called from soft IRQ or process context. Finding an idle
695+
* service thread and marking it BUSY is atomic with respect to
696+
* other calls to svc_pool_wake_idle_thread().
697+
*
698+
* Return value:
699+
* %true: An idle thread was awoken
700+
* %false: No idle thread was found
701+
*/
702+
bool svc_pool_wake_idle_thread(struct svc_pool *pool)
703+
{
704+
struct svc_rqst *rqstp;
705+
706+
rcu_read_lock();
707+
list_for_each_entry_rcu(rqstp, &pool->sp_all_threads, rq_all) {
708+
if (test_and_set_bit(RQ_BUSY, &rqstp->rq_flags))
709+
continue;
710+
711+
WRITE_ONCE(rqstp->rq_qtime, ktime_get());
712+
wake_up_process(rqstp->rq_task);
713+
rcu_read_unlock();
714+
percpu_counter_inc(&pool->sp_threads_woken);
715+
trace_svc_wake_up(rqstp->rq_task->pid);
716+
return true;
717+
}
718+
rcu_read_unlock();
719+
720+
set_bit(SP_CONGESTED, &pool->sp_flags);
721+
return false;
722+
}
723+
690724
/*
691725
* Choose a pool in which to create a new thread, for svc_set_num_threads
692726
*/

net/sunrpc/svc_xprt.c

Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,6 @@ static bool svc_xprt_ready(struct svc_xprt *xprt)
457457
void svc_xprt_enqueue(struct svc_xprt *xprt)
458458
{
459459
struct svc_pool *pool;
460-
struct svc_rqst *rqstp = NULL;
461460

462461
if (!svc_xprt_ready(xprt))
463462
return;
@@ -477,20 +476,7 @@ void svc_xprt_enqueue(struct svc_xprt *xprt)
477476
list_add_tail(&xprt->xpt_ready, &pool->sp_sockets);
478477
spin_unlock_bh(&pool->sp_lock);
479478

480-
/* find a thread for this xprt */
481-
rcu_read_lock();
482-
list_for_each_entry_rcu(rqstp, &pool->sp_all_threads, rq_all) {
483-
if (test_and_set_bit(RQ_BUSY, &rqstp->rq_flags))
484-
continue;
485-
percpu_counter_inc(&pool->sp_threads_woken);
486-
rqstp->rq_qtime = ktime_get();
487-
wake_up_process(rqstp->rq_task);
488-
goto out_unlock;
489-
}
490-
set_bit(SP_CONGESTED, &pool->sp_flags);
491-
rqstp = NULL;
492-
out_unlock:
493-
rcu_read_unlock();
479+
svc_pool_wake_idle_thread(pool);
494480
}
495481
EXPORT_SYMBOL_GPL(svc_xprt_enqueue);
496482

@@ -581,7 +567,10 @@ static void svc_xprt_release(struct svc_rqst *rqstp)
581567
svc_xprt_put(xprt);
582568
}
583569

584-
/*
570+
/**
571+
* svc_wake_up - Wake up a service thread for non-transport work
572+
* @serv: RPC service
573+
*
585574
* Some svc_serv's will have occasional work to do, even when a xprt is not
586575
* waiting to be serviced. This function is there to "kick" a task in one of
587576
* those services so that it can wake up and do that work. Note that we only
@@ -590,27 +579,10 @@ static void svc_xprt_release(struct svc_rqst *rqstp)
590579
*/
591580
void svc_wake_up(struct svc_serv *serv)
592581
{
593-
struct svc_rqst *rqstp;
594-
struct svc_pool *pool;
595-
596-
pool = &serv->sv_pools[0];
597-
598-
rcu_read_lock();
599-
list_for_each_entry_rcu(rqstp, &pool->sp_all_threads, rq_all) {
600-
/* skip any that aren't queued */
601-
if (test_bit(RQ_BUSY, &rqstp->rq_flags))
602-
continue;
603-
rcu_read_unlock();
604-
wake_up_process(rqstp->rq_task);
605-
trace_svc_wake_up(rqstp->rq_task->pid);
606-
return;
607-
}
608-
rcu_read_unlock();
582+
struct svc_pool *pool = &serv->sv_pools[0];
609583

610-
/* No free entries available */
611-
set_bit(SP_TASK_PENDING, &pool->sp_flags);
612-
smp_wmb();
613-
trace_svc_wake_up(0);
584+
if (!svc_pool_wake_idle_thread(pool))
585+
set_bit(SP_TASK_PENDING, &pool->sp_flags);
614586
}
615587
EXPORT_SYMBOL_GPL(svc_wake_up);
616588

0 commit comments

Comments
 (0)