Skip to content

Commit baefd3f

Browse files
author
Kent Overstreet
committed
bcachefs: btree_cache.freeable list fixes
When allocating new btree nodes, we were leaving them on the freeable list - unlocked - allowing them to be reclaimed: ouch. Additionally, bch2_btree_node_free_never_used() -> bch2_btree_node_hash_remove was putting it on the freelist, while bch2_btree_node_free_never_used() was putting it back on the btree update reserve list - ouch. Originally, the code was written to always keep btree nodes on a list - live or freeable - and this worked when new nodes were kept locked. But now with the cycle detector, we can't keep nodes locked that aren't tracked by the cycle detector; and this is fine as long as they're not reachable. We also have better and more robust leak detection now, with memory allocation profiling, so the original justification no longer applies. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
1 parent 9bb3385 commit baefd3f

File tree

3 files changed

+67
-48
lines changed

3 files changed

+67
-48
lines changed

fs/bcachefs/btree_cache.c

Lines changed: 61 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,38 @@ static inline size_t btree_cache_can_free(struct btree_cache_list *list)
5959

6060
static void btree_node_to_freedlist(struct btree_cache *bc, struct btree *b)
6161
{
62+
BUG_ON(!list_empty(&b->list));
63+
6264
if (b->c.lock.readers)
63-
list_move(&b->list, &bc->freed_pcpu);
65+
list_add(&b->list, &bc->freed_pcpu);
6466
else
65-
list_move(&b->list, &bc->freed_nonpcpu);
67+
list_add(&b->list, &bc->freed_nonpcpu);
6668
}
6769

68-
static void btree_node_data_free(struct bch_fs *c, struct btree *b)
70+
static void __bch2_btree_node_to_freelist(struct btree_cache *bc, struct btree *b)
71+
{
72+
BUG_ON(!list_empty(&b->list));
73+
BUG_ON(!b->data);
74+
75+
bc->nr_freeable++;
76+
list_add(&b->list, &bc->freeable);
77+
}
78+
79+
void bch2_btree_node_to_freelist(struct bch_fs *c, struct btree *b)
6980
{
7081
struct btree_cache *bc = &c->btree_cache;
7182

83+
mutex_lock(&bc->lock);
84+
__bch2_btree_node_to_freelist(bc, b);
85+
mutex_unlock(&bc->lock);
86+
87+
six_unlock_write(&b->c.lock);
88+
six_unlock_intent(&b->c.lock);
89+
}
90+
91+
static void __btree_node_data_free(struct btree_cache *bc, struct btree *b)
92+
{
93+
BUG_ON(!list_empty(&b->list));
7294
BUG_ON(btree_node_hashed(b));
7395

7496
/*
@@ -94,11 +116,17 @@ static void btree_node_data_free(struct bch_fs *c, struct btree *b)
94116
#endif
95117
b->aux_data = NULL;
96118

97-
bc->nr_freeable--;
98-
99119
btree_node_to_freedlist(bc, b);
100120
}
101121

122+
static void btree_node_data_free(struct btree_cache *bc, struct btree *b)
123+
{
124+
BUG_ON(list_empty(&b->list));
125+
list_del_init(&b->list);
126+
--bc->nr_freeable;
127+
__btree_node_data_free(bc, b);
128+
}
129+
102130
static int bch2_btree_cache_cmp_fn(struct rhashtable_compare_arg *arg,
103131
const void *obj)
104132
{
@@ -174,21 +202,10 @@ struct btree *__bch2_btree_node_mem_alloc(struct bch_fs *c)
174202

175203
bch2_btree_lock_init(&b->c, 0);
176204

177-
bc->nr_freeable++;
178-
list_add(&b->list, &bc->freeable);
205+
__bch2_btree_node_to_freelist(bc, b);
179206
return b;
180207
}
181208

182-
void bch2_btree_node_to_freelist(struct bch_fs *c, struct btree *b)
183-
{
184-
mutex_lock(&c->btree_cache.lock);
185-
list_move(&b->list, &c->btree_cache.freeable);
186-
mutex_unlock(&c->btree_cache.lock);
187-
188-
six_unlock_write(&b->c.lock);
189-
six_unlock_intent(&b->c.lock);
190-
}
191-
192209
static inline bool __btree_node_pinned(struct btree_cache *bc, struct btree *b)
193210
{
194211
struct bbpos pos = BBPOS(b->c.btree_id, b->key.k.p);
@@ -236,29 +253,34 @@ void bch2_btree_cache_unpin(struct bch_fs *c)
236253

237254
/* Btree in memory cache - hash table */
238255

239-
void bch2_btree_node_hash_remove(struct btree_cache *bc, struct btree *b)
256+
void __bch2_btree_node_hash_remove(struct btree_cache *bc, struct btree *b)
240257
{
241258
lockdep_assert_held(&bc->lock);
242-
int ret = rhashtable_remove_fast(&bc->table, &b->hash, bch_btree_cache_params);
243259

260+
int ret = rhashtable_remove_fast(&bc->table, &b->hash, bch_btree_cache_params);
244261
BUG_ON(ret);
245262

246263
/* Cause future lookups for this node to fail: */
247264
b->hash_val = 0;
248265

249266
if (b->c.btree_id < BTREE_ID_NR)
250267
--bc->nr_by_btree[b->c.btree_id];
268+
--bc->live[btree_node_pinned(b)].nr;
269+
list_del_init(&b->list);
270+
}
251271

252-
bc->live[btree_node_pinned(b)].nr--;
253-
bc->nr_freeable++;
254-
list_move(&b->list, &bc->freeable);
272+
void bch2_btree_node_hash_remove(struct btree_cache *bc, struct btree *b)
273+
{
274+
__bch2_btree_node_hash_remove(bc, b);
275+
__bch2_btree_node_to_freelist(bc, b);
255276
}
256277

257278
int __bch2_btree_node_hash_insert(struct btree_cache *bc, struct btree *b)
258279
{
280+
BUG_ON(!list_empty(&b->list));
259281
BUG_ON(b->hash_val);
260-
b->hash_val = btree_ptr_hash_val(&b->key);
261282

283+
b->hash_val = btree_ptr_hash_val(&b->key);
262284
int ret = rhashtable_lookup_insert_fast(&bc->table, &b->hash,
263285
bch_btree_cache_params);
264286
if (ret)
@@ -270,10 +292,8 @@ int __bch2_btree_node_hash_insert(struct btree_cache *bc, struct btree *b)
270292
bool p = __btree_node_pinned(bc, b);
271293
mod_bit(BTREE_NODE_pinned, &b->flags, p);
272294

273-
list_move_tail(&b->list, &bc->live[p].list);
295+
list_add_tail(&b->list, &bc->live[p].list);
274296
bc->live[p].nr++;
275-
276-
bc->nr_freeable--;
277297
return 0;
278298
}
279299

@@ -485,7 +505,7 @@ static unsigned long bch2_btree_cache_scan(struct shrinker *shrink,
485505
goto out;
486506

487507
if (!btree_node_reclaim(c, b, true)) {
488-
btree_node_data_free(c, b);
508+
btree_node_data_free(bc, b);
489509
six_unlock_write(&b->c.lock);
490510
six_unlock_intent(&b->c.lock);
491511
freed++;
@@ -501,10 +521,10 @@ static unsigned long bch2_btree_cache_scan(struct shrinker *shrink,
501521
bc->not_freed[BCH_BTREE_CACHE_NOT_FREED_access_bit]++;
502522
--touched;;
503523
} else if (!btree_node_reclaim(c, b, true)) {
504-
bch2_btree_node_hash_remove(bc, b);
524+
__bch2_btree_node_hash_remove(bc, b);
525+
__btree_node_data_free(bc, b);
505526

506527
freed++;
507-
btree_node_data_free(c, b);
508528
bc->nr_freed++;
509529

510530
six_unlock_write(&b->c.lock);
@@ -587,7 +607,7 @@ void bch2_fs_btree_cache_exit(struct bch_fs *c)
587607
BUG_ON(btree_node_read_in_flight(b) ||
588608
btree_node_write_in_flight(b));
589609

590-
btree_node_data_free(c, b);
610+
btree_node_data_free(bc, b);
591611
}
592612

593613
BUG_ON(!bch2_journal_error(&c->journal) &&
@@ -786,8 +806,8 @@ struct btree *bch2_btree_node_mem_alloc(struct btree_trans *trans, bool pcpu_rea
786806

787807
BUG_ON(!six_trylock_intent(&b->c.lock));
788808
BUG_ON(!six_trylock_write(&b->c.lock));
789-
got_node:
790809

810+
got_node:
791811
/*
792812
* btree_free() doesn't free memory; it sticks the node on the end of
793813
* the list. Check if there's any freed nodes there:
@@ -796,7 +816,12 @@ struct btree *bch2_btree_node_mem_alloc(struct btree_trans *trans, bool pcpu_rea
796816
if (!btree_node_reclaim(c, b2, false)) {
797817
swap(b->data, b2->data);
798818
swap(b->aux_data, b2->aux_data);
819+
820+
list_del_init(&b2->list);
821+
--bc->nr_freeable;
799822
btree_node_to_freedlist(bc, b2);
823+
mutex_unlock(&bc->lock);
824+
800825
six_unlock_write(&b2->c.lock);
801826
six_unlock_intent(&b2->c.lock);
802827
goto got_mem;
@@ -810,11 +835,8 @@ struct btree *bch2_btree_node_mem_alloc(struct btree_trans *trans, bool pcpu_rea
810835
goto err;
811836
}
812837

813-
mutex_lock(&bc->lock);
814-
bc->nr_freeable++;
815838
got_mem:
816-
mutex_unlock(&bc->lock);
817-
839+
BUG_ON(!list_empty(&b->list));
818840
BUG_ON(btree_node_hashed(b));
819841
BUG_ON(btree_node_dirty(b));
820842
BUG_ON(btree_node_write_in_flight(b));
@@ -845,7 +867,7 @@ struct btree *bch2_btree_node_mem_alloc(struct btree_trans *trans, bool pcpu_rea
845867
if (bc->alloc_lock == current) {
846868
b2 = btree_node_cannibalize(c);
847869
clear_btree_node_just_written(b2);
848-
bch2_btree_node_hash_remove(bc, b2);
870+
__bch2_btree_node_hash_remove(bc, b2);
849871

850872
if (b) {
851873
swap(b->data, b2->data);
@@ -855,9 +877,9 @@ struct btree *bch2_btree_node_mem_alloc(struct btree_trans *trans, bool pcpu_rea
855877
six_unlock_intent(&b2->c.lock);
856878
} else {
857879
b = b2;
858-
list_del_init(&b->list);
859880
}
860881

882+
BUG_ON(!list_empty(&b->list));
861883
mutex_unlock(&bc->lock);
862884

863885
trace_and_count(c, btree_cache_cannibalize, trans);
@@ -936,7 +958,7 @@ static noinline struct btree *bch2_btree_node_fill(struct btree_trans *trans,
936958
b->hash_val = 0;
937959

938960
mutex_lock(&bc->lock);
939-
list_add(&b->list, &bc->freeable);
961+
__bch2_btree_node_to_freelist(bc, b);
940962
mutex_unlock(&bc->lock);
941963

942964
six_unlock_write(&b->c.lock);
@@ -1356,7 +1378,7 @@ void bch2_btree_node_evict(struct btree_trans *trans, const struct bkey_i *k)
13561378

13571379
mutex_lock(&bc->lock);
13581380
bch2_btree_node_hash_remove(bc, b);
1359-
btree_node_data_free(c, b);
1381+
btree_node_data_free(bc, b);
13601382
mutex_unlock(&bc->lock);
13611383
out:
13621384
six_unlock_write(&b->c.lock);

fs/bcachefs/btree_cache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ void bch2_recalc_btree_reserve(struct bch_fs *);
1414

1515
void bch2_btree_node_to_freelist(struct bch_fs *, struct btree *);
1616

17+
void __bch2_btree_node_hash_remove(struct btree_cache *, struct btree *);
1718
void bch2_btree_node_hash_remove(struct btree_cache *, struct btree *);
19+
1820
int __bch2_btree_node_hash_insert(struct btree_cache *, struct btree *);
1921
int bch2_btree_node_hash_insert(struct btree_cache *, struct btree *,
2022
unsigned, enum btree_id);

fs/bcachefs/btree_update_interior.c

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,6 @@ static void __btree_node_free(struct btree_trans *trans, struct btree *b)
237237
BUG_ON(b->will_make_reachable);
238238

239239
clear_btree_node_noevict(b);
240-
241-
mutex_lock(&c->btree_cache.lock);
242-
list_move(&b->list, &c->btree_cache.freeable);
243-
mutex_unlock(&c->btree_cache.lock);
244240
}
245241

246242
static void bch2_btree_node_free_inmem(struct btree_trans *trans,
@@ -252,12 +248,12 @@ static void bch2_btree_node_free_inmem(struct btree_trans *trans,
252248

253249
bch2_btree_node_lock_write_nofail(trans, path, &b->c);
254250

251+
__btree_node_free(trans, b);
252+
255253
mutex_lock(&c->btree_cache.lock);
256254
bch2_btree_node_hash_remove(&c->btree_cache, b);
257255
mutex_unlock(&c->btree_cache.lock);
258256

259-
__btree_node_free(trans, b);
260-
261257
six_unlock_write(&b->c.lock);
262258
mark_btree_node_locked_noreset(path, level, BTREE_NODE_INTENT_LOCKED);
263259

@@ -289,7 +285,7 @@ static void bch2_btree_node_free_never_used(struct btree_update *as,
289285
clear_btree_node_need_write(b);
290286

291287
mutex_lock(&c->btree_cache.lock);
292-
bch2_btree_node_hash_remove(&c->btree_cache, b);
288+
__bch2_btree_node_hash_remove(&c->btree_cache, b);
293289
mutex_unlock(&c->btree_cache.lock);
294290

295291
BUG_ON(p->nr >= ARRAY_SIZE(p->b));
@@ -521,8 +517,7 @@ static void bch2_btree_reserve_put(struct btree_update *as, struct btree_trans *
521517
btree_node_lock_nopath_nofail(trans, &b->c, SIX_LOCK_intent);
522518
btree_node_lock_nopath_nofail(trans, &b->c, SIX_LOCK_write);
523519
__btree_node_free(trans, b);
524-
six_unlock_write(&b->c.lock);
525-
six_unlock_intent(&b->c.lock);
520+
bch2_btree_node_to_freelist(c, b);
526521
}
527522
}
528523
}

0 commit comments

Comments
 (0)