Patch-mainline: submitted 04aug2009 References: bnc#498708 From: NeilBrown Date: Tue, 4 Aug 2009 15:06:38 +1000 Subject: [PATCH 07/12] sunrpc/cache: allow thread to block while waiting for cache update. The current practice of waiting for cache updates by queueing the whole request to be retried has (at least) two problems. 1/ We NFSv4, requests can be quite complex and re-trying a whole request when a later part fails should only be a list-resort, not a normal practice. 2/ Large requests, and in particular any 'write' request, will not be queued by the current code and doing so would be undesirable. In many cases only a very sort wait is needed before the cache gets valid data. So, providing the underlying transport permits it by setting ->thread_wait, arrange to wait briefly for an upcall to be completed (as reflected in the clearing of CACHE_PENDING). If the short wait was not long enough and CACHE_PENDING is still set, fall back on the old approach. The 'thread_wait' value is set to 5 seconds when there are spare threads, and 1 second when there are no spare threads. These values are probably much higher than needed, but will ensure some forward progress. [Fixed 18Jan2010 to return -ve from cache_refer_req waits for the upcall to complete instead of deferring the request. Thanks to Dong Yang Li ] Signed-off-by: NeilBrown --- include/linux/sunrpc/cache.h | 3 ++ net/sunrpc/cache.c | 44 ++++++++++++++++++++++++++++++++++++++++++- net/sunrpc/svc_xprt.c | 11 ++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -125,6 +125,9 @@ struct cache_detail { */ struct cache_req { struct cache_deferred_req *(*defer)(struct cache_req *req); + int thread_wait; /* How long (jiffies) we can block the + * current thread to wait for updates. + */ }; /* this must be embedded in a deferred_request that is being * delayed awaiting cache-fill --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -497,10 +497,22 @@ static LIST_HEAD(cache_defer_list); static struct list_head cache_defer_hash[DFR_HASHSIZE]; static int cache_defer_cnt; +struct thread_deferred_req { + struct cache_deferred_req handle; + wait_queue_head_t wait; +}; +static void cache_restart_thread(struct cache_deferred_req *dreq, int too_many) +{ + struct thread_deferred_req *dr = + container_of(dreq, struct thread_deferred_req, handle); + wake_up(&dr->wait); +} + static int cache_defer_req(struct cache_req *req, struct cache_head *item) { struct cache_deferred_req *dreq, *discard; int hash = DFR_HASH(item); + struct thread_deferred_req sleeper; if (cache_defer_cnt >= DFR_MAX) { /* too much in the cache, randomly drop this one, @@ -509,7 +521,14 @@ static int cache_defer_req(struct cache_ if (net_random()&1) return -ENOMEM; } - dreq = req->defer(req); + if (req->thread_wait) { + dreq = &sleeper.handle; + init_waitqueue_head(&sleeper.wait); + dreq->revisit = cache_restart_thread; + } else + dreq = req->defer(req); + + retry: if (dreq == NULL) return -ENOMEM; @@ -543,6 +562,29 @@ static int cache_defer_req(struct cache_ cache_revisit_request(item); return -EAGAIN; } + + if (dreq == &sleeper.handle) { + wait_event_interruptible_timeout( + sleeper.wait, + !test_bit(CACHE_PENDING, &item->flags) + || list_empty(&sleeper.handle.hash), + req->thread_wait); + spin_lock(&cache_defer_lock); + if (!list_empty(&sleeper.handle.hash)) { + list_del_init(&sleeper.handle.recent); + list_del_init(&sleeper.handle.hash); + cache_defer_cnt--; + } + spin_unlock(&cache_defer_lock); + if (test_bit(CACHE_PENDING, &item->flags)) { + /* item is still pending, try request + * deferral + */ + dreq = req->defer(req); + goto retry; + } + return -EAGAIN; + } return 0; } --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -650,6 +650,11 @@ int svc_recv(struct svc_rqst *rqstp, lon if (signalled() || kthread_should_stop()) return -EINTR; + /* Normally we will wait up to 5 seconds for any required + * cache information to be provided. + */ + rqstp->rq_chandle.thread_wait = 5*HZ; + spin_lock_bh(&pool->sp_lock); xprt = svc_xprt_dequeue(pool); if (xprt) { @@ -657,6 +662,12 @@ int svc_recv(struct svc_rqst *rqstp, lon svc_xprt_get(xprt); rqstp->rq_reserved = serv->sv_max_mesg; atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved); + + /* As there is a shortage of threads and this request + * had to be queue, don't allow the thread to wait so + * long for cache updates. + */ + rqstp->rq_chandle.thread_wait = 1*HZ; } else { /* No data pending. Go to sleep */ svc_thread_enqueue(pool, rqstp);