Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 013920eb authored by NeilBrown's avatar NeilBrown Committed by J. Bruce Fields
Browse files

sunrpc/cache: ensure items removed from cache do not have pending upcalls.



It is possible for a race to set CACHE_PENDING after cache_clean()
has removed a cache entry from the cache.
If CACHE_PENDING is still set when the entry is finally 'put',
the cache_dequeue() will never happen and we can leak memory.

So set a new flag 'CACHE_CLEANED' when we remove something from
the cache, and don't queue any upcall if it is set.

If CACHE_PENDING is set before CACHE_CLEANED, the call that
cache_clean() makes to cache_fresh_unlocked() will free memory
as needed.  If CACHE_PENDING is set after CACHE_CLEANED, the
test in sunrpc_cache_pipe_upcall will ensure that the memory
is not allocated.

Reported-by: default avatar <bstroesser@ts.fujitsu.com>
Signed-off-by: default avatarNeilBrown <neilb@suse.de>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 2a1c7f53
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ struct cache_head {
#define	CACHE_VALID	0	/* Entry contains valid data */
#define	CACHE_NEGATIVE	1	/* Negative entry - there is no match for the key */
#define	CACHE_PENDING	2	/* An upcall has been sent but no reply received yet*/
#define	CACHE_CLEANED	3	/* Entry has been cleaned from cache */

#define	CACHE_NEW_EXPIRY 120	/* keep new things pending confirmation for 120 seconds */

+5 −1
Original line number Diff line number Diff line
@@ -306,7 +306,7 @@ EXPORT_SYMBOL_GPL(cache_check);
 * a current pointer into that list and into the table
 * for that entry.
 *
 * Each time clean_cache is called it finds the next non-empty entry
 * Each time cache_clean is called it finds the next non-empty entry
 * in the current table and walks the list in that entry
 * looking for entries that can be removed.
 *
@@ -453,6 +453,7 @@ static int cache_clean(void)
			current_index ++;
		spin_unlock(&cache_list_lock);
		if (ch) {
			set_bit(CACHE_CLEANED, &ch->flags);
			cache_fresh_unlocked(ch, d);
			cache_put(ch, d);
		}
@@ -1178,6 +1179,9 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
		warn_no_listener(detail);
		return -EINVAL;
	}
	if (test_bit(CACHE_CLEANED, &h->flags))
		/* Too late to make an upcall */
		return -EAGAIN;

	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
	if (!buf)