1*3bd94003SHeinz Mauelshagen // SPDX-License-Identifier: GPL-2.0-only 266a63635SJoe Thornber /* 366a63635SJoe Thornber * Copyright (C) 2015 Red Hat. All rights reserved. 466a63635SJoe Thornber * 566a63635SJoe Thornber * This file is released under the GPL. 666a63635SJoe Thornber */ 766a63635SJoe Thornber 8b29d4986SJoe Thornber #include "dm-cache-background-tracker.h" 966a63635SJoe Thornber #include "dm-cache-policy-internal.h" 10b29d4986SJoe Thornber #include "dm-cache-policy.h" 1166a63635SJoe Thornber #include "dm.h" 1266a63635SJoe Thornber 1366a63635SJoe Thornber #include <linux/hash.h> 1466a63635SJoe Thornber #include <linux/jiffies.h> 1566a63635SJoe Thornber #include <linux/module.h> 1666a63635SJoe Thornber #include <linux/mutex.h> 1766a63635SJoe Thornber #include <linux/vmalloc.h> 1866a63635SJoe Thornber #include <linux/math64.h> 1966a63635SJoe Thornber 2066a63635SJoe Thornber #define DM_MSG_PREFIX "cache-policy-smq" 2166a63635SJoe Thornber 2266a63635SJoe Thornber /*----------------------------------------------------------------*/ 2366a63635SJoe Thornber 2466a63635SJoe Thornber /* 2566a63635SJoe Thornber * Safe division functions that return zero on divide by zero. 2666a63635SJoe Thornber */ 2766a63635SJoe Thornber static unsigned safe_div(unsigned n, unsigned d) 2866a63635SJoe Thornber { 2966a63635SJoe Thornber return d ? n / d : 0u; 3066a63635SJoe Thornber } 3166a63635SJoe Thornber 3266a63635SJoe Thornber static unsigned safe_mod(unsigned n, unsigned d) 3366a63635SJoe Thornber { 3466a63635SJoe Thornber return d ? n % d : 0u; 3566a63635SJoe Thornber } 3666a63635SJoe Thornber 3766a63635SJoe Thornber /*----------------------------------------------------------------*/ 3866a63635SJoe Thornber 3966a63635SJoe Thornber struct entry { 4066a63635SJoe Thornber unsigned hash_next:28; 4166a63635SJoe Thornber unsigned prev:28; 4266a63635SJoe Thornber unsigned next:28; 43b29d4986SJoe Thornber unsigned level:6; 4466a63635SJoe Thornber bool dirty:1; 4566a63635SJoe Thornber bool allocated:1; 4666a63635SJoe Thornber bool sentinel:1; 47b29d4986SJoe Thornber bool pending_work:1; 4866a63635SJoe Thornber 4966a63635SJoe Thornber dm_oblock_t oblock; 5066a63635SJoe Thornber }; 5166a63635SJoe Thornber 5266a63635SJoe Thornber /*----------------------------------------------------------------*/ 5366a63635SJoe Thornber 5466a63635SJoe Thornber #define INDEXER_NULL ((1u << 28u) - 1u) 5566a63635SJoe Thornber 5666a63635SJoe Thornber /* 5766a63635SJoe Thornber * An entry_space manages a set of entries that we use for the queues. 5866a63635SJoe Thornber * The clean and dirty queues share entries, so this object is separate 5966a63635SJoe Thornber * from the queue itself. 6066a63635SJoe Thornber */ 6166a63635SJoe Thornber struct entry_space { 6266a63635SJoe Thornber struct entry *begin; 6366a63635SJoe Thornber struct entry *end; 6466a63635SJoe Thornber }; 6566a63635SJoe Thornber 6666a63635SJoe Thornber static int space_init(struct entry_space *es, unsigned nr_entries) 6766a63635SJoe Thornber { 6866a63635SJoe Thornber if (!nr_entries) { 6966a63635SJoe Thornber es->begin = es->end = NULL; 7066a63635SJoe Thornber return 0; 7166a63635SJoe Thornber } 7266a63635SJoe Thornber 73fad953ceSKees Cook es->begin = vzalloc(array_size(nr_entries, sizeof(struct entry))); 7466a63635SJoe Thornber if (!es->begin) 7566a63635SJoe Thornber return -ENOMEM; 7666a63635SJoe Thornber 7766a63635SJoe Thornber es->end = es->begin + nr_entries; 7866a63635SJoe Thornber return 0; 7966a63635SJoe Thornber } 8066a63635SJoe Thornber 8166a63635SJoe Thornber static void space_exit(struct entry_space *es) 8266a63635SJoe Thornber { 8366a63635SJoe Thornber vfree(es->begin); 8466a63635SJoe Thornber } 8566a63635SJoe Thornber 8666a63635SJoe Thornber static struct entry *__get_entry(struct entry_space *es, unsigned block) 8766a63635SJoe Thornber { 8866a63635SJoe Thornber struct entry *e; 8966a63635SJoe Thornber 9066a63635SJoe Thornber e = es->begin + block; 9166a63635SJoe Thornber BUG_ON(e >= es->end); 9266a63635SJoe Thornber 9366a63635SJoe Thornber return e; 9466a63635SJoe Thornber } 9566a63635SJoe Thornber 9666a63635SJoe Thornber static unsigned to_index(struct entry_space *es, struct entry *e) 9766a63635SJoe Thornber { 9866a63635SJoe Thornber BUG_ON(e < es->begin || e >= es->end); 9966a63635SJoe Thornber return e - es->begin; 10066a63635SJoe Thornber } 10166a63635SJoe Thornber 10266a63635SJoe Thornber static struct entry *to_entry(struct entry_space *es, unsigned block) 10366a63635SJoe Thornber { 10466a63635SJoe Thornber if (block == INDEXER_NULL) 10566a63635SJoe Thornber return NULL; 10666a63635SJoe Thornber 10766a63635SJoe Thornber return __get_entry(es, block); 10866a63635SJoe Thornber } 10966a63635SJoe Thornber 11066a63635SJoe Thornber /*----------------------------------------------------------------*/ 11166a63635SJoe Thornber 11266a63635SJoe Thornber struct ilist { 11366a63635SJoe Thornber unsigned nr_elts; /* excluding sentinel entries */ 11466a63635SJoe Thornber unsigned head, tail; 11566a63635SJoe Thornber }; 11666a63635SJoe Thornber 11766a63635SJoe Thornber static void l_init(struct ilist *l) 11866a63635SJoe Thornber { 11966a63635SJoe Thornber l->nr_elts = 0; 12066a63635SJoe Thornber l->head = l->tail = INDEXER_NULL; 12166a63635SJoe Thornber } 12266a63635SJoe Thornber 12366a63635SJoe Thornber static struct entry *l_head(struct entry_space *es, struct ilist *l) 12466a63635SJoe Thornber { 12566a63635SJoe Thornber return to_entry(es, l->head); 12666a63635SJoe Thornber } 12766a63635SJoe Thornber 12866a63635SJoe Thornber static struct entry *l_tail(struct entry_space *es, struct ilist *l) 12966a63635SJoe Thornber { 13066a63635SJoe Thornber return to_entry(es, l->tail); 13166a63635SJoe Thornber } 13266a63635SJoe Thornber 13366a63635SJoe Thornber static struct entry *l_next(struct entry_space *es, struct entry *e) 13466a63635SJoe Thornber { 13566a63635SJoe Thornber return to_entry(es, e->next); 13666a63635SJoe Thornber } 13766a63635SJoe Thornber 13866a63635SJoe Thornber static struct entry *l_prev(struct entry_space *es, struct entry *e) 13966a63635SJoe Thornber { 14066a63635SJoe Thornber return to_entry(es, e->prev); 14166a63635SJoe Thornber } 14266a63635SJoe Thornber 14366a63635SJoe Thornber static bool l_empty(struct ilist *l) 14466a63635SJoe Thornber { 14566a63635SJoe Thornber return l->head == INDEXER_NULL; 14666a63635SJoe Thornber } 14766a63635SJoe Thornber 14866a63635SJoe Thornber static void l_add_head(struct entry_space *es, struct ilist *l, struct entry *e) 14966a63635SJoe Thornber { 15066a63635SJoe Thornber struct entry *head = l_head(es, l); 15166a63635SJoe Thornber 15266a63635SJoe Thornber e->next = l->head; 15366a63635SJoe Thornber e->prev = INDEXER_NULL; 15466a63635SJoe Thornber 15566a63635SJoe Thornber if (head) 15666a63635SJoe Thornber head->prev = l->head = to_index(es, e); 15766a63635SJoe Thornber else 15866a63635SJoe Thornber l->head = l->tail = to_index(es, e); 15966a63635SJoe Thornber 16066a63635SJoe Thornber if (!e->sentinel) 16166a63635SJoe Thornber l->nr_elts++; 16266a63635SJoe Thornber } 16366a63635SJoe Thornber 16466a63635SJoe Thornber static void l_add_tail(struct entry_space *es, struct ilist *l, struct entry *e) 16566a63635SJoe Thornber { 16666a63635SJoe Thornber struct entry *tail = l_tail(es, l); 16766a63635SJoe Thornber 16866a63635SJoe Thornber e->next = INDEXER_NULL; 16966a63635SJoe Thornber e->prev = l->tail; 17066a63635SJoe Thornber 17166a63635SJoe Thornber if (tail) 17266a63635SJoe Thornber tail->next = l->tail = to_index(es, e); 17366a63635SJoe Thornber else 17466a63635SJoe Thornber l->head = l->tail = to_index(es, e); 17566a63635SJoe Thornber 17666a63635SJoe Thornber if (!e->sentinel) 17766a63635SJoe Thornber l->nr_elts++; 17866a63635SJoe Thornber } 17966a63635SJoe Thornber 18066a63635SJoe Thornber static void l_add_before(struct entry_space *es, struct ilist *l, 18166a63635SJoe Thornber struct entry *old, struct entry *e) 18266a63635SJoe Thornber { 18366a63635SJoe Thornber struct entry *prev = l_prev(es, old); 18466a63635SJoe Thornber 18566a63635SJoe Thornber if (!prev) 18666a63635SJoe Thornber l_add_head(es, l, e); 18766a63635SJoe Thornber 18866a63635SJoe Thornber else { 18966a63635SJoe Thornber e->prev = old->prev; 19066a63635SJoe Thornber e->next = to_index(es, old); 19166a63635SJoe Thornber prev->next = old->prev = to_index(es, e); 19266a63635SJoe Thornber 19366a63635SJoe Thornber if (!e->sentinel) 19466a63635SJoe Thornber l->nr_elts++; 19566a63635SJoe Thornber } 19666a63635SJoe Thornber } 19766a63635SJoe Thornber 19866a63635SJoe Thornber static void l_del(struct entry_space *es, struct ilist *l, struct entry *e) 19966a63635SJoe Thornber { 20066a63635SJoe Thornber struct entry *prev = l_prev(es, e); 20166a63635SJoe Thornber struct entry *next = l_next(es, e); 20266a63635SJoe Thornber 20366a63635SJoe Thornber if (prev) 20466a63635SJoe Thornber prev->next = e->next; 20566a63635SJoe Thornber else 20666a63635SJoe Thornber l->head = e->next; 20766a63635SJoe Thornber 20866a63635SJoe Thornber if (next) 20966a63635SJoe Thornber next->prev = e->prev; 21066a63635SJoe Thornber else 21166a63635SJoe Thornber l->tail = e->prev; 21266a63635SJoe Thornber 21366a63635SJoe Thornber if (!e->sentinel) 21466a63635SJoe Thornber l->nr_elts--; 21566a63635SJoe Thornber } 21666a63635SJoe Thornber 2179768a10dSJoe Thornber static struct entry *l_pop_head(struct entry_space *es, struct ilist *l) 2189768a10dSJoe Thornber { 2199768a10dSJoe Thornber struct entry *e; 2209768a10dSJoe Thornber 2219768a10dSJoe Thornber for (e = l_head(es, l); e; e = l_next(es, e)) 2229768a10dSJoe Thornber if (!e->sentinel) { 2239768a10dSJoe Thornber l_del(es, l, e); 2249768a10dSJoe Thornber return e; 2259768a10dSJoe Thornber } 2269768a10dSJoe Thornber 2279768a10dSJoe Thornber return NULL; 2289768a10dSJoe Thornber } 2299768a10dSJoe Thornber 23066a63635SJoe Thornber static struct entry *l_pop_tail(struct entry_space *es, struct ilist *l) 23166a63635SJoe Thornber { 23266a63635SJoe Thornber struct entry *e; 23366a63635SJoe Thornber 23466a63635SJoe Thornber for (e = l_tail(es, l); e; e = l_prev(es, e)) 23566a63635SJoe Thornber if (!e->sentinel) { 23666a63635SJoe Thornber l_del(es, l, e); 23766a63635SJoe Thornber return e; 23866a63635SJoe Thornber } 23966a63635SJoe Thornber 24066a63635SJoe Thornber return NULL; 24166a63635SJoe Thornber } 24266a63635SJoe Thornber 24366a63635SJoe Thornber /*----------------------------------------------------------------*/ 24466a63635SJoe Thornber 24566a63635SJoe Thornber /* 24666a63635SJoe Thornber * The stochastic-multi-queue is a set of lru lists stacked into levels. 24766a63635SJoe Thornber * Entries are moved up levels when they are used, which loosely orders the 24866a63635SJoe Thornber * most accessed entries in the top levels and least in the bottom. This 24966a63635SJoe Thornber * structure is *much* better than a single lru list. 25066a63635SJoe Thornber */ 25166a63635SJoe Thornber #define MAX_LEVELS 64u 25266a63635SJoe Thornber 25366a63635SJoe Thornber struct queue { 25466a63635SJoe Thornber struct entry_space *es; 25566a63635SJoe Thornber 25666a63635SJoe Thornber unsigned nr_elts; 25766a63635SJoe Thornber unsigned nr_levels; 25866a63635SJoe Thornber struct ilist qs[MAX_LEVELS]; 25966a63635SJoe Thornber 26066a63635SJoe Thornber /* 26166a63635SJoe Thornber * We maintain a count of the number of entries we would like in each 26266a63635SJoe Thornber * level. 26366a63635SJoe Thornber */ 26466a63635SJoe Thornber unsigned last_target_nr_elts; 26566a63635SJoe Thornber unsigned nr_top_levels; 26666a63635SJoe Thornber unsigned nr_in_top_levels; 26766a63635SJoe Thornber unsigned target_count[MAX_LEVELS]; 26866a63635SJoe Thornber }; 26966a63635SJoe Thornber 27066a63635SJoe Thornber static void q_init(struct queue *q, struct entry_space *es, unsigned nr_levels) 27166a63635SJoe Thornber { 27266a63635SJoe Thornber unsigned i; 27366a63635SJoe Thornber 27466a63635SJoe Thornber q->es = es; 27566a63635SJoe Thornber q->nr_elts = 0; 27666a63635SJoe Thornber q->nr_levels = nr_levels; 27766a63635SJoe Thornber 27866a63635SJoe Thornber for (i = 0; i < q->nr_levels; i++) { 27966a63635SJoe Thornber l_init(q->qs + i); 28066a63635SJoe Thornber q->target_count[i] = 0u; 28166a63635SJoe Thornber } 28266a63635SJoe Thornber 28366a63635SJoe Thornber q->last_target_nr_elts = 0u; 28466a63635SJoe Thornber q->nr_top_levels = 0u; 28566a63635SJoe Thornber q->nr_in_top_levels = 0u; 28666a63635SJoe Thornber } 28766a63635SJoe Thornber 28866a63635SJoe Thornber static unsigned q_size(struct queue *q) 28966a63635SJoe Thornber { 29066a63635SJoe Thornber return q->nr_elts; 29166a63635SJoe Thornber } 29266a63635SJoe Thornber 29366a63635SJoe Thornber /* 29466a63635SJoe Thornber * Insert an entry to the back of the given level. 29566a63635SJoe Thornber */ 29666a63635SJoe Thornber static void q_push(struct queue *q, struct entry *e) 29766a63635SJoe Thornber { 298b29d4986SJoe Thornber BUG_ON(e->pending_work); 299b29d4986SJoe Thornber 30066a63635SJoe Thornber if (!e->sentinel) 30166a63635SJoe Thornber q->nr_elts++; 30266a63635SJoe Thornber 30366a63635SJoe Thornber l_add_tail(q->es, q->qs + e->level, e); 30466a63635SJoe Thornber } 30566a63635SJoe Thornber 306b29d4986SJoe Thornber static void q_push_front(struct queue *q, struct entry *e) 307b29d4986SJoe Thornber { 308b29d4986SJoe Thornber BUG_ON(e->pending_work); 309b29d4986SJoe Thornber 310b29d4986SJoe Thornber if (!e->sentinel) 311b29d4986SJoe Thornber q->nr_elts++; 312b29d4986SJoe Thornber 313b29d4986SJoe Thornber l_add_head(q->es, q->qs + e->level, e); 314b29d4986SJoe Thornber } 315b29d4986SJoe Thornber 31666a63635SJoe Thornber static void q_push_before(struct queue *q, struct entry *old, struct entry *e) 31766a63635SJoe Thornber { 318b29d4986SJoe Thornber BUG_ON(e->pending_work); 319b29d4986SJoe Thornber 32066a63635SJoe Thornber if (!e->sentinel) 32166a63635SJoe Thornber q->nr_elts++; 32266a63635SJoe Thornber 32366a63635SJoe Thornber l_add_before(q->es, q->qs + e->level, old, e); 32466a63635SJoe Thornber } 32566a63635SJoe Thornber 32666a63635SJoe Thornber static void q_del(struct queue *q, struct entry *e) 32766a63635SJoe Thornber { 32866a63635SJoe Thornber l_del(q->es, q->qs + e->level, e); 32966a63635SJoe Thornber if (!e->sentinel) 33066a63635SJoe Thornber q->nr_elts--; 33166a63635SJoe Thornber } 33266a63635SJoe Thornber 33366a63635SJoe Thornber /* 33466a63635SJoe Thornber * Return the oldest entry of the lowest populated level. 33566a63635SJoe Thornber */ 33666a63635SJoe Thornber static struct entry *q_peek(struct queue *q, unsigned max_level, bool can_cross_sentinel) 33766a63635SJoe Thornber { 33866a63635SJoe Thornber unsigned level; 33966a63635SJoe Thornber struct entry *e; 34066a63635SJoe Thornber 34166a63635SJoe Thornber max_level = min(max_level, q->nr_levels); 34266a63635SJoe Thornber 34366a63635SJoe Thornber for (level = 0; level < max_level; level++) 34466a63635SJoe Thornber for (e = l_head(q->es, q->qs + level); e; e = l_next(q->es, e)) { 34566a63635SJoe Thornber if (e->sentinel) { 34666a63635SJoe Thornber if (can_cross_sentinel) 34766a63635SJoe Thornber continue; 34866a63635SJoe Thornber else 34966a63635SJoe Thornber break; 35066a63635SJoe Thornber } 35166a63635SJoe Thornber 35266a63635SJoe Thornber return e; 35366a63635SJoe Thornber } 35466a63635SJoe Thornber 35566a63635SJoe Thornber return NULL; 35666a63635SJoe Thornber } 35766a63635SJoe Thornber 35866a63635SJoe Thornber static struct entry *q_pop(struct queue *q) 35966a63635SJoe Thornber { 36066a63635SJoe Thornber struct entry *e = q_peek(q, q->nr_levels, true); 36166a63635SJoe Thornber 36266a63635SJoe Thornber if (e) 36366a63635SJoe Thornber q_del(q, e); 36466a63635SJoe Thornber 36566a63635SJoe Thornber return e; 36666a63635SJoe Thornber } 36766a63635SJoe Thornber 36866a63635SJoe Thornber /* 36966a63635SJoe Thornber * This function assumes there is a non-sentinel entry to pop. It's only 37066a63635SJoe Thornber * used by redistribute, so we know this is true. It also doesn't adjust 37166a63635SJoe Thornber * the q->nr_elts count. 37266a63635SJoe Thornber */ 37366a63635SJoe Thornber static struct entry *__redist_pop_from(struct queue *q, unsigned level) 37466a63635SJoe Thornber { 37566a63635SJoe Thornber struct entry *e; 37666a63635SJoe Thornber 37766a63635SJoe Thornber for (; level < q->nr_levels; level++) 37866a63635SJoe Thornber for (e = l_head(q->es, q->qs + level); e; e = l_next(q->es, e)) 37966a63635SJoe Thornber if (!e->sentinel) { 38066a63635SJoe Thornber l_del(q->es, q->qs + e->level, e); 38166a63635SJoe Thornber return e; 38266a63635SJoe Thornber } 38366a63635SJoe Thornber 38466a63635SJoe Thornber return NULL; 38566a63635SJoe Thornber } 38666a63635SJoe Thornber 38766a63635SJoe Thornber static void q_set_targets_subrange_(struct queue *q, unsigned nr_elts, unsigned lbegin, unsigned lend) 38866a63635SJoe Thornber { 38966a63635SJoe Thornber unsigned level, nr_levels, entries_per_level, remainder; 39066a63635SJoe Thornber 39166a63635SJoe Thornber BUG_ON(lbegin > lend); 39266a63635SJoe Thornber BUG_ON(lend > q->nr_levels); 39366a63635SJoe Thornber nr_levels = lend - lbegin; 39466a63635SJoe Thornber entries_per_level = safe_div(nr_elts, nr_levels); 39566a63635SJoe Thornber remainder = safe_mod(nr_elts, nr_levels); 39666a63635SJoe Thornber 39766a63635SJoe Thornber for (level = lbegin; level < lend; level++) 39866a63635SJoe Thornber q->target_count[level] = 39966a63635SJoe Thornber (level < (lbegin + remainder)) ? entries_per_level + 1u : entries_per_level; 40066a63635SJoe Thornber } 40166a63635SJoe Thornber 40266a63635SJoe Thornber /* 40366a63635SJoe Thornber * Typically we have fewer elements in the top few levels which allows us 40466a63635SJoe Thornber * to adjust the promote threshold nicely. 40566a63635SJoe Thornber */ 40666a63635SJoe Thornber static void q_set_targets(struct queue *q) 40766a63635SJoe Thornber { 40866a63635SJoe Thornber if (q->last_target_nr_elts == q->nr_elts) 40966a63635SJoe Thornber return; 41066a63635SJoe Thornber 41166a63635SJoe Thornber q->last_target_nr_elts = q->nr_elts; 41266a63635SJoe Thornber 41366a63635SJoe Thornber if (q->nr_top_levels > q->nr_levels) 41466a63635SJoe Thornber q_set_targets_subrange_(q, q->nr_elts, 0, q->nr_levels); 41566a63635SJoe Thornber 41666a63635SJoe Thornber else { 41766a63635SJoe Thornber q_set_targets_subrange_(q, q->nr_in_top_levels, 41866a63635SJoe Thornber q->nr_levels - q->nr_top_levels, q->nr_levels); 41966a63635SJoe Thornber 42066a63635SJoe Thornber if (q->nr_in_top_levels < q->nr_elts) 42166a63635SJoe Thornber q_set_targets_subrange_(q, q->nr_elts - q->nr_in_top_levels, 42266a63635SJoe Thornber 0, q->nr_levels - q->nr_top_levels); 42366a63635SJoe Thornber else 42466a63635SJoe Thornber q_set_targets_subrange_(q, 0, 0, q->nr_levels - q->nr_top_levels); 42566a63635SJoe Thornber } 42666a63635SJoe Thornber } 42766a63635SJoe Thornber 42866a63635SJoe Thornber static void q_redistribute(struct queue *q) 42966a63635SJoe Thornber { 43066a63635SJoe Thornber unsigned target, level; 43166a63635SJoe Thornber struct ilist *l, *l_above; 43266a63635SJoe Thornber struct entry *e; 43366a63635SJoe Thornber 43466a63635SJoe Thornber q_set_targets(q); 43566a63635SJoe Thornber 43666a63635SJoe Thornber for (level = 0u; level < q->nr_levels - 1u; level++) { 43766a63635SJoe Thornber l = q->qs + level; 43866a63635SJoe Thornber target = q->target_count[level]; 43966a63635SJoe Thornber 44066a63635SJoe Thornber /* 44166a63635SJoe Thornber * Pull down some entries from the level above. 44266a63635SJoe Thornber */ 44366a63635SJoe Thornber while (l->nr_elts < target) { 44466a63635SJoe Thornber e = __redist_pop_from(q, level + 1u); 44566a63635SJoe Thornber if (!e) { 44666a63635SJoe Thornber /* bug in nr_elts */ 44766a63635SJoe Thornber break; 44866a63635SJoe Thornber } 44966a63635SJoe Thornber 45066a63635SJoe Thornber e->level = level; 45166a63635SJoe Thornber l_add_tail(q->es, l, e); 45266a63635SJoe Thornber } 45366a63635SJoe Thornber 45466a63635SJoe Thornber /* 45566a63635SJoe Thornber * Push some entries up. 45666a63635SJoe Thornber */ 45766a63635SJoe Thornber l_above = q->qs + level + 1u; 45866a63635SJoe Thornber while (l->nr_elts > target) { 45966a63635SJoe Thornber e = l_pop_tail(q->es, l); 46066a63635SJoe Thornber 46166a63635SJoe Thornber if (!e) 46266a63635SJoe Thornber /* bug in nr_elts */ 46366a63635SJoe Thornber break; 46466a63635SJoe Thornber 46566a63635SJoe Thornber e->level = level + 1u; 466b29d4986SJoe Thornber l_add_tail(q->es, l_above, e); 46766a63635SJoe Thornber } 46866a63635SJoe Thornber } 46966a63635SJoe Thornber } 47066a63635SJoe Thornber 471b29d4986SJoe Thornber static void q_requeue(struct queue *q, struct entry *e, unsigned extra_levels, 472b29d4986SJoe Thornber struct entry *s1, struct entry *s2) 47366a63635SJoe Thornber { 47466a63635SJoe Thornber struct entry *de; 475b29d4986SJoe Thornber unsigned sentinels_passed = 0; 476b29d4986SJoe Thornber unsigned new_level = min(q->nr_levels - 1u, e->level + extra_levels); 47766a63635SJoe Thornber 478b29d4986SJoe Thornber /* try and find an entry to swap with */ 47966a63635SJoe Thornber if (extra_levels && (e->level < q->nr_levels - 1u)) { 480b29d4986SJoe Thornber for (de = l_head(q->es, q->qs + new_level); de && de->sentinel; de = l_next(q->es, de)) 481b29d4986SJoe Thornber sentinels_passed++; 48266a63635SJoe Thornber 483b29d4986SJoe Thornber if (de) { 48466a63635SJoe Thornber q_del(q, de); 48566a63635SJoe Thornber de->level = e->level; 486b29d4986SJoe Thornber if (s1) { 487b29d4986SJoe Thornber switch (sentinels_passed) { 488b29d4986SJoe Thornber case 0: 489b29d4986SJoe Thornber q_push_before(q, s1, de); 49066a63635SJoe Thornber break; 491b29d4986SJoe Thornber 492b29d4986SJoe Thornber case 1: 493b29d4986SJoe Thornber q_push_before(q, s2, de); 494b29d4986SJoe Thornber break; 495b29d4986SJoe Thornber 496b29d4986SJoe Thornber default: 497b29d4986SJoe Thornber q_push(q, de); 498b29d4986SJoe Thornber } 499b29d4986SJoe Thornber } else 500b29d4986SJoe Thornber q_push(q, de); 501b29d4986SJoe Thornber } 50266a63635SJoe Thornber } 50366a63635SJoe Thornber 504b29d4986SJoe Thornber q_del(q, e); 50566a63635SJoe Thornber e->level = new_level; 50666a63635SJoe Thornber q_push(q, e); 50766a63635SJoe Thornber } 50866a63635SJoe Thornber 50966a63635SJoe Thornber /*----------------------------------------------------------------*/ 51066a63635SJoe Thornber 51166a63635SJoe Thornber #define FP_SHIFT 8 51266a63635SJoe Thornber #define SIXTEENTH (1u << (FP_SHIFT - 4u)) 51366a63635SJoe Thornber #define EIGHTH (1u << (FP_SHIFT - 3u)) 51466a63635SJoe Thornber 51566a63635SJoe Thornber struct stats { 51666a63635SJoe Thornber unsigned hit_threshold; 51766a63635SJoe Thornber unsigned hits; 51866a63635SJoe Thornber unsigned misses; 51966a63635SJoe Thornber }; 52066a63635SJoe Thornber 52166a63635SJoe Thornber enum performance { 52266a63635SJoe Thornber Q_POOR, 52366a63635SJoe Thornber Q_FAIR, 52466a63635SJoe Thornber Q_WELL 52566a63635SJoe Thornber }; 52666a63635SJoe Thornber 52766a63635SJoe Thornber static void stats_init(struct stats *s, unsigned nr_levels) 52866a63635SJoe Thornber { 52966a63635SJoe Thornber s->hit_threshold = (nr_levels * 3u) / 4u; 53066a63635SJoe Thornber s->hits = 0u; 53166a63635SJoe Thornber s->misses = 0u; 53266a63635SJoe Thornber } 53366a63635SJoe Thornber 53466a63635SJoe Thornber static void stats_reset(struct stats *s) 53566a63635SJoe Thornber { 53666a63635SJoe Thornber s->hits = s->misses = 0u; 53766a63635SJoe Thornber } 53866a63635SJoe Thornber 53966a63635SJoe Thornber static void stats_level_accessed(struct stats *s, unsigned level) 54066a63635SJoe Thornber { 54166a63635SJoe Thornber if (level >= s->hit_threshold) 54266a63635SJoe Thornber s->hits++; 54366a63635SJoe Thornber else 54466a63635SJoe Thornber s->misses++; 54566a63635SJoe Thornber } 54666a63635SJoe Thornber 54766a63635SJoe Thornber static void stats_miss(struct stats *s) 54866a63635SJoe Thornber { 54966a63635SJoe Thornber s->misses++; 55066a63635SJoe Thornber } 55166a63635SJoe Thornber 55266a63635SJoe Thornber /* 55366a63635SJoe Thornber * There are times when we don't have any confidence in the hotspot queue. 55466a63635SJoe Thornber * Such as when a fresh cache is created and the blocks have been spread 55566a63635SJoe Thornber * out across the levels, or if an io load changes. We detect this by 55666a63635SJoe Thornber * seeing how often a lookup is in the top levels of the hotspot queue. 55766a63635SJoe Thornber */ 55866a63635SJoe Thornber static enum performance stats_assess(struct stats *s) 55966a63635SJoe Thornber { 56066a63635SJoe Thornber unsigned confidence = safe_div(s->hits << FP_SHIFT, s->hits + s->misses); 56166a63635SJoe Thornber 56266a63635SJoe Thornber if (confidence < SIXTEENTH) 56366a63635SJoe Thornber return Q_POOR; 56466a63635SJoe Thornber 56566a63635SJoe Thornber else if (confidence < EIGHTH) 56666a63635SJoe Thornber return Q_FAIR; 56766a63635SJoe Thornber 56866a63635SJoe Thornber else 56966a63635SJoe Thornber return Q_WELL; 57066a63635SJoe Thornber } 57166a63635SJoe Thornber 57266a63635SJoe Thornber /*----------------------------------------------------------------*/ 57366a63635SJoe Thornber 574b29d4986SJoe Thornber struct smq_hash_table { 57566a63635SJoe Thornber struct entry_space *es; 57666a63635SJoe Thornber unsigned long long hash_bits; 57766a63635SJoe Thornber unsigned *buckets; 57866a63635SJoe Thornber }; 57966a63635SJoe Thornber 58066a63635SJoe Thornber /* 58166a63635SJoe Thornber * All cache entries are stored in a chained hash table. To save space we 58266a63635SJoe Thornber * use indexing again, and only store indexes to the next entry. 58366a63635SJoe Thornber */ 584b29d4986SJoe Thornber static int h_init(struct smq_hash_table *ht, struct entry_space *es, unsigned nr_entries) 58566a63635SJoe Thornber { 58666a63635SJoe Thornber unsigned i, nr_buckets; 58766a63635SJoe Thornber 58866a63635SJoe Thornber ht->es = es; 58966a63635SJoe Thornber nr_buckets = roundup_pow_of_two(max(nr_entries / 4u, 16u)); 590a3d939aeSMikulas Patocka ht->hash_bits = __ffs(nr_buckets); 59166a63635SJoe Thornber 59242bc47b3SKees Cook ht->buckets = vmalloc(array_size(nr_buckets, sizeof(*ht->buckets))); 59366a63635SJoe Thornber if (!ht->buckets) 59466a63635SJoe Thornber return -ENOMEM; 59566a63635SJoe Thornber 59666a63635SJoe Thornber for (i = 0; i < nr_buckets; i++) 59766a63635SJoe Thornber ht->buckets[i] = INDEXER_NULL; 59866a63635SJoe Thornber 59966a63635SJoe Thornber return 0; 60066a63635SJoe Thornber } 60166a63635SJoe Thornber 602b29d4986SJoe Thornber static void h_exit(struct smq_hash_table *ht) 60366a63635SJoe Thornber { 60466a63635SJoe Thornber vfree(ht->buckets); 60566a63635SJoe Thornber } 60666a63635SJoe Thornber 607b29d4986SJoe Thornber static struct entry *h_head(struct smq_hash_table *ht, unsigned bucket) 60866a63635SJoe Thornber { 60966a63635SJoe Thornber return to_entry(ht->es, ht->buckets[bucket]); 61066a63635SJoe Thornber } 61166a63635SJoe Thornber 612b29d4986SJoe Thornber static struct entry *h_next(struct smq_hash_table *ht, struct entry *e) 61366a63635SJoe Thornber { 61466a63635SJoe Thornber return to_entry(ht->es, e->hash_next); 61566a63635SJoe Thornber } 61666a63635SJoe Thornber 617b29d4986SJoe Thornber static void __h_insert(struct smq_hash_table *ht, unsigned bucket, struct entry *e) 61866a63635SJoe Thornber { 61966a63635SJoe Thornber e->hash_next = ht->buckets[bucket]; 62066a63635SJoe Thornber ht->buckets[bucket] = to_index(ht->es, e); 62166a63635SJoe Thornber } 62266a63635SJoe Thornber 623b29d4986SJoe Thornber static void h_insert(struct smq_hash_table *ht, struct entry *e) 62466a63635SJoe Thornber { 62566a63635SJoe Thornber unsigned h = hash_64(from_oblock(e->oblock), ht->hash_bits); 62666a63635SJoe Thornber __h_insert(ht, h, e); 62766a63635SJoe Thornber } 62866a63635SJoe Thornber 629b29d4986SJoe Thornber static struct entry *__h_lookup(struct smq_hash_table *ht, unsigned h, dm_oblock_t oblock, 63066a63635SJoe Thornber struct entry **prev) 63166a63635SJoe Thornber { 63266a63635SJoe Thornber struct entry *e; 63366a63635SJoe Thornber 63466a63635SJoe Thornber *prev = NULL; 63566a63635SJoe Thornber for (e = h_head(ht, h); e; e = h_next(ht, e)) { 63666a63635SJoe Thornber if (e->oblock == oblock) 63766a63635SJoe Thornber return e; 63866a63635SJoe Thornber 63966a63635SJoe Thornber *prev = e; 64066a63635SJoe Thornber } 64166a63635SJoe Thornber 64266a63635SJoe Thornber return NULL; 64366a63635SJoe Thornber } 64466a63635SJoe Thornber 645b29d4986SJoe Thornber static void __h_unlink(struct smq_hash_table *ht, unsigned h, 64666a63635SJoe Thornber struct entry *e, struct entry *prev) 64766a63635SJoe Thornber { 64866a63635SJoe Thornber if (prev) 64966a63635SJoe Thornber prev->hash_next = e->hash_next; 65066a63635SJoe Thornber else 65166a63635SJoe Thornber ht->buckets[h] = e->hash_next; 65266a63635SJoe Thornber } 65366a63635SJoe Thornber 65466a63635SJoe Thornber /* 65566a63635SJoe Thornber * Also moves each entry to the front of the bucket. 65666a63635SJoe Thornber */ 657b29d4986SJoe Thornber static struct entry *h_lookup(struct smq_hash_table *ht, dm_oblock_t oblock) 65866a63635SJoe Thornber { 65966a63635SJoe Thornber struct entry *e, *prev; 66066a63635SJoe Thornber unsigned h = hash_64(from_oblock(oblock), ht->hash_bits); 66166a63635SJoe Thornber 66266a63635SJoe Thornber e = __h_lookup(ht, h, oblock, &prev); 66366a63635SJoe Thornber if (e && prev) { 66466a63635SJoe Thornber /* 66566a63635SJoe Thornber * Move to the front because this entry is likely 66666a63635SJoe Thornber * to be hit again. 66766a63635SJoe Thornber */ 66866a63635SJoe Thornber __h_unlink(ht, h, e, prev); 66966a63635SJoe Thornber __h_insert(ht, h, e); 67066a63635SJoe Thornber } 67166a63635SJoe Thornber 67266a63635SJoe Thornber return e; 67366a63635SJoe Thornber } 67466a63635SJoe Thornber 675b29d4986SJoe Thornber static void h_remove(struct smq_hash_table *ht, struct entry *e) 67666a63635SJoe Thornber { 67766a63635SJoe Thornber unsigned h = hash_64(from_oblock(e->oblock), ht->hash_bits); 67866a63635SJoe Thornber struct entry *prev; 67966a63635SJoe Thornber 68066a63635SJoe Thornber /* 68166a63635SJoe Thornber * The down side of using a singly linked list is we have to 68266a63635SJoe Thornber * iterate the bucket to remove an item. 68366a63635SJoe Thornber */ 68466a63635SJoe Thornber e = __h_lookup(ht, h, e->oblock, &prev); 68566a63635SJoe Thornber if (e) 68666a63635SJoe Thornber __h_unlink(ht, h, e, prev); 68766a63635SJoe Thornber } 68866a63635SJoe Thornber 68966a63635SJoe Thornber /*----------------------------------------------------------------*/ 69066a63635SJoe Thornber 69166a63635SJoe Thornber struct entry_alloc { 69266a63635SJoe Thornber struct entry_space *es; 69366a63635SJoe Thornber unsigned begin; 69466a63635SJoe Thornber 69566a63635SJoe Thornber unsigned nr_allocated; 69666a63635SJoe Thornber struct ilist free; 69766a63635SJoe Thornber }; 69866a63635SJoe Thornber 69966a63635SJoe Thornber static void init_allocator(struct entry_alloc *ea, struct entry_space *es, 70066a63635SJoe Thornber unsigned begin, unsigned end) 70166a63635SJoe Thornber { 70266a63635SJoe Thornber unsigned i; 70366a63635SJoe Thornber 70466a63635SJoe Thornber ea->es = es; 70566a63635SJoe Thornber ea->nr_allocated = 0u; 70666a63635SJoe Thornber ea->begin = begin; 70766a63635SJoe Thornber 70866a63635SJoe Thornber l_init(&ea->free); 70966a63635SJoe Thornber for (i = begin; i != end; i++) 71066a63635SJoe Thornber l_add_tail(ea->es, &ea->free, __get_entry(ea->es, i)); 71166a63635SJoe Thornber } 71266a63635SJoe Thornber 71366a63635SJoe Thornber static void init_entry(struct entry *e) 71466a63635SJoe Thornber { 71566a63635SJoe Thornber /* 71666a63635SJoe Thornber * We can't memset because that would clear the hotspot and 71766a63635SJoe Thornber * sentinel bits which remain constant. 71866a63635SJoe Thornber */ 71966a63635SJoe Thornber e->hash_next = INDEXER_NULL; 72066a63635SJoe Thornber e->next = INDEXER_NULL; 72166a63635SJoe Thornber e->prev = INDEXER_NULL; 72266a63635SJoe Thornber e->level = 0u; 723b29d4986SJoe Thornber e->dirty = true; /* FIXME: audit */ 72466a63635SJoe Thornber e->allocated = true; 725b29d4986SJoe Thornber e->sentinel = false; 726b29d4986SJoe Thornber e->pending_work = false; 72766a63635SJoe Thornber } 72866a63635SJoe Thornber 72966a63635SJoe Thornber static struct entry *alloc_entry(struct entry_alloc *ea) 73066a63635SJoe Thornber { 73166a63635SJoe Thornber struct entry *e; 73266a63635SJoe Thornber 73366a63635SJoe Thornber if (l_empty(&ea->free)) 73466a63635SJoe Thornber return NULL; 73566a63635SJoe Thornber 7369768a10dSJoe Thornber e = l_pop_head(ea->es, &ea->free); 73766a63635SJoe Thornber init_entry(e); 73866a63635SJoe Thornber ea->nr_allocated++; 73966a63635SJoe Thornber 74066a63635SJoe Thornber return e; 74166a63635SJoe Thornber } 74266a63635SJoe Thornber 74366a63635SJoe Thornber /* 74466a63635SJoe Thornber * This assumes the cblock hasn't already been allocated. 74566a63635SJoe Thornber */ 74666a63635SJoe Thornber static struct entry *alloc_particular_entry(struct entry_alloc *ea, unsigned i) 74766a63635SJoe Thornber { 74866a63635SJoe Thornber struct entry *e = __get_entry(ea->es, ea->begin + i); 74966a63635SJoe Thornber 75066a63635SJoe Thornber BUG_ON(e->allocated); 75166a63635SJoe Thornber 75266a63635SJoe Thornber l_del(ea->es, &ea->free, e); 75366a63635SJoe Thornber init_entry(e); 75466a63635SJoe Thornber ea->nr_allocated++; 75566a63635SJoe Thornber 75666a63635SJoe Thornber return e; 75766a63635SJoe Thornber } 75866a63635SJoe Thornber 75966a63635SJoe Thornber static void free_entry(struct entry_alloc *ea, struct entry *e) 76066a63635SJoe Thornber { 76166a63635SJoe Thornber BUG_ON(!ea->nr_allocated); 76266a63635SJoe Thornber BUG_ON(!e->allocated); 76366a63635SJoe Thornber 76466a63635SJoe Thornber ea->nr_allocated--; 76566a63635SJoe Thornber e->allocated = false; 76666a63635SJoe Thornber l_add_tail(ea->es, &ea->free, e); 76766a63635SJoe Thornber } 76866a63635SJoe Thornber 76966a63635SJoe Thornber static bool allocator_empty(struct entry_alloc *ea) 77066a63635SJoe Thornber { 77166a63635SJoe Thornber return l_empty(&ea->free); 77266a63635SJoe Thornber } 77366a63635SJoe Thornber 77466a63635SJoe Thornber static unsigned get_index(struct entry_alloc *ea, struct entry *e) 77566a63635SJoe Thornber { 77666a63635SJoe Thornber return to_index(ea->es, e) - ea->begin; 77766a63635SJoe Thornber } 77866a63635SJoe Thornber 77966a63635SJoe Thornber static struct entry *get_entry(struct entry_alloc *ea, unsigned index) 78066a63635SJoe Thornber { 78166a63635SJoe Thornber return __get_entry(ea->es, ea->begin + index); 78266a63635SJoe Thornber } 78366a63635SJoe Thornber 78466a63635SJoe Thornber /*----------------------------------------------------------------*/ 78566a63635SJoe Thornber 78666a63635SJoe Thornber #define NR_HOTSPOT_LEVELS 64u 78766a63635SJoe Thornber #define NR_CACHE_LEVELS 64u 78866a63635SJoe Thornber 789b29d4986SJoe Thornber #define WRITEBACK_PERIOD (10ul * HZ) 790b29d4986SJoe Thornber #define DEMOTE_PERIOD (60ul * HZ) 79166a63635SJoe Thornber 79266a63635SJoe Thornber #define HOTSPOT_UPDATE_PERIOD (HZ) 793b29d4986SJoe Thornber #define CACHE_UPDATE_PERIOD (60ul * HZ) 79466a63635SJoe Thornber 79566a63635SJoe Thornber struct smq_policy { 79666a63635SJoe Thornber struct dm_cache_policy policy; 79766a63635SJoe Thornber 79866a63635SJoe Thornber /* protects everything */ 7994051aab7SJoe Thornber spinlock_t lock; 80066a63635SJoe Thornber dm_cblock_t cache_size; 80166a63635SJoe Thornber sector_t cache_block_size; 80266a63635SJoe Thornber 80366a63635SJoe Thornber sector_t hotspot_block_size; 80466a63635SJoe Thornber unsigned nr_hotspot_blocks; 80566a63635SJoe Thornber unsigned cache_blocks_per_hotspot_block; 80666a63635SJoe Thornber unsigned hotspot_level_jump; 80766a63635SJoe Thornber 80866a63635SJoe Thornber struct entry_space es; 80966a63635SJoe Thornber struct entry_alloc writeback_sentinel_alloc; 81066a63635SJoe Thornber struct entry_alloc demote_sentinel_alloc; 81166a63635SJoe Thornber struct entry_alloc hotspot_alloc; 81266a63635SJoe Thornber struct entry_alloc cache_alloc; 81366a63635SJoe Thornber 81466a63635SJoe Thornber unsigned long *hotspot_hit_bits; 81566a63635SJoe Thornber unsigned long *cache_hit_bits; 81666a63635SJoe Thornber 81766a63635SJoe Thornber /* 81866a63635SJoe Thornber * We maintain three queues of entries. The cache proper, 81966a63635SJoe Thornber * consisting of a clean and dirty queue, containing the currently 82066a63635SJoe Thornber * active mappings. The hotspot queue uses a larger block size to 82166a63635SJoe Thornber * track blocks that are being hit frequently and potential 82266a63635SJoe Thornber * candidates for promotion to the cache. 82366a63635SJoe Thornber */ 82466a63635SJoe Thornber struct queue hotspot; 82566a63635SJoe Thornber struct queue clean; 82666a63635SJoe Thornber struct queue dirty; 82766a63635SJoe Thornber 82866a63635SJoe Thornber struct stats hotspot_stats; 82966a63635SJoe Thornber struct stats cache_stats; 83066a63635SJoe Thornber 83166a63635SJoe Thornber /* 83266a63635SJoe Thornber * Keeps track of time, incremented by the core. We use this to 83366a63635SJoe Thornber * avoid attributing multiple hits within the same tick. 83466a63635SJoe Thornber */ 83566a63635SJoe Thornber unsigned tick; 83666a63635SJoe Thornber 83766a63635SJoe Thornber /* 83866a63635SJoe Thornber * The hash tables allows us to quickly find an entry by origin 83966a63635SJoe Thornber * block. 84066a63635SJoe Thornber */ 841b29d4986SJoe Thornber struct smq_hash_table table; 842b29d4986SJoe Thornber struct smq_hash_table hotspot_table; 84366a63635SJoe Thornber 84466a63635SJoe Thornber bool current_writeback_sentinels; 84566a63635SJoe Thornber unsigned long next_writeback_period; 84666a63635SJoe Thornber 84766a63635SJoe Thornber bool current_demote_sentinels; 84866a63635SJoe Thornber unsigned long next_demote_period; 84966a63635SJoe Thornber 85066a63635SJoe Thornber unsigned write_promote_level; 85166a63635SJoe Thornber unsigned read_promote_level; 85266a63635SJoe Thornber 85366a63635SJoe Thornber unsigned long next_hotspot_period; 85466a63635SJoe Thornber unsigned long next_cache_period; 855b29d4986SJoe Thornber 856b29d4986SJoe Thornber struct background_tracker *bg_work; 857b29d4986SJoe Thornber 858b29d4986SJoe Thornber bool migrations_allowed; 85966a63635SJoe Thornber }; 86066a63635SJoe Thornber 86166a63635SJoe Thornber /*----------------------------------------------------------------*/ 86266a63635SJoe Thornber 86366a63635SJoe Thornber static struct entry *get_sentinel(struct entry_alloc *ea, unsigned level, bool which) 86466a63635SJoe Thornber { 86566a63635SJoe Thornber return get_entry(ea, which ? level : NR_CACHE_LEVELS + level); 86666a63635SJoe Thornber } 86766a63635SJoe Thornber 86866a63635SJoe Thornber static struct entry *writeback_sentinel(struct smq_policy *mq, unsigned level) 86966a63635SJoe Thornber { 87066a63635SJoe Thornber return get_sentinel(&mq->writeback_sentinel_alloc, level, mq->current_writeback_sentinels); 87166a63635SJoe Thornber } 87266a63635SJoe Thornber 87366a63635SJoe Thornber static struct entry *demote_sentinel(struct smq_policy *mq, unsigned level) 87466a63635SJoe Thornber { 87566a63635SJoe Thornber return get_sentinel(&mq->demote_sentinel_alloc, level, mq->current_demote_sentinels); 87666a63635SJoe Thornber } 87766a63635SJoe Thornber 87866a63635SJoe Thornber static void __update_writeback_sentinels(struct smq_policy *mq) 87966a63635SJoe Thornber { 88066a63635SJoe Thornber unsigned level; 88166a63635SJoe Thornber struct queue *q = &mq->dirty; 88266a63635SJoe Thornber struct entry *sentinel; 88366a63635SJoe Thornber 88466a63635SJoe Thornber for (level = 0; level < q->nr_levels; level++) { 88566a63635SJoe Thornber sentinel = writeback_sentinel(mq, level); 88666a63635SJoe Thornber q_del(q, sentinel); 88766a63635SJoe Thornber q_push(q, sentinel); 88866a63635SJoe Thornber } 88966a63635SJoe Thornber } 89066a63635SJoe Thornber 89166a63635SJoe Thornber static void __update_demote_sentinels(struct smq_policy *mq) 89266a63635SJoe Thornber { 89366a63635SJoe Thornber unsigned level; 89466a63635SJoe Thornber struct queue *q = &mq->clean; 89566a63635SJoe Thornber struct entry *sentinel; 89666a63635SJoe Thornber 89766a63635SJoe Thornber for (level = 0; level < q->nr_levels; level++) { 89866a63635SJoe Thornber sentinel = demote_sentinel(mq, level); 89966a63635SJoe Thornber q_del(q, sentinel); 90066a63635SJoe Thornber q_push(q, sentinel); 90166a63635SJoe Thornber } 90266a63635SJoe Thornber } 90366a63635SJoe Thornber 90466a63635SJoe Thornber static void update_sentinels(struct smq_policy *mq) 90566a63635SJoe Thornber { 90666a63635SJoe Thornber if (time_after(jiffies, mq->next_writeback_period)) { 90766a63635SJoe Thornber mq->next_writeback_period = jiffies + WRITEBACK_PERIOD; 90866a63635SJoe Thornber mq->current_writeback_sentinels = !mq->current_writeback_sentinels; 909b29d4986SJoe Thornber __update_writeback_sentinels(mq); 91066a63635SJoe Thornber } 91166a63635SJoe Thornber 91266a63635SJoe Thornber if (time_after(jiffies, mq->next_demote_period)) { 91366a63635SJoe Thornber mq->next_demote_period = jiffies + DEMOTE_PERIOD; 91466a63635SJoe Thornber mq->current_demote_sentinels = !mq->current_demote_sentinels; 915b29d4986SJoe Thornber __update_demote_sentinels(mq); 91666a63635SJoe Thornber } 91766a63635SJoe Thornber } 91866a63635SJoe Thornber 91966a63635SJoe Thornber static void __sentinels_init(struct smq_policy *mq) 92066a63635SJoe Thornber { 92166a63635SJoe Thornber unsigned level; 92266a63635SJoe Thornber struct entry *sentinel; 92366a63635SJoe Thornber 92466a63635SJoe Thornber for (level = 0; level < NR_CACHE_LEVELS; level++) { 92566a63635SJoe Thornber sentinel = writeback_sentinel(mq, level); 92666a63635SJoe Thornber sentinel->level = level; 92766a63635SJoe Thornber q_push(&mq->dirty, sentinel); 92866a63635SJoe Thornber 92966a63635SJoe Thornber sentinel = demote_sentinel(mq, level); 93066a63635SJoe Thornber sentinel->level = level; 93166a63635SJoe Thornber q_push(&mq->clean, sentinel); 93266a63635SJoe Thornber } 93366a63635SJoe Thornber } 93466a63635SJoe Thornber 93566a63635SJoe Thornber static void sentinels_init(struct smq_policy *mq) 93666a63635SJoe Thornber { 93766a63635SJoe Thornber mq->next_writeback_period = jiffies + WRITEBACK_PERIOD; 93866a63635SJoe Thornber mq->next_demote_period = jiffies + DEMOTE_PERIOD; 93966a63635SJoe Thornber 94066a63635SJoe Thornber mq->current_writeback_sentinels = false; 94166a63635SJoe Thornber mq->current_demote_sentinels = false; 94266a63635SJoe Thornber __sentinels_init(mq); 94366a63635SJoe Thornber 94466a63635SJoe Thornber mq->current_writeback_sentinels = !mq->current_writeback_sentinels; 94566a63635SJoe Thornber mq->current_demote_sentinels = !mq->current_demote_sentinels; 94666a63635SJoe Thornber __sentinels_init(mq); 94766a63635SJoe Thornber } 94866a63635SJoe Thornber 94966a63635SJoe Thornber /*----------------------------------------------------------------*/ 95066a63635SJoe Thornber 951b29d4986SJoe Thornber static void del_queue(struct smq_policy *mq, struct entry *e) 95266a63635SJoe Thornber { 953b29d4986SJoe Thornber q_del(e->dirty ? &mq->dirty : &mq->clean, e); 95466a63635SJoe Thornber } 95566a63635SJoe Thornber 956b29d4986SJoe Thornber static void push_queue(struct smq_policy *mq, struct entry *e) 957b29d4986SJoe Thornber { 958b29d4986SJoe Thornber if (e->dirty) 959b29d4986SJoe Thornber q_push(&mq->dirty, e); 960b29d4986SJoe Thornber else 961b29d4986SJoe Thornber q_push(&mq->clean, e); 962b29d4986SJoe Thornber } 963b29d4986SJoe Thornber 964b29d4986SJoe Thornber // !h, !q, a -> h, q, a 96566a63635SJoe Thornber static void push(struct smq_policy *mq, struct entry *e) 96666a63635SJoe Thornber { 96766a63635SJoe Thornber h_insert(&mq->table, e); 968b29d4986SJoe Thornber if (!e->pending_work) 969b29d4986SJoe Thornber push_queue(mq, e); 97066a63635SJoe Thornber } 97166a63635SJoe Thornber 972b29d4986SJoe Thornber static void push_queue_front(struct smq_policy *mq, struct entry *e) 97366a63635SJoe Thornber { 974b29d4986SJoe Thornber if (e->dirty) 975b29d4986SJoe Thornber q_push_front(&mq->dirty, e); 976b29d4986SJoe Thornber else 977b29d4986SJoe Thornber q_push_front(&mq->clean, e); 97866a63635SJoe Thornber } 97966a63635SJoe Thornber 980b29d4986SJoe Thornber static void push_front(struct smq_policy *mq, struct entry *e) 98166a63635SJoe Thornber { 982b29d4986SJoe Thornber h_insert(&mq->table, e); 983b29d4986SJoe Thornber if (!e->pending_work) 984b29d4986SJoe Thornber push_queue_front(mq, e); 98566a63635SJoe Thornber } 98666a63635SJoe Thornber 98766a63635SJoe Thornber static dm_cblock_t infer_cblock(struct smq_policy *mq, struct entry *e) 98866a63635SJoe Thornber { 98966a63635SJoe Thornber return to_cblock(get_index(&mq->cache_alloc, e)); 99066a63635SJoe Thornber } 99166a63635SJoe Thornber 99266a63635SJoe Thornber static void requeue(struct smq_policy *mq, struct entry *e) 99366a63635SJoe Thornber { 994b29d4986SJoe Thornber /* 995b29d4986SJoe Thornber * Pending work has temporarily been taken out of the queues. 996b29d4986SJoe Thornber */ 997b29d4986SJoe Thornber if (e->pending_work) 998b29d4986SJoe Thornber return; 99966a63635SJoe Thornber 100066a63635SJoe Thornber if (!test_and_set_bit(from_cblock(infer_cblock(mq, e)), mq->cache_hit_bits)) { 1001b29d4986SJoe Thornber if (!e->dirty) { 1002b29d4986SJoe Thornber q_requeue(&mq->clean, e, 1u, NULL, NULL); 1003b29d4986SJoe Thornber return; 100466a63635SJoe Thornber } 1005b29d4986SJoe Thornber 1006b29d4986SJoe Thornber q_requeue(&mq->dirty, e, 1u, 1007b29d4986SJoe Thornber get_sentinel(&mq->writeback_sentinel_alloc, e->level, !mq->current_writeback_sentinels), 1008b29d4986SJoe Thornber get_sentinel(&mq->writeback_sentinel_alloc, e->level, mq->current_writeback_sentinels)); 100966a63635SJoe Thornber } 101066a63635SJoe Thornber } 101166a63635SJoe Thornber 101266a63635SJoe Thornber static unsigned default_promote_level(struct smq_policy *mq) 101366a63635SJoe Thornber { 101466a63635SJoe Thornber /* 101566a63635SJoe Thornber * The promote level depends on the current performance of the 101666a63635SJoe Thornber * cache. 101766a63635SJoe Thornber * 101866a63635SJoe Thornber * If the cache is performing badly, then we can't afford 101966a63635SJoe Thornber * to promote much without causing performance to drop below that 102066a63635SJoe Thornber * of the origin device. 102166a63635SJoe Thornber * 102266a63635SJoe Thornber * If the cache is performing well, then we don't need to promote 102366a63635SJoe Thornber * much. If it isn't broken, don't fix it. 102466a63635SJoe Thornber * 102566a63635SJoe Thornber * If the cache is middling then we promote more. 102666a63635SJoe Thornber * 102766a63635SJoe Thornber * This scheme reminds me of a graph of entropy vs probability of a 102866a63635SJoe Thornber * binary variable. 102966a63635SJoe Thornber */ 1030302f0351SColin Ian King static const unsigned int table[] = { 1031302f0351SColin Ian King 1, 1, 1, 2, 4, 6, 7, 8, 7, 6, 4, 4, 3, 3, 2, 2, 1 1032302f0351SColin Ian King }; 103366a63635SJoe Thornber 103466a63635SJoe Thornber unsigned hits = mq->cache_stats.hits; 103566a63635SJoe Thornber unsigned misses = mq->cache_stats.misses; 103666a63635SJoe Thornber unsigned index = safe_div(hits << 4u, hits + misses); 103766a63635SJoe Thornber return table[index]; 103866a63635SJoe Thornber } 103966a63635SJoe Thornber 104066a63635SJoe Thornber static void update_promote_levels(struct smq_policy *mq) 104166a63635SJoe Thornber { 104266a63635SJoe Thornber /* 104366a63635SJoe Thornber * If there are unused cache entries then we want to be really 104466a63635SJoe Thornber * eager to promote. 104566a63635SJoe Thornber */ 104666a63635SJoe Thornber unsigned threshold_level = allocator_empty(&mq->cache_alloc) ? 104766a63635SJoe Thornber default_promote_level(mq) : (NR_HOTSPOT_LEVELS / 2u); 104866a63635SJoe Thornber 1049b29d4986SJoe Thornber threshold_level = max(threshold_level, NR_HOTSPOT_LEVELS); 1050b29d4986SJoe Thornber 105166a63635SJoe Thornber /* 105266a63635SJoe Thornber * If the hotspot queue is performing badly then we have little 105366a63635SJoe Thornber * confidence that we know which blocks to promote. So we cut down 105466a63635SJoe Thornber * the amount of promotions. 105566a63635SJoe Thornber */ 105666a63635SJoe Thornber switch (stats_assess(&mq->hotspot_stats)) { 105766a63635SJoe Thornber case Q_POOR: 105866a63635SJoe Thornber threshold_level /= 4u; 105966a63635SJoe Thornber break; 106066a63635SJoe Thornber 106166a63635SJoe Thornber case Q_FAIR: 106266a63635SJoe Thornber threshold_level /= 2u; 106366a63635SJoe Thornber break; 106466a63635SJoe Thornber 106566a63635SJoe Thornber case Q_WELL: 106666a63635SJoe Thornber break; 106766a63635SJoe Thornber } 106866a63635SJoe Thornber 106966a63635SJoe Thornber mq->read_promote_level = NR_HOTSPOT_LEVELS - threshold_level; 1070b29d4986SJoe Thornber mq->write_promote_level = (NR_HOTSPOT_LEVELS - threshold_level); 107166a63635SJoe Thornber } 107266a63635SJoe Thornber 107366a63635SJoe Thornber /* 107466a63635SJoe Thornber * If the hotspot queue is performing badly, then we try and move entries 107566a63635SJoe Thornber * around more quickly. 107666a63635SJoe Thornber */ 107766a63635SJoe Thornber static void update_level_jump(struct smq_policy *mq) 107866a63635SJoe Thornber { 107966a63635SJoe Thornber switch (stats_assess(&mq->hotspot_stats)) { 108066a63635SJoe Thornber case Q_POOR: 108166a63635SJoe Thornber mq->hotspot_level_jump = 4u; 108266a63635SJoe Thornber break; 108366a63635SJoe Thornber 108466a63635SJoe Thornber case Q_FAIR: 108566a63635SJoe Thornber mq->hotspot_level_jump = 2u; 108666a63635SJoe Thornber break; 108766a63635SJoe Thornber 108866a63635SJoe Thornber case Q_WELL: 108966a63635SJoe Thornber mq->hotspot_level_jump = 1u; 109066a63635SJoe Thornber break; 109166a63635SJoe Thornber } 109266a63635SJoe Thornber } 109366a63635SJoe Thornber 109466a63635SJoe Thornber static void end_hotspot_period(struct smq_policy *mq) 109566a63635SJoe Thornber { 109666a63635SJoe Thornber clear_bitset(mq->hotspot_hit_bits, mq->nr_hotspot_blocks); 109766a63635SJoe Thornber update_promote_levels(mq); 109866a63635SJoe Thornber 109966a63635SJoe Thornber if (time_after(jiffies, mq->next_hotspot_period)) { 110066a63635SJoe Thornber update_level_jump(mq); 110166a63635SJoe Thornber q_redistribute(&mq->hotspot); 110266a63635SJoe Thornber stats_reset(&mq->hotspot_stats); 110366a63635SJoe Thornber mq->next_hotspot_period = jiffies + HOTSPOT_UPDATE_PERIOD; 110466a63635SJoe Thornber } 110566a63635SJoe Thornber } 110666a63635SJoe Thornber 110766a63635SJoe Thornber static void end_cache_period(struct smq_policy *mq) 110866a63635SJoe Thornber { 110966a63635SJoe Thornber if (time_after(jiffies, mq->next_cache_period)) { 111066a63635SJoe Thornber clear_bitset(mq->cache_hit_bits, from_cblock(mq->cache_size)); 111166a63635SJoe Thornber 111266a63635SJoe Thornber q_redistribute(&mq->dirty); 111366a63635SJoe Thornber q_redistribute(&mq->clean); 111466a63635SJoe Thornber stats_reset(&mq->cache_stats); 111566a63635SJoe Thornber 111666a63635SJoe Thornber mq->next_cache_period = jiffies + CACHE_UPDATE_PERIOD; 111766a63635SJoe Thornber } 111866a63635SJoe Thornber } 111966a63635SJoe Thornber 1120b29d4986SJoe Thornber /*----------------------------------------------------------------*/ 1121b29d4986SJoe Thornber 1122b29d4986SJoe Thornber /* 1123b29d4986SJoe Thornber * Targets are given as a percentage. 1124b29d4986SJoe Thornber */ 1125b29d4986SJoe Thornber #define CLEAN_TARGET 25u 1126b29d4986SJoe Thornber #define FREE_TARGET 25u 1127b29d4986SJoe Thornber 1128b29d4986SJoe Thornber static unsigned percent_to_target(struct smq_policy *mq, unsigned p) 112966a63635SJoe Thornber { 1130b29d4986SJoe Thornber return from_cblock(mq->cache_size) * p / 100u; 113166a63635SJoe Thornber } 113266a63635SJoe Thornber 1133b29d4986SJoe Thornber static bool clean_target_met(struct smq_policy *mq, bool idle) 1134b29d4986SJoe Thornber { 1135b29d4986SJoe Thornber /* 1136b29d4986SJoe Thornber * Cache entries may not be populated. So we cannot rely on the 1137b29d4986SJoe Thornber * size of the clean queue. 1138b29d4986SJoe Thornber */ 113997dfb203SMike Snitzer if (idle) { 1140b29d4986SJoe Thornber /* 1141b29d4986SJoe Thornber * We'd like to clean everything. 1142b29d4986SJoe Thornber */ 1143b29d4986SJoe Thornber return q_size(&mq->dirty) == 0u; 114497dfb203SMike Snitzer } 114597dfb203SMike Snitzer 11462e633095SJoe Thornber /* 11472e633095SJoe Thornber * If we're busy we don't worry about cleaning at all. 11482e633095SJoe Thornber */ 11492e633095SJoe Thornber return true; 1150b29d4986SJoe Thornber } 1151b29d4986SJoe Thornber 11526cf4cc8fSJoe Thornber static bool free_target_met(struct smq_policy *mq) 1153b29d4986SJoe Thornber { 115497dfb203SMike Snitzer unsigned nr_free; 1155b29d4986SJoe Thornber 115697dfb203SMike Snitzer nr_free = from_cblock(mq->cache_size) - mq->cache_alloc.nr_allocated; 1157b29d4986SJoe Thornber return (nr_free + btracker_nr_demotions_queued(mq->bg_work)) >= 1158b29d4986SJoe Thornber percent_to_target(mq, FREE_TARGET); 1159b29d4986SJoe Thornber } 1160b29d4986SJoe Thornber 1161b29d4986SJoe Thornber /*----------------------------------------------------------------*/ 1162b29d4986SJoe Thornber 1163b29d4986SJoe Thornber static void mark_pending(struct smq_policy *mq, struct entry *e) 1164b29d4986SJoe Thornber { 1165b29d4986SJoe Thornber BUG_ON(e->sentinel); 1166b29d4986SJoe Thornber BUG_ON(!e->allocated); 1167b29d4986SJoe Thornber BUG_ON(e->pending_work); 1168b29d4986SJoe Thornber e->pending_work = true; 1169b29d4986SJoe Thornber } 1170b29d4986SJoe Thornber 1171b29d4986SJoe Thornber static void clear_pending(struct smq_policy *mq, struct entry *e) 1172b29d4986SJoe Thornber { 1173b29d4986SJoe Thornber BUG_ON(!e->pending_work); 1174b29d4986SJoe Thornber e->pending_work = false; 1175b29d4986SJoe Thornber } 1176b29d4986SJoe Thornber 1177deb71918SJoe Thornber static void queue_writeback(struct smq_policy *mq, bool idle) 1178b29d4986SJoe Thornber { 1179b29d4986SJoe Thornber int r; 1180b29d4986SJoe Thornber struct policy_work work; 1181b29d4986SJoe Thornber struct entry *e; 1182b29d4986SJoe Thornber 1183deb71918SJoe Thornber e = q_peek(&mq->dirty, mq->dirty.nr_levels, idle); 1184b29d4986SJoe Thornber if (e) { 1185b29d4986SJoe Thornber mark_pending(mq, e); 1186b29d4986SJoe Thornber q_del(&mq->dirty, e); 1187b29d4986SJoe Thornber 1188b29d4986SJoe Thornber work.op = POLICY_WRITEBACK; 1189b29d4986SJoe Thornber work.oblock = e->oblock; 1190b29d4986SJoe Thornber work.cblock = infer_cblock(mq, e); 1191b29d4986SJoe Thornber 1192b29d4986SJoe Thornber r = btracker_queue(mq->bg_work, &work, NULL); 11931e72a8e8SJoe Thornber if (r) { 11941e72a8e8SJoe Thornber clear_pending(mq, e); 11951e72a8e8SJoe Thornber q_push_front(&mq->dirty, e); 11961e72a8e8SJoe Thornber } 1197b29d4986SJoe Thornber } 1198b29d4986SJoe Thornber } 1199b29d4986SJoe Thornber 1200b29d4986SJoe Thornber static void queue_demotion(struct smq_policy *mq) 1201b29d4986SJoe Thornber { 12021e72a8e8SJoe Thornber int r; 1203b29d4986SJoe Thornber struct policy_work work; 1204b29d4986SJoe Thornber struct entry *e; 1205b29d4986SJoe Thornber 1206bab5d988SIgor Stoppa if (WARN_ON_ONCE(!mq->migrations_allowed)) 1207b29d4986SJoe Thornber return; 1208b29d4986SJoe Thornber 1209a8cd1ebaSJoe Thornber e = q_peek(&mq->clean, mq->clean.nr_levels / 2, true); 1210b29d4986SJoe Thornber if (!e) { 121178c45607SJoe Thornber if (!clean_target_met(mq, true)) 1212deb71918SJoe Thornber queue_writeback(mq, false); 1213b29d4986SJoe Thornber return; 1214b29d4986SJoe Thornber } 1215b29d4986SJoe Thornber 1216b29d4986SJoe Thornber mark_pending(mq, e); 1217b29d4986SJoe Thornber q_del(&mq->clean, e); 1218b29d4986SJoe Thornber 1219b29d4986SJoe Thornber work.op = POLICY_DEMOTE; 1220b29d4986SJoe Thornber work.oblock = e->oblock; 1221b29d4986SJoe Thornber work.cblock = infer_cblock(mq, e); 12221e72a8e8SJoe Thornber r = btracker_queue(mq->bg_work, &work, NULL); 12231e72a8e8SJoe Thornber if (r) { 12241e72a8e8SJoe Thornber clear_pending(mq, e); 12251e72a8e8SJoe Thornber q_push_front(&mq->clean, e); 12261e72a8e8SJoe Thornber } 1227b29d4986SJoe Thornber } 1228b29d4986SJoe Thornber 1229b29d4986SJoe Thornber static void queue_promotion(struct smq_policy *mq, dm_oblock_t oblock, 1230b29d4986SJoe Thornber struct policy_work **workp) 1231b29d4986SJoe Thornber { 12321e72a8e8SJoe Thornber int r; 1233b29d4986SJoe Thornber struct entry *e; 1234b29d4986SJoe Thornber struct policy_work work; 1235b29d4986SJoe Thornber 1236b29d4986SJoe Thornber if (!mq->migrations_allowed) 1237b29d4986SJoe Thornber return; 1238b29d4986SJoe Thornber 1239b29d4986SJoe Thornber if (allocator_empty(&mq->cache_alloc)) { 1240ce1d64e8SJoe Thornber /* 1241ce1d64e8SJoe Thornber * We always claim to be 'idle' to ensure some demotions happen 1242ce1d64e8SJoe Thornber * with continuous loads. 1243ce1d64e8SJoe Thornber */ 12446cf4cc8fSJoe Thornber if (!free_target_met(mq)) 1245b29d4986SJoe Thornber queue_demotion(mq); 1246b29d4986SJoe Thornber return; 1247b29d4986SJoe Thornber } 1248b29d4986SJoe Thornber 1249b29d4986SJoe Thornber if (btracker_promotion_already_present(mq->bg_work, oblock)) 1250b29d4986SJoe Thornber return; 1251b29d4986SJoe Thornber 1252b29d4986SJoe Thornber /* 1253b29d4986SJoe Thornber * We allocate the entry now to reserve the cblock. If the 1254b29d4986SJoe Thornber * background work is aborted we must remember to free it. 1255b29d4986SJoe Thornber */ 1256b29d4986SJoe Thornber e = alloc_entry(&mq->cache_alloc); 1257b29d4986SJoe Thornber BUG_ON(!e); 1258b29d4986SJoe Thornber e->pending_work = true; 1259b29d4986SJoe Thornber work.op = POLICY_PROMOTE; 1260b29d4986SJoe Thornber work.oblock = oblock; 1261b29d4986SJoe Thornber work.cblock = infer_cblock(mq, e); 12621e72a8e8SJoe Thornber r = btracker_queue(mq->bg_work, &work, workp); 12631e72a8e8SJoe Thornber if (r) 12641e72a8e8SJoe Thornber free_entry(&mq->cache_alloc, e); 1265b29d4986SJoe Thornber } 1266b29d4986SJoe Thornber 1267b29d4986SJoe Thornber /*----------------------------------------------------------------*/ 1268b29d4986SJoe Thornber 126966a63635SJoe Thornber enum promote_result { 127066a63635SJoe Thornber PROMOTE_NOT, 127166a63635SJoe Thornber PROMOTE_TEMPORARY, 127266a63635SJoe Thornber PROMOTE_PERMANENT 127366a63635SJoe Thornber }; 127466a63635SJoe Thornber 127566a63635SJoe Thornber /* 127666a63635SJoe Thornber * Converts a boolean into a promote result. 127766a63635SJoe Thornber */ 127866a63635SJoe Thornber static enum promote_result maybe_promote(bool promote) 127966a63635SJoe Thornber { 128066a63635SJoe Thornber return promote ? PROMOTE_PERMANENT : PROMOTE_NOT; 128166a63635SJoe Thornber } 128266a63635SJoe Thornber 1283b29d4986SJoe Thornber static enum promote_result should_promote(struct smq_policy *mq, struct entry *hs_e, 1284b29d4986SJoe Thornber int data_dir, bool fast_promote) 128566a63635SJoe Thornber { 1286b29d4986SJoe Thornber if (data_dir == WRITE) { 128766a63635SJoe Thornber if (!allocator_empty(&mq->cache_alloc) && fast_promote) 128866a63635SJoe Thornber return PROMOTE_TEMPORARY; 128966a63635SJoe Thornber 129066a63635SJoe Thornber return maybe_promote(hs_e->level >= mq->write_promote_level); 129166a63635SJoe Thornber } else 129266a63635SJoe Thornber return maybe_promote(hs_e->level >= mq->read_promote_level); 129366a63635SJoe Thornber } 129466a63635SJoe Thornber 129566a63635SJoe Thornber static dm_oblock_t to_hblock(struct smq_policy *mq, dm_oblock_t b) 129666a63635SJoe Thornber { 129766a63635SJoe Thornber sector_t r = from_oblock(b); 129866a63635SJoe Thornber (void) sector_div(r, mq->cache_blocks_per_hotspot_block); 129966a63635SJoe Thornber return to_oblock(r); 130066a63635SJoe Thornber } 130166a63635SJoe Thornber 1302b29d4986SJoe Thornber static struct entry *update_hotspot_queue(struct smq_policy *mq, dm_oblock_t b) 130366a63635SJoe Thornber { 130466a63635SJoe Thornber unsigned hi; 130566a63635SJoe Thornber dm_oblock_t hb = to_hblock(mq, b); 130666a63635SJoe Thornber struct entry *e = h_lookup(&mq->hotspot_table, hb); 130766a63635SJoe Thornber 130866a63635SJoe Thornber if (e) { 130966a63635SJoe Thornber stats_level_accessed(&mq->hotspot_stats, e->level); 131066a63635SJoe Thornber 131166a63635SJoe Thornber hi = get_index(&mq->hotspot_alloc, e); 131266a63635SJoe Thornber q_requeue(&mq->hotspot, e, 131366a63635SJoe Thornber test_and_set_bit(hi, mq->hotspot_hit_bits) ? 1314b29d4986SJoe Thornber 0u : mq->hotspot_level_jump, 1315b29d4986SJoe Thornber NULL, NULL); 131666a63635SJoe Thornber 131766a63635SJoe Thornber } else { 131866a63635SJoe Thornber stats_miss(&mq->hotspot_stats); 131966a63635SJoe Thornber 132066a63635SJoe Thornber e = alloc_entry(&mq->hotspot_alloc); 132166a63635SJoe Thornber if (!e) { 132266a63635SJoe Thornber e = q_pop(&mq->hotspot); 132366a63635SJoe Thornber if (e) { 132466a63635SJoe Thornber h_remove(&mq->hotspot_table, e); 132566a63635SJoe Thornber hi = get_index(&mq->hotspot_alloc, e); 132666a63635SJoe Thornber clear_bit(hi, mq->hotspot_hit_bits); 132766a63635SJoe Thornber } 132866a63635SJoe Thornber 132966a63635SJoe Thornber } 133066a63635SJoe Thornber 133166a63635SJoe Thornber if (e) { 133266a63635SJoe Thornber e->oblock = hb; 133366a63635SJoe Thornber q_push(&mq->hotspot, e); 133466a63635SJoe Thornber h_insert(&mq->hotspot_table, e); 133566a63635SJoe Thornber } 133666a63635SJoe Thornber } 133766a63635SJoe Thornber 133866a63635SJoe Thornber return e; 133966a63635SJoe Thornber } 134066a63635SJoe Thornber 134166a63635SJoe Thornber /*----------------------------------------------------------------*/ 134266a63635SJoe Thornber 134366a63635SJoe Thornber /* 134466a63635SJoe Thornber * Public interface, via the policy struct. See dm-cache-policy.h for a 134566a63635SJoe Thornber * description of these. 134666a63635SJoe Thornber */ 134766a63635SJoe Thornber 134866a63635SJoe Thornber static struct smq_policy *to_smq_policy(struct dm_cache_policy *p) 134966a63635SJoe Thornber { 135066a63635SJoe Thornber return container_of(p, struct smq_policy, policy); 135166a63635SJoe Thornber } 135266a63635SJoe Thornber 135366a63635SJoe Thornber static void smq_destroy(struct dm_cache_policy *p) 135466a63635SJoe Thornber { 135566a63635SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 135666a63635SJoe Thornber 1357b29d4986SJoe Thornber btracker_destroy(mq->bg_work); 135866a63635SJoe Thornber h_exit(&mq->hotspot_table); 135966a63635SJoe Thornber h_exit(&mq->table); 136066a63635SJoe Thornber free_bitset(mq->hotspot_hit_bits); 136166a63635SJoe Thornber free_bitset(mq->cache_hit_bits); 136266a63635SJoe Thornber space_exit(&mq->es); 136366a63635SJoe Thornber kfree(mq); 136466a63635SJoe Thornber } 136566a63635SJoe Thornber 1366b29d4986SJoe Thornber /*----------------------------------------------------------------*/ 1367b29d4986SJoe Thornber 1368b29d4986SJoe Thornber static int __lookup(struct smq_policy *mq, dm_oblock_t oblock, dm_cblock_t *cblock, 1369b29d4986SJoe Thornber int data_dir, bool fast_copy, 1370b29d4986SJoe Thornber struct policy_work **work, bool *background_work) 137166a63635SJoe Thornber { 1372b29d4986SJoe Thornber struct entry *e, *hs_e; 1373b29d4986SJoe Thornber enum promote_result pr; 137466a63635SJoe Thornber 1375b29d4986SJoe Thornber *background_work = false; 137666a63635SJoe Thornber 137766a63635SJoe Thornber e = h_lookup(&mq->table, oblock); 137866a63635SJoe Thornber if (e) { 1379b29d4986SJoe Thornber stats_level_accessed(&mq->cache_stats, e->level); 1380b29d4986SJoe Thornber 1381b29d4986SJoe Thornber requeue(mq, e); 138266a63635SJoe Thornber *cblock = infer_cblock(mq, e); 1383b29d4986SJoe Thornber return 0; 1384b29d4986SJoe Thornber 1385b29d4986SJoe Thornber } else { 1386b29d4986SJoe Thornber stats_miss(&mq->cache_stats); 1387b29d4986SJoe Thornber 1388b29d4986SJoe Thornber /* 1389b29d4986SJoe Thornber * The hotspot queue only gets updated with misses. 1390b29d4986SJoe Thornber */ 1391b29d4986SJoe Thornber hs_e = update_hotspot_queue(mq, oblock); 1392b29d4986SJoe Thornber 1393b29d4986SJoe Thornber pr = should_promote(mq, hs_e, data_dir, fast_copy); 1394b29d4986SJoe Thornber if (pr != PROMOTE_NOT) { 1395b29d4986SJoe Thornber queue_promotion(mq, oblock, work); 1396b29d4986SJoe Thornber *background_work = true; 1397b29d4986SJoe Thornber } 1398b29d4986SJoe Thornber 1399b29d4986SJoe Thornber return -ENOENT; 1400b29d4986SJoe Thornber } 1401b29d4986SJoe Thornber } 1402b29d4986SJoe Thornber 1403b29d4986SJoe Thornber static int smq_lookup(struct dm_cache_policy *p, dm_oblock_t oblock, dm_cblock_t *cblock, 1404b29d4986SJoe Thornber int data_dir, bool fast_copy, 1405b29d4986SJoe Thornber bool *background_work) 1406b29d4986SJoe Thornber { 1407b29d4986SJoe Thornber int r; 1408b29d4986SJoe Thornber unsigned long flags; 1409b29d4986SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 1410b29d4986SJoe Thornber 1411b29d4986SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 1412b29d4986SJoe Thornber r = __lookup(mq, oblock, cblock, 1413b29d4986SJoe Thornber data_dir, fast_copy, 1414b29d4986SJoe Thornber NULL, background_work); 14154051aab7SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 141666a63635SJoe Thornber 141766a63635SJoe Thornber return r; 141866a63635SJoe Thornber } 141966a63635SJoe Thornber 1420b29d4986SJoe Thornber static int smq_lookup_with_work(struct dm_cache_policy *p, 1421b29d4986SJoe Thornber dm_oblock_t oblock, dm_cblock_t *cblock, 1422b29d4986SJoe Thornber int data_dir, bool fast_copy, 1423b29d4986SJoe Thornber struct policy_work **work) 142466a63635SJoe Thornber { 1425b29d4986SJoe Thornber int r; 1426b29d4986SJoe Thornber bool background_queued; 1427b29d4986SJoe Thornber unsigned long flags; 1428b29d4986SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 142966a63635SJoe Thornber 1430b29d4986SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 1431b29d4986SJoe Thornber r = __lookup(mq, oblock, cblock, data_dir, fast_copy, work, &background_queued); 1432b29d4986SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 143366a63635SJoe Thornber 1434b29d4986SJoe Thornber return r; 143566a63635SJoe Thornber } 143666a63635SJoe Thornber 1437b29d4986SJoe Thornber static int smq_get_background_work(struct dm_cache_policy *p, bool idle, 1438b29d4986SJoe Thornber struct policy_work **result) 1439b29d4986SJoe Thornber { 1440b29d4986SJoe Thornber int r; 1441b29d4986SJoe Thornber unsigned long flags; 1442b29d4986SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 1443b29d4986SJoe Thornber 1444b29d4986SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 1445b29d4986SJoe Thornber r = btracker_issue(mq->bg_work, result); 1446b29d4986SJoe Thornber if (r == -ENODATA) { 14476cf4cc8fSJoe Thornber if (!clean_target_met(mq, idle)) { 1448deb71918SJoe Thornber queue_writeback(mq, idle); 1449b29d4986SJoe Thornber r = btracker_issue(mq->bg_work, result); 1450b29d4986SJoe Thornber } 14516cf4cc8fSJoe Thornber } 1452b29d4986SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 1453b29d4986SJoe Thornber 1454b29d4986SJoe Thornber return r; 1455b29d4986SJoe Thornber } 1456b29d4986SJoe Thornber 1457b29d4986SJoe Thornber /* 1458b29d4986SJoe Thornber * We need to clear any pending work flags that have been set, and in the 1459b29d4986SJoe Thornber * case of promotion free the entry for the destination cblock. 1460b29d4986SJoe Thornber */ 1461b29d4986SJoe Thornber static void __complete_background_work(struct smq_policy *mq, 1462b29d4986SJoe Thornber struct policy_work *work, 1463b29d4986SJoe Thornber bool success) 1464b29d4986SJoe Thornber { 1465b29d4986SJoe Thornber struct entry *e = get_entry(&mq->cache_alloc, 1466b29d4986SJoe Thornber from_cblock(work->cblock)); 1467b29d4986SJoe Thornber 1468b29d4986SJoe Thornber switch (work->op) { 1469b29d4986SJoe Thornber case POLICY_PROMOTE: 1470b29d4986SJoe Thornber // !h, !q, a 1471b29d4986SJoe Thornber clear_pending(mq, e); 1472b29d4986SJoe Thornber if (success) { 1473b29d4986SJoe Thornber e->oblock = work->oblock; 14744d44ec5aSJoe Thornber e->level = NR_CACHE_LEVELS - 1; 1475b29d4986SJoe Thornber push(mq, e); 1476b29d4986SJoe Thornber // h, q, a 1477b29d4986SJoe Thornber } else { 1478b29d4986SJoe Thornber free_entry(&mq->cache_alloc, e); 1479b29d4986SJoe Thornber // !h, !q, !a 1480b29d4986SJoe Thornber } 1481b29d4986SJoe Thornber break; 1482b29d4986SJoe Thornber 1483b29d4986SJoe Thornber case POLICY_DEMOTE: 1484b29d4986SJoe Thornber // h, !q, a 1485b29d4986SJoe Thornber if (success) { 1486b29d4986SJoe Thornber h_remove(&mq->table, e); 1487b29d4986SJoe Thornber free_entry(&mq->cache_alloc, e); 1488b29d4986SJoe Thornber // !h, !q, !a 1489b29d4986SJoe Thornber } else { 1490b29d4986SJoe Thornber clear_pending(mq, e); 1491b29d4986SJoe Thornber push_queue(mq, e); 1492b29d4986SJoe Thornber // h, q, a 1493b29d4986SJoe Thornber } 1494b29d4986SJoe Thornber break; 1495b29d4986SJoe Thornber 1496b29d4986SJoe Thornber case POLICY_WRITEBACK: 1497b29d4986SJoe Thornber // h, !q, a 1498b29d4986SJoe Thornber clear_pending(mq, e); 1499b29d4986SJoe Thornber push_queue(mq, e); 1500b29d4986SJoe Thornber // h, q, a 1501b29d4986SJoe Thornber break; 1502b29d4986SJoe Thornber } 1503b29d4986SJoe Thornber 1504b29d4986SJoe Thornber btracker_complete(mq->bg_work, work); 1505b29d4986SJoe Thornber } 1506b29d4986SJoe Thornber 1507b29d4986SJoe Thornber static void smq_complete_background_work(struct dm_cache_policy *p, 1508b29d4986SJoe Thornber struct policy_work *work, 1509b29d4986SJoe Thornber bool success) 151066a63635SJoe Thornber { 15114051aab7SJoe Thornber unsigned long flags; 151266a63635SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 151366a63635SJoe Thornber 15144051aab7SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 1515b29d4986SJoe Thornber __complete_background_work(mq, work, success); 15164051aab7SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 151766a63635SJoe Thornber } 151866a63635SJoe Thornber 1519b29d4986SJoe Thornber // in_hash(oblock) -> in_hash(oblock) 1520b29d4986SJoe Thornber static void __smq_set_clear_dirty(struct smq_policy *mq, dm_cblock_t cblock, bool set) 1521b29d4986SJoe Thornber { 1522b29d4986SJoe Thornber struct entry *e = get_entry(&mq->cache_alloc, from_cblock(cblock)); 1523b29d4986SJoe Thornber 1524b29d4986SJoe Thornber if (e->pending_work) 1525b29d4986SJoe Thornber e->dirty = set; 1526b29d4986SJoe Thornber else { 1527b29d4986SJoe Thornber del_queue(mq, e); 1528b29d4986SJoe Thornber e->dirty = set; 1529b29d4986SJoe Thornber push_queue(mq, e); 1530b29d4986SJoe Thornber } 1531b29d4986SJoe Thornber } 1532b29d4986SJoe Thornber 1533b29d4986SJoe Thornber static void smq_set_dirty(struct dm_cache_policy *p, dm_cblock_t cblock) 1534b29d4986SJoe Thornber { 1535b29d4986SJoe Thornber unsigned long flags; 1536b29d4986SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 1537b29d4986SJoe Thornber 1538b29d4986SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 1539b29d4986SJoe Thornber __smq_set_clear_dirty(mq, cblock, true); 1540b29d4986SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 1541b29d4986SJoe Thornber } 1542b29d4986SJoe Thornber 1543b29d4986SJoe Thornber static void smq_clear_dirty(struct dm_cache_policy *p, dm_cblock_t cblock) 154466a63635SJoe Thornber { 154566a63635SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 15464051aab7SJoe Thornber unsigned long flags; 154766a63635SJoe Thornber 15484051aab7SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 1549b29d4986SJoe Thornber __smq_set_clear_dirty(mq, cblock, false); 15504051aab7SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 155166a63635SJoe Thornber } 155266a63635SJoe Thornber 15539d1b404cSJoe Thornber static unsigned random_level(dm_cblock_t cblock) 15549d1b404cSJoe Thornber { 1555e99dda8fSMike Snitzer return hash_32(from_cblock(cblock), 9) & (NR_CACHE_LEVELS - 1); 15569d1b404cSJoe Thornber } 15579d1b404cSJoe Thornber 155866a63635SJoe Thornber static int smq_load_mapping(struct dm_cache_policy *p, 155966a63635SJoe Thornber dm_oblock_t oblock, dm_cblock_t cblock, 1560b29d4986SJoe Thornber bool dirty, uint32_t hint, bool hint_valid) 156166a63635SJoe Thornber { 156266a63635SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 156366a63635SJoe Thornber struct entry *e; 156466a63635SJoe Thornber 156566a63635SJoe Thornber e = alloc_particular_entry(&mq->cache_alloc, from_cblock(cblock)); 156666a63635SJoe Thornber e->oblock = oblock; 1567b29d4986SJoe Thornber e->dirty = dirty; 15689d1b404cSJoe Thornber e->level = hint_valid ? min(hint, NR_CACHE_LEVELS - 1) : random_level(cblock); 1569b29d4986SJoe Thornber e->pending_work = false; 157066a63635SJoe Thornber 1571b29d4986SJoe Thornber /* 1572b29d4986SJoe Thornber * When we load mappings we push ahead of both sentinels in order to 1573b29d4986SJoe Thornber * allow demotions and cleaning to occur immediately. 1574b29d4986SJoe Thornber */ 1575b29d4986SJoe Thornber push_front(mq, e); 1576b29d4986SJoe Thornber 1577b29d4986SJoe Thornber return 0; 1578b29d4986SJoe Thornber } 1579b29d4986SJoe Thornber 1580b29d4986SJoe Thornber static int smq_invalidate_mapping(struct dm_cache_policy *p, dm_cblock_t cblock) 1581b29d4986SJoe Thornber { 1582b29d4986SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 1583b29d4986SJoe Thornber struct entry *e = get_entry(&mq->cache_alloc, from_cblock(cblock)); 1584b29d4986SJoe Thornber 1585b29d4986SJoe Thornber if (!e->allocated) 1586b29d4986SJoe Thornber return -ENODATA; 1587b29d4986SJoe Thornber 1588b29d4986SJoe Thornber // FIXME: what if this block has pending background work? 1589b29d4986SJoe Thornber del_queue(mq, e); 1590b29d4986SJoe Thornber h_remove(&mq->table, e); 1591b29d4986SJoe Thornber free_entry(&mq->cache_alloc, e); 159266a63635SJoe Thornber return 0; 159366a63635SJoe Thornber } 159466a63635SJoe Thornber 15954e781b49SJoe Thornber static uint32_t smq_get_hint(struct dm_cache_policy *p, dm_cblock_t cblock) 159666a63635SJoe Thornber { 159766a63635SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 15984e781b49SJoe Thornber struct entry *e = get_entry(&mq->cache_alloc, from_cblock(cblock)); 159966a63635SJoe Thornber 16004e781b49SJoe Thornber if (!e->allocated) 16014e781b49SJoe Thornber return 0; 160266a63635SJoe Thornber 16034e781b49SJoe Thornber return e->level; 160466a63635SJoe Thornber } 160566a63635SJoe Thornber 160666a63635SJoe Thornber static dm_cblock_t smq_residency(struct dm_cache_policy *p) 160766a63635SJoe Thornber { 160866a63635SJoe Thornber dm_cblock_t r; 16094051aab7SJoe Thornber unsigned long flags; 161066a63635SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 161166a63635SJoe Thornber 16124051aab7SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 161366a63635SJoe Thornber r = to_cblock(mq->cache_alloc.nr_allocated); 16144051aab7SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 161566a63635SJoe Thornber 161666a63635SJoe Thornber return r; 161766a63635SJoe Thornber } 161866a63635SJoe Thornber 1619fba10109SJoe Thornber static void smq_tick(struct dm_cache_policy *p, bool can_block) 162066a63635SJoe Thornber { 162166a63635SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 162266a63635SJoe Thornber unsigned long flags; 162366a63635SJoe Thornber 16244051aab7SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 16254051aab7SJoe Thornber mq->tick++; 16264051aab7SJoe Thornber update_sentinels(mq); 16274051aab7SJoe Thornber end_hotspot_period(mq); 16284051aab7SJoe Thornber end_cache_period(mq); 16294051aab7SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 163066a63635SJoe Thornber } 163166a63635SJoe Thornber 1632b29d4986SJoe Thornber static void smq_allow_migrations(struct dm_cache_policy *p, bool allow) 1633b29d4986SJoe Thornber { 1634b29d4986SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 1635b29d4986SJoe Thornber mq->migrations_allowed = allow; 1636b29d4986SJoe Thornber } 1637b29d4986SJoe Thornber 16389ed84698SJoe Thornber /* 16399ed84698SJoe Thornber * smq has no config values, but the old mq policy did. To avoid breaking 16409ed84698SJoe Thornber * software we continue to accept these configurables for the mq policy, 16419ed84698SJoe Thornber * but they have no effect. 16429ed84698SJoe Thornber */ 16439ed84698SJoe Thornber static int mq_set_config_value(struct dm_cache_policy *p, 16449ed84698SJoe Thornber const char *key, const char *value) 16459ed84698SJoe Thornber { 16469ed84698SJoe Thornber unsigned long tmp; 16479ed84698SJoe Thornber 16489ed84698SJoe Thornber if (kstrtoul(value, 10, &tmp)) 16499ed84698SJoe Thornber return -EINVAL; 16509ed84698SJoe Thornber 16519ed84698SJoe Thornber if (!strcasecmp(key, "random_threshold") || 16529ed84698SJoe Thornber !strcasecmp(key, "sequential_threshold") || 16539ed84698SJoe Thornber !strcasecmp(key, "discard_promote_adjustment") || 16549ed84698SJoe Thornber !strcasecmp(key, "read_promote_adjustment") || 16559ed84698SJoe Thornber !strcasecmp(key, "write_promote_adjustment")) { 16569ed84698SJoe Thornber DMWARN("tunable '%s' no longer has any effect, mq policy is now an alias for smq", key); 16579ed84698SJoe Thornber return 0; 16589ed84698SJoe Thornber } 16599ed84698SJoe Thornber 16609ed84698SJoe Thornber return -EINVAL; 16619ed84698SJoe Thornber } 16629ed84698SJoe Thornber 16639ed84698SJoe Thornber static int mq_emit_config_values(struct dm_cache_policy *p, char *result, 16649ed84698SJoe Thornber unsigned maxlen, ssize_t *sz_ptr) 16659ed84698SJoe Thornber { 16669ed84698SJoe Thornber ssize_t sz = *sz_ptr; 16679ed84698SJoe Thornber 16689ed84698SJoe Thornber DMEMIT("10 random_threshold 0 " 16699ed84698SJoe Thornber "sequential_threshold 0 " 16709ed84698SJoe Thornber "discard_promote_adjustment 0 " 16719ed84698SJoe Thornber "read_promote_adjustment 0 " 16729ed84698SJoe Thornber "write_promote_adjustment 0 "); 16739ed84698SJoe Thornber 16749ed84698SJoe Thornber *sz_ptr = sz; 16759ed84698SJoe Thornber return 0; 16769ed84698SJoe Thornber } 16779ed84698SJoe Thornber 167866a63635SJoe Thornber /* Init the policy plugin interface function pointers. */ 16799ed84698SJoe Thornber static void init_policy_functions(struct smq_policy *mq, bool mimic_mq) 168066a63635SJoe Thornber { 168166a63635SJoe Thornber mq->policy.destroy = smq_destroy; 168266a63635SJoe Thornber mq->policy.lookup = smq_lookup; 1683b29d4986SJoe Thornber mq->policy.lookup_with_work = smq_lookup_with_work; 1684b29d4986SJoe Thornber mq->policy.get_background_work = smq_get_background_work; 1685b29d4986SJoe Thornber mq->policy.complete_background_work = smq_complete_background_work; 168666a63635SJoe Thornber mq->policy.set_dirty = smq_set_dirty; 168766a63635SJoe Thornber mq->policy.clear_dirty = smq_clear_dirty; 168866a63635SJoe Thornber mq->policy.load_mapping = smq_load_mapping; 1689b29d4986SJoe Thornber mq->policy.invalidate_mapping = smq_invalidate_mapping; 16904e781b49SJoe Thornber mq->policy.get_hint = smq_get_hint; 169166a63635SJoe Thornber mq->policy.residency = smq_residency; 169266a63635SJoe Thornber mq->policy.tick = smq_tick; 1693b29d4986SJoe Thornber mq->policy.allow_migrations = smq_allow_migrations; 16949ed84698SJoe Thornber 16959ed84698SJoe Thornber if (mimic_mq) { 16969ed84698SJoe Thornber mq->policy.set_config_value = mq_set_config_value; 16979ed84698SJoe Thornber mq->policy.emit_config_values = mq_emit_config_values; 16989ed84698SJoe Thornber } 169966a63635SJoe Thornber } 170066a63635SJoe Thornber 170166a63635SJoe Thornber static bool too_many_hotspot_blocks(sector_t origin_size, 170266a63635SJoe Thornber sector_t hotspot_block_size, 170366a63635SJoe Thornber unsigned nr_hotspot_blocks) 170466a63635SJoe Thornber { 170566a63635SJoe Thornber return (hotspot_block_size * nr_hotspot_blocks) > origin_size; 170666a63635SJoe Thornber } 170766a63635SJoe Thornber 170866a63635SJoe Thornber static void calc_hotspot_params(sector_t origin_size, 170966a63635SJoe Thornber sector_t cache_block_size, 171066a63635SJoe Thornber unsigned nr_cache_blocks, 171166a63635SJoe Thornber sector_t *hotspot_block_size, 171266a63635SJoe Thornber unsigned *nr_hotspot_blocks) 171366a63635SJoe Thornber { 171466a63635SJoe Thornber *hotspot_block_size = cache_block_size * 16u; 171566a63635SJoe Thornber *nr_hotspot_blocks = max(nr_cache_blocks / 4u, 1024u); 171666a63635SJoe Thornber 171766a63635SJoe Thornber while ((*hotspot_block_size > cache_block_size) && 171866a63635SJoe Thornber too_many_hotspot_blocks(origin_size, *hotspot_block_size, *nr_hotspot_blocks)) 171966a63635SJoe Thornber *hotspot_block_size /= 2u; 172066a63635SJoe Thornber } 172166a63635SJoe Thornber 17229ed84698SJoe Thornber static struct dm_cache_policy *__smq_create(dm_cblock_t cache_size, 172366a63635SJoe Thornber sector_t origin_size, 17249ed84698SJoe Thornber sector_t cache_block_size, 1725b29d4986SJoe Thornber bool mimic_mq, 1726b29d4986SJoe Thornber bool migrations_allowed) 172766a63635SJoe Thornber { 172866a63635SJoe Thornber unsigned i; 172966a63635SJoe Thornber unsigned nr_sentinels_per_queue = 2u * NR_CACHE_LEVELS; 173066a63635SJoe Thornber unsigned total_sentinels = 2u * nr_sentinels_per_queue; 173166a63635SJoe Thornber struct smq_policy *mq = kzalloc(sizeof(*mq), GFP_KERNEL); 173266a63635SJoe Thornber 173366a63635SJoe Thornber if (!mq) 173466a63635SJoe Thornber return NULL; 173566a63635SJoe Thornber 17369ed84698SJoe Thornber init_policy_functions(mq, mimic_mq); 173766a63635SJoe Thornber mq->cache_size = cache_size; 173866a63635SJoe Thornber mq->cache_block_size = cache_block_size; 173966a63635SJoe Thornber 174066a63635SJoe Thornber calc_hotspot_params(origin_size, cache_block_size, from_cblock(cache_size), 174166a63635SJoe Thornber &mq->hotspot_block_size, &mq->nr_hotspot_blocks); 174266a63635SJoe Thornber 174366a63635SJoe Thornber mq->cache_blocks_per_hotspot_block = div64_u64(mq->hotspot_block_size, mq->cache_block_size); 174466a63635SJoe Thornber mq->hotspot_level_jump = 1u; 174566a63635SJoe Thornber if (space_init(&mq->es, total_sentinels + mq->nr_hotspot_blocks + from_cblock(cache_size))) { 174666a63635SJoe Thornber DMERR("couldn't initialize entry space"); 174766a63635SJoe Thornber goto bad_pool_init; 174866a63635SJoe Thornber } 174966a63635SJoe Thornber 175066a63635SJoe Thornber init_allocator(&mq->writeback_sentinel_alloc, &mq->es, 0, nr_sentinels_per_queue); 175166a63635SJoe Thornber for (i = 0; i < nr_sentinels_per_queue; i++) 175266a63635SJoe Thornber get_entry(&mq->writeback_sentinel_alloc, i)->sentinel = true; 175366a63635SJoe Thornber 175466a63635SJoe Thornber init_allocator(&mq->demote_sentinel_alloc, &mq->es, nr_sentinels_per_queue, total_sentinels); 175566a63635SJoe Thornber for (i = 0; i < nr_sentinels_per_queue; i++) 175666a63635SJoe Thornber get_entry(&mq->demote_sentinel_alloc, i)->sentinel = true; 175766a63635SJoe Thornber 175866a63635SJoe Thornber init_allocator(&mq->hotspot_alloc, &mq->es, total_sentinels, 175966a63635SJoe Thornber total_sentinels + mq->nr_hotspot_blocks); 176066a63635SJoe Thornber 176166a63635SJoe Thornber init_allocator(&mq->cache_alloc, &mq->es, 176266a63635SJoe Thornber total_sentinels + mq->nr_hotspot_blocks, 176366a63635SJoe Thornber total_sentinels + mq->nr_hotspot_blocks + from_cblock(cache_size)); 176466a63635SJoe Thornber 176566a63635SJoe Thornber mq->hotspot_hit_bits = alloc_bitset(mq->nr_hotspot_blocks); 176666a63635SJoe Thornber if (!mq->hotspot_hit_bits) { 176766a63635SJoe Thornber DMERR("couldn't allocate hotspot hit bitset"); 176866a63635SJoe Thornber goto bad_hotspot_hit_bits; 176966a63635SJoe Thornber } 177066a63635SJoe Thornber clear_bitset(mq->hotspot_hit_bits, mq->nr_hotspot_blocks); 177166a63635SJoe Thornber 177266a63635SJoe Thornber if (from_cblock(cache_size)) { 177366a63635SJoe Thornber mq->cache_hit_bits = alloc_bitset(from_cblock(cache_size)); 1774134bf30cSColin Ian King if (!mq->cache_hit_bits) { 177566a63635SJoe Thornber DMERR("couldn't allocate cache hit bitset"); 177666a63635SJoe Thornber goto bad_cache_hit_bits; 177766a63635SJoe Thornber } 177866a63635SJoe Thornber clear_bitset(mq->cache_hit_bits, from_cblock(mq->cache_size)); 177966a63635SJoe Thornber } else 178066a63635SJoe Thornber mq->cache_hit_bits = NULL; 178166a63635SJoe Thornber 178266a63635SJoe Thornber mq->tick = 0; 17834051aab7SJoe Thornber spin_lock_init(&mq->lock); 178466a63635SJoe Thornber 178566a63635SJoe Thornber q_init(&mq->hotspot, &mq->es, NR_HOTSPOT_LEVELS); 178666a63635SJoe Thornber mq->hotspot.nr_top_levels = 8; 178766a63635SJoe Thornber mq->hotspot.nr_in_top_levels = min(mq->nr_hotspot_blocks / NR_HOTSPOT_LEVELS, 178866a63635SJoe Thornber from_cblock(mq->cache_size) / mq->cache_blocks_per_hotspot_block); 178966a63635SJoe Thornber 179066a63635SJoe Thornber q_init(&mq->clean, &mq->es, NR_CACHE_LEVELS); 179166a63635SJoe Thornber q_init(&mq->dirty, &mq->es, NR_CACHE_LEVELS); 179266a63635SJoe Thornber 179366a63635SJoe Thornber stats_init(&mq->hotspot_stats, NR_HOTSPOT_LEVELS); 179466a63635SJoe Thornber stats_init(&mq->cache_stats, NR_CACHE_LEVELS); 179566a63635SJoe Thornber 179666a63635SJoe Thornber if (h_init(&mq->table, &mq->es, from_cblock(cache_size))) 179766a63635SJoe Thornber goto bad_alloc_table; 179866a63635SJoe Thornber 179966a63635SJoe Thornber if (h_init(&mq->hotspot_table, &mq->es, mq->nr_hotspot_blocks)) 180066a63635SJoe Thornber goto bad_alloc_hotspot_table; 180166a63635SJoe Thornber 180266a63635SJoe Thornber sentinels_init(mq); 180366a63635SJoe Thornber mq->write_promote_level = mq->read_promote_level = NR_HOTSPOT_LEVELS; 180466a63635SJoe Thornber 180566a63635SJoe Thornber mq->next_hotspot_period = jiffies; 180666a63635SJoe Thornber mq->next_cache_period = jiffies; 180766a63635SJoe Thornber 18088ee18edeSJoe Thornber mq->bg_work = btracker_create(4096); /* FIXME: hard coded value */ 1809b29d4986SJoe Thornber if (!mq->bg_work) 1810b29d4986SJoe Thornber goto bad_btracker; 1811b29d4986SJoe Thornber 1812b29d4986SJoe Thornber mq->migrations_allowed = migrations_allowed; 1813b29d4986SJoe Thornber 181466a63635SJoe Thornber return &mq->policy; 181566a63635SJoe Thornber 1816b29d4986SJoe Thornber bad_btracker: 1817b29d4986SJoe Thornber h_exit(&mq->hotspot_table); 181866a63635SJoe Thornber bad_alloc_hotspot_table: 181966a63635SJoe Thornber h_exit(&mq->table); 182066a63635SJoe Thornber bad_alloc_table: 182166a63635SJoe Thornber free_bitset(mq->cache_hit_bits); 182266a63635SJoe Thornber bad_cache_hit_bits: 182366a63635SJoe Thornber free_bitset(mq->hotspot_hit_bits); 182466a63635SJoe Thornber bad_hotspot_hit_bits: 182566a63635SJoe Thornber space_exit(&mq->es); 182666a63635SJoe Thornber bad_pool_init: 182766a63635SJoe Thornber kfree(mq); 182866a63635SJoe Thornber 182966a63635SJoe Thornber return NULL; 183066a63635SJoe Thornber } 183166a63635SJoe Thornber 18329ed84698SJoe Thornber static struct dm_cache_policy *smq_create(dm_cblock_t cache_size, 18339ed84698SJoe Thornber sector_t origin_size, 18349ed84698SJoe Thornber sector_t cache_block_size) 18359ed84698SJoe Thornber { 1836b29d4986SJoe Thornber return __smq_create(cache_size, origin_size, cache_block_size, false, true); 18379ed84698SJoe Thornber } 18389ed84698SJoe Thornber 18399ed84698SJoe Thornber static struct dm_cache_policy *mq_create(dm_cblock_t cache_size, 18409ed84698SJoe Thornber sector_t origin_size, 18419ed84698SJoe Thornber sector_t cache_block_size) 18429ed84698SJoe Thornber { 1843b29d4986SJoe Thornber return __smq_create(cache_size, origin_size, cache_block_size, true, true); 1844b29d4986SJoe Thornber } 1845b29d4986SJoe Thornber 1846b29d4986SJoe Thornber static struct dm_cache_policy *cleaner_create(dm_cblock_t cache_size, 1847b29d4986SJoe Thornber sector_t origin_size, 1848b29d4986SJoe Thornber sector_t cache_block_size) 1849b29d4986SJoe Thornber { 1850b29d4986SJoe Thornber return __smq_create(cache_size, origin_size, cache_block_size, false, false); 18519ed84698SJoe Thornber } 18529ed84698SJoe Thornber 185366a63635SJoe Thornber /*----------------------------------------------------------------*/ 185466a63635SJoe Thornber 185566a63635SJoe Thornber static struct dm_cache_policy_type smq_policy_type = { 185666a63635SJoe Thornber .name = "smq", 1857b29d4986SJoe Thornber .version = {2, 0, 0}, 185866a63635SJoe Thornber .hint_size = 4, 185966a63635SJoe Thornber .owner = THIS_MODULE, 186066a63635SJoe Thornber .create = smq_create 186166a63635SJoe Thornber }; 186266a63635SJoe Thornber 18639ed84698SJoe Thornber static struct dm_cache_policy_type mq_policy_type = { 18649ed84698SJoe Thornber .name = "mq", 1865b29d4986SJoe Thornber .version = {2, 0, 0}, 18669ed84698SJoe Thornber .hint_size = 4, 18679ed84698SJoe Thornber .owner = THIS_MODULE, 18689ed84698SJoe Thornber .create = mq_create, 18699ed84698SJoe Thornber }; 18709ed84698SJoe Thornber 1871b29d4986SJoe Thornber static struct dm_cache_policy_type cleaner_policy_type = { 1872b29d4986SJoe Thornber .name = "cleaner", 1873b29d4986SJoe Thornber .version = {2, 0, 0}, 1874b29d4986SJoe Thornber .hint_size = 4, 1875b29d4986SJoe Thornber .owner = THIS_MODULE, 1876b29d4986SJoe Thornber .create = cleaner_create, 1877b29d4986SJoe Thornber }; 1878b29d4986SJoe Thornber 1879bccab6a0SMike Snitzer static struct dm_cache_policy_type default_policy_type = { 1880bccab6a0SMike Snitzer .name = "default", 1881b29d4986SJoe Thornber .version = {2, 0, 0}, 1882bccab6a0SMike Snitzer .hint_size = 4, 1883bccab6a0SMike Snitzer .owner = THIS_MODULE, 1884bccab6a0SMike Snitzer .create = smq_create, 1885bccab6a0SMike Snitzer .real = &smq_policy_type 1886bccab6a0SMike Snitzer }; 1887bccab6a0SMike Snitzer 188866a63635SJoe Thornber static int __init smq_init(void) 188966a63635SJoe Thornber { 189066a63635SJoe Thornber int r; 189166a63635SJoe Thornber 189266a63635SJoe Thornber r = dm_cache_policy_register(&smq_policy_type); 189366a63635SJoe Thornber if (r) { 189466a63635SJoe Thornber DMERR("register failed %d", r); 189566a63635SJoe Thornber return -ENOMEM; 189666a63635SJoe Thornber } 189766a63635SJoe Thornber 18989ed84698SJoe Thornber r = dm_cache_policy_register(&mq_policy_type); 18999ed84698SJoe Thornber if (r) { 19007dd85bb0SMike Snitzer DMERR("register failed (as mq) %d", r); 1901b29d4986SJoe Thornber goto out_mq; 1902b29d4986SJoe Thornber } 1903b29d4986SJoe Thornber 1904b29d4986SJoe Thornber r = dm_cache_policy_register(&cleaner_policy_type); 1905b29d4986SJoe Thornber if (r) { 1906b29d4986SJoe Thornber DMERR("register failed (as cleaner) %d", r); 1907b29d4986SJoe Thornber goto out_cleaner; 19089ed84698SJoe Thornber } 19099ed84698SJoe Thornber 1910bccab6a0SMike Snitzer r = dm_cache_policy_register(&default_policy_type); 1911bccab6a0SMike Snitzer if (r) { 1912bccab6a0SMike Snitzer DMERR("register failed (as default) %d", r); 1913b29d4986SJoe Thornber goto out_default; 1914bccab6a0SMike Snitzer } 1915bccab6a0SMike Snitzer 191666a63635SJoe Thornber return 0; 1917b29d4986SJoe Thornber 1918b29d4986SJoe Thornber out_default: 1919b29d4986SJoe Thornber dm_cache_policy_unregister(&cleaner_policy_type); 1920b29d4986SJoe Thornber out_cleaner: 1921b29d4986SJoe Thornber dm_cache_policy_unregister(&mq_policy_type); 1922b29d4986SJoe Thornber out_mq: 1923b29d4986SJoe Thornber dm_cache_policy_unregister(&smq_policy_type); 1924b29d4986SJoe Thornber 1925b29d4986SJoe Thornber return -ENOMEM; 192666a63635SJoe Thornber } 192766a63635SJoe Thornber 192866a63635SJoe Thornber static void __exit smq_exit(void) 192966a63635SJoe Thornber { 1930b29d4986SJoe Thornber dm_cache_policy_unregister(&cleaner_policy_type); 193166a63635SJoe Thornber dm_cache_policy_unregister(&smq_policy_type); 19329ed84698SJoe Thornber dm_cache_policy_unregister(&mq_policy_type); 1933bccab6a0SMike Snitzer dm_cache_policy_unregister(&default_policy_type); 193466a63635SJoe Thornber } 193566a63635SJoe Thornber 193666a63635SJoe Thornber module_init(smq_init); 193766a63635SJoe Thornber module_exit(smq_exit); 193866a63635SJoe Thornber 193966a63635SJoe Thornber MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>"); 194066a63635SJoe Thornber MODULE_LICENSE("GPL"); 194166a63635SJoe Thornber MODULE_DESCRIPTION("smq cache policy"); 194234dd0517SYi Zhang 194334dd0517SYi Zhang MODULE_ALIAS("dm-cache-default"); 19449ed84698SJoe Thornber MODULE_ALIAS("dm-cache-mq"); 1945b29d4986SJoe Thornber MODULE_ALIAS("dm-cache-cleaner"); 1946