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

bcache: improve multithreaded bch_sectors_dirty_init()

Commit b144e45fc576 ("bcache: make bch_sectors_dirty_init() to be
multithreaded") makes bch_sectors_dirty_init() to be much faster
when counting dirty sectors by iterating all dirty keys in the btree.
But it isn't in ideal shape yet, still can be improved.

This patch does the following changes to improve current parallel dirty
keys iteration on the btree,
- Add read lock to root node when multiple threads iterating the btree,
to prevent the root node gets split by I/Os from other registered
bcache devices.
- Remove local variable "char name[32]" and generate kernel thread name
string directly when calling kthread_run().
- Allocate "struct bch_dirty_init_state state" directly on stack and
avoid the unnecessary dynamic memory allocation for it.
- Decrease BCH_DIRTY_INIT_THRD_MAX from 64 to 12 which is enough indeed.
- Increase &state->started to count created kernel thread after it
succeeds to create.
- When wait for all dirty key counting threads to finish, use
wait_event() to replace wait_event_interruptible().

With the above changes, the code is more clear, and some potential error
conditions are avoided.

Fixes: b144e45fc576 ("bcache: make bch_sectors_dirty_init() to be multithreaded")
Signed-off-by: Coly Li <colyli@suse.de>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20220524102336.10684-3-colyli@suse.de
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Coly Li and committed by
Jens Axboe
4dc34ae1 62253644

+25 -37
+24 -36
drivers/md/bcache/writeback.c
··· 948 948 struct btree_iter iter; 949 949 struct sectors_dirty_init op; 950 950 struct cache_set *c = d->c; 951 - struct bch_dirty_init_state *state; 952 - char name[32]; 951 + struct bch_dirty_init_state state; 953 952 954 953 /* Just count root keys if no leaf node */ 954 + rw_lock(0, c->root, c->root->level); 955 955 if (c->root->level == 0) { 956 956 bch_btree_op_init(&op.op, -1); 957 957 op.inode = d->id; ··· 961 961 for_each_key_filter(&c->root->keys, 962 962 k, &iter, bch_ptr_invalid) 963 963 sectors_dirty_init_fn(&op.op, c->root, k); 964 + rw_unlock(0, c->root); 964 965 return; 965 966 } 966 967 967 - state = kzalloc(sizeof(struct bch_dirty_init_state), GFP_KERNEL); 968 - if (!state) { 969 - pr_warn("sectors dirty init failed: cannot allocate memory\n"); 970 - return; 971 - } 968 + state.c = c; 969 + state.d = d; 970 + state.total_threads = bch_btre_dirty_init_thread_nr(); 971 + state.key_idx = 0; 972 + spin_lock_init(&state.idx_lock); 973 + atomic_set(&state.started, 0); 974 + atomic_set(&state.enough, 0); 975 + init_waitqueue_head(&state.wait); 972 976 973 - state->c = c; 974 - state->d = d; 975 - state->total_threads = bch_btre_dirty_init_thread_nr(); 976 - state->key_idx = 0; 977 - spin_lock_init(&state->idx_lock); 978 - atomic_set(&state->started, 0); 979 - atomic_set(&state->enough, 0); 980 - init_waitqueue_head(&state->wait); 981 - 982 - for (i = 0; i < state->total_threads; i++) { 983 - /* Fetch latest state->enough earlier */ 977 + for (i = 0; i < state.total_threads; i++) { 978 + /* Fetch latest state.enough earlier */ 984 979 smp_mb__before_atomic(); 985 - if (atomic_read(&state->enough)) 980 + if (atomic_read(&state.enough)) 986 981 break; 987 982 988 - state->infos[i].state = state; 989 - atomic_inc(&state->started); 990 - snprintf(name, sizeof(name), "bch_dirty_init[%d]", i); 991 - 992 - state->infos[i].thread = 993 - kthread_run(bch_dirty_init_thread, 994 - &state->infos[i], 995 - name); 996 - if (IS_ERR(state->infos[i].thread)) { 983 + state.infos[i].state = &state; 984 + state.infos[i].thread = 985 + kthread_run(bch_dirty_init_thread, &state.infos[i], 986 + "bch_dirtcnt[%d]", i); 987 + if (IS_ERR(state.infos[i].thread)) { 997 988 pr_err("fails to run thread bch_dirty_init[%d]\n", i); 998 989 for (--i; i >= 0; i--) 999 - kthread_stop(state->infos[i].thread); 990 + kthread_stop(state.infos[i].thread); 1000 991 goto out; 1001 992 } 993 + atomic_inc(&state.started); 1002 994 } 1003 995 1004 - /* 1005 - * Must wait for all threads to stop. 1006 - */ 1007 - wait_event_interruptible(state->wait, 1008 - atomic_read(&state->started) == 0); 1009 - 1010 996 out: 1011 - kfree(state); 997 + /* Must wait for all threads to stop. */ 998 + wait_event(state.wait, atomic_read(&state.started) == 0); 999 + rw_unlock(0, c->root); 1012 1000 } 1013 1001 1014 1002 void bch_cached_dev_writeback_init(struct cached_dev *dc)
+1 -1
drivers/md/bcache/writeback.h
··· 20 20 #define BCH_WRITEBACK_FRAGMENT_THRESHOLD_MID 57 21 21 #define BCH_WRITEBACK_FRAGMENT_THRESHOLD_HIGH 64 22 22 23 - #define BCH_DIRTY_INIT_THRD_MAX 64 23 + #define BCH_DIRTY_INIT_THRD_MAX 12 24 24 /* 25 25 * 14 (16384ths) is chosen here as something that each backing device 26 26 * should be a reasonable fraction of the share, and not to blow up