[dm-devel] [for-3.19 PATCH v2 02/17] dm bufio: evict buffers that are past the max age but retain some buffers
Mikulas Patocka
mpatocka at redhat.com
Fri Oct 31 16:37:07 UTC 2014
Hi
I've already created a patch for this purpose, see patches
dm-bufio-ratio.patch and dm-bufio-minimum-buffers.patch here:
http://people.redhat.com/~mpatocka/patches/kernel/dm-bufio-cache/series.html
The difference is that my patch sets the default amount of memory to keep
dynamically, as 1/500 of total memory.
Mikulas
On Fri, 17 Oct 2014, Mike Snitzer wrote:
> From: Joe Thornber <ejt at redhat.com>
>
> These changes help keep metadata backed by dm-bufio in-core longer which
> fixes reports of metadata churn in the face of heavy random IO workloads.
>
> Before, bufio evicted all buffers older than DM_BUFIO_DEFAULT_AGE_SECS.
> Having a device (e.g. dm-thinp or dm-cache) lose all metadata just
> because associated buffers had been idle for some time is unfriendly.
>
> Now, the user may now configure the number of bytes that bufio retains
> using the 'retain_bytes' module parameter. The default is 256K.
>
> Also, the DM_BUFIO_WORK_TIMER_SECS and DM_BUFIO_DEFAULT_AGE_SECS
> defaults were quite low so increase them (to 30 and 300 respectively).
>
> Signed-off-by: Joe Thornber <ejt at redhat.com>
> Signed-off-by: Mike Snitzer <snitzer at redhat.com>
> ---
> drivers/md/dm-bufio.c | 109 ++++++++++++++++++++++++++++++++++----------------
> 1 file changed, 75 insertions(+), 34 deletions(-)
>
> diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
> index dcaa1d9..99579c8 100644
> --- a/drivers/md/dm-bufio.c
> +++ b/drivers/md/dm-bufio.c
> @@ -35,12 +35,17 @@
> /*
> * Check buffer ages in this interval (seconds)
> */
> -#define DM_BUFIO_WORK_TIMER_SECS 10
> +#define DM_BUFIO_WORK_TIMER_SECS 30
>
> /*
> * Free buffers when they are older than this (seconds)
> */
> -#define DM_BUFIO_DEFAULT_AGE_SECS 60
> +#define DM_BUFIO_DEFAULT_AGE_SECS 300
> +
> +/*
> + * The nr of bytes of cached data to keep around.
> + */
> +#define DM_BUFIO_DEFAULT_RETAIN_BYTES (256 * 1024)
>
> /*
> * The number of bvec entries that are embedded directly in the buffer.
> @@ -216,6 +221,7 @@ static DEFINE_SPINLOCK(param_spinlock);
> * Buffers are freed after this timeout
> */
> static unsigned dm_bufio_max_age = DM_BUFIO_DEFAULT_AGE_SECS;
> +static unsigned dm_bufio_retain_bytes = DM_BUFIO_DEFAULT_RETAIN_BYTES;
>
> static unsigned long dm_bufio_peak_allocated;
> static unsigned long dm_bufio_allocated_kmem_cache;
> @@ -1457,45 +1463,52 @@ static void drop_buffers(struct dm_bufio_client *c)
> }
>
> /*
> - * Test if the buffer is unused and too old, and commit it.
> + * We may not be able to evict this buffer if IO pending or the client
> + * is still using it. Caller is expected to know buffer is too old.
> + *
> * And if GFP_NOFS is used, we must not do any I/O because we hold
> * dm_bufio_clients_lock and we would risk deadlock if the I/O gets
> * rerouted to different bufio client.
> */
> -static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp,
> - unsigned long max_jiffies)
> +static bool __try_evict_buffer(struct dm_buffer *b, gfp_t gfp)
> {
> - if (jiffies - b->last_accessed < max_jiffies)
> - return 0;
> -
> if (!(gfp & __GFP_FS)) {
> if (test_bit(B_READING, &b->state) ||
> test_bit(B_WRITING, &b->state) ||
> test_bit(B_DIRTY, &b->state))
> - return 0;
> + return false;
> }
>
> if (b->hold_count)
> - return 0;
> + return false;
>
> __make_buffer_clean(b);
> __unlink_buffer(b);
> __free_buffer_wake(b);
>
> - return 1;
> + return true;
> }
>
> -static long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan,
> - gfp_t gfp_mask)
> +static unsigned get_retain_buffers(struct dm_bufio_client *c)
> +{
> + unsigned retain_bytes = ACCESS_ONCE(dm_bufio_retain_bytes);
> + return retain_bytes / c->block_size;
> +}
> +
> +static unsigned long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan,
> + gfp_t gfp_mask)
> {
> int l;
> struct dm_buffer *b, *tmp;
> - long freed = 0;
> + unsigned long freed = 0;
> + unsigned long count = nr_to_scan;
> + unsigned retain_target = get_retain_buffers(c);
>
> for (l = 0; l < LIST_SIZE; l++) {
> list_for_each_entry_safe_reverse(b, tmp, &c->lru[l], lru_list) {
> - freed += __cleanup_old_buffer(b, gfp_mask, 0);
> - if (!--nr_to_scan)
> + if (__try_evict_buffer(b, gfp_mask))
> + freed++;
> + if (!--nr_to_scan || ((count - freed) <= retain_target))
> return freed;
> dm_bufio_cond_resched();
> }
> @@ -1697,31 +1710,56 @@ void dm_bufio_client_destroy(struct dm_bufio_client *c)
> }
> EXPORT_SYMBOL_GPL(dm_bufio_client_destroy);
>
> -static void cleanup_old_buffers(void)
> +static unsigned get_max_age_hz(void)
> {
> - unsigned long max_age = ACCESS_ONCE(dm_bufio_max_age);
> - struct dm_bufio_client *c;
> + unsigned max_age = ACCESS_ONCE(dm_bufio_max_age);
>
> - if (max_age > ULONG_MAX / HZ)
> - max_age = ULONG_MAX / HZ;
> + if (max_age > UINT_MAX / HZ)
> + max_age = UINT_MAX / HZ;
>
> - mutex_lock(&dm_bufio_clients_lock);
> - list_for_each_entry(c, &dm_bufio_all_clients, client_list) {
> - if (!dm_bufio_trylock(c))
> - continue;
> + return max_age * HZ;
> +}
>
> - while (!list_empty(&c->lru[LIST_CLEAN])) {
> - struct dm_buffer *b;
> - b = list_entry(c->lru[LIST_CLEAN].prev,
> - struct dm_buffer, lru_list);
> - if (!__cleanup_old_buffer(b, 0, max_age * HZ))
> - break;
> - dm_bufio_cond_resched();
> - }
> +static bool older_than(struct dm_buffer *b, unsigned long age_hz)
> +{
> + return (jiffies - b->last_accessed) >= age_hz;
> +}
> +
> +static void __evict_old_buffers(struct dm_bufio_client *c, unsigned long age_hz)
> +{
> + struct dm_buffer *b, *tmp;
> + unsigned retain_target = get_retain_buffers(c);
> + unsigned count;
> +
> + dm_bufio_lock(c);
> +
> + count = c->n_buffers[LIST_CLEAN] + c->n_buffers[LIST_DIRTY];
> + list_for_each_entry_safe_reverse(b, tmp, &c->lru[LIST_CLEAN], lru_list) {
> + if (count <= retain_target)
> + break;
> +
> + if (!older_than(b, age_hz))
> + break;
> +
> + if (__try_evict_buffer(b, 0))
> + count--;
>
> - dm_bufio_unlock(c);
> dm_bufio_cond_resched();
> }
> +
> + dm_bufio_unlock(c);
> +}
> +
> +static void cleanup_old_buffers(void)
> +{
> + unsigned long max_age_hz = get_max_age_hz();
> + struct dm_bufio_client *c;
> +
> + mutex_lock(&dm_bufio_clients_lock);
> +
> + list_for_each_entry(c, &dm_bufio_all_clients, client_list)
> + __evict_old_buffers(c, max_age_hz);
> +
> mutex_unlock(&dm_bufio_clients_lock);
> }
>
> @@ -1846,6 +1884,9 @@ MODULE_PARM_DESC(max_cache_size_bytes, "Size of metadata cache");
> module_param_named(max_age_seconds, dm_bufio_max_age, uint, S_IRUGO | S_IWUSR);
> MODULE_PARM_DESC(max_age_seconds, "Max age of a buffer in seconds");
>
> +module_param_named(retain_bytes, dm_bufio_retain_bytes, uint, S_IRUGO | S_IWUSR);
> +MODULE_PARM_DESC(retain_bytes, "Try to keep at least this many bytes cached in memory");
> +
> module_param_named(peak_allocated_bytes, dm_bufio_peak_allocated, ulong, S_IRUGO | S_IWUSR);
> MODULE_PARM_DESC(peak_allocated_bytes, "Tracks the maximum allocated memory");
>
> --
> 1.8.3.1
>
> --
> dm-devel mailing list
> dm-devel at redhat.com
> https://www.redhat.com/mailman/listinfo/dm-devel
>
More information about the dm-devel
mailing list