Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

bcache: fixup btree_cache_wait list damage

We get a kernel crash about "list_add corruption. next->prev should be
prev (ffff9c801bc01210), but was ffff9c77b688237c.
(next=ffffae586d8afe68)."

crash> struct list_head 0xffff9c801bc01210
struct list_head {
next = 0xffffae586d8afe68,
prev = 0xffffae586d8afe68
}
crash> struct list_head 0xffff9c77b688237c
struct list_head {
next = 0x0,
prev = 0x0
}
crash> struct list_head 0xffffae586d8afe68
struct list_head struct: invalid kernel virtual address: ffffae586d8afe68 type: "gdb_readmem_callback"
Cannot access memory at address 0xffffae586d8afe68

[230469.019492] Call Trace:
[230469.032041] prepare_to_wait+0x8a/0xb0
[230469.044363] ? bch_btree_keys_free+0x6c/0xc0 [escache]
[230469.056533] mca_cannibalize_lock+0x72/0x90 [escache]
[230469.068788] mca_alloc+0x2ae/0x450 [escache]
[230469.080790] bch_btree_node_get+0x136/0x2d0 [escache]
[230469.092681] bch_btree_check_thread+0x1e1/0x260 [escache]
[230469.104382] ? finish_wait+0x80/0x80
[230469.115884] ? bch_btree_check_recurse+0x1a0/0x1a0 [escache]
[230469.127259] kthread+0x112/0x130
[230469.138448] ? kthread_flush_work_fn+0x10/0x10
[230469.149477] ret_from_fork+0x35/0x40

bch_btree_check_thread() and bch_dirty_init_thread() may call
mca_cannibalize() to cannibalize other cached btree nodes. Only one thread
can do it at a time, so the op of other threads will be added to the
btree_cache_wait list.

We must call finish_wait() to remove op from btree_cache_wait before free
it's memory address. Otherwise, the list will be damaged. Also should call
bch_cannibalize_unlock() to release the btree_cache_alloc_lock and wake_up
other waiters.

Fixes: 8e7102273f59 ("bcache: make bch_btree_check() to be multithreaded")
Fixes: b144e45fc576 ("bcache: make bch_sectors_dirty_init() to be multithreaded")
Cc: stable@vger.kernel.org
Signed-off-by: Mingzhe Zou <mingzhe.zou@easystack.cn>
Signed-off-by: Coly Li <colyli@suse.de>
Link: https://lore.kernel.org/r/20230615121223.22502-7-colyli@suse.de
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Mingzhe Zou and committed by
Jens Axboe
f0854489 80fca8a1

+21 -1
+10 -1
drivers/md/bcache/btree.c
··· 885 885 * cannibalize_bucket() will take. This means every time we unlock the root of 886 886 * the btree, we need to release this lock if we have it held. 887 887 */ 888 - static void bch_cannibalize_unlock(struct cache_set *c) 888 + void bch_cannibalize_unlock(struct cache_set *c) 889 889 { 890 890 spin_lock(&c->btree_cannibalize_lock); 891 891 if (c->btree_cache_alloc_lock == current) { ··· 1970 1970 c->gc_stats.nodes++; 1971 1971 bch_btree_op_init(&op, 0); 1972 1972 ret = bcache_btree(check_recurse, p, c->root, &op); 1973 + /* 1974 + * The op may be added to cache_set's btree_cache_wait 1975 + * in mca_cannibalize(), must ensure it is removed from 1976 + * the list and release btree_cache_alloc_lock before 1977 + * free op memory. 1978 + * Otherwise, the btree_cache_wait will be damaged. 1979 + */ 1980 + bch_cannibalize_unlock(c); 1981 + finish_wait(&c->btree_cache_wait, &(&op)->wait); 1973 1982 if (ret) 1974 1983 goto out; 1975 1984 }
+1
drivers/md/bcache/btree.h
··· 282 282 void bch_moving_gc(struct cache_set *c); 283 283 int bch_btree_check(struct cache_set *c); 284 284 void bch_initial_mark_key(struct cache_set *c, int level, struct bkey *k); 285 + void bch_cannibalize_unlock(struct cache_set *c); 285 286 286 287 static inline void wake_up_gc(struct cache_set *c) 287 288 {
+10
drivers/md/bcache/writeback.c
··· 890 890 if (ret < 0) 891 891 pr_warn("sectors dirty init failed, ret=%d!\n", ret); 892 892 893 + /* 894 + * The op may be added to cache_set's btree_cache_wait 895 + * in mca_cannibalize(), must ensure it is removed from 896 + * the list and release btree_cache_alloc_lock before 897 + * free op memory. 898 + * Otherwise, the btree_cache_wait will be damaged. 899 + */ 900 + bch_cannibalize_unlock(c); 901 + finish_wait(&c->btree_cache_wait, &(&op.op)->wait); 902 + 893 903 return ret; 894 904 } 895 905