From cf3cb3f19763a1261c78c5615cac7791698526a6 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 12 Apr 2021 10:32:21 -0700 Subject: [PATCH 1/2] Wait for rhashtable to rehash on insert EBUSY The rhashtable can return EBUSY if you insert fast enough to trigger an expansion of the next table size that is waiting to be rehashed in an rcu callback. If we get EBUSY from rhasthable_insert we call synchronize_rcu to wait for the rehash to complete before trying again. This was hit in testing restores of a very large namespace and took a few hours to hit. Signed-off-by: Zach Brown --- kmod/src/block.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kmod/src/block.c b/kmod/src/block.c index 1d80a667..ef6a1cb3 100644 --- a/kmod/src/block.c +++ b/kmod/src/block.c @@ -286,10 +286,16 @@ static int block_insert(struct super_block *sb, struct block_private *bp) WARN_ON_ONCE(atomic_read(&bp->refcount) & BLOCK_REF_INSERTED); +retry: atomic_add(BLOCK_REF_INSERTED, &bp->refcount); ret = rhashtable_insert_fast(&binf->ht, &bp->ht_head, block_ht_params); if (ret < 0) { atomic_sub(BLOCK_REF_INSERTED, &bp->refcount); + if (ret == -EBUSY) { + /* wait for pending rebalance to finish */ + synchronize_rcu(); + goto retry; + } } else { atomic_inc(&binf->total_inserted); TRACE_BLOCK(insert, bp); From c3290771a07ca743d28ac0935aac79eaa2b7e377 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Wed, 7 Apr 2021 14:34:05 -0700 Subject: [PATCH 2/2] Block cache use rht _lookup_ insert for EEXIST The sneaky rhashtable_insert_fast() can't return -EEXIST despite the last line of the function *REALLY* making it look like it can. It just inserts new objects at the head of the bucket lists without comparing the insertion with existing objects. The block cache was relying on insertion to resolve duplicate racing allocated blocks. Because it couldn't return -EEXIST we could get duplicate cached blocks present in the hash table. rhashtable_lookup_insert_fast() fixes this by actually comparing the inserted objects key with the objects found in the insertion bucket. A racing allocator trying to insert a duplicate cached block will get an error, drop their allocated block, and retry their lookup. Signed-off-by: Zach Brown --- kmod/src/block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kmod/src/block.c b/kmod/src/block.c index ef6a1cb3..bb581173 100644 --- a/kmod/src/block.c +++ b/kmod/src/block.c @@ -288,7 +288,7 @@ static int block_insert(struct super_block *sb, struct block_private *bp) retry: atomic_add(BLOCK_REF_INSERTED, &bp->refcount); - ret = rhashtable_insert_fast(&binf->ht, &bp->ht_head, block_ht_params); + ret = rhashtable_lookup_insert_fast(&binf->ht, &bp->ht_head, block_ht_params); if (ret < 0) { atomic_sub(BLOCK_REF_INSERTED, &bp->refcount); if (ret == -EBUSY) {