aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2011-01-02 21:28:34 -0500
committerJ. Bruce Fields <bfields@redhat.com>2011-01-04 16:49:21 -0500
commitd76d1815f3e72fb627ad7f95ef63120b0a557c9c (patch)
treee639629aabf50fe9b5441286ff12cfc4ec77de98
parent3beb6cd1d448e7ded938bbd676493e6a08e9a6cd (diff)
svcrpc: avoid double reply caused by deferral race
Commit d29068c431599fa "sunrpc: Simplify cache_defer_req and related functions." asserted that cache_check() could determine success or failure of cache_defer_req() by checking the CACHE_PENDING bit. This isn't quite right. We need to know whether cache_defer_req() created a deferred request, in which case sending an rpc reply has become the responsibility of the deferred request, and it is important that we not send our own reply, resulting in two different replies to the same request. And the CACHE_PENDING bit doesn't tell us that; we could have succesfully created a deferred request at the same time as another thread cleared the CACHE_PENDING bit. So, partially revert that commit, to ensure that cache_check() returns -EAGAIN if and only if a deferred request has been created. Signed-off-by: J. Bruce Fields <bfields@redhat.com> Acked-by: NeilBrown <neilb@suse.de>
-rw-r--r--net/sunrpc/cache.c18
1 files changed, 11 insertions, 7 deletions
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index e433e7580e27..0d6002f26d82 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -37,7 +37,7 @@
37 37
38#define RPCDBG_FACILITY RPCDBG_CACHE 38#define RPCDBG_FACILITY RPCDBG_CACHE
39 39
40static void cache_defer_req(struct cache_req *req, struct cache_head *item); 40static bool cache_defer_req(struct cache_req *req, struct cache_head *item);
41static void cache_revisit_request(struct cache_head *item); 41static void cache_revisit_request(struct cache_head *item);
42 42
43static void cache_init(struct cache_head *h) 43static void cache_init(struct cache_head *h)
@@ -268,9 +268,11 @@ int cache_check(struct cache_detail *detail,
268 } 268 }
269 269
270 if (rv == -EAGAIN) { 270 if (rv == -EAGAIN) {
271 cache_defer_req(rqstp, h); 271 if (!cache_defer_req(rqstp, h)) {
272 if (!test_bit(CACHE_PENDING, &h->flags)) { 272 /*
273 /* Request is not deferred */ 273 * Request was not deferred; handle it as best
274 * we can ourselves:
275 */
274 rv = cache_is_valid(detail, h); 276 rv = cache_is_valid(detail, h);
275 if (rv == -EAGAIN) 277 if (rv == -EAGAIN)
276 rv = -ETIMEDOUT; 278 rv = -ETIMEDOUT;
@@ -618,18 +620,19 @@ static void cache_limit_defers(void)
618 discard->revisit(discard, 1); 620 discard->revisit(discard, 1);
619} 621}
620 622
621static void cache_defer_req(struct cache_req *req, struct cache_head *item) 623/* Return true if and only if a deferred request is queued. */
624static bool cache_defer_req(struct cache_req *req, struct cache_head *item)
622{ 625{
623 struct cache_deferred_req *dreq; 626 struct cache_deferred_req *dreq;
624 627
625 if (req->thread_wait) { 628 if (req->thread_wait) {
626 cache_wait_req(req, item); 629 cache_wait_req(req, item);
627 if (!test_bit(CACHE_PENDING, &item->flags)) 630 if (!test_bit(CACHE_PENDING, &item->flags))
628 return; 631 return false;
629 } 632 }
630 dreq = req->defer(req); 633 dreq = req->defer(req);
631 if (dreq == NULL) 634 if (dreq == NULL)
632 return; 635 return false;
633 setup_deferral(dreq, item, 1); 636 setup_deferral(dreq, item, 1);
634 if (!test_bit(CACHE_PENDING, &item->flags)) 637 if (!test_bit(CACHE_PENDING, &item->flags))
635 /* Bit could have been cleared before we managed to 638 /* Bit could have been cleared before we managed to
@@ -638,6 +641,7 @@ static void cache_defer_req(struct cache_req *req, struct cache_head *item)
638 cache_revisit_request(item); 641 cache_revisit_request(item);
639 642
640 cache_limit_defers(); 643 cache_limit_defers();
644 return true;
641} 645}
642 646
643static void cache_revisit_request(struct cache_head *item) 647static void cache_revisit_request(struct cache_head *item)