diff options
author | Kinglong Mee <kinglongmee@gmail.com> | 2017-02-07 20:54:33 -0500 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2017-02-08 16:49:32 -0500 |
commit | 471a930ad7d1b868456e835a67169a661ec73f65 (patch) | |
tree | 0b4cc18a4de82c303aceaa5ab72511acf21ebace | |
parent | 81fa3275f95ac357188fe3ca81b8e7c75360c88d (diff) |
SUNRPC: Drop all entries from cache_detail when cache_purge()
User always free the cache_detail after sunrpc_destroy_cache_detail(),
so, it must cleanup up entries that left in the cache_detail,
otherwise, NULL reference may be caused when using the left entries.
Also, NeriBrown suggests "write a stand-alone cache_purge()."
v3, move the cache_fresh_unlocked() out of write lock,
v2, a stand-alone cache_purge(), not only for sunrpc_destroy_cache_detail
Signed-off-by: Kinglong Mee <kinglongmee@gmail.com>
Reviewed-by: NeilBrown <neilb@suse.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r-- | net/sunrpc/cache.c | 41 |
1 files changed, 26 insertions, 15 deletions
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 502b9fe9817b..f5d6c60aae26 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c | |||
@@ -362,11 +362,6 @@ void sunrpc_destroy_cache_detail(struct cache_detail *cd) | |||
362 | cache_purge(cd); | 362 | cache_purge(cd); |
363 | spin_lock(&cache_list_lock); | 363 | spin_lock(&cache_list_lock); |
364 | write_lock(&cd->hash_lock); | 364 | write_lock(&cd->hash_lock); |
365 | if (cd->entries) { | ||
366 | write_unlock(&cd->hash_lock); | ||
367 | spin_unlock(&cache_list_lock); | ||
368 | goto out; | ||
369 | } | ||
370 | if (current_detail == cd) | 365 | if (current_detail == cd) |
371 | current_detail = NULL; | 366 | current_detail = NULL; |
372 | list_del_init(&cd->others); | 367 | list_del_init(&cd->others); |
@@ -376,9 +371,6 @@ void sunrpc_destroy_cache_detail(struct cache_detail *cd) | |||
376 | /* module must be being unloaded so its safe to kill the worker */ | 371 | /* module must be being unloaded so its safe to kill the worker */ |
377 | cancel_delayed_work_sync(&cache_cleaner); | 372 | cancel_delayed_work_sync(&cache_cleaner); |
378 | } | 373 | } |
379 | return; | ||
380 | out: | ||
381 | printk(KERN_ERR "RPC: failed to unregister %s cache\n", cd->name); | ||
382 | } | 374 | } |
383 | EXPORT_SYMBOL_GPL(sunrpc_destroy_cache_detail); | 375 | EXPORT_SYMBOL_GPL(sunrpc_destroy_cache_detail); |
384 | 376 | ||
@@ -497,13 +489,32 @@ EXPORT_SYMBOL_GPL(cache_flush); | |||
497 | 489 | ||
498 | void cache_purge(struct cache_detail *detail) | 490 | void cache_purge(struct cache_detail *detail) |
499 | { | 491 | { |
500 | time_t now = seconds_since_boot(); | 492 | struct cache_head *ch = NULL; |
501 | if (detail->flush_time >= now) | 493 | struct hlist_head *head = NULL; |
502 | now = detail->flush_time + 1; | 494 | struct hlist_node *tmp = NULL; |
503 | /* 'now' is the maximum value any 'last_refresh' can have */ | 495 | int i = 0; |
504 | detail->flush_time = now; | 496 | |
505 | detail->nextcheck = seconds_since_boot(); | 497 | write_lock(&detail->hash_lock); |
506 | cache_flush(); | 498 | if (!detail->entries) { |
499 | write_unlock(&detail->hash_lock); | ||
500 | return; | ||
501 | } | ||
502 | |||
503 | dprintk("RPC: %d entries in %s cache\n", detail->entries, detail->name); | ||
504 | for (i = 0; i < detail->hash_size; i++) { | ||
505 | head = &detail->hash_table[i]; | ||
506 | hlist_for_each_entry_safe(ch, tmp, head, cache_list) { | ||
507 | hlist_del_init(&ch->cache_list); | ||
508 | detail->entries--; | ||
509 | |||
510 | set_bit(CACHE_CLEANED, &ch->flags); | ||
511 | write_unlock(&detail->hash_lock); | ||
512 | cache_fresh_unlocked(ch, detail); | ||
513 | cache_put(ch, detail); | ||
514 | write_lock(&detail->hash_lock); | ||
515 | } | ||
516 | } | ||
517 | write_unlock(&detail->hash_lock); | ||
507 | } | 518 | } |
508 | EXPORT_SYMBOL_GPL(cache_purge); | 519 | EXPORT_SYMBOL_GPL(cache_purge); |
509 | 520 | ||