Skip to content

Commit be9badc

Browse files
Eric BiggersMikulas Patocka
authored andcommitted
dm-bufio: avoid redundant buffer_tree lookups
dm-bufio's map from block number to buffer is organized as a hash table of red-black trees. It does far more lookups in this hash table than necessary: typically one lookup to lock the tree, one lookup to search the tree, and one lookup to unlock the tree. Only one of those lookups is needed. Optimize it to do only the minimum number of lookups. This improves performance. It also reduces the object code size, considering that the redundant hash table lookups were being inlined. For example, the size of the text section of dm-bufio.o decreases from 15599 to 15070 bytes with gcc 15 and x86_64, or from 20652 to 20244 bytes with clang 21 and arm64. Signed-off-by: Eric Biggers <ebiggers@kernel.org> Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
1 parent 1bf7ba4 commit be9badc

1 file changed

Lines changed: 89 additions & 53 deletions

File tree

drivers/md/dm-bufio.c

Lines changed: 89 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -401,36 +401,51 @@ static inline unsigned int cache_index(sector_t block, unsigned int num_locks)
401401
return dm_hash_locks_index(block, num_locks);
402402
}
403403

404-
static inline void cache_read_lock(struct dm_buffer_cache *bc, sector_t block)
404+
/* Get the buffer tree in the cache for the given block. Doesn't lock it. */
405+
static inline struct buffer_tree *cache_get_tree(struct dm_buffer_cache *bc,
406+
sector_t block)
407+
{
408+
return &bc->trees[cache_index(block, bc->num_locks)];
409+
}
410+
411+
/* Lock the given buffer tree in the cache for reading. */
412+
static inline void cache_read_lock(struct dm_buffer_cache *bc,
413+
struct buffer_tree *tree)
405414
{
406415
if (static_branch_unlikely(&no_sleep_enabled) && bc->no_sleep)
407-
read_lock_bh(&bc->trees[cache_index(block, bc->num_locks)].u.spinlock);
416+
read_lock_bh(&tree->u.spinlock);
408417
else
409-
down_read(&bc->trees[cache_index(block, bc->num_locks)].u.lock);
418+
down_read(&tree->u.lock);
410419
}
411420

412-
static inline void cache_read_unlock(struct dm_buffer_cache *bc, sector_t block)
421+
/* Unlock the given buffer tree in the cache for reading. */
422+
static inline void cache_read_unlock(struct dm_buffer_cache *bc,
423+
struct buffer_tree *tree)
413424
{
414425
if (static_branch_unlikely(&no_sleep_enabled) && bc->no_sleep)
415-
read_unlock_bh(&bc->trees[cache_index(block, bc->num_locks)].u.spinlock);
426+
read_unlock_bh(&tree->u.spinlock);
416427
else
417-
up_read(&bc->trees[cache_index(block, bc->num_locks)].u.lock);
428+
up_read(&tree->u.lock);
418429
}
419430

420-
static inline void cache_write_lock(struct dm_buffer_cache *bc, sector_t block)
431+
/* Lock the given buffer tree in the cache for writing. */
432+
static inline void cache_write_lock(struct dm_buffer_cache *bc,
433+
struct buffer_tree *tree)
421434
{
422435
if (static_branch_unlikely(&no_sleep_enabled) && bc->no_sleep)
423-
write_lock_bh(&bc->trees[cache_index(block, bc->num_locks)].u.spinlock);
436+
write_lock_bh(&tree->u.spinlock);
424437
else
425-
down_write(&bc->trees[cache_index(block, bc->num_locks)].u.lock);
438+
down_write(&tree->u.lock);
426439
}
427440

428-
static inline void cache_write_unlock(struct dm_buffer_cache *bc, sector_t block)
441+
/* Unlock the given buffer tree in the cache for writing. */
442+
static inline void cache_write_unlock(struct dm_buffer_cache *bc,
443+
struct buffer_tree *tree)
429444
{
430445
if (static_branch_unlikely(&no_sleep_enabled) && bc->no_sleep)
431-
write_unlock_bh(&bc->trees[cache_index(block, bc->num_locks)].u.spinlock);
446+
write_unlock_bh(&tree->u.spinlock);
432447
else
433-
up_write(&bc->trees[cache_index(block, bc->num_locks)].u.lock);
448+
up_write(&tree->u.lock);
434449
}
435450

436451
/*
@@ -602,17 +617,19 @@ static void __cache_inc_buffer(struct dm_buffer *b)
602617
WRITE_ONCE(b->last_accessed, jiffies);
603618
}
604619

605-
static struct dm_buffer *cache_get(struct dm_buffer_cache *bc, sector_t block)
620+
static struct dm_buffer *cache_get(struct dm_buffer_cache *bc,
621+
struct buffer_tree *tree, sector_t block)
606622
{
607623
struct dm_buffer *b;
608624

609-
cache_read_lock(bc, block);
610-
b = __cache_get(&bc->trees[cache_index(block, bc->num_locks)].root, block);
625+
/* Assuming tree == cache_get_tree(bc, block) */
626+
cache_read_lock(bc, tree);
627+
b = __cache_get(&tree->root, block);
611628
if (b) {
612629
lru_reference(&b->lru);
613630
__cache_inc_buffer(b);
614631
}
615-
cache_read_unlock(bc, block);
632+
cache_read_unlock(bc, tree);
616633

617634
return b;
618635
}
@@ -663,7 +680,7 @@ static struct dm_buffer *__cache_evict(struct dm_buffer_cache *bc, int list_mode
663680

664681
b = le_to_buffer(le);
665682
/* __evict_pred will have locked the appropriate tree. */
666-
rb_erase(&b->node, &bc->trees[cache_index(b->block, bc->num_locks)].root);
683+
rb_erase(&b->node, &cache_get_tree(bc, b->block)->root);
667684

668685
return b;
669686
}
@@ -686,15 +703,17 @@ static struct dm_buffer *cache_evict(struct dm_buffer_cache *bc, int list_mode,
686703
/*
687704
* Mark a buffer as clean or dirty. Not threadsafe.
688705
*/
689-
static void cache_mark(struct dm_buffer_cache *bc, struct dm_buffer *b, int list_mode)
706+
static void cache_mark(struct dm_buffer_cache *bc, struct buffer_tree *tree,
707+
struct dm_buffer *b, int list_mode)
690708
{
691-
cache_write_lock(bc, b->block);
709+
/* Assuming tree == cache_get_tree(bc, b->block) */
710+
cache_write_lock(bc, tree);
692711
if (list_mode != b->list_mode) {
693712
lru_remove(&bc->lru[b->list_mode], &b->lru);
694713
b->list_mode = list_mode;
695714
lru_insert(&bc->lru[b->list_mode], &b->lru);
696715
}
697-
cache_write_unlock(bc, b->block);
716+
cache_write_unlock(bc, tree);
698717
}
699718

700719
/*--------------*/
@@ -820,19 +839,21 @@ static bool __cache_insert(struct rb_root *root, struct dm_buffer *b)
820839
return true;
821840
}
822841

823-
static bool cache_insert(struct dm_buffer_cache *bc, struct dm_buffer *b)
842+
static bool cache_insert(struct dm_buffer_cache *bc, struct buffer_tree *tree,
843+
struct dm_buffer *b)
824844
{
825845
bool r;
826846

827847
if (WARN_ON_ONCE(b->list_mode >= LIST_SIZE))
828848
return false;
829849

830-
cache_write_lock(bc, b->block);
850+
/* Assuming tree == cache_get_tree(bc, b->block) */
851+
cache_write_lock(bc, tree);
831852
BUG_ON(atomic_read(&b->hold_count) != 1);
832-
r = __cache_insert(&bc->trees[cache_index(b->block, bc->num_locks)].root, b);
853+
r = __cache_insert(&tree->root, b);
833854
if (r)
834855
lru_insert(&bc->lru[b->list_mode], &b->lru);
835-
cache_write_unlock(bc, b->block);
856+
cache_write_unlock(bc, tree);
836857

837858
return r;
838859
}
@@ -845,21 +866,23 @@ static bool cache_insert(struct dm_buffer_cache *bc, struct dm_buffer *b)
845866
*
846867
* Not threadsafe.
847868
*/
848-
static bool cache_remove(struct dm_buffer_cache *bc, struct dm_buffer *b)
869+
static bool cache_remove(struct dm_buffer_cache *bc, struct buffer_tree *tree,
870+
struct dm_buffer *b)
849871
{
850872
bool r;
851873

852-
cache_write_lock(bc, b->block);
874+
/* Assuming tree == cache_get_tree(bc, b->block) */
875+
cache_write_lock(bc, tree);
853876

854877
if (atomic_read(&b->hold_count) != 1) {
855878
r = false;
856879
} else {
857880
r = true;
858-
rb_erase(&b->node, &bc->trees[cache_index(b->block, bc->num_locks)].root);
881+
rb_erase(&b->node, &tree->root);
859882
lru_remove(&bc->lru[b->list_mode], &b->lru);
860883
}
861884

862-
cache_write_unlock(bc, b->block);
885+
cache_write_unlock(bc, tree);
863886

864887
return r;
865888
}
@@ -1725,14 +1748,16 @@ static void __check_watermark(struct dm_bufio_client *c,
17251748
*--------------------------------------------------------------
17261749
*/
17271750

1728-
static void cache_put_and_wake(struct dm_bufio_client *c, struct dm_buffer *b)
1751+
static void cache_put_and_wake(struct dm_bufio_client *c,
1752+
struct buffer_tree *tree, struct dm_buffer *b)
17291753
{
17301754
bool wake;
17311755

1732-
cache_read_lock(&c->cache, b->block);
1756+
/* Assuming tree == cache_get_tree(&c->cache, b->block) */
1757+
cache_read_lock(&c->cache, tree);
17331758
BUG_ON(!atomic_read(&b->hold_count));
17341759
wake = atomic_dec_and_test(&b->hold_count);
1735-
cache_read_unlock(&c->cache, b->block);
1760+
cache_read_unlock(&c->cache, tree);
17361761

17371762
/*
17381763
* Relying on waitqueue_active() is racey, but we sleep
@@ -1746,7 +1771,8 @@ static void cache_put_and_wake(struct dm_bufio_client *c, struct dm_buffer *b)
17461771
* This assumes you have already checked the cache to see if the buffer
17471772
* is already present (it will recheck after dropping the lock for allocation).
17481773
*/
1749-
static struct dm_buffer *__bufio_new(struct dm_bufio_client *c, sector_t block,
1774+
static struct dm_buffer *__bufio_new(struct dm_bufio_client *c,
1775+
struct buffer_tree *tree, sector_t block,
17501776
enum new_flag nf, int *need_submit,
17511777
struct list_head *write_list)
17521778
{
@@ -1766,7 +1792,7 @@ static struct dm_buffer *__bufio_new(struct dm_bufio_client *c, sector_t block,
17661792
* We've had a period where the mutex was unlocked, so need to
17671793
* recheck the buffer tree.
17681794
*/
1769-
b = cache_get(&c->cache, block);
1795+
b = cache_get(&c->cache, tree, block);
17701796
if (b) {
17711797
__free_buffer_wake(new_b);
17721798
goto found_buffer;
@@ -1794,13 +1820,13 @@ static struct dm_buffer *__bufio_new(struct dm_bufio_client *c, sector_t block,
17941820
* is set. Otherwise another thread could get it and use
17951821
* it before it had been read.
17961822
*/
1797-
cache_insert(&c->cache, b);
1823+
cache_insert(&c->cache, tree, b);
17981824

17991825
return b;
18001826

18011827
found_buffer:
18021828
if (nf == NF_PREFETCH) {
1803-
cache_put_and_wake(c, b);
1829+
cache_put_and_wake(c, tree, b);
18041830
return NULL;
18051831
}
18061832

@@ -1812,7 +1838,7 @@ static struct dm_buffer *__bufio_new(struct dm_bufio_client *c, sector_t block,
18121838
* the same buffer, it would deadlock if we waited.
18131839
*/
18141840
if (nf == NF_GET && unlikely(test_bit_acquire(B_READING, &b->state))) {
1815-
cache_put_and_wake(c, b);
1841+
cache_put_and_wake(c, tree, b);
18161842
return NULL;
18171843
}
18181844

@@ -1846,6 +1872,7 @@ static void *new_read(struct dm_bufio_client *c, sector_t block,
18461872
enum new_flag nf, struct dm_buffer **bp,
18471873
unsigned short ioprio)
18481874
{
1875+
struct buffer_tree *tree;
18491876
int need_submit = 0;
18501877
struct dm_buffer *b;
18511878

@@ -1857,10 +1884,11 @@ static void *new_read(struct dm_bufio_client *c, sector_t block,
18571884
* Fast path, hopefully the block is already in the cache. No need
18581885
* to get the client lock for this.
18591886
*/
1860-
b = cache_get(&c->cache, block);
1887+
tree = cache_get_tree(&c->cache, block);
1888+
b = cache_get(&c->cache, tree, block);
18611889
if (b) {
18621890
if (nf == NF_PREFETCH) {
1863-
cache_put_and_wake(c, b);
1891+
cache_put_and_wake(c, tree, b);
18641892
return NULL;
18651893
}
18661894

@@ -1872,7 +1900,7 @@ static void *new_read(struct dm_bufio_client *c, sector_t block,
18721900
* the same buffer, it would deadlock if we waited.
18731901
*/
18741902
if (nf == NF_GET && unlikely(test_bit_acquire(B_READING, &b->state))) {
1875-
cache_put_and_wake(c, b);
1903+
cache_put_and_wake(c, tree, b);
18761904
return NULL;
18771905
}
18781906
}
@@ -1882,7 +1910,7 @@ static void *new_read(struct dm_bufio_client *c, sector_t block,
18821910
return NULL;
18831911

18841912
dm_bufio_lock(c);
1885-
b = __bufio_new(c, block, nf, &need_submit, &write_list);
1913+
b = __bufio_new(c, tree, block, nf, &need_submit, &write_list);
18861914
dm_bufio_unlock(c);
18871915
}
18881916

@@ -1969,18 +1997,20 @@ static void __dm_bufio_prefetch(struct dm_bufio_client *c,
19691997
blk_start_plug(&plug);
19701998

19711999
for (; n_blocks--; block++) {
1972-
int need_submit;
2000+
struct buffer_tree *tree;
19732001
struct dm_buffer *b;
2002+
int need_submit;
19742003

1975-
b = cache_get(&c->cache, block);
2004+
tree = cache_get_tree(&c->cache, block);
2005+
b = cache_get(&c->cache, tree, block);
19762006
if (b) {
19772007
/* already in cache */
1978-
cache_put_and_wake(c, b);
2008+
cache_put_and_wake(c, tree, b);
19792009
continue;
19802010
}
19812011

19822012
dm_bufio_lock(c);
1983-
b = __bufio_new(c, block, NF_PREFETCH, &need_submit,
2013+
b = __bufio_new(c, tree, block, NF_PREFETCH, &need_submit,
19842014
&write_list);
19852015
if (unlikely(!list_empty(&write_list))) {
19862016
dm_bufio_unlock(c);
@@ -2025,6 +2055,7 @@ EXPORT_SYMBOL_GPL(dm_bufio_prefetch_with_ioprio);
20252055
void dm_bufio_release(struct dm_buffer *b)
20262056
{
20272057
struct dm_bufio_client *c = b->c;
2058+
struct buffer_tree *tree = cache_get_tree(&c->cache, b->block);
20282059

20292060
/*
20302061
* If there were errors on the buffer, and the buffer is not
@@ -2038,7 +2069,7 @@ void dm_bufio_release(struct dm_buffer *b)
20382069
dm_bufio_lock(c);
20392070

20402071
/* cache remove can fail if there are other holders */
2041-
if (cache_remove(&c->cache, b)) {
2072+
if (cache_remove(&c->cache, tree, b)) {
20422073
__free_buffer_wake(b);
20432074
dm_bufio_unlock(c);
20442075
return;
@@ -2047,7 +2078,7 @@ void dm_bufio_release(struct dm_buffer *b)
20472078
dm_bufio_unlock(c);
20482079
}
20492080

2050-
cache_put_and_wake(c, b);
2081+
cache_put_and_wake(c, tree, b);
20512082
}
20522083
EXPORT_SYMBOL_GPL(dm_bufio_release);
20532084

@@ -2066,7 +2097,8 @@ void dm_bufio_mark_partial_buffer_dirty(struct dm_buffer *b,
20662097
if (!test_and_set_bit(B_DIRTY, &b->state)) {
20672098
b->dirty_start = start;
20682099
b->dirty_end = end;
2069-
cache_mark(&c->cache, b, LIST_DIRTY);
2100+
cache_mark(&c->cache, cache_get_tree(&c->cache, b->block), b,
2101+
LIST_DIRTY);
20702102
} else {
20712103
if (start < b->dirty_start)
20722104
b->dirty_start = start;
@@ -2131,6 +2163,7 @@ int dm_bufio_write_dirty_buffers(struct dm_bufio_client *c)
21312163
lru_iter_begin(&c->cache.lru[LIST_DIRTY], &it);
21322164
while ((e = lru_iter_next(&it, is_writing, c))) {
21332165
struct dm_buffer *b = le_to_buffer(e);
2166+
struct buffer_tree *tree;
21342167
__cache_inc_buffer(b);
21352168

21362169
BUG_ON(test_bit(B_READING, &b->state));
@@ -2144,10 +2177,12 @@ int dm_bufio_write_dirty_buffers(struct dm_bufio_client *c)
21442177
wait_on_bit_io(&b->state, B_WRITING, TASK_UNINTERRUPTIBLE);
21452178
}
21462179

2180+
tree = cache_get_tree(&c->cache, b->block);
2181+
21472182
if (!test_bit(B_DIRTY, &b->state) && !test_bit(B_WRITING, &b->state))
2148-
cache_mark(&c->cache, b, LIST_CLEAN);
2183+
cache_mark(&c->cache, tree, b, LIST_CLEAN);
21492184

2150-
cache_put_and_wake(c, b);
2185+
cache_put_and_wake(c, tree, b);
21512186

21522187
cond_resched();
21532188
}
@@ -2215,17 +2250,18 @@ EXPORT_SYMBOL_GPL(dm_bufio_issue_discard);
22152250

22162251
static void forget_buffer(struct dm_bufio_client *c, sector_t block)
22172252
{
2253+
struct buffer_tree *tree = cache_get_tree(&c->cache, block);
22182254
struct dm_buffer *b;
22192255

2220-
b = cache_get(&c->cache, block);
2256+
b = cache_get(&c->cache, tree, block);
22212257
if (b) {
22222258
if (likely(!smp_load_acquire(&b->state))) {
2223-
if (cache_remove(&c->cache, b))
2259+
if (cache_remove(&c->cache, tree, b))
22242260
__free_buffer_wake(b);
22252261
else
2226-
cache_put_and_wake(c, b);
2262+
cache_put_and_wake(c, tree, b);
22272263
} else {
2228-
cache_put_and_wake(c, b);
2264+
cache_put_and_wake(c, tree, b);
22292265
}
22302266
}
22312267
}

0 commit comments

Comments
 (0)