Skip to content

Commit a0022a3

Browse files
jtlaytonchucklever
authored andcommitted
sunrpc: allow svc_recv() to return -ETIMEDOUT and -EBUSY
To dynamically adjust the thread count, nfsd requires some information about how busy things are. Change svc_recv() to take a timeout value, and then allow the wait for work to time out if it's set. If a timeout is not defined, then the schedule will be set to MAX_SCHEDULE_TIMEOUT. If the task waits for the full timeout, then have it return -ETIMEDOUT to the caller. If it wakes up, finds that there is more work and that no threads are available, then attempt to set SP_TASK_STARTING. If wasn't already set, have the task return -EBUSY to cue to the caller that the service could use more threads. Signed-off-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
1 parent 7f221b3 commit a0022a3

6 files changed

Lines changed: 47 additions & 13 deletions

File tree

fs/lockd/svc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ lockd(void *vrqstp)
141141
*/
142142
while (!svc_thread_should_stop(rqstp)) {
143143
nlmsvc_retry_blocked(rqstp);
144-
svc_recv(rqstp);
144+
svc_recv(rqstp, 0);
145145
}
146146
if (nlmsvc_ops)
147147
nlmsvc_invalidate_all();

fs/nfs/callback.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ nfs4_callback_svc(void *vrqstp)
8181
set_freezable();
8282

8383
while (!svc_thread_should_stop(rqstp))
84-
svc_recv(rqstp);
84+
svc_recv(rqstp, 0);
8585

8686
svc_exit_thread(rqstp);
8787
return 0;

fs/nfsd/nfssvc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,7 @@ nfsd(void *vrqstp)
902902
* The main request loop
903903
*/
904904
while (!svc_thread_should_stop(rqstp)) {
905-
svc_recv(rqstp);
905+
svc_recv(rqstp, 0);
906906
nfsd_file_net_dispose(nn);
907907
}
908908

include/linux/sunrpc/svc.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ enum {
5555
SP_TASK_PENDING, /* still work to do even if no xprt is queued */
5656
SP_NEED_VICTIM, /* One thread needs to agree to exit */
5757
SP_VICTIM_REMAINS, /* One thread needs to actually exit */
58+
SP_TASK_STARTING, /* Task has started but not added to idle yet */
5859
};
5960

6061

include/linux/sunrpc/svcsock.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ static inline u32 svc_sock_final_rec(struct svc_sock *svsk)
6161
/*
6262
* Function prototypes.
6363
*/
64-
void svc_recv(struct svc_rqst *rqstp);
64+
int svc_recv(struct svc_rqst *rqstp, long timeo);
6565
void svc_send(struct svc_rqst *rqstp);
6666
int svc_addsock(struct svc_serv *serv, struct net *net,
6767
const int fd, char *name_return, const size_t len,

net/sunrpc/svc_xprt.c

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -714,15 +714,21 @@ svc_thread_should_sleep(struct svc_rqst *rqstp)
714714
return true;
715715
}
716716

717-
static void svc_thread_wait_for_work(struct svc_rqst *rqstp)
717+
static bool svc_schedule_timeout(long timeo)
718+
{
719+
return schedule_timeout(timeo ? timeo : MAX_SCHEDULE_TIMEOUT) == 0;
720+
}
721+
722+
static bool svc_thread_wait_for_work(struct svc_rqst *rqstp, long timeo)
718723
{
719724
struct svc_pool *pool = rqstp->rq_pool;
725+
bool did_timeout = false;
720726

721727
if (svc_thread_should_sleep(rqstp)) {
722728
set_current_state(TASK_IDLE | TASK_FREEZABLE);
723729
llist_add(&rqstp->rq_idle, &pool->sp_idle_threads);
724730
if (likely(svc_thread_should_sleep(rqstp)))
725-
schedule();
731+
did_timeout = svc_schedule_timeout(timeo);
726732

727733
while (!llist_del_first_this(&pool->sp_idle_threads,
728734
&rqstp->rq_idle)) {
@@ -734,14 +740,15 @@ static void svc_thread_wait_for_work(struct svc_rqst *rqstp)
734740
* for this new work. This thread can safely sleep
735741
* until woken again.
736742
*/
737-
schedule();
743+
did_timeout = svc_schedule_timeout(timeo);
738744
set_current_state(TASK_IDLE | TASK_FREEZABLE);
739745
}
740746
__set_current_state(TASK_RUNNING);
741747
} else {
742748
cond_resched();
743749
}
744750
try_to_freeze();
751+
return did_timeout;
745752
}
746753

747754
static void svc_add_new_temp_xprt(struct svc_serv *serv, struct svc_xprt *newxpt)
@@ -835,25 +842,38 @@ static void svc_thread_wake_next(struct svc_rqst *rqstp)
835842
/**
836843
* svc_recv - Receive and process the next request on any transport
837844
* @rqstp: an idle RPC service thread
845+
* @timeo: timeout (in jiffies) (0 means infinite timeout)
838846
*
839847
* This code is carefully organised not to touch any cachelines in
840848
* the shared svc_serv structure, only cachelines in the local
841849
* svc_pool.
850+
*
851+
* If the timeout is 0, then the sleep will never time out.
852+
*
853+
* Returns -ETIMEDOUT if idle for an extended period
854+
* -EBUSY if there is more work to do than available threads
855+
* 0 otherwise.
842856
*/
843-
void svc_recv(struct svc_rqst *rqstp)
857+
int svc_recv(struct svc_rqst *rqstp, long timeo)
844858
{
845859
struct svc_pool *pool = rqstp->rq_pool;
860+
bool did_timeout;
861+
int ret = 0;
846862

847863
if (!svc_alloc_arg(rqstp))
848-
return;
864+
return ret;
865+
866+
did_timeout = svc_thread_wait_for_work(rqstp, timeo);
849867

850-
svc_thread_wait_for_work(rqstp);
868+
if (did_timeout && svc_thread_should_sleep(rqstp) &&
869+
pool->sp_nrthrmin && pool->sp_nrthreads > pool->sp_nrthrmin)
870+
ret = -ETIMEDOUT;
851871

852872
clear_bit(SP_TASK_PENDING, &pool->sp_flags);
853873

854874
if (svc_thread_should_stop(rqstp)) {
855875
svc_thread_wake_next(rqstp);
856-
return;
876+
return ret;
857877
}
858878

859879
rqstp->rq_xprt = svc_xprt_dequeue(pool);
@@ -865,10 +885,22 @@ void svc_recv(struct svc_rqst *rqstp)
865885
* cache information to be provided. When there are no
866886
* idle threads, we reduce the wait time.
867887
*/
868-
if (pool->sp_idle_threads.first)
888+
if (pool->sp_idle_threads.first) {
869889
rqstp->rq_chandle.thread_wait = 5 * HZ;
870-
else
890+
} else {
871891
rqstp->rq_chandle.thread_wait = 1 * HZ;
892+
/*
893+
* No idle threads: signal -EBUSY so the caller
894+
* can consider spawning another thread. Use
895+
* SP_TASK_STARTING to limit this signal to one
896+
* thread at a time; the caller clears this flag
897+
* after starting a new thread.
898+
*/
899+
if (!did_timeout && timeo &&
900+
!test_and_set_bit(SP_TASK_STARTING,
901+
&pool->sp_flags))
902+
ret = -EBUSY;
903+
}
872904

873905
trace_svc_xprt_dequeue(rqstp);
874906
svc_handle_xprt(rqstp, xprt);
@@ -887,6 +919,7 @@ void svc_recv(struct svc_rqst *rqstp)
887919
}
888920
}
889921
#endif
922+
return ret;
890923
}
891924
EXPORT_SYMBOL_GPL(svc_recv);
892925

0 commit comments

Comments
 (0)