Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e65ff870 authored by Joe Thornber's avatar Joe Thornber Committed by Mike Snitzer
Browse files

dm cache policy mq: try not to writeback data that changed in the last second



Writeback takes out a lock on the cache block, so will increase the
latency for any concurrent io.

This patch works by placing 2 sentinel objects on each level of the
multiqueues.  Every WRITEBACK_PERIOD the oldest sentinel gets moved to
the newest end of the queue level.

When looking for writeback work:
  if less than 25% of the cache is clean:
    we select the oldest object with the lowest hit count
  otherwise:
    we select the oldest object that is not past a writeback sentinel.

Signed-off-by: default avatarJoe Thornber <ejt@redhat.com>
Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
parent fdecee32
Loading
Loading
Loading
Loading
+93 −1
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include "dm.h"

#include <linux/hash.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
@@ -126,8 +127,12 @@ static void iot_examine_bio(struct io_tracker *t, struct bio *bio)
#define NR_QUEUE_LEVELS 16u
#define NR_SENTINELS NR_QUEUE_LEVELS * 3

#define WRITEBACK_PERIOD HZ

struct queue {
	unsigned nr_elts;
	bool current_writeback_sentinels;
	unsigned long next_writeback;
	struct list_head qs[NR_QUEUE_LEVELS];
	struct list_head sentinels[NR_SENTINELS];
};
@@ -137,10 +142,19 @@ static void queue_init(struct queue *q)
	unsigned i;

	q->nr_elts = 0;
	q->current_writeback_sentinels = false;
	q->next_writeback = 0;
	for (i = 0; i < NR_QUEUE_LEVELS; i++) {
		INIT_LIST_HEAD(q->qs + i);
		INIT_LIST_HEAD(q->sentinels + i);
		INIT_LIST_HEAD(q->sentinels + NR_QUEUE_LEVELS + i);
		INIT_LIST_HEAD(q->sentinels + (2 * NR_QUEUE_LEVELS) + i);
	}
}

static unsigned queue_size(struct queue *q)
{
	return q->nr_elts;
}

static bool queue_empty(struct queue *q)
@@ -197,6 +211,27 @@ static struct list_head *queue_pop(struct queue *q)
	return r;
}

/*
 * Pops an entry from a level that is not past a sentinel.
 */
static struct list_head *queue_pop_old(struct queue *q)
{
	unsigned level;
	struct list_head *h;

	for (level = 0; level < NR_QUEUE_LEVELS; level++)
		list_for_each(h, q->qs + level) {
			if (is_sentinel(q, h))
				break;

			q->nr_elts--;
			list_del(h);
			return h;
		}

	return NULL;
}

static struct list_head *list_pop(struct list_head *lh)
{
	struct list_head *r = lh->next;
@@ -207,6 +242,31 @@ static struct list_head *list_pop(struct list_head *lh)
	return r;
}

static struct list_head *writeback_sentinel(struct queue *q, unsigned level)
{
	if (q->current_writeback_sentinels)
		return q->sentinels + NR_QUEUE_LEVELS + level;
	else
		return q->sentinels + 2 * NR_QUEUE_LEVELS + level;
}

static void queue_update_writeback_sentinels(struct queue *q)
{
	unsigned i;
	struct list_head *h;

	if (time_after(jiffies, q->next_writeback)) {
		for (i = 0; i < NR_QUEUE_LEVELS; i++) {
			h = writeback_sentinel(q, i);
			list_del(h);
			list_add_tail(h, q->qs + i);
		}

		q->next_writeback = jiffies + WRITEBACK_PERIOD;
		q->current_writeback_sentinels = !q->current_writeback_sentinels;
	}
}

/*
 * Sometimes we want to iterate through entries that have been pushed since
 * a certain event.  We use sentinel entries on the queues to delimit these
@@ -540,6 +600,20 @@ static struct entry *pop(struct mq_policy *mq, struct queue *q)
	return e;
}

static struct entry *pop_old(struct mq_policy *mq, struct queue *q)
{
	struct entry *e;
	struct list_head *h = queue_pop_old(q);

	if (!h)
		return NULL;

	e = container_of(h, struct entry, list);
	hash_remove(e);

	return e;
}

static struct entry *peek(struct queue *q)
{
	struct list_head *h = queue_peek(q);
@@ -932,6 +1006,7 @@ static void copy_tick(struct mq_policy *mq)
	queue_tick(&mq->pre_cache);
	queue_tick(&mq->cache_dirty);
	queue_tick(&mq->cache_clean);
	queue_update_writeback_sentinels(&mq->cache_dirty);
	spin_unlock_irqrestore(&mq->tick_lock, flags);
}

@@ -1112,10 +1187,27 @@ static int mq_remove_cblock(struct dm_cache_policy *p, dm_cblock_t cblock)
	return r;
}

#define CLEAN_TARGET_PERCENTAGE 25

static bool clean_target_met(struct mq_policy *mq)
{
	/*
	 * Cache entries may not be populated.  So we're cannot rely on the
	 * size of the clean queue.
	 */
	unsigned nr_clean = from_cblock(mq->cache_size) - queue_size(&mq->cache_dirty);
	unsigned target = from_cblock(mq->cache_size) * CLEAN_TARGET_PERCENTAGE / 100;

	return nr_clean >= target;
}

static int __mq_writeback_work(struct mq_policy *mq, dm_oblock_t *oblock,
			      dm_cblock_t *cblock)
{
	struct entry *e = pop(mq, &mq->cache_dirty);
	struct entry *e = pop_old(mq, &mq->cache_dirty);

	if (!e && !clean_target_met(mq))
		e = pop(mq, &mq->cache_dirty);

	if (!e)
		return -ENODATA;