diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index d58eed96a8ff..edad64654ef6 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -2545,6 +2545,13 @@ struct dm_bufio_client *dm_bufio_client_create(struct block_device *bdev, unsign 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(unsigned long nr_buffers) 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);