[dm-devel] A patch to reduce dm-bufio locking
Mikulas Patocka
mpatocka at redhat.com
Tue Sep 27 13:59:31 UTC 2022
Hi
Regarding that dm-bufio overhead, I created this patch.
The patch exports dm_bufio_lock, dm_bufio_trylock and dm_bufio_unlock and
introduces a new function dm_bufio_get_unlocked.
dm_bufio_get_unlocked is like dm_bufio_get, except that it doesn't take
the lock and it assumes that the client structure is already locked with
dm_bufio_lock.
When you walk the thinp metadata, it is recommended that you call
dm_bufio_lock, then you call dm_bufio_get_unlocked repeatedly for each
b-tree level and finally you call dm_bufio_unlock. So, you can walk the
b-tree with just one lock/unlock pair.
When dm_bufio_get_unlocked returns NULL (the block is not in cache), you
must call dm_bufio_unlock and then read the data with dm_bufio_read as
usual - in this situation, the I/O will be bottleneck rather than locking.
Mikulas
Signed-off-by: Mikulas Patocka <mpatocka at redhat.com>
---
drivers/md/dm-bufio.c | 29 ++++++++++++++++++++++-------
include/linux/dm-bufio.h | 15 +++++++++++++++
2 files changed, 37 insertions(+), 7 deletions(-)
Index: linux-dm/drivers/md/dm-bufio.c
===================================================================
--- linux-dm.orig/drivers/md/dm-bufio.c 2022-09-05 13:29:52.000000000 +0200
+++ linux-dm/drivers/md/dm-bufio.c 2022-09-27 15:43:18.000000000 +0200
@@ -169,29 +169,32 @@ static DEFINE_STATIC_KEY_FALSE(no_sleep_
#define dm_bufio_in_request() (!!current->bio_list)
-static void dm_bufio_lock(struct dm_bufio_client *c)
+void dm_bufio_lock(struct dm_bufio_client *c)
{
if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep)
spin_lock_bh(&c->spinlock);
else
mutex_lock_nested(&c->lock, dm_bufio_in_request());
}
+EXPORT_SYMBOL_GPL(dm_bufio_lock);
-static int dm_bufio_trylock(struct dm_bufio_client *c)
+int dm_bufio_trylock(struct dm_bufio_client *c)
{
if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep)
return spin_trylock_bh(&c->spinlock);
else
return mutex_trylock(&c->lock);
}
+EXPORT_SYMBOL_GPL(dm_bufio_trylock);
-static void dm_bufio_unlock(struct dm_bufio_client *c)
+void dm_bufio_unlock(struct dm_bufio_client *c)
{
if (static_branch_unlikely(&no_sleep_enabled) && c->no_sleep)
spin_unlock_bh(&c->spinlock);
else
mutex_unlock(&c->lock);
}
+EXPORT_SYMBOL_GPL(dm_bufio_unlock);
/*----------------------------------------------------------------*/
@@ -870,7 +873,8 @@ enum new_flag {
NF_FRESH = 0,
NF_READ = 1,
NF_GET = 2,
- NF_PREFETCH = 3
+ NF_GET_UNLOCKED = 3,
+ NF_PREFETCH = 4
};
/*
@@ -1013,7 +1017,7 @@ static struct dm_buffer *__bufio_new(str
if (b)
goto found_buffer;
- if (nf == NF_GET)
+ if (nf == NF_GET || nf == NF_GET_UNLOCKED)
return NULL;
new_b = __alloc_buffer_wait(c, nf);
@@ -1058,10 +1062,11 @@ found_buffer:
* If the user called both dm_bufio_prefetch and dm_bufio_get on
* the same buffer, it would deadlock if we waited.
*/
- if (nf == NF_GET && unlikely(test_bit(B_READING, &b->state)))
+ if ((nf == NF_GET || nf == NF_GET_UNLOCKED) && unlikely(test_bit(B_READING, &b->state)))
return NULL;
- b->hold_count++;
+ b->hold_count += nf != NF_GET_UNLOCKED;
+
__relink_lru(b, test_bit(B_DIRTY, &b->state) ||
test_bit(B_WRITING, &b->state));
return b;
@@ -1154,6 +1159,16 @@ void *dm_bufio_new(struct dm_bufio_clien
}
EXPORT_SYMBOL_GPL(dm_bufio_new);
+void *dm_bufio_get_unlocked(struct dm_bufio_client *c, sector_t block)
+{
+ int need_submit;
+ struct dm_buffer *b = __bufio_new(c, block, NF_GET_UNLOCKED, &need_submit, NULL);
+ if (b)
+ return b->data;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get_unlocked);
+
void dm_bufio_prefetch(struct dm_bufio_client *c,
sector_t block, unsigned n_blocks)
{
Index: linux-dm/include/linux/dm-bufio.h
===================================================================
--- linux-dm.orig/include/linux/dm-bufio.h 2022-09-05 13:29:56.000000000 +0200
+++ linux-dm/include/linux/dm-bufio.h 2022-09-27 15:36:49.000000000 +0200
@@ -38,6 +38,14 @@ dm_bufio_client_create(struct block_devi
void dm_bufio_client_destroy(struct dm_bufio_client *c);
/*
+ * Lock and unlock the bufio client - this is needed if we want to call
+ * dm_bufio_get_unlocked.
+ */
+void dm_bufio_lock(struct dm_bufio_client *c);
+int dm_bufio_trylock(struct dm_bufio_client *c);
+void dm_bufio_unlock(struct dm_bufio_client *c);
+
+/*
* Set the sector range.
* When this function is called, there must be no I/O in progress on the bufio
* client.
@@ -76,6 +84,13 @@ void *dm_bufio_new(struct dm_bufio_clien
struct dm_buffer **bp);
/*
+ * Like dm_bufio_get, but assume that the client is already locked with
+ * dm_bufio_lock/dm_bufio_unlock. dm_bufio_release should not be called;
+ * the caller should call dm_bufio_unlock to release the buffer.
+ */
+void *dm_bufio_get_unlocked(struct dm_bufio_client *c, sector_t block);
+
+/*
* Prefetch the specified blocks to the cache.
* The function starts to read the blocks and returns without waiting for
* I/O to finish.
More information about the dm-devel
mailing list