diff options
Diffstat (limited to 'net/sunrpc/cache.c')
-rw-r--r-- | net/sunrpc/cache.c | 54 |
1 files changed, 39 insertions, 15 deletions
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index e433e7580e27..72ad836e4fe0 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 | ||
40 | static void cache_defer_req(struct cache_req *req, struct cache_head *item); | 40 | static bool cache_defer_req(struct cache_req *req, struct cache_head *item); |
41 | static void cache_revisit_request(struct cache_head *item); | 41 | static void cache_revisit_request(struct cache_head *item); |
42 | 42 | ||
43 | static void cache_init(struct cache_head *h) | 43 | static void cache_init(struct cache_head *h) |
@@ -128,6 +128,7 @@ static void cache_fresh_locked(struct cache_head *head, time_t expiry) | |||
128 | { | 128 | { |
129 | head->expiry_time = expiry; | 129 | head->expiry_time = expiry; |
130 | head->last_refresh = seconds_since_boot(); | 130 | head->last_refresh = seconds_since_boot(); |
131 | smp_wmb(); /* paired with smp_rmb() in cache_is_valid() */ | ||
131 | set_bit(CACHE_VALID, &head->flags); | 132 | set_bit(CACHE_VALID, &head->flags); |
132 | } | 133 | } |
133 | 134 | ||
@@ -208,11 +209,36 @@ static inline int cache_is_valid(struct cache_detail *detail, struct cache_head | |||
208 | /* entry is valid */ | 209 | /* entry is valid */ |
209 | if (test_bit(CACHE_NEGATIVE, &h->flags)) | 210 | if (test_bit(CACHE_NEGATIVE, &h->flags)) |
210 | return -ENOENT; | 211 | return -ENOENT; |
211 | else | 212 | else { |
213 | /* | ||
214 | * In combination with write barrier in | ||
215 | * sunrpc_cache_update, ensures that anyone | ||
216 | * using the cache entry after this sees the | ||
217 | * updated contents: | ||
218 | */ | ||
219 | smp_rmb(); | ||
212 | return 0; | 220 | return 0; |
221 | } | ||
213 | } | 222 | } |
214 | } | 223 | } |
215 | 224 | ||
225 | static int try_to_negate_entry(struct cache_detail *detail, struct cache_head *h) | ||
226 | { | ||
227 | int rv; | ||
228 | |||
229 | write_lock(&detail->hash_lock); | ||
230 | rv = cache_is_valid(detail, h); | ||
231 | if (rv != -EAGAIN) { | ||
232 | write_unlock(&detail->hash_lock); | ||
233 | return rv; | ||
234 | } | ||
235 | set_bit(CACHE_NEGATIVE, &h->flags); | ||
236 | cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY); | ||
237 | write_unlock(&detail->hash_lock); | ||
238 | cache_fresh_unlocked(h, detail); | ||
239 | return -ENOENT; | ||
240 | } | ||
241 | |||
216 | /* | 242 | /* |
217 | * This is the generic cache management routine for all | 243 | * This is the generic cache management routine for all |
218 | * the authentication caches. | 244 | * the authentication caches. |
@@ -251,14 +277,8 @@ int cache_check(struct cache_detail *detail, | |||
251 | case -EINVAL: | 277 | case -EINVAL: |
252 | clear_bit(CACHE_PENDING, &h->flags); | 278 | clear_bit(CACHE_PENDING, &h->flags); |
253 | cache_revisit_request(h); | 279 | cache_revisit_request(h); |
254 | if (rv == -EAGAIN) { | 280 | rv = try_to_negate_entry(detail, h); |
255 | set_bit(CACHE_NEGATIVE, &h->flags); | ||
256 | cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY); | ||
257 | cache_fresh_unlocked(h, detail); | ||
258 | rv = -ENOENT; | ||
259 | } | ||
260 | break; | 281 | break; |
261 | |||
262 | case -EAGAIN: | 282 | case -EAGAIN: |
263 | clear_bit(CACHE_PENDING, &h->flags); | 283 | clear_bit(CACHE_PENDING, &h->flags); |
264 | cache_revisit_request(h); | 284 | cache_revisit_request(h); |
@@ -268,9 +288,11 @@ int cache_check(struct cache_detail *detail, | |||
268 | } | 288 | } |
269 | 289 | ||
270 | if (rv == -EAGAIN) { | 290 | if (rv == -EAGAIN) { |
271 | cache_defer_req(rqstp, h); | 291 | if (!cache_defer_req(rqstp, h)) { |
272 | if (!test_bit(CACHE_PENDING, &h->flags)) { | 292 | /* |
273 | /* Request is not deferred */ | 293 | * Request was not deferred; handle it as best |
294 | * we can ourselves: | ||
295 | */ | ||
274 | rv = cache_is_valid(detail, h); | 296 | rv = cache_is_valid(detail, h); |
275 | if (rv == -EAGAIN) | 297 | if (rv == -EAGAIN) |
276 | rv = -ETIMEDOUT; | 298 | rv = -ETIMEDOUT; |
@@ -618,18 +640,19 @@ static void cache_limit_defers(void) | |||
618 | discard->revisit(discard, 1); | 640 | discard->revisit(discard, 1); |
619 | } | 641 | } |
620 | 642 | ||
621 | static void cache_defer_req(struct cache_req *req, struct cache_head *item) | 643 | /* Return true if and only if a deferred request is queued. */ |
644 | static bool cache_defer_req(struct cache_req *req, struct cache_head *item) | ||
622 | { | 645 | { |
623 | struct cache_deferred_req *dreq; | 646 | struct cache_deferred_req *dreq; |
624 | 647 | ||
625 | if (req->thread_wait) { | 648 | if (req->thread_wait) { |
626 | cache_wait_req(req, item); | 649 | cache_wait_req(req, item); |
627 | if (!test_bit(CACHE_PENDING, &item->flags)) | 650 | if (!test_bit(CACHE_PENDING, &item->flags)) |
628 | return; | 651 | return false; |
629 | } | 652 | } |
630 | dreq = req->defer(req); | 653 | dreq = req->defer(req); |
631 | if (dreq == NULL) | 654 | if (dreq == NULL) |
632 | return; | 655 | return false; |
633 | setup_deferral(dreq, item, 1); | 656 | setup_deferral(dreq, item, 1); |
634 | if (!test_bit(CACHE_PENDING, &item->flags)) | 657 | if (!test_bit(CACHE_PENDING, &item->flags)) |
635 | /* Bit could have been cleared before we managed to | 658 | /* Bit could have been cleared before we managed to |
@@ -638,6 +661,7 @@ static void cache_defer_req(struct cache_req *req, struct cache_head *item) | |||
638 | cache_revisit_request(item); | 661 | cache_revisit_request(item); |
639 | 662 | ||
640 | cache_limit_defers(); | 663 | cache_limit_defers(); |
664 | return true; | ||
641 | } | 665 | } |
642 | 666 | ||
643 | static void cache_revisit_request(struct cache_head *item) | 667 | static void cache_revisit_request(struct cache_head *item) |