The function evict_old is supposed to loop and evict buffers while the
allocated size is above the threshold. However if it hits one bufio
client that is empty, it stops and doesn't attempt to scan other
clients.
This commit fixes it, so that it skips empty clients and retries up to
dm_bufio_client_count times.
Fixes: 450e8dee51aa ("dm bufio: improve concurrent IO performance")
Cc: [email protected]
Signed-off-by: Mikulas Patocka <[email protected]>
Assisted-by: Claude:claude-opus-5
---
drivers/md/dm-bufio.c | 41 +++++++++++++++++++++++++++++++++++++----
1 file changed, 37 insertions(+), 4 deletions(-)
Index: linux-2.6/drivers/md/dm-bufio.c
===================================================================
--- linux-2.6.orig/drivers/md/dm-bufio.c
+++ linux-2.6/drivers/md/dm-bufio.c
@@ -2545,6 +2545,13 @@ struct dm_bufio_client *dm_bufio_client_
init_waitqueue_head(&c->free_buffer_wait);
c->async_write_error = 0;
+ /*
+ * A client with no buffers yet is not holding the oldest buffer in
+ * the system; leaving this zeroed would claim exactly that and keep
+ * the client pinned at the front of dm_bufio_all_clients.
+ */
+ c->oldest_buffer = jiffies;
+
c->dm_io = dm_io_client_create();
if (IS_ERR(c->dm_io)) {
r = PTR_ERR(c->dm_io);
@@ -2779,9 +2786,23 @@ static unsigned long __evict_a_few(unsig
dm_bufio_unlock(c);
- if (count)
+ if (count) {
c->oldest_buffer = oldest_buffer;
- __insert_client(c);
+ __insert_client(c);
+ } else {
+ /*
+ * We evicted nothing, so 'oldest_buffer' tells us nothing new
+ * and the client keeps its old, possibly very stale, value.
+ * Requeue it at the back rather than by that value, otherwise
+ * __insert_client() would put it straight back at the front
+ * and every subsequent call would pop this same client again,
+ * never reaching any of the others.
+ *
+ * This leaves the list slightly out of order, which is fine -
+ * it is only ever an approximation.
+ */
+ list_add_tail(&c->client_list, &dm_bufio_all_clients);
+ }
return count;
}
@@ -2806,11 +2827,23 @@ static void evict_old(void)
{
unsigned long threshold = dm_bufio_cache_size -
dm_bufio_cache_size / DM_BUFIO_LOW_WATERMARK_RATIO;
+ int unproductive = 0;
mutex_lock(&dm_bufio_clients_lock);
while (dm_bufio_current_allocated > threshold) {
- if (!__evict_a_few(64))
- break;
+ if (__evict_a_few(64)) {
+ unproductive = 0;
+ } else {
+ /*
+ * This client had nothing to give. Try the others
+ * before giving up: one idle client, or one whose
+ * buffers are all held, must not stop us evicting
+ * from everybody else. Stop once a whole sweep of
+ * the client list has freed nothing.
+ */
+ if (++unproductive >= dm_bufio_client_count)
+ break;
+ }
cond_resched();
}
mutex_unlock(&dm_bufio_clients_lock);