After commit d202cce8963d expired cache_head can be removed from hash on cache_detail. However expired cache_head can wait for reply from previously submitted request. Such cache_head have increased refcounter and therefore it cannot be freed after cache_put(freeme). Because cache_head was removed from hash it cannot be found during cache_clean() and can be leaked forever together with stalled cache_request and other taken resources. Fixes d202cce8963d ("sunrpc: never return expired entries in sunrpc_cache_lookup") Cc: Pavel Tikhomirov <ptikhomirov@xxxxxxxxxxxxx> Cc: stable@xxxxxxxxxx # 2.6.35 Signed-off-by: Vasily Averin <vvs@xxxxxxxxxxxxx> --- net/sunrpc/cache.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 109fbe591e7b..483a5cfd60f0 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -54,6 +54,11 @@ static void cache_init(struct cache_head *h, struct cache_detail *detail) h->last_refresh = now; } +static void cache_fresh_locked(struct cache_head *head, time_t expiry, + struct cache_detail *detail); +static void cache_fresh_unlocked(struct cache_head *head, + struct cache_detail *detail); + struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, struct cache_head *key, int hash) { @@ -95,6 +100,7 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, if (cache_is_expired(detail, tmp)) { hlist_del_init(&tmp->cache_list); detail->entries --; + cache_fresh_locked(tmp, 0, detail); freeme = tmp; break; } @@ -110,8 +116,10 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, cache_get(new); write_unlock(&detail->hash_lock); - if (freeme) + if (freeme) { + cache_fresh_unlocked(freeme, detail); cache_put(freeme, detail); + } return new; } EXPORT_SYMBOL_GPL(sunrpc_cache_lookup); -- 2.17.1