Commit 2298abcb authored by Long Li's avatar Long Li Committed by Chuck Lever
Browse files

sunrpc: fix race in cache cleanup causing stale nextcheck time



When cache cleanup runs concurrently with cache entry removal, a race
condition can occur that leads to incorrect nextcheck times. This can
delay cache cleanup for the cache_detail by up to 1800 seconds:

1. cache_clean() sets nextcheck to current time plus 1800 seconds
2. While scanning a non-empty bucket, concurrent cache entry removal can
   empty that bucket
3. cache_clean() finds no cache entries in the now-empty bucket to update
   the nextcheck time
4. This maybe delays the next scan of the cache_detail by up to 1800
   seconds even when it should be scanned earlier based on remaining
   entries

Fix this by moving the hash_lock acquisition earlier in cache_clean().
This ensures bucket emptiness checks and nextcheck updates happen
atomically, preventing the race between cleanup and entry removal.

Signed-off-by: default avatarLong Li <leo.lilong@huawei.com>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
parent 5ca00634
Loading
Loading
Loading
Loading
+7 −8
Original line number Diff line number Diff line
@@ -464,24 +464,21 @@ static int cache_clean(void)
		}
	}

	spin_lock(&current_detail->hash_lock);

	/* find a non-empty bucket in the table */
	while (current_detail &&
	       current_index < current_detail->hash_size &&
	while (current_index < current_detail->hash_size &&
	       hlist_empty(&current_detail->hash_table[current_index]))
		current_index++;

	/* find a cleanable entry in the bucket and clean it, or set to next bucket */

	if (current_detail && current_index < current_detail->hash_size) {
	if (current_index < current_detail->hash_size) {
		struct cache_head *ch = NULL;
		struct cache_detail *d;
		struct hlist_head *head;
		struct hlist_node *tmp;

		spin_lock(&current_detail->hash_lock);

		/* Ok, now to clean this strand */

		head = &current_detail->hash_table[current_index];
		hlist_for_each_entry_safe(ch, tmp, head, cache_list) {
			if (current_detail->nextcheck > ch->expiry_time)
@@ -502,8 +499,10 @@ static int cache_clean(void)
		spin_unlock(&cache_list_lock);
		if (ch)
			sunrpc_end_cache_remove_entry(ch, d);
	} else
	} else {
		spin_unlock(&current_detail->hash_lock);
		spin_unlock(&cache_list_lock);
	}

	return rv;
}