13bd94003SHeinz 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 */ 2786a3238cSHeinz Mauelshagen static unsigned int safe_div(unsigned int n, unsigned int d) 2866a63635SJoe Thornber { 2966a63635SJoe Thornber return d ? n / d : 0u; 3066a63635SJoe Thornber } 3166a63635SJoe Thornber 3286a3238cSHeinz Mauelshagen static unsigned int safe_mod(unsigned int n, unsigned int d) 3366a63635SJoe Thornber { 3466a63635SJoe Thornber return d ? n % d : 0u; 3566a63635SJoe Thornber } 3666a63635SJoe Thornber 3766a63635SJoe Thornber /*----------------------------------------------------------------*/ 3866a63635SJoe Thornber 3966a63635SJoe Thornber struct entry { 4086a3238cSHeinz Mauelshagen unsigned int hash_next:28; 4186a3238cSHeinz Mauelshagen unsigned int prev:28; 4286a3238cSHeinz Mauelshagen unsigned int next:28; 4386a3238cSHeinz Mauelshagen unsigned int 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 6686a3238cSHeinz Mauelshagen static int space_init(struct entry_space *es, unsigned int 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 8686a3238cSHeinz Mauelshagen static struct entry *__get_entry(struct entry_space *es, unsigned int 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 9686a3238cSHeinz Mauelshagen static unsigned int 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 10286a3238cSHeinz Mauelshagen static struct entry *to_entry(struct entry_space *es, unsigned int 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 { 11386a3238cSHeinz Mauelshagen unsigned int nr_elts; /* excluding sentinel entries */ 11486a3238cSHeinz Mauelshagen unsigned int 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 25686a3238cSHeinz Mauelshagen unsigned int nr_elts; 25786a3238cSHeinz Mauelshagen unsigned int 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 */ 26486a3238cSHeinz Mauelshagen unsigned int last_target_nr_elts; 26586a3238cSHeinz Mauelshagen unsigned int nr_top_levels; 26686a3238cSHeinz Mauelshagen unsigned int nr_in_top_levels; 26786a3238cSHeinz Mauelshagen unsigned int target_count[MAX_LEVELS]; 26866a63635SJoe Thornber }; 26966a63635SJoe Thornber 27086a3238cSHeinz Mauelshagen static void q_init(struct queue *q, struct entry_space *es, unsigned int nr_levels) 27166a63635SJoe Thornber { 27286a3238cSHeinz Mauelshagen unsigned int 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 28886a3238cSHeinz Mauelshagen static unsigned int 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 */ 33686a3238cSHeinz Mauelshagen static struct entry *q_peek(struct queue *q, unsigned int max_level, bool can_cross_sentinel) 33766a63635SJoe Thornber { 33886a3238cSHeinz Mauelshagen unsigned int 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 */ 37386a3238cSHeinz Mauelshagen static struct entry *__redist_pop_from(struct queue *q, unsigned int 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 38786a3238cSHeinz Mauelshagen static void q_set_targets_subrange_(struct queue *q, unsigned int nr_elts, 38886a3238cSHeinz Mauelshagen unsigned int lbegin, unsigned int lend) 38966a63635SJoe Thornber { 39086a3238cSHeinz Mauelshagen unsigned int level, nr_levels, entries_per_level, remainder; 39166a63635SJoe Thornber 39266a63635SJoe Thornber BUG_ON(lbegin > lend); 39366a63635SJoe Thornber BUG_ON(lend > q->nr_levels); 39466a63635SJoe Thornber nr_levels = lend - lbegin; 39566a63635SJoe Thornber entries_per_level = safe_div(nr_elts, nr_levels); 39666a63635SJoe Thornber remainder = safe_mod(nr_elts, nr_levels); 39766a63635SJoe Thornber 39866a63635SJoe Thornber for (level = lbegin; level < lend; level++) 39966a63635SJoe Thornber q->target_count[level] = 40066a63635SJoe Thornber (level < (lbegin + remainder)) ? entries_per_level + 1u : entries_per_level; 40166a63635SJoe Thornber } 40266a63635SJoe Thornber 40366a63635SJoe Thornber /* 40466a63635SJoe Thornber * Typically we have fewer elements in the top few levels which allows us 40566a63635SJoe Thornber * to adjust the promote threshold nicely. 40666a63635SJoe Thornber */ 40766a63635SJoe Thornber static void q_set_targets(struct queue *q) 40866a63635SJoe Thornber { 40966a63635SJoe Thornber if (q->last_target_nr_elts == q->nr_elts) 41066a63635SJoe Thornber return; 41166a63635SJoe Thornber 41266a63635SJoe Thornber q->last_target_nr_elts = q->nr_elts; 41366a63635SJoe Thornber 41466a63635SJoe Thornber if (q->nr_top_levels > q->nr_levels) 41566a63635SJoe Thornber q_set_targets_subrange_(q, q->nr_elts, 0, q->nr_levels); 41666a63635SJoe Thornber 41766a63635SJoe Thornber else { 41866a63635SJoe Thornber q_set_targets_subrange_(q, q->nr_in_top_levels, 41966a63635SJoe Thornber q->nr_levels - q->nr_top_levels, q->nr_levels); 42066a63635SJoe Thornber 42166a63635SJoe Thornber if (q->nr_in_top_levels < q->nr_elts) 42266a63635SJoe Thornber q_set_targets_subrange_(q, q->nr_elts - q->nr_in_top_levels, 42366a63635SJoe Thornber 0, q->nr_levels - q->nr_top_levels); 42466a63635SJoe Thornber else 42566a63635SJoe Thornber q_set_targets_subrange_(q, 0, 0, q->nr_levels - q->nr_top_levels); 42666a63635SJoe Thornber } 42766a63635SJoe Thornber } 42866a63635SJoe Thornber 42966a63635SJoe Thornber static void q_redistribute(struct queue *q) 43066a63635SJoe Thornber { 43186a3238cSHeinz Mauelshagen unsigned int target, level; 43266a63635SJoe Thornber struct ilist *l, *l_above; 43366a63635SJoe Thornber struct entry *e; 43466a63635SJoe Thornber 43566a63635SJoe Thornber q_set_targets(q); 43666a63635SJoe Thornber 43766a63635SJoe Thornber for (level = 0u; level < q->nr_levels - 1u; level++) { 43866a63635SJoe Thornber l = q->qs + level; 43966a63635SJoe Thornber target = q->target_count[level]; 44066a63635SJoe Thornber 44166a63635SJoe Thornber /* 44266a63635SJoe Thornber * Pull down some entries from the level above. 44366a63635SJoe Thornber */ 44466a63635SJoe Thornber while (l->nr_elts < target) { 44566a63635SJoe Thornber e = __redist_pop_from(q, level + 1u); 44666a63635SJoe Thornber if (!e) { 44766a63635SJoe Thornber /* bug in nr_elts */ 44866a63635SJoe Thornber break; 44966a63635SJoe Thornber } 45066a63635SJoe Thornber 45166a63635SJoe Thornber e->level = level; 45266a63635SJoe Thornber l_add_tail(q->es, l, e); 45366a63635SJoe Thornber } 45466a63635SJoe Thornber 45566a63635SJoe Thornber /* 45666a63635SJoe Thornber * Push some entries up. 45766a63635SJoe Thornber */ 45866a63635SJoe Thornber l_above = q->qs + level + 1u; 45966a63635SJoe Thornber while (l->nr_elts > target) { 46066a63635SJoe Thornber e = l_pop_tail(q->es, l); 46166a63635SJoe Thornber 46266a63635SJoe Thornber if (!e) 46366a63635SJoe Thornber /* bug in nr_elts */ 46466a63635SJoe Thornber break; 46566a63635SJoe Thornber 46666a63635SJoe Thornber e->level = level + 1u; 467b29d4986SJoe Thornber l_add_tail(q->es, l_above, e); 46866a63635SJoe Thornber } 46966a63635SJoe Thornber } 47066a63635SJoe Thornber } 47166a63635SJoe Thornber 47286a3238cSHeinz Mauelshagen static void q_requeue(struct queue *q, struct entry *e, unsigned int extra_levels, 473b29d4986SJoe Thornber struct entry *s1, struct entry *s2) 47466a63635SJoe Thornber { 47566a63635SJoe Thornber struct entry *de; 47686a3238cSHeinz Mauelshagen unsigned int sentinels_passed = 0; 47786a3238cSHeinz Mauelshagen unsigned int new_level = min(q->nr_levels - 1u, e->level + extra_levels); 47866a63635SJoe Thornber 479b29d4986SJoe Thornber /* try and find an entry to swap with */ 48066a63635SJoe Thornber if (extra_levels && (e->level < q->nr_levels - 1u)) { 481b29d4986SJoe Thornber for (de = l_head(q->es, q->qs + new_level); de && de->sentinel; de = l_next(q->es, de)) 482b29d4986SJoe Thornber sentinels_passed++; 48366a63635SJoe Thornber 484b29d4986SJoe Thornber if (de) { 48566a63635SJoe Thornber q_del(q, de); 48666a63635SJoe Thornber de->level = e->level; 487b29d4986SJoe Thornber if (s1) { 488b29d4986SJoe Thornber switch (sentinels_passed) { 489b29d4986SJoe Thornber case 0: 490b29d4986SJoe Thornber q_push_before(q, s1, de); 49166a63635SJoe Thornber break; 492b29d4986SJoe Thornber 493b29d4986SJoe Thornber case 1: 494b29d4986SJoe Thornber q_push_before(q, s2, de); 495b29d4986SJoe Thornber break; 496b29d4986SJoe Thornber 497b29d4986SJoe Thornber default: 498b29d4986SJoe Thornber q_push(q, de); 499b29d4986SJoe Thornber } 500b29d4986SJoe Thornber } else 501b29d4986SJoe Thornber q_push(q, de); 502b29d4986SJoe Thornber } 50366a63635SJoe Thornber } 50466a63635SJoe Thornber 505b29d4986SJoe Thornber q_del(q, e); 50666a63635SJoe Thornber e->level = new_level; 50766a63635SJoe Thornber q_push(q, e); 50866a63635SJoe Thornber } 50966a63635SJoe Thornber 51066a63635SJoe Thornber /*----------------------------------------------------------------*/ 51166a63635SJoe Thornber 51266a63635SJoe Thornber #define FP_SHIFT 8 51366a63635SJoe Thornber #define SIXTEENTH (1u << (FP_SHIFT - 4u)) 51466a63635SJoe Thornber #define EIGHTH (1u << (FP_SHIFT - 3u)) 51566a63635SJoe Thornber 51666a63635SJoe Thornber struct stats { 51786a3238cSHeinz Mauelshagen unsigned int hit_threshold; 51886a3238cSHeinz Mauelshagen unsigned int hits; 51986a3238cSHeinz Mauelshagen unsigned int misses; 52066a63635SJoe Thornber }; 52166a63635SJoe Thornber 52266a63635SJoe Thornber enum performance { 52366a63635SJoe Thornber Q_POOR, 52466a63635SJoe Thornber Q_FAIR, 52566a63635SJoe Thornber Q_WELL 52666a63635SJoe Thornber }; 52766a63635SJoe Thornber 52886a3238cSHeinz Mauelshagen static void stats_init(struct stats *s, unsigned int nr_levels) 52966a63635SJoe Thornber { 53066a63635SJoe Thornber s->hit_threshold = (nr_levels * 3u) / 4u; 53166a63635SJoe Thornber s->hits = 0u; 53266a63635SJoe Thornber s->misses = 0u; 53366a63635SJoe Thornber } 53466a63635SJoe Thornber 53566a63635SJoe Thornber static void stats_reset(struct stats *s) 53666a63635SJoe Thornber { 53766a63635SJoe Thornber s->hits = s->misses = 0u; 53866a63635SJoe Thornber } 53966a63635SJoe Thornber 54086a3238cSHeinz Mauelshagen static void stats_level_accessed(struct stats *s, unsigned int level) 54166a63635SJoe Thornber { 54266a63635SJoe Thornber if (level >= s->hit_threshold) 54366a63635SJoe Thornber s->hits++; 54466a63635SJoe Thornber else 54566a63635SJoe Thornber s->misses++; 54666a63635SJoe Thornber } 54766a63635SJoe Thornber 54866a63635SJoe Thornber static void stats_miss(struct stats *s) 54966a63635SJoe Thornber { 55066a63635SJoe Thornber s->misses++; 55166a63635SJoe Thornber } 55266a63635SJoe Thornber 55366a63635SJoe Thornber /* 55466a63635SJoe Thornber * There are times when we don't have any confidence in the hotspot queue. 55566a63635SJoe Thornber * Such as when a fresh cache is created and the blocks have been spread 55666a63635SJoe Thornber * out across the levels, or if an io load changes. We detect this by 55766a63635SJoe Thornber * seeing how often a lookup is in the top levels of the hotspot queue. 55866a63635SJoe Thornber */ 55966a63635SJoe Thornber static enum performance stats_assess(struct stats *s) 56066a63635SJoe Thornber { 56186a3238cSHeinz Mauelshagen unsigned int confidence = safe_div(s->hits << FP_SHIFT, s->hits + s->misses); 56266a63635SJoe Thornber 56366a63635SJoe Thornber if (confidence < SIXTEENTH) 56466a63635SJoe Thornber return Q_POOR; 56566a63635SJoe Thornber 56666a63635SJoe Thornber else if (confidence < EIGHTH) 56766a63635SJoe Thornber return Q_FAIR; 56866a63635SJoe Thornber 56966a63635SJoe Thornber else 57066a63635SJoe Thornber return Q_WELL; 57166a63635SJoe Thornber } 57266a63635SJoe Thornber 57366a63635SJoe Thornber /*----------------------------------------------------------------*/ 57466a63635SJoe Thornber 575b29d4986SJoe Thornber struct smq_hash_table { 57666a63635SJoe Thornber struct entry_space *es; 57766a63635SJoe Thornber unsigned long long hash_bits; 57886a3238cSHeinz Mauelshagen unsigned int *buckets; 57966a63635SJoe Thornber }; 58066a63635SJoe Thornber 58166a63635SJoe Thornber /* 58266a63635SJoe Thornber * All cache entries are stored in a chained hash table. To save space we 58366a63635SJoe Thornber * use indexing again, and only store indexes to the next entry. 58466a63635SJoe Thornber */ 58586a3238cSHeinz Mauelshagen static int h_init(struct smq_hash_table *ht, struct entry_space *es, unsigned int nr_entries) 58666a63635SJoe Thornber { 58786a3238cSHeinz Mauelshagen unsigned int i, nr_buckets; 58866a63635SJoe Thornber 58966a63635SJoe Thornber ht->es = es; 59066a63635SJoe Thornber nr_buckets = roundup_pow_of_two(max(nr_entries / 4u, 16u)); 591a3d939aeSMikulas Patocka ht->hash_bits = __ffs(nr_buckets); 59266a63635SJoe Thornber 59342bc47b3SKees Cook ht->buckets = vmalloc(array_size(nr_buckets, sizeof(*ht->buckets))); 59466a63635SJoe Thornber if (!ht->buckets) 59566a63635SJoe Thornber return -ENOMEM; 59666a63635SJoe Thornber 59766a63635SJoe Thornber for (i = 0; i < nr_buckets; i++) 59866a63635SJoe Thornber ht->buckets[i] = INDEXER_NULL; 59966a63635SJoe Thornber 60066a63635SJoe Thornber return 0; 60166a63635SJoe Thornber } 60266a63635SJoe Thornber 603b29d4986SJoe Thornber static void h_exit(struct smq_hash_table *ht) 60466a63635SJoe Thornber { 60566a63635SJoe Thornber vfree(ht->buckets); 60666a63635SJoe Thornber } 60766a63635SJoe Thornber 60886a3238cSHeinz Mauelshagen static struct entry *h_head(struct smq_hash_table *ht, unsigned int bucket) 60966a63635SJoe Thornber { 61066a63635SJoe Thornber return to_entry(ht->es, ht->buckets[bucket]); 61166a63635SJoe Thornber } 61266a63635SJoe Thornber 613b29d4986SJoe Thornber static struct entry *h_next(struct smq_hash_table *ht, struct entry *e) 61466a63635SJoe Thornber { 61566a63635SJoe Thornber return to_entry(ht->es, e->hash_next); 61666a63635SJoe Thornber } 61766a63635SJoe Thornber 61886a3238cSHeinz Mauelshagen static void __h_insert(struct smq_hash_table *ht, unsigned int bucket, struct entry *e) 61966a63635SJoe Thornber { 62066a63635SJoe Thornber e->hash_next = ht->buckets[bucket]; 62166a63635SJoe Thornber ht->buckets[bucket] = to_index(ht->es, e); 62266a63635SJoe Thornber } 62366a63635SJoe Thornber 624b29d4986SJoe Thornber static void h_insert(struct smq_hash_table *ht, struct entry *e) 62566a63635SJoe Thornber { 62686a3238cSHeinz Mauelshagen unsigned int h = hash_64(from_oblock(e->oblock), ht->hash_bits); 627*0ef0b471SHeinz Mauelshagen 62866a63635SJoe Thornber __h_insert(ht, h, e); 62966a63635SJoe Thornber } 63066a63635SJoe Thornber 63186a3238cSHeinz Mauelshagen static struct entry *__h_lookup(struct smq_hash_table *ht, unsigned int h, dm_oblock_t oblock, 63266a63635SJoe Thornber struct entry **prev) 63366a63635SJoe Thornber { 63466a63635SJoe Thornber struct entry *e; 63566a63635SJoe Thornber 63666a63635SJoe Thornber *prev = NULL; 63766a63635SJoe Thornber for (e = h_head(ht, h); e; e = h_next(ht, e)) { 63866a63635SJoe Thornber if (e->oblock == oblock) 63966a63635SJoe Thornber return e; 64066a63635SJoe Thornber 64166a63635SJoe Thornber *prev = e; 64266a63635SJoe Thornber } 64366a63635SJoe Thornber 64466a63635SJoe Thornber return NULL; 64566a63635SJoe Thornber } 64666a63635SJoe Thornber 64786a3238cSHeinz Mauelshagen static void __h_unlink(struct smq_hash_table *ht, unsigned int h, 64866a63635SJoe Thornber struct entry *e, struct entry *prev) 64966a63635SJoe Thornber { 65066a63635SJoe Thornber if (prev) 65166a63635SJoe Thornber prev->hash_next = e->hash_next; 65266a63635SJoe Thornber else 65366a63635SJoe Thornber ht->buckets[h] = e->hash_next; 65466a63635SJoe Thornber } 65566a63635SJoe Thornber 65666a63635SJoe Thornber /* 65766a63635SJoe Thornber * Also moves each entry to the front of the bucket. 65866a63635SJoe Thornber */ 659b29d4986SJoe Thornber static struct entry *h_lookup(struct smq_hash_table *ht, dm_oblock_t oblock) 66066a63635SJoe Thornber { 66166a63635SJoe Thornber struct entry *e, *prev; 66286a3238cSHeinz Mauelshagen unsigned int h = hash_64(from_oblock(oblock), ht->hash_bits); 66366a63635SJoe Thornber 66466a63635SJoe Thornber e = __h_lookup(ht, h, oblock, &prev); 66566a63635SJoe Thornber if (e && prev) { 66666a63635SJoe Thornber /* 66766a63635SJoe Thornber * Move to the front because this entry is likely 66866a63635SJoe Thornber * to be hit again. 66966a63635SJoe Thornber */ 67066a63635SJoe Thornber __h_unlink(ht, h, e, prev); 67166a63635SJoe Thornber __h_insert(ht, h, e); 67266a63635SJoe Thornber } 67366a63635SJoe Thornber 67466a63635SJoe Thornber return e; 67566a63635SJoe Thornber } 67666a63635SJoe Thornber 677b29d4986SJoe Thornber static void h_remove(struct smq_hash_table *ht, struct entry *e) 67866a63635SJoe Thornber { 67986a3238cSHeinz Mauelshagen unsigned int h = hash_64(from_oblock(e->oblock), ht->hash_bits); 68066a63635SJoe Thornber struct entry *prev; 68166a63635SJoe Thornber 68266a63635SJoe Thornber /* 68366a63635SJoe Thornber * The down side of using a singly linked list is we have to 68466a63635SJoe Thornber * iterate the bucket to remove an item. 68566a63635SJoe Thornber */ 68666a63635SJoe Thornber e = __h_lookup(ht, h, e->oblock, &prev); 68766a63635SJoe Thornber if (e) 68866a63635SJoe Thornber __h_unlink(ht, h, e, prev); 68966a63635SJoe Thornber } 69066a63635SJoe Thornber 69166a63635SJoe Thornber /*----------------------------------------------------------------*/ 69266a63635SJoe Thornber 69366a63635SJoe Thornber struct entry_alloc { 69466a63635SJoe Thornber struct entry_space *es; 69586a3238cSHeinz Mauelshagen unsigned int begin; 69666a63635SJoe Thornber 69786a3238cSHeinz Mauelshagen unsigned int nr_allocated; 69866a63635SJoe Thornber struct ilist free; 69966a63635SJoe Thornber }; 70066a63635SJoe Thornber 70166a63635SJoe Thornber static void init_allocator(struct entry_alloc *ea, struct entry_space *es, 70286a3238cSHeinz Mauelshagen unsigned int begin, unsigned int end) 70366a63635SJoe Thornber { 70486a3238cSHeinz Mauelshagen unsigned int i; 70566a63635SJoe Thornber 70666a63635SJoe Thornber ea->es = es; 70766a63635SJoe Thornber ea->nr_allocated = 0u; 70866a63635SJoe Thornber ea->begin = begin; 70966a63635SJoe Thornber 71066a63635SJoe Thornber l_init(&ea->free); 71166a63635SJoe Thornber for (i = begin; i != end; i++) 71266a63635SJoe Thornber l_add_tail(ea->es, &ea->free, __get_entry(ea->es, i)); 71366a63635SJoe Thornber } 71466a63635SJoe Thornber 71566a63635SJoe Thornber static void init_entry(struct entry *e) 71666a63635SJoe Thornber { 71766a63635SJoe Thornber /* 71866a63635SJoe Thornber * We can't memset because that would clear the hotspot and 71966a63635SJoe Thornber * sentinel bits which remain constant. 72066a63635SJoe Thornber */ 72166a63635SJoe Thornber e->hash_next = INDEXER_NULL; 72266a63635SJoe Thornber e->next = INDEXER_NULL; 72366a63635SJoe Thornber e->prev = INDEXER_NULL; 72466a63635SJoe Thornber e->level = 0u; 725b29d4986SJoe Thornber e->dirty = true; /* FIXME: audit */ 72666a63635SJoe Thornber e->allocated = true; 727b29d4986SJoe Thornber e->sentinel = false; 728b29d4986SJoe Thornber e->pending_work = false; 72966a63635SJoe Thornber } 73066a63635SJoe Thornber 73166a63635SJoe Thornber static struct entry *alloc_entry(struct entry_alloc *ea) 73266a63635SJoe Thornber { 73366a63635SJoe Thornber struct entry *e; 73466a63635SJoe Thornber 73566a63635SJoe Thornber if (l_empty(&ea->free)) 73666a63635SJoe Thornber return NULL; 73766a63635SJoe Thornber 7389768a10dSJoe Thornber e = l_pop_head(ea->es, &ea->free); 73966a63635SJoe Thornber init_entry(e); 74066a63635SJoe Thornber ea->nr_allocated++; 74166a63635SJoe Thornber 74266a63635SJoe Thornber return e; 74366a63635SJoe Thornber } 74466a63635SJoe Thornber 74566a63635SJoe Thornber /* 74666a63635SJoe Thornber * This assumes the cblock hasn't already been allocated. 74766a63635SJoe Thornber */ 74886a3238cSHeinz Mauelshagen static struct entry *alloc_particular_entry(struct entry_alloc *ea, unsigned int i) 74966a63635SJoe Thornber { 75066a63635SJoe Thornber struct entry *e = __get_entry(ea->es, ea->begin + i); 75166a63635SJoe Thornber 75266a63635SJoe Thornber BUG_ON(e->allocated); 75366a63635SJoe Thornber 75466a63635SJoe Thornber l_del(ea->es, &ea->free, e); 75566a63635SJoe Thornber init_entry(e); 75666a63635SJoe Thornber ea->nr_allocated++; 75766a63635SJoe Thornber 75866a63635SJoe Thornber return e; 75966a63635SJoe Thornber } 76066a63635SJoe Thornber 76166a63635SJoe Thornber static void free_entry(struct entry_alloc *ea, struct entry *e) 76266a63635SJoe Thornber { 76366a63635SJoe Thornber BUG_ON(!ea->nr_allocated); 76466a63635SJoe Thornber BUG_ON(!e->allocated); 76566a63635SJoe Thornber 76666a63635SJoe Thornber ea->nr_allocated--; 76766a63635SJoe Thornber e->allocated = false; 76866a63635SJoe Thornber l_add_tail(ea->es, &ea->free, e); 76966a63635SJoe Thornber } 77066a63635SJoe Thornber 77166a63635SJoe Thornber static bool allocator_empty(struct entry_alloc *ea) 77266a63635SJoe Thornber { 77366a63635SJoe Thornber return l_empty(&ea->free); 77466a63635SJoe Thornber } 77566a63635SJoe Thornber 77686a3238cSHeinz Mauelshagen static unsigned int get_index(struct entry_alloc *ea, struct entry *e) 77766a63635SJoe Thornber { 77866a63635SJoe Thornber return to_index(ea->es, e) - ea->begin; 77966a63635SJoe Thornber } 78066a63635SJoe Thornber 78186a3238cSHeinz Mauelshagen static struct entry *get_entry(struct entry_alloc *ea, unsigned int index) 78266a63635SJoe Thornber { 78366a63635SJoe Thornber return __get_entry(ea->es, ea->begin + index); 78466a63635SJoe Thornber } 78566a63635SJoe Thornber 78666a63635SJoe Thornber /*----------------------------------------------------------------*/ 78766a63635SJoe Thornber 78866a63635SJoe Thornber #define NR_HOTSPOT_LEVELS 64u 78966a63635SJoe Thornber #define NR_CACHE_LEVELS 64u 79066a63635SJoe Thornber 791b29d4986SJoe Thornber #define WRITEBACK_PERIOD (10ul * HZ) 792b29d4986SJoe Thornber #define DEMOTE_PERIOD (60ul * HZ) 79366a63635SJoe Thornber 79466a63635SJoe Thornber #define HOTSPOT_UPDATE_PERIOD (HZ) 795b29d4986SJoe Thornber #define CACHE_UPDATE_PERIOD (60ul * HZ) 79666a63635SJoe Thornber 79766a63635SJoe Thornber struct smq_policy { 79866a63635SJoe Thornber struct dm_cache_policy policy; 79966a63635SJoe Thornber 80066a63635SJoe Thornber /* protects everything */ 8014051aab7SJoe Thornber spinlock_t lock; 80266a63635SJoe Thornber dm_cblock_t cache_size; 80366a63635SJoe Thornber sector_t cache_block_size; 80466a63635SJoe Thornber 80566a63635SJoe Thornber sector_t hotspot_block_size; 80686a3238cSHeinz Mauelshagen unsigned int nr_hotspot_blocks; 80786a3238cSHeinz Mauelshagen unsigned int cache_blocks_per_hotspot_block; 80886a3238cSHeinz Mauelshagen unsigned int hotspot_level_jump; 80966a63635SJoe Thornber 81066a63635SJoe Thornber struct entry_space es; 81166a63635SJoe Thornber struct entry_alloc writeback_sentinel_alloc; 81266a63635SJoe Thornber struct entry_alloc demote_sentinel_alloc; 81366a63635SJoe Thornber struct entry_alloc hotspot_alloc; 81466a63635SJoe Thornber struct entry_alloc cache_alloc; 81566a63635SJoe Thornber 81666a63635SJoe Thornber unsigned long *hotspot_hit_bits; 81766a63635SJoe Thornber unsigned long *cache_hit_bits; 81866a63635SJoe Thornber 81966a63635SJoe Thornber /* 82066a63635SJoe Thornber * We maintain three queues of entries. The cache proper, 82166a63635SJoe Thornber * consisting of a clean and dirty queue, containing the currently 82266a63635SJoe Thornber * active mappings. The hotspot queue uses a larger block size to 82366a63635SJoe Thornber * track blocks that are being hit frequently and potential 82466a63635SJoe Thornber * candidates for promotion to the cache. 82566a63635SJoe Thornber */ 82666a63635SJoe Thornber struct queue hotspot; 82766a63635SJoe Thornber struct queue clean; 82866a63635SJoe Thornber struct queue dirty; 82966a63635SJoe Thornber 83066a63635SJoe Thornber struct stats hotspot_stats; 83166a63635SJoe Thornber struct stats cache_stats; 83266a63635SJoe Thornber 83366a63635SJoe Thornber /* 83466a63635SJoe Thornber * Keeps track of time, incremented by the core. We use this to 83566a63635SJoe Thornber * avoid attributing multiple hits within the same tick. 83666a63635SJoe Thornber */ 83786a3238cSHeinz Mauelshagen unsigned int tick; 83866a63635SJoe Thornber 83966a63635SJoe Thornber /* 84066a63635SJoe Thornber * The hash tables allows us to quickly find an entry by origin 84166a63635SJoe Thornber * block. 84266a63635SJoe Thornber */ 843b29d4986SJoe Thornber struct smq_hash_table table; 844b29d4986SJoe Thornber struct smq_hash_table hotspot_table; 84566a63635SJoe Thornber 84666a63635SJoe Thornber bool current_writeback_sentinels; 84766a63635SJoe Thornber unsigned long next_writeback_period; 84866a63635SJoe Thornber 84966a63635SJoe Thornber bool current_demote_sentinels; 85066a63635SJoe Thornber unsigned long next_demote_period; 85166a63635SJoe Thornber 85286a3238cSHeinz Mauelshagen unsigned int write_promote_level; 85386a3238cSHeinz Mauelshagen unsigned int read_promote_level; 85466a63635SJoe Thornber 85566a63635SJoe Thornber unsigned long next_hotspot_period; 85666a63635SJoe Thornber unsigned long next_cache_period; 857b29d4986SJoe Thornber 858b29d4986SJoe Thornber struct background_tracker *bg_work; 859b29d4986SJoe Thornber 860b29d4986SJoe Thornber bool migrations_allowed; 86166a63635SJoe Thornber }; 86266a63635SJoe Thornber 86366a63635SJoe Thornber /*----------------------------------------------------------------*/ 86466a63635SJoe Thornber 86586a3238cSHeinz Mauelshagen static struct entry *get_sentinel(struct entry_alloc *ea, unsigned int level, bool which) 86666a63635SJoe Thornber { 86766a63635SJoe Thornber return get_entry(ea, which ? level : NR_CACHE_LEVELS + level); 86866a63635SJoe Thornber } 86966a63635SJoe Thornber 87086a3238cSHeinz Mauelshagen static struct entry *writeback_sentinel(struct smq_policy *mq, unsigned int level) 87166a63635SJoe Thornber { 87266a63635SJoe Thornber return get_sentinel(&mq->writeback_sentinel_alloc, level, mq->current_writeback_sentinels); 87366a63635SJoe Thornber } 87466a63635SJoe Thornber 87586a3238cSHeinz Mauelshagen static struct entry *demote_sentinel(struct smq_policy *mq, unsigned int level) 87666a63635SJoe Thornber { 87766a63635SJoe Thornber return get_sentinel(&mq->demote_sentinel_alloc, level, mq->current_demote_sentinels); 87866a63635SJoe Thornber } 87966a63635SJoe Thornber 88066a63635SJoe Thornber static void __update_writeback_sentinels(struct smq_policy *mq) 88166a63635SJoe Thornber { 88286a3238cSHeinz Mauelshagen unsigned int level; 88366a63635SJoe Thornber struct queue *q = &mq->dirty; 88466a63635SJoe Thornber struct entry *sentinel; 88566a63635SJoe Thornber 88666a63635SJoe Thornber for (level = 0; level < q->nr_levels; level++) { 88766a63635SJoe Thornber sentinel = writeback_sentinel(mq, level); 88866a63635SJoe Thornber q_del(q, sentinel); 88966a63635SJoe Thornber q_push(q, sentinel); 89066a63635SJoe Thornber } 89166a63635SJoe Thornber } 89266a63635SJoe Thornber 89366a63635SJoe Thornber static void __update_demote_sentinels(struct smq_policy *mq) 89466a63635SJoe Thornber { 89586a3238cSHeinz Mauelshagen unsigned int level; 89666a63635SJoe Thornber struct queue *q = &mq->clean; 89766a63635SJoe Thornber struct entry *sentinel; 89866a63635SJoe Thornber 89966a63635SJoe Thornber for (level = 0; level < q->nr_levels; level++) { 90066a63635SJoe Thornber sentinel = demote_sentinel(mq, level); 90166a63635SJoe Thornber q_del(q, sentinel); 90266a63635SJoe Thornber q_push(q, sentinel); 90366a63635SJoe Thornber } 90466a63635SJoe Thornber } 90566a63635SJoe Thornber 90666a63635SJoe Thornber static void update_sentinels(struct smq_policy *mq) 90766a63635SJoe Thornber { 90866a63635SJoe Thornber if (time_after(jiffies, mq->next_writeback_period)) { 90966a63635SJoe Thornber mq->next_writeback_period = jiffies + WRITEBACK_PERIOD; 91066a63635SJoe Thornber mq->current_writeback_sentinels = !mq->current_writeback_sentinels; 911b29d4986SJoe Thornber __update_writeback_sentinels(mq); 91266a63635SJoe Thornber } 91366a63635SJoe Thornber 91466a63635SJoe Thornber if (time_after(jiffies, mq->next_demote_period)) { 91566a63635SJoe Thornber mq->next_demote_period = jiffies + DEMOTE_PERIOD; 91666a63635SJoe Thornber mq->current_demote_sentinels = !mq->current_demote_sentinels; 917b29d4986SJoe Thornber __update_demote_sentinels(mq); 91866a63635SJoe Thornber } 91966a63635SJoe Thornber } 92066a63635SJoe Thornber 92166a63635SJoe Thornber static void __sentinels_init(struct smq_policy *mq) 92266a63635SJoe Thornber { 92386a3238cSHeinz Mauelshagen unsigned int level; 92466a63635SJoe Thornber struct entry *sentinel; 92566a63635SJoe Thornber 92666a63635SJoe Thornber for (level = 0; level < NR_CACHE_LEVELS; level++) { 92766a63635SJoe Thornber sentinel = writeback_sentinel(mq, level); 92866a63635SJoe Thornber sentinel->level = level; 92966a63635SJoe Thornber q_push(&mq->dirty, sentinel); 93066a63635SJoe Thornber 93166a63635SJoe Thornber sentinel = demote_sentinel(mq, level); 93266a63635SJoe Thornber sentinel->level = level; 93366a63635SJoe Thornber q_push(&mq->clean, sentinel); 93466a63635SJoe Thornber } 93566a63635SJoe Thornber } 93666a63635SJoe Thornber 93766a63635SJoe Thornber static void sentinels_init(struct smq_policy *mq) 93866a63635SJoe Thornber { 93966a63635SJoe Thornber mq->next_writeback_period = jiffies + WRITEBACK_PERIOD; 94066a63635SJoe Thornber mq->next_demote_period = jiffies + DEMOTE_PERIOD; 94166a63635SJoe Thornber 94266a63635SJoe Thornber mq->current_writeback_sentinels = false; 94366a63635SJoe Thornber mq->current_demote_sentinels = false; 94466a63635SJoe Thornber __sentinels_init(mq); 94566a63635SJoe Thornber 94666a63635SJoe Thornber mq->current_writeback_sentinels = !mq->current_writeback_sentinels; 94766a63635SJoe Thornber mq->current_demote_sentinels = !mq->current_demote_sentinels; 94866a63635SJoe Thornber __sentinels_init(mq); 94966a63635SJoe Thornber } 95066a63635SJoe Thornber 95166a63635SJoe Thornber /*----------------------------------------------------------------*/ 95266a63635SJoe Thornber 953b29d4986SJoe Thornber static void del_queue(struct smq_policy *mq, struct entry *e) 95466a63635SJoe Thornber { 955b29d4986SJoe Thornber q_del(e->dirty ? &mq->dirty : &mq->clean, e); 95666a63635SJoe Thornber } 95766a63635SJoe Thornber 958b29d4986SJoe Thornber static void push_queue(struct smq_policy *mq, struct entry *e) 959b29d4986SJoe Thornber { 960b29d4986SJoe Thornber if (e->dirty) 961b29d4986SJoe Thornber q_push(&mq->dirty, e); 962b29d4986SJoe Thornber else 963b29d4986SJoe Thornber q_push(&mq->clean, e); 964b29d4986SJoe Thornber } 965b29d4986SJoe Thornber 966b29d4986SJoe Thornber // !h, !q, a -> h, q, a 96766a63635SJoe Thornber static void push(struct smq_policy *mq, struct entry *e) 96866a63635SJoe Thornber { 96966a63635SJoe Thornber h_insert(&mq->table, e); 970b29d4986SJoe Thornber if (!e->pending_work) 971b29d4986SJoe Thornber push_queue(mq, e); 97266a63635SJoe Thornber } 97366a63635SJoe Thornber 974b29d4986SJoe Thornber static void push_queue_front(struct smq_policy *mq, struct entry *e) 97566a63635SJoe Thornber { 976b29d4986SJoe Thornber if (e->dirty) 977b29d4986SJoe Thornber q_push_front(&mq->dirty, e); 978b29d4986SJoe Thornber else 979b29d4986SJoe Thornber q_push_front(&mq->clean, e); 98066a63635SJoe Thornber } 98166a63635SJoe Thornber 982b29d4986SJoe Thornber static void push_front(struct smq_policy *mq, struct entry *e) 98366a63635SJoe Thornber { 984b29d4986SJoe Thornber h_insert(&mq->table, e); 985b29d4986SJoe Thornber if (!e->pending_work) 986b29d4986SJoe Thornber push_queue_front(mq, e); 98766a63635SJoe Thornber } 98866a63635SJoe Thornber 98966a63635SJoe Thornber static dm_cblock_t infer_cblock(struct smq_policy *mq, struct entry *e) 99066a63635SJoe Thornber { 99166a63635SJoe Thornber return to_cblock(get_index(&mq->cache_alloc, e)); 99266a63635SJoe Thornber } 99366a63635SJoe Thornber 99466a63635SJoe Thornber static void requeue(struct smq_policy *mq, struct entry *e) 99566a63635SJoe Thornber { 996b29d4986SJoe Thornber /* 997b29d4986SJoe Thornber * Pending work has temporarily been taken out of the queues. 998b29d4986SJoe Thornber */ 999b29d4986SJoe Thornber if (e->pending_work) 1000b29d4986SJoe Thornber return; 100166a63635SJoe Thornber 100266a63635SJoe Thornber if (!test_and_set_bit(from_cblock(infer_cblock(mq, e)), mq->cache_hit_bits)) { 1003b29d4986SJoe Thornber if (!e->dirty) { 1004b29d4986SJoe Thornber q_requeue(&mq->clean, e, 1u, NULL, NULL); 1005b29d4986SJoe Thornber return; 100666a63635SJoe Thornber } 1007b29d4986SJoe Thornber 1008b29d4986SJoe Thornber q_requeue(&mq->dirty, e, 1u, 1009b29d4986SJoe Thornber get_sentinel(&mq->writeback_sentinel_alloc, e->level, !mq->current_writeback_sentinels), 1010b29d4986SJoe Thornber get_sentinel(&mq->writeback_sentinel_alloc, e->level, mq->current_writeback_sentinels)); 101166a63635SJoe Thornber } 101266a63635SJoe Thornber } 101366a63635SJoe Thornber 101486a3238cSHeinz Mauelshagen static unsigned int default_promote_level(struct smq_policy *mq) 101566a63635SJoe Thornber { 101666a63635SJoe Thornber /* 101766a63635SJoe Thornber * The promote level depends on the current performance of the 101866a63635SJoe Thornber * cache. 101966a63635SJoe Thornber * 102066a63635SJoe Thornber * If the cache is performing badly, then we can't afford 102166a63635SJoe Thornber * to promote much without causing performance to drop below that 102266a63635SJoe Thornber * of the origin device. 102366a63635SJoe Thornber * 102466a63635SJoe Thornber * If the cache is performing well, then we don't need to promote 102566a63635SJoe Thornber * much. If it isn't broken, don't fix it. 102666a63635SJoe Thornber * 102766a63635SJoe Thornber * If the cache is middling then we promote more. 102866a63635SJoe Thornber * 102966a63635SJoe Thornber * This scheme reminds me of a graph of entropy vs probability of a 103066a63635SJoe Thornber * binary variable. 103166a63635SJoe Thornber */ 1032302f0351SColin Ian King static const unsigned int table[] = { 1033302f0351SColin Ian King 1, 1, 1, 2, 4, 6, 7, 8, 7, 6, 4, 4, 3, 3, 2, 2, 1 1034302f0351SColin Ian King }; 103566a63635SJoe Thornber 103686a3238cSHeinz Mauelshagen unsigned int hits = mq->cache_stats.hits; 103786a3238cSHeinz Mauelshagen unsigned int misses = mq->cache_stats.misses; 103886a3238cSHeinz Mauelshagen unsigned int index = safe_div(hits << 4u, hits + misses); 103966a63635SJoe Thornber return table[index]; 104066a63635SJoe Thornber } 104166a63635SJoe Thornber 104266a63635SJoe Thornber static void update_promote_levels(struct smq_policy *mq) 104366a63635SJoe Thornber { 104466a63635SJoe Thornber /* 104566a63635SJoe Thornber * If there are unused cache entries then we want to be really 104666a63635SJoe Thornber * eager to promote. 104766a63635SJoe Thornber */ 104886a3238cSHeinz Mauelshagen unsigned int threshold_level = allocator_empty(&mq->cache_alloc) ? 104966a63635SJoe Thornber default_promote_level(mq) : (NR_HOTSPOT_LEVELS / 2u); 105066a63635SJoe Thornber 1051b29d4986SJoe Thornber threshold_level = max(threshold_level, NR_HOTSPOT_LEVELS); 1052b29d4986SJoe Thornber 105366a63635SJoe Thornber /* 105466a63635SJoe Thornber * If the hotspot queue is performing badly then we have little 105566a63635SJoe Thornber * confidence that we know which blocks to promote. So we cut down 105666a63635SJoe Thornber * the amount of promotions. 105766a63635SJoe Thornber */ 105866a63635SJoe Thornber switch (stats_assess(&mq->hotspot_stats)) { 105966a63635SJoe Thornber case Q_POOR: 106066a63635SJoe Thornber threshold_level /= 4u; 106166a63635SJoe Thornber break; 106266a63635SJoe Thornber 106366a63635SJoe Thornber case Q_FAIR: 106466a63635SJoe Thornber threshold_level /= 2u; 106566a63635SJoe Thornber break; 106666a63635SJoe Thornber 106766a63635SJoe Thornber case Q_WELL: 106866a63635SJoe Thornber break; 106966a63635SJoe Thornber } 107066a63635SJoe Thornber 107166a63635SJoe Thornber mq->read_promote_level = NR_HOTSPOT_LEVELS - threshold_level; 1072b29d4986SJoe Thornber mq->write_promote_level = (NR_HOTSPOT_LEVELS - threshold_level); 107366a63635SJoe Thornber } 107466a63635SJoe Thornber 107566a63635SJoe Thornber /* 107666a63635SJoe Thornber * If the hotspot queue is performing badly, then we try and move entries 107766a63635SJoe Thornber * around more quickly. 107866a63635SJoe Thornber */ 107966a63635SJoe Thornber static void update_level_jump(struct smq_policy *mq) 108066a63635SJoe Thornber { 108166a63635SJoe Thornber switch (stats_assess(&mq->hotspot_stats)) { 108266a63635SJoe Thornber case Q_POOR: 108366a63635SJoe Thornber mq->hotspot_level_jump = 4u; 108466a63635SJoe Thornber break; 108566a63635SJoe Thornber 108666a63635SJoe Thornber case Q_FAIR: 108766a63635SJoe Thornber mq->hotspot_level_jump = 2u; 108866a63635SJoe Thornber break; 108966a63635SJoe Thornber 109066a63635SJoe Thornber case Q_WELL: 109166a63635SJoe Thornber mq->hotspot_level_jump = 1u; 109266a63635SJoe Thornber break; 109366a63635SJoe Thornber } 109466a63635SJoe Thornber } 109566a63635SJoe Thornber 109666a63635SJoe Thornber static void end_hotspot_period(struct smq_policy *mq) 109766a63635SJoe Thornber { 109866a63635SJoe Thornber clear_bitset(mq->hotspot_hit_bits, mq->nr_hotspot_blocks); 109966a63635SJoe Thornber update_promote_levels(mq); 110066a63635SJoe Thornber 110166a63635SJoe Thornber if (time_after(jiffies, mq->next_hotspot_period)) { 110266a63635SJoe Thornber update_level_jump(mq); 110366a63635SJoe Thornber q_redistribute(&mq->hotspot); 110466a63635SJoe Thornber stats_reset(&mq->hotspot_stats); 110566a63635SJoe Thornber mq->next_hotspot_period = jiffies + HOTSPOT_UPDATE_PERIOD; 110666a63635SJoe Thornber } 110766a63635SJoe Thornber } 110866a63635SJoe Thornber 110966a63635SJoe Thornber static void end_cache_period(struct smq_policy *mq) 111066a63635SJoe Thornber { 111166a63635SJoe Thornber if (time_after(jiffies, mq->next_cache_period)) { 111266a63635SJoe Thornber clear_bitset(mq->cache_hit_bits, from_cblock(mq->cache_size)); 111366a63635SJoe Thornber 111466a63635SJoe Thornber q_redistribute(&mq->dirty); 111566a63635SJoe Thornber q_redistribute(&mq->clean); 111666a63635SJoe Thornber stats_reset(&mq->cache_stats); 111766a63635SJoe Thornber 111866a63635SJoe Thornber mq->next_cache_period = jiffies + CACHE_UPDATE_PERIOD; 111966a63635SJoe Thornber } 112066a63635SJoe Thornber } 112166a63635SJoe Thornber 1122b29d4986SJoe Thornber /*----------------------------------------------------------------*/ 1123b29d4986SJoe Thornber 1124b29d4986SJoe Thornber /* 1125b29d4986SJoe Thornber * Targets are given as a percentage. 1126b29d4986SJoe Thornber */ 1127b29d4986SJoe Thornber #define CLEAN_TARGET 25u 1128b29d4986SJoe Thornber #define FREE_TARGET 25u 1129b29d4986SJoe Thornber 113086a3238cSHeinz Mauelshagen static unsigned int percent_to_target(struct smq_policy *mq, unsigned int p) 113166a63635SJoe Thornber { 1132b29d4986SJoe Thornber return from_cblock(mq->cache_size) * p / 100u; 113366a63635SJoe Thornber } 113466a63635SJoe Thornber 1135b29d4986SJoe Thornber static bool clean_target_met(struct smq_policy *mq, bool idle) 1136b29d4986SJoe Thornber { 1137b29d4986SJoe Thornber /* 1138b29d4986SJoe Thornber * Cache entries may not be populated. So we cannot rely on the 1139b29d4986SJoe Thornber * size of the clean queue. 1140b29d4986SJoe Thornber */ 114197dfb203SMike Snitzer if (idle) { 1142b29d4986SJoe Thornber /* 1143b29d4986SJoe Thornber * We'd like to clean everything. 1144b29d4986SJoe Thornber */ 1145b29d4986SJoe Thornber return q_size(&mq->dirty) == 0u; 114697dfb203SMike Snitzer } 114797dfb203SMike Snitzer 11482e633095SJoe Thornber /* 11492e633095SJoe Thornber * If we're busy we don't worry about cleaning at all. 11502e633095SJoe Thornber */ 11512e633095SJoe Thornber return true; 1152b29d4986SJoe Thornber } 1153b29d4986SJoe Thornber 11546cf4cc8fSJoe Thornber static bool free_target_met(struct smq_policy *mq) 1155b29d4986SJoe Thornber { 115686a3238cSHeinz Mauelshagen unsigned int nr_free; 1157b29d4986SJoe Thornber 115897dfb203SMike Snitzer nr_free = from_cblock(mq->cache_size) - mq->cache_alloc.nr_allocated; 1159b29d4986SJoe Thornber return (nr_free + btracker_nr_demotions_queued(mq->bg_work)) >= 1160b29d4986SJoe Thornber percent_to_target(mq, FREE_TARGET); 1161b29d4986SJoe Thornber } 1162b29d4986SJoe Thornber 1163b29d4986SJoe Thornber /*----------------------------------------------------------------*/ 1164b29d4986SJoe Thornber 1165b29d4986SJoe Thornber static void mark_pending(struct smq_policy *mq, struct entry *e) 1166b29d4986SJoe Thornber { 1167b29d4986SJoe Thornber BUG_ON(e->sentinel); 1168b29d4986SJoe Thornber BUG_ON(!e->allocated); 1169b29d4986SJoe Thornber BUG_ON(e->pending_work); 1170b29d4986SJoe Thornber e->pending_work = true; 1171b29d4986SJoe Thornber } 1172b29d4986SJoe Thornber 1173b29d4986SJoe Thornber static void clear_pending(struct smq_policy *mq, struct entry *e) 1174b29d4986SJoe Thornber { 1175b29d4986SJoe Thornber BUG_ON(!e->pending_work); 1176b29d4986SJoe Thornber e->pending_work = false; 1177b29d4986SJoe Thornber } 1178b29d4986SJoe Thornber 1179deb71918SJoe Thornber static void queue_writeback(struct smq_policy *mq, bool idle) 1180b29d4986SJoe Thornber { 1181b29d4986SJoe Thornber int r; 1182b29d4986SJoe Thornber struct policy_work work; 1183b29d4986SJoe Thornber struct entry *e; 1184b29d4986SJoe Thornber 1185deb71918SJoe Thornber e = q_peek(&mq->dirty, mq->dirty.nr_levels, idle); 1186b29d4986SJoe Thornber if (e) { 1187b29d4986SJoe Thornber mark_pending(mq, e); 1188b29d4986SJoe Thornber q_del(&mq->dirty, e); 1189b29d4986SJoe Thornber 1190b29d4986SJoe Thornber work.op = POLICY_WRITEBACK; 1191b29d4986SJoe Thornber work.oblock = e->oblock; 1192b29d4986SJoe Thornber work.cblock = infer_cblock(mq, e); 1193b29d4986SJoe Thornber 1194b29d4986SJoe Thornber r = btracker_queue(mq->bg_work, &work, NULL); 11951e72a8e8SJoe Thornber if (r) { 11961e72a8e8SJoe Thornber clear_pending(mq, e); 11971e72a8e8SJoe Thornber q_push_front(&mq->dirty, e); 11981e72a8e8SJoe Thornber } 1199b29d4986SJoe Thornber } 1200b29d4986SJoe Thornber } 1201b29d4986SJoe Thornber 1202b29d4986SJoe Thornber static void queue_demotion(struct smq_policy *mq) 1203b29d4986SJoe Thornber { 12041e72a8e8SJoe Thornber int r; 1205b29d4986SJoe Thornber struct policy_work work; 1206b29d4986SJoe Thornber struct entry *e; 1207b29d4986SJoe Thornber 1208bab5d988SIgor Stoppa if (WARN_ON_ONCE(!mq->migrations_allowed)) 1209b29d4986SJoe Thornber return; 1210b29d4986SJoe Thornber 1211a8cd1ebaSJoe Thornber e = q_peek(&mq->clean, mq->clean.nr_levels / 2, true); 1212b29d4986SJoe Thornber if (!e) { 121378c45607SJoe Thornber if (!clean_target_met(mq, true)) 1214deb71918SJoe Thornber queue_writeback(mq, false); 1215b29d4986SJoe Thornber return; 1216b29d4986SJoe Thornber } 1217b29d4986SJoe Thornber 1218b29d4986SJoe Thornber mark_pending(mq, e); 1219b29d4986SJoe Thornber q_del(&mq->clean, e); 1220b29d4986SJoe Thornber 1221b29d4986SJoe Thornber work.op = POLICY_DEMOTE; 1222b29d4986SJoe Thornber work.oblock = e->oblock; 1223b29d4986SJoe Thornber work.cblock = infer_cblock(mq, e); 12241e72a8e8SJoe Thornber r = btracker_queue(mq->bg_work, &work, NULL); 12251e72a8e8SJoe Thornber if (r) { 12261e72a8e8SJoe Thornber clear_pending(mq, e); 12271e72a8e8SJoe Thornber q_push_front(&mq->clean, e); 12281e72a8e8SJoe Thornber } 1229b29d4986SJoe Thornber } 1230b29d4986SJoe Thornber 1231b29d4986SJoe Thornber static void queue_promotion(struct smq_policy *mq, dm_oblock_t oblock, 1232b29d4986SJoe Thornber struct policy_work **workp) 1233b29d4986SJoe Thornber { 12341e72a8e8SJoe Thornber int r; 1235b29d4986SJoe Thornber struct entry *e; 1236b29d4986SJoe Thornber struct policy_work work; 1237b29d4986SJoe Thornber 1238b29d4986SJoe Thornber if (!mq->migrations_allowed) 1239b29d4986SJoe Thornber return; 1240b29d4986SJoe Thornber 1241b29d4986SJoe Thornber if (allocator_empty(&mq->cache_alloc)) { 1242ce1d64e8SJoe Thornber /* 1243ce1d64e8SJoe Thornber * We always claim to be 'idle' to ensure some demotions happen 1244ce1d64e8SJoe Thornber * with continuous loads. 1245ce1d64e8SJoe Thornber */ 12466cf4cc8fSJoe Thornber if (!free_target_met(mq)) 1247b29d4986SJoe Thornber queue_demotion(mq); 1248b29d4986SJoe Thornber return; 1249b29d4986SJoe Thornber } 1250b29d4986SJoe Thornber 1251b29d4986SJoe Thornber if (btracker_promotion_already_present(mq->bg_work, oblock)) 1252b29d4986SJoe Thornber return; 1253b29d4986SJoe Thornber 1254b29d4986SJoe Thornber /* 1255b29d4986SJoe Thornber * We allocate the entry now to reserve the cblock. If the 1256b29d4986SJoe Thornber * background work is aborted we must remember to free it. 1257b29d4986SJoe Thornber */ 1258b29d4986SJoe Thornber e = alloc_entry(&mq->cache_alloc); 1259b29d4986SJoe Thornber BUG_ON(!e); 1260b29d4986SJoe Thornber e->pending_work = true; 1261b29d4986SJoe Thornber work.op = POLICY_PROMOTE; 1262b29d4986SJoe Thornber work.oblock = oblock; 1263b29d4986SJoe Thornber work.cblock = infer_cblock(mq, e); 12641e72a8e8SJoe Thornber r = btracker_queue(mq->bg_work, &work, workp); 12651e72a8e8SJoe Thornber if (r) 12661e72a8e8SJoe Thornber free_entry(&mq->cache_alloc, e); 1267b29d4986SJoe Thornber } 1268b29d4986SJoe Thornber 1269b29d4986SJoe Thornber /*----------------------------------------------------------------*/ 1270b29d4986SJoe Thornber 127166a63635SJoe Thornber enum promote_result { 127266a63635SJoe Thornber PROMOTE_NOT, 127366a63635SJoe Thornber PROMOTE_TEMPORARY, 127466a63635SJoe Thornber PROMOTE_PERMANENT 127566a63635SJoe Thornber }; 127666a63635SJoe Thornber 127766a63635SJoe Thornber /* 127866a63635SJoe Thornber * Converts a boolean into a promote result. 127966a63635SJoe Thornber */ 128066a63635SJoe Thornber static enum promote_result maybe_promote(bool promote) 128166a63635SJoe Thornber { 128266a63635SJoe Thornber return promote ? PROMOTE_PERMANENT : PROMOTE_NOT; 128366a63635SJoe Thornber } 128466a63635SJoe Thornber 1285b29d4986SJoe Thornber static enum promote_result should_promote(struct smq_policy *mq, struct entry *hs_e, 1286b29d4986SJoe Thornber int data_dir, bool fast_promote) 128766a63635SJoe Thornber { 1288b29d4986SJoe Thornber if (data_dir == WRITE) { 128966a63635SJoe Thornber if (!allocator_empty(&mq->cache_alloc) && fast_promote) 129066a63635SJoe Thornber return PROMOTE_TEMPORARY; 129166a63635SJoe Thornber 129266a63635SJoe Thornber return maybe_promote(hs_e->level >= mq->write_promote_level); 129366a63635SJoe Thornber } else 129466a63635SJoe Thornber return maybe_promote(hs_e->level >= mq->read_promote_level); 129566a63635SJoe Thornber } 129666a63635SJoe Thornber 129766a63635SJoe Thornber static dm_oblock_t to_hblock(struct smq_policy *mq, dm_oblock_t b) 129866a63635SJoe Thornber { 129966a63635SJoe Thornber sector_t r = from_oblock(b); 130066a63635SJoe Thornber (void) sector_div(r, mq->cache_blocks_per_hotspot_block); 130166a63635SJoe Thornber return to_oblock(r); 130266a63635SJoe Thornber } 130366a63635SJoe Thornber 1304b29d4986SJoe Thornber static struct entry *update_hotspot_queue(struct smq_policy *mq, dm_oblock_t b) 130566a63635SJoe Thornber { 130686a3238cSHeinz Mauelshagen unsigned int hi; 130766a63635SJoe Thornber dm_oblock_t hb = to_hblock(mq, b); 130866a63635SJoe Thornber struct entry *e = h_lookup(&mq->hotspot_table, hb); 130966a63635SJoe Thornber 131066a63635SJoe Thornber if (e) { 131166a63635SJoe Thornber stats_level_accessed(&mq->hotspot_stats, e->level); 131266a63635SJoe Thornber 131366a63635SJoe Thornber hi = get_index(&mq->hotspot_alloc, e); 131466a63635SJoe Thornber q_requeue(&mq->hotspot, e, 131566a63635SJoe Thornber test_and_set_bit(hi, mq->hotspot_hit_bits) ? 1316b29d4986SJoe Thornber 0u : mq->hotspot_level_jump, 1317b29d4986SJoe Thornber NULL, NULL); 131866a63635SJoe Thornber 131966a63635SJoe Thornber } else { 132066a63635SJoe Thornber stats_miss(&mq->hotspot_stats); 132166a63635SJoe Thornber 132266a63635SJoe Thornber e = alloc_entry(&mq->hotspot_alloc); 132366a63635SJoe Thornber if (!e) { 132466a63635SJoe Thornber e = q_pop(&mq->hotspot); 132566a63635SJoe Thornber if (e) { 132666a63635SJoe Thornber h_remove(&mq->hotspot_table, e); 132766a63635SJoe Thornber hi = get_index(&mq->hotspot_alloc, e); 132866a63635SJoe Thornber clear_bit(hi, mq->hotspot_hit_bits); 132966a63635SJoe Thornber } 133066a63635SJoe Thornber 133166a63635SJoe Thornber } 133266a63635SJoe Thornber 133366a63635SJoe Thornber if (e) { 133466a63635SJoe Thornber e->oblock = hb; 133566a63635SJoe Thornber q_push(&mq->hotspot, e); 133666a63635SJoe Thornber h_insert(&mq->hotspot_table, e); 133766a63635SJoe Thornber } 133866a63635SJoe Thornber } 133966a63635SJoe Thornber 134066a63635SJoe Thornber return e; 134166a63635SJoe Thornber } 134266a63635SJoe Thornber 134366a63635SJoe Thornber /*----------------------------------------------------------------*/ 134466a63635SJoe Thornber 134566a63635SJoe Thornber /* 134666a63635SJoe Thornber * Public interface, via the policy struct. See dm-cache-policy.h for a 134766a63635SJoe Thornber * description of these. 134866a63635SJoe Thornber */ 134966a63635SJoe Thornber 135066a63635SJoe Thornber static struct smq_policy *to_smq_policy(struct dm_cache_policy *p) 135166a63635SJoe Thornber { 135266a63635SJoe Thornber return container_of(p, struct smq_policy, policy); 135366a63635SJoe Thornber } 135466a63635SJoe Thornber 135566a63635SJoe Thornber static void smq_destroy(struct dm_cache_policy *p) 135666a63635SJoe Thornber { 135766a63635SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 135866a63635SJoe Thornber 1359b29d4986SJoe Thornber btracker_destroy(mq->bg_work); 136066a63635SJoe Thornber h_exit(&mq->hotspot_table); 136166a63635SJoe Thornber h_exit(&mq->table); 136266a63635SJoe Thornber free_bitset(mq->hotspot_hit_bits); 136366a63635SJoe Thornber free_bitset(mq->cache_hit_bits); 136466a63635SJoe Thornber space_exit(&mq->es); 136566a63635SJoe Thornber kfree(mq); 136666a63635SJoe Thornber } 136766a63635SJoe Thornber 1368b29d4986SJoe Thornber /*----------------------------------------------------------------*/ 1369b29d4986SJoe Thornber 1370b29d4986SJoe Thornber static int __lookup(struct smq_policy *mq, dm_oblock_t oblock, dm_cblock_t *cblock, 1371b29d4986SJoe Thornber int data_dir, bool fast_copy, 1372b29d4986SJoe Thornber struct policy_work **work, bool *background_work) 137366a63635SJoe Thornber { 1374b29d4986SJoe Thornber struct entry *e, *hs_e; 1375b29d4986SJoe Thornber enum promote_result pr; 137666a63635SJoe Thornber 1377b29d4986SJoe Thornber *background_work = false; 137866a63635SJoe Thornber 137966a63635SJoe Thornber e = h_lookup(&mq->table, oblock); 138066a63635SJoe Thornber if (e) { 1381b29d4986SJoe Thornber stats_level_accessed(&mq->cache_stats, e->level); 1382b29d4986SJoe Thornber 1383b29d4986SJoe Thornber requeue(mq, e); 138466a63635SJoe Thornber *cblock = infer_cblock(mq, e); 1385b29d4986SJoe Thornber return 0; 1386b29d4986SJoe Thornber 1387b29d4986SJoe Thornber } else { 1388b29d4986SJoe Thornber stats_miss(&mq->cache_stats); 1389b29d4986SJoe Thornber 1390b29d4986SJoe Thornber /* 1391b29d4986SJoe Thornber * The hotspot queue only gets updated with misses. 1392b29d4986SJoe Thornber */ 1393b29d4986SJoe Thornber hs_e = update_hotspot_queue(mq, oblock); 1394b29d4986SJoe Thornber 1395b29d4986SJoe Thornber pr = should_promote(mq, hs_e, data_dir, fast_copy); 1396b29d4986SJoe Thornber if (pr != PROMOTE_NOT) { 1397b29d4986SJoe Thornber queue_promotion(mq, oblock, work); 1398b29d4986SJoe Thornber *background_work = true; 1399b29d4986SJoe Thornber } 1400b29d4986SJoe Thornber 1401b29d4986SJoe Thornber return -ENOENT; 1402b29d4986SJoe Thornber } 1403b29d4986SJoe Thornber } 1404b29d4986SJoe Thornber 1405b29d4986SJoe Thornber static int smq_lookup(struct dm_cache_policy *p, dm_oblock_t oblock, dm_cblock_t *cblock, 1406b29d4986SJoe Thornber int data_dir, bool fast_copy, 1407b29d4986SJoe Thornber bool *background_work) 1408b29d4986SJoe Thornber { 1409b29d4986SJoe Thornber int r; 1410b29d4986SJoe Thornber unsigned long flags; 1411b29d4986SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 1412b29d4986SJoe Thornber 1413b29d4986SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 1414b29d4986SJoe Thornber r = __lookup(mq, oblock, cblock, 1415b29d4986SJoe Thornber data_dir, fast_copy, 1416b29d4986SJoe Thornber NULL, background_work); 14174051aab7SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 141866a63635SJoe Thornber 141966a63635SJoe Thornber return r; 142066a63635SJoe Thornber } 142166a63635SJoe Thornber 1422b29d4986SJoe Thornber static int smq_lookup_with_work(struct dm_cache_policy *p, 1423b29d4986SJoe Thornber dm_oblock_t oblock, dm_cblock_t *cblock, 1424b29d4986SJoe Thornber int data_dir, bool fast_copy, 1425b29d4986SJoe Thornber struct policy_work **work) 142666a63635SJoe Thornber { 1427b29d4986SJoe Thornber int r; 1428b29d4986SJoe Thornber bool background_queued; 1429b29d4986SJoe Thornber unsigned long flags; 1430b29d4986SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 143166a63635SJoe Thornber 1432b29d4986SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 1433b29d4986SJoe Thornber r = __lookup(mq, oblock, cblock, data_dir, fast_copy, work, &background_queued); 1434b29d4986SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 143566a63635SJoe Thornber 1436b29d4986SJoe Thornber return r; 143766a63635SJoe Thornber } 143866a63635SJoe Thornber 1439b29d4986SJoe Thornber static int smq_get_background_work(struct dm_cache_policy *p, bool idle, 1440b29d4986SJoe Thornber struct policy_work **result) 1441b29d4986SJoe Thornber { 1442b29d4986SJoe Thornber int r; 1443b29d4986SJoe Thornber unsigned long flags; 1444b29d4986SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 1445b29d4986SJoe Thornber 1446b29d4986SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 1447b29d4986SJoe Thornber r = btracker_issue(mq->bg_work, result); 1448b29d4986SJoe Thornber if (r == -ENODATA) { 14496cf4cc8fSJoe Thornber if (!clean_target_met(mq, idle)) { 1450deb71918SJoe Thornber queue_writeback(mq, idle); 1451b29d4986SJoe Thornber r = btracker_issue(mq->bg_work, result); 1452b29d4986SJoe Thornber } 14536cf4cc8fSJoe Thornber } 1454b29d4986SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 1455b29d4986SJoe Thornber 1456b29d4986SJoe Thornber return r; 1457b29d4986SJoe Thornber } 1458b29d4986SJoe Thornber 1459b29d4986SJoe Thornber /* 1460b29d4986SJoe Thornber * We need to clear any pending work flags that have been set, and in the 1461b29d4986SJoe Thornber * case of promotion free the entry for the destination cblock. 1462b29d4986SJoe Thornber */ 1463b29d4986SJoe Thornber static void __complete_background_work(struct smq_policy *mq, 1464b29d4986SJoe Thornber struct policy_work *work, 1465b29d4986SJoe Thornber bool success) 1466b29d4986SJoe Thornber { 1467b29d4986SJoe Thornber struct entry *e = get_entry(&mq->cache_alloc, 1468b29d4986SJoe Thornber from_cblock(work->cblock)); 1469b29d4986SJoe Thornber 1470b29d4986SJoe Thornber switch (work->op) { 1471b29d4986SJoe Thornber case POLICY_PROMOTE: 1472b29d4986SJoe Thornber // !h, !q, a 1473b29d4986SJoe Thornber clear_pending(mq, e); 1474b29d4986SJoe Thornber if (success) { 1475b29d4986SJoe Thornber e->oblock = work->oblock; 14764d44ec5aSJoe Thornber e->level = NR_CACHE_LEVELS - 1; 1477b29d4986SJoe Thornber push(mq, e); 1478b29d4986SJoe Thornber // h, q, a 1479b29d4986SJoe Thornber } else { 1480b29d4986SJoe Thornber free_entry(&mq->cache_alloc, e); 1481b29d4986SJoe Thornber // !h, !q, !a 1482b29d4986SJoe Thornber } 1483b29d4986SJoe Thornber break; 1484b29d4986SJoe Thornber 1485b29d4986SJoe Thornber case POLICY_DEMOTE: 1486b29d4986SJoe Thornber // h, !q, a 1487b29d4986SJoe Thornber if (success) { 1488b29d4986SJoe Thornber h_remove(&mq->table, e); 1489b29d4986SJoe Thornber free_entry(&mq->cache_alloc, e); 1490b29d4986SJoe Thornber // !h, !q, !a 1491b29d4986SJoe Thornber } else { 1492b29d4986SJoe Thornber clear_pending(mq, e); 1493b29d4986SJoe Thornber push_queue(mq, e); 1494b29d4986SJoe Thornber // h, q, a 1495b29d4986SJoe Thornber } 1496b29d4986SJoe Thornber break; 1497b29d4986SJoe Thornber 1498b29d4986SJoe Thornber case POLICY_WRITEBACK: 1499b29d4986SJoe Thornber // h, !q, a 1500b29d4986SJoe Thornber clear_pending(mq, e); 1501b29d4986SJoe Thornber push_queue(mq, e); 1502b29d4986SJoe Thornber // h, q, a 1503b29d4986SJoe Thornber break; 1504b29d4986SJoe Thornber } 1505b29d4986SJoe Thornber 1506b29d4986SJoe Thornber btracker_complete(mq->bg_work, work); 1507b29d4986SJoe Thornber } 1508b29d4986SJoe Thornber 1509b29d4986SJoe Thornber static void smq_complete_background_work(struct dm_cache_policy *p, 1510b29d4986SJoe Thornber struct policy_work *work, 1511b29d4986SJoe Thornber bool success) 151266a63635SJoe Thornber { 15134051aab7SJoe Thornber unsigned long flags; 151466a63635SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 151566a63635SJoe Thornber 15164051aab7SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 1517b29d4986SJoe Thornber __complete_background_work(mq, work, success); 15184051aab7SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 151966a63635SJoe Thornber } 152066a63635SJoe Thornber 1521b29d4986SJoe Thornber // in_hash(oblock) -> in_hash(oblock) 1522b29d4986SJoe Thornber static void __smq_set_clear_dirty(struct smq_policy *mq, dm_cblock_t cblock, bool set) 1523b29d4986SJoe Thornber { 1524b29d4986SJoe Thornber struct entry *e = get_entry(&mq->cache_alloc, from_cblock(cblock)); 1525b29d4986SJoe Thornber 1526b29d4986SJoe Thornber if (e->pending_work) 1527b29d4986SJoe Thornber e->dirty = set; 1528b29d4986SJoe Thornber else { 1529b29d4986SJoe Thornber del_queue(mq, e); 1530b29d4986SJoe Thornber e->dirty = set; 1531b29d4986SJoe Thornber push_queue(mq, e); 1532b29d4986SJoe Thornber } 1533b29d4986SJoe Thornber } 1534b29d4986SJoe Thornber 1535b29d4986SJoe Thornber static void smq_set_dirty(struct dm_cache_policy *p, dm_cblock_t cblock) 1536b29d4986SJoe Thornber { 1537b29d4986SJoe Thornber unsigned long flags; 1538b29d4986SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 1539b29d4986SJoe Thornber 1540b29d4986SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 1541b29d4986SJoe Thornber __smq_set_clear_dirty(mq, cblock, true); 1542b29d4986SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 1543b29d4986SJoe Thornber } 1544b29d4986SJoe Thornber 1545b29d4986SJoe Thornber static void smq_clear_dirty(struct dm_cache_policy *p, dm_cblock_t cblock) 154666a63635SJoe Thornber { 154766a63635SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 15484051aab7SJoe Thornber unsigned long flags; 154966a63635SJoe Thornber 15504051aab7SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 1551b29d4986SJoe Thornber __smq_set_clear_dirty(mq, cblock, false); 15524051aab7SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 155366a63635SJoe Thornber } 155466a63635SJoe Thornber 155586a3238cSHeinz Mauelshagen static unsigned int random_level(dm_cblock_t cblock) 15569d1b404cSJoe Thornber { 1557e99dda8fSMike Snitzer return hash_32(from_cblock(cblock), 9) & (NR_CACHE_LEVELS - 1); 15589d1b404cSJoe Thornber } 15599d1b404cSJoe Thornber 156066a63635SJoe Thornber static int smq_load_mapping(struct dm_cache_policy *p, 156166a63635SJoe Thornber dm_oblock_t oblock, dm_cblock_t cblock, 1562b29d4986SJoe Thornber bool dirty, uint32_t hint, bool hint_valid) 156366a63635SJoe Thornber { 156466a63635SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 156566a63635SJoe Thornber struct entry *e; 156666a63635SJoe Thornber 156766a63635SJoe Thornber e = alloc_particular_entry(&mq->cache_alloc, from_cblock(cblock)); 156866a63635SJoe Thornber e->oblock = oblock; 1569b29d4986SJoe Thornber e->dirty = dirty; 15709d1b404cSJoe Thornber e->level = hint_valid ? min(hint, NR_CACHE_LEVELS - 1) : random_level(cblock); 1571b29d4986SJoe Thornber e->pending_work = false; 157266a63635SJoe Thornber 1573b29d4986SJoe Thornber /* 1574b29d4986SJoe Thornber * When we load mappings we push ahead of both sentinels in order to 1575b29d4986SJoe Thornber * allow demotions and cleaning to occur immediately. 1576b29d4986SJoe Thornber */ 1577b29d4986SJoe Thornber push_front(mq, e); 1578b29d4986SJoe Thornber 1579b29d4986SJoe Thornber return 0; 1580b29d4986SJoe Thornber } 1581b29d4986SJoe Thornber 1582b29d4986SJoe Thornber static int smq_invalidate_mapping(struct dm_cache_policy *p, dm_cblock_t cblock) 1583b29d4986SJoe Thornber { 1584b29d4986SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 1585b29d4986SJoe Thornber struct entry *e = get_entry(&mq->cache_alloc, from_cblock(cblock)); 1586b29d4986SJoe Thornber 1587b29d4986SJoe Thornber if (!e->allocated) 1588b29d4986SJoe Thornber return -ENODATA; 1589b29d4986SJoe Thornber 1590b29d4986SJoe Thornber // FIXME: what if this block has pending background work? 1591b29d4986SJoe Thornber del_queue(mq, e); 1592b29d4986SJoe Thornber h_remove(&mq->table, e); 1593b29d4986SJoe Thornber free_entry(&mq->cache_alloc, e); 159466a63635SJoe Thornber return 0; 159566a63635SJoe Thornber } 159666a63635SJoe Thornber 15974e781b49SJoe Thornber static uint32_t smq_get_hint(struct dm_cache_policy *p, dm_cblock_t cblock) 159866a63635SJoe Thornber { 159966a63635SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 16004e781b49SJoe Thornber struct entry *e = get_entry(&mq->cache_alloc, from_cblock(cblock)); 160166a63635SJoe Thornber 16024e781b49SJoe Thornber if (!e->allocated) 16034e781b49SJoe Thornber return 0; 160466a63635SJoe Thornber 16054e781b49SJoe Thornber return e->level; 160666a63635SJoe Thornber } 160766a63635SJoe Thornber 160866a63635SJoe Thornber static dm_cblock_t smq_residency(struct dm_cache_policy *p) 160966a63635SJoe Thornber { 161066a63635SJoe Thornber dm_cblock_t r; 16114051aab7SJoe Thornber unsigned long flags; 161266a63635SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 161366a63635SJoe Thornber 16144051aab7SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 161566a63635SJoe Thornber r = to_cblock(mq->cache_alloc.nr_allocated); 16164051aab7SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 161766a63635SJoe Thornber 161866a63635SJoe Thornber return r; 161966a63635SJoe Thornber } 162066a63635SJoe Thornber 1621fba10109SJoe Thornber static void smq_tick(struct dm_cache_policy *p, bool can_block) 162266a63635SJoe Thornber { 162366a63635SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 162466a63635SJoe Thornber unsigned long flags; 162566a63635SJoe Thornber 16264051aab7SJoe Thornber spin_lock_irqsave(&mq->lock, flags); 16274051aab7SJoe Thornber mq->tick++; 16284051aab7SJoe Thornber update_sentinels(mq); 16294051aab7SJoe Thornber end_hotspot_period(mq); 16304051aab7SJoe Thornber end_cache_period(mq); 16314051aab7SJoe Thornber spin_unlock_irqrestore(&mq->lock, flags); 163266a63635SJoe Thornber } 163366a63635SJoe Thornber 1634b29d4986SJoe Thornber static void smq_allow_migrations(struct dm_cache_policy *p, bool allow) 1635b29d4986SJoe Thornber { 1636b29d4986SJoe Thornber struct smq_policy *mq = to_smq_policy(p); 1637*0ef0b471SHeinz Mauelshagen 1638b29d4986SJoe Thornber mq->migrations_allowed = allow; 1639b29d4986SJoe Thornber } 1640b29d4986SJoe Thornber 16419ed84698SJoe Thornber /* 16429ed84698SJoe Thornber * smq has no config values, but the old mq policy did. To avoid breaking 16439ed84698SJoe Thornber * software we continue to accept these configurables for the mq policy, 16449ed84698SJoe Thornber * but they have no effect. 16459ed84698SJoe Thornber */ 16469ed84698SJoe Thornber static int mq_set_config_value(struct dm_cache_policy *p, 16479ed84698SJoe Thornber const char *key, const char *value) 16489ed84698SJoe Thornber { 16499ed84698SJoe Thornber unsigned long tmp; 16509ed84698SJoe Thornber 16519ed84698SJoe Thornber if (kstrtoul(value, 10, &tmp)) 16529ed84698SJoe Thornber return -EINVAL; 16539ed84698SJoe Thornber 16549ed84698SJoe Thornber if (!strcasecmp(key, "random_threshold") || 16559ed84698SJoe Thornber !strcasecmp(key, "sequential_threshold") || 16569ed84698SJoe Thornber !strcasecmp(key, "discard_promote_adjustment") || 16579ed84698SJoe Thornber !strcasecmp(key, "read_promote_adjustment") || 16589ed84698SJoe Thornber !strcasecmp(key, "write_promote_adjustment")) { 16599ed84698SJoe Thornber DMWARN("tunable '%s' no longer has any effect, mq policy is now an alias for smq", key); 16609ed84698SJoe Thornber return 0; 16619ed84698SJoe Thornber } 16629ed84698SJoe Thornber 16639ed84698SJoe Thornber return -EINVAL; 16649ed84698SJoe Thornber } 16659ed84698SJoe Thornber 16669ed84698SJoe Thornber static int mq_emit_config_values(struct dm_cache_policy *p, char *result, 166786a3238cSHeinz Mauelshagen unsigned int maxlen, ssize_t *sz_ptr) 16689ed84698SJoe Thornber { 16699ed84698SJoe Thornber ssize_t sz = *sz_ptr; 16709ed84698SJoe Thornber 16719ed84698SJoe Thornber DMEMIT("10 random_threshold 0 " 16729ed84698SJoe Thornber "sequential_threshold 0 " 16739ed84698SJoe Thornber "discard_promote_adjustment 0 " 16749ed84698SJoe Thornber "read_promote_adjustment 0 " 16759ed84698SJoe Thornber "write_promote_adjustment 0 "); 16769ed84698SJoe Thornber 16779ed84698SJoe Thornber *sz_ptr = sz; 16789ed84698SJoe Thornber return 0; 16799ed84698SJoe Thornber } 16809ed84698SJoe Thornber 168166a63635SJoe Thornber /* Init the policy plugin interface function pointers. */ 16829ed84698SJoe Thornber static void init_policy_functions(struct smq_policy *mq, bool mimic_mq) 168366a63635SJoe Thornber { 168466a63635SJoe Thornber mq->policy.destroy = smq_destroy; 168566a63635SJoe Thornber mq->policy.lookup = smq_lookup; 1686b29d4986SJoe Thornber mq->policy.lookup_with_work = smq_lookup_with_work; 1687b29d4986SJoe Thornber mq->policy.get_background_work = smq_get_background_work; 1688b29d4986SJoe Thornber mq->policy.complete_background_work = smq_complete_background_work; 168966a63635SJoe Thornber mq->policy.set_dirty = smq_set_dirty; 169066a63635SJoe Thornber mq->policy.clear_dirty = smq_clear_dirty; 169166a63635SJoe Thornber mq->policy.load_mapping = smq_load_mapping; 1692b29d4986SJoe Thornber mq->policy.invalidate_mapping = smq_invalidate_mapping; 16934e781b49SJoe Thornber mq->policy.get_hint = smq_get_hint; 169466a63635SJoe Thornber mq->policy.residency = smq_residency; 169566a63635SJoe Thornber mq->policy.tick = smq_tick; 1696b29d4986SJoe Thornber mq->policy.allow_migrations = smq_allow_migrations; 16979ed84698SJoe Thornber 16989ed84698SJoe Thornber if (mimic_mq) { 16999ed84698SJoe Thornber mq->policy.set_config_value = mq_set_config_value; 17009ed84698SJoe Thornber mq->policy.emit_config_values = mq_emit_config_values; 17019ed84698SJoe Thornber } 170266a63635SJoe Thornber } 170366a63635SJoe Thornber 170466a63635SJoe Thornber static bool too_many_hotspot_blocks(sector_t origin_size, 170566a63635SJoe Thornber sector_t hotspot_block_size, 170686a3238cSHeinz Mauelshagen unsigned int nr_hotspot_blocks) 170766a63635SJoe Thornber { 170866a63635SJoe Thornber return (hotspot_block_size * nr_hotspot_blocks) > origin_size; 170966a63635SJoe Thornber } 171066a63635SJoe Thornber 171166a63635SJoe Thornber static void calc_hotspot_params(sector_t origin_size, 171266a63635SJoe Thornber sector_t cache_block_size, 171386a3238cSHeinz Mauelshagen unsigned int nr_cache_blocks, 171466a63635SJoe Thornber sector_t *hotspot_block_size, 171586a3238cSHeinz Mauelshagen unsigned int *nr_hotspot_blocks) 171666a63635SJoe Thornber { 171766a63635SJoe Thornber *hotspot_block_size = cache_block_size * 16u; 171866a63635SJoe Thornber *nr_hotspot_blocks = max(nr_cache_blocks / 4u, 1024u); 171966a63635SJoe Thornber 172066a63635SJoe Thornber while ((*hotspot_block_size > cache_block_size) && 172166a63635SJoe Thornber too_many_hotspot_blocks(origin_size, *hotspot_block_size, *nr_hotspot_blocks)) 172266a63635SJoe Thornber *hotspot_block_size /= 2u; 172366a63635SJoe Thornber } 172466a63635SJoe Thornber 17259ed84698SJoe Thornber static struct dm_cache_policy *__smq_create(dm_cblock_t cache_size, 172666a63635SJoe Thornber sector_t origin_size, 17279ed84698SJoe Thornber sector_t cache_block_size, 1728b29d4986SJoe Thornber bool mimic_mq, 1729b29d4986SJoe Thornber bool migrations_allowed) 173066a63635SJoe Thornber { 173186a3238cSHeinz Mauelshagen unsigned int i; 173286a3238cSHeinz Mauelshagen unsigned int nr_sentinels_per_queue = 2u * NR_CACHE_LEVELS; 173386a3238cSHeinz Mauelshagen unsigned int total_sentinels = 2u * nr_sentinels_per_queue; 173466a63635SJoe Thornber struct smq_policy *mq = kzalloc(sizeof(*mq), GFP_KERNEL); 173566a63635SJoe Thornber 173666a63635SJoe Thornber if (!mq) 173766a63635SJoe Thornber return NULL; 173866a63635SJoe Thornber 17399ed84698SJoe Thornber init_policy_functions(mq, mimic_mq); 174066a63635SJoe Thornber mq->cache_size = cache_size; 174166a63635SJoe Thornber mq->cache_block_size = cache_block_size; 174266a63635SJoe Thornber 174366a63635SJoe Thornber calc_hotspot_params(origin_size, cache_block_size, from_cblock(cache_size), 174466a63635SJoe Thornber &mq->hotspot_block_size, &mq->nr_hotspot_blocks); 174566a63635SJoe Thornber 174666a63635SJoe Thornber mq->cache_blocks_per_hotspot_block = div64_u64(mq->hotspot_block_size, mq->cache_block_size); 174766a63635SJoe Thornber mq->hotspot_level_jump = 1u; 174866a63635SJoe Thornber if (space_init(&mq->es, total_sentinels + mq->nr_hotspot_blocks + from_cblock(cache_size))) { 174966a63635SJoe Thornber DMERR("couldn't initialize entry space"); 175066a63635SJoe Thornber goto bad_pool_init; 175166a63635SJoe Thornber } 175266a63635SJoe Thornber 175366a63635SJoe Thornber init_allocator(&mq->writeback_sentinel_alloc, &mq->es, 0, nr_sentinels_per_queue); 175466a63635SJoe Thornber for (i = 0; i < nr_sentinels_per_queue; i++) 175566a63635SJoe Thornber get_entry(&mq->writeback_sentinel_alloc, i)->sentinel = true; 175666a63635SJoe Thornber 175766a63635SJoe Thornber init_allocator(&mq->demote_sentinel_alloc, &mq->es, nr_sentinels_per_queue, total_sentinels); 175866a63635SJoe Thornber for (i = 0; i < nr_sentinels_per_queue; i++) 175966a63635SJoe Thornber get_entry(&mq->demote_sentinel_alloc, i)->sentinel = true; 176066a63635SJoe Thornber 176166a63635SJoe Thornber init_allocator(&mq->hotspot_alloc, &mq->es, total_sentinels, 176266a63635SJoe Thornber total_sentinels + mq->nr_hotspot_blocks); 176366a63635SJoe Thornber 176466a63635SJoe Thornber init_allocator(&mq->cache_alloc, &mq->es, 176566a63635SJoe Thornber total_sentinels + mq->nr_hotspot_blocks, 176666a63635SJoe Thornber total_sentinels + mq->nr_hotspot_blocks + from_cblock(cache_size)); 176766a63635SJoe Thornber 176866a63635SJoe Thornber mq->hotspot_hit_bits = alloc_bitset(mq->nr_hotspot_blocks); 176966a63635SJoe Thornber if (!mq->hotspot_hit_bits) { 177066a63635SJoe Thornber DMERR("couldn't allocate hotspot hit bitset"); 177166a63635SJoe Thornber goto bad_hotspot_hit_bits; 177266a63635SJoe Thornber } 177366a63635SJoe Thornber clear_bitset(mq->hotspot_hit_bits, mq->nr_hotspot_blocks); 177466a63635SJoe Thornber 177566a63635SJoe Thornber if (from_cblock(cache_size)) { 177666a63635SJoe Thornber mq->cache_hit_bits = alloc_bitset(from_cblock(cache_size)); 1777134bf30cSColin Ian King if (!mq->cache_hit_bits) { 177866a63635SJoe Thornber DMERR("couldn't allocate cache hit bitset"); 177966a63635SJoe Thornber goto bad_cache_hit_bits; 178066a63635SJoe Thornber } 178166a63635SJoe Thornber clear_bitset(mq->cache_hit_bits, from_cblock(mq->cache_size)); 178266a63635SJoe Thornber } else 178366a63635SJoe Thornber mq->cache_hit_bits = NULL; 178466a63635SJoe Thornber 178566a63635SJoe Thornber mq->tick = 0; 17864051aab7SJoe Thornber spin_lock_init(&mq->lock); 178766a63635SJoe Thornber 178866a63635SJoe Thornber q_init(&mq->hotspot, &mq->es, NR_HOTSPOT_LEVELS); 178966a63635SJoe Thornber mq->hotspot.nr_top_levels = 8; 179066a63635SJoe Thornber mq->hotspot.nr_in_top_levels = min(mq->nr_hotspot_blocks / NR_HOTSPOT_LEVELS, 179166a63635SJoe Thornber from_cblock(mq->cache_size) / mq->cache_blocks_per_hotspot_block); 179266a63635SJoe Thornber 179366a63635SJoe Thornber q_init(&mq->clean, &mq->es, NR_CACHE_LEVELS); 179466a63635SJoe Thornber q_init(&mq->dirty, &mq->es, NR_CACHE_LEVELS); 179566a63635SJoe Thornber 179666a63635SJoe Thornber stats_init(&mq->hotspot_stats, NR_HOTSPOT_LEVELS); 179766a63635SJoe Thornber stats_init(&mq->cache_stats, NR_CACHE_LEVELS); 179866a63635SJoe Thornber 179966a63635SJoe Thornber if (h_init(&mq->table, &mq->es, from_cblock(cache_size))) 180066a63635SJoe Thornber goto bad_alloc_table; 180166a63635SJoe Thornber 180266a63635SJoe Thornber if (h_init(&mq->hotspot_table, &mq->es, mq->nr_hotspot_blocks)) 180366a63635SJoe Thornber goto bad_alloc_hotspot_table; 180466a63635SJoe Thornber 180566a63635SJoe Thornber sentinels_init(mq); 180666a63635SJoe Thornber mq->write_promote_level = mq->read_promote_level = NR_HOTSPOT_LEVELS; 180766a63635SJoe Thornber 180866a63635SJoe Thornber mq->next_hotspot_period = jiffies; 180966a63635SJoe Thornber mq->next_cache_period = jiffies; 181066a63635SJoe Thornber 18118ee18edeSJoe Thornber mq->bg_work = btracker_create(4096); /* FIXME: hard coded value */ 1812b29d4986SJoe Thornber if (!mq->bg_work) 1813b29d4986SJoe Thornber goto bad_btracker; 1814b29d4986SJoe Thornber 1815b29d4986SJoe Thornber mq->migrations_allowed = migrations_allowed; 1816b29d4986SJoe Thornber 181766a63635SJoe Thornber return &mq->policy; 181866a63635SJoe Thornber 1819b29d4986SJoe Thornber bad_btracker: 1820b29d4986SJoe Thornber h_exit(&mq->hotspot_table); 182166a63635SJoe Thornber bad_alloc_hotspot_table: 182266a63635SJoe Thornber h_exit(&mq->table); 182366a63635SJoe Thornber bad_alloc_table: 182466a63635SJoe Thornber free_bitset(mq->cache_hit_bits); 182566a63635SJoe Thornber bad_cache_hit_bits: 182666a63635SJoe Thornber free_bitset(mq->hotspot_hit_bits); 182766a63635SJoe Thornber bad_hotspot_hit_bits: 182866a63635SJoe Thornber space_exit(&mq->es); 182966a63635SJoe Thornber bad_pool_init: 183066a63635SJoe Thornber kfree(mq); 183166a63635SJoe Thornber 183266a63635SJoe Thornber return NULL; 183366a63635SJoe Thornber } 183466a63635SJoe Thornber 18359ed84698SJoe Thornber static struct dm_cache_policy *smq_create(dm_cblock_t cache_size, 18369ed84698SJoe Thornber sector_t origin_size, 18379ed84698SJoe Thornber sector_t cache_block_size) 18389ed84698SJoe Thornber { 1839b29d4986SJoe Thornber return __smq_create(cache_size, origin_size, cache_block_size, false, true); 18409ed84698SJoe Thornber } 18419ed84698SJoe Thornber 18429ed84698SJoe Thornber static struct dm_cache_policy *mq_create(dm_cblock_t cache_size, 18439ed84698SJoe Thornber sector_t origin_size, 18449ed84698SJoe Thornber sector_t cache_block_size) 18459ed84698SJoe Thornber { 1846b29d4986SJoe Thornber return __smq_create(cache_size, origin_size, cache_block_size, true, true); 1847b29d4986SJoe Thornber } 1848b29d4986SJoe Thornber 1849b29d4986SJoe Thornber static struct dm_cache_policy *cleaner_create(dm_cblock_t cache_size, 1850b29d4986SJoe Thornber sector_t origin_size, 1851b29d4986SJoe Thornber sector_t cache_block_size) 1852b29d4986SJoe Thornber { 1853b29d4986SJoe Thornber return __smq_create(cache_size, origin_size, cache_block_size, false, false); 18549ed84698SJoe Thornber } 18559ed84698SJoe Thornber 185666a63635SJoe Thornber /*----------------------------------------------------------------*/ 185766a63635SJoe Thornber 185866a63635SJoe Thornber static struct dm_cache_policy_type smq_policy_type = { 185966a63635SJoe Thornber .name = "smq", 1860b29d4986SJoe Thornber .version = {2, 0, 0}, 186166a63635SJoe Thornber .hint_size = 4, 186266a63635SJoe Thornber .owner = THIS_MODULE, 186366a63635SJoe Thornber .create = smq_create 186466a63635SJoe Thornber }; 186566a63635SJoe Thornber 18669ed84698SJoe Thornber static struct dm_cache_policy_type mq_policy_type = { 18679ed84698SJoe Thornber .name = "mq", 1868b29d4986SJoe Thornber .version = {2, 0, 0}, 18699ed84698SJoe Thornber .hint_size = 4, 18709ed84698SJoe Thornber .owner = THIS_MODULE, 18719ed84698SJoe Thornber .create = mq_create, 18729ed84698SJoe Thornber }; 18739ed84698SJoe Thornber 1874b29d4986SJoe Thornber static struct dm_cache_policy_type cleaner_policy_type = { 1875b29d4986SJoe Thornber .name = "cleaner", 1876b29d4986SJoe Thornber .version = {2, 0, 0}, 1877b29d4986SJoe Thornber .hint_size = 4, 1878b29d4986SJoe Thornber .owner = THIS_MODULE, 1879b29d4986SJoe Thornber .create = cleaner_create, 1880b29d4986SJoe Thornber }; 1881b29d4986SJoe Thornber 1882bccab6a0SMike Snitzer static struct dm_cache_policy_type default_policy_type = { 1883bccab6a0SMike Snitzer .name = "default", 1884b29d4986SJoe Thornber .version = {2, 0, 0}, 1885bccab6a0SMike Snitzer .hint_size = 4, 1886bccab6a0SMike Snitzer .owner = THIS_MODULE, 1887bccab6a0SMike Snitzer .create = smq_create, 1888bccab6a0SMike Snitzer .real = &smq_policy_type 1889bccab6a0SMike Snitzer }; 1890bccab6a0SMike Snitzer 189166a63635SJoe Thornber static int __init smq_init(void) 189266a63635SJoe Thornber { 189366a63635SJoe Thornber int r; 189466a63635SJoe Thornber 189566a63635SJoe Thornber r = dm_cache_policy_register(&smq_policy_type); 189666a63635SJoe Thornber if (r) { 189766a63635SJoe Thornber DMERR("register failed %d", r); 189866a63635SJoe Thornber return -ENOMEM; 189966a63635SJoe Thornber } 190066a63635SJoe Thornber 19019ed84698SJoe Thornber r = dm_cache_policy_register(&mq_policy_type); 19029ed84698SJoe Thornber if (r) { 19037dd85bb0SMike Snitzer DMERR("register failed (as mq) %d", r); 1904b29d4986SJoe Thornber goto out_mq; 1905b29d4986SJoe Thornber } 1906b29d4986SJoe Thornber 1907b29d4986SJoe Thornber r = dm_cache_policy_register(&cleaner_policy_type); 1908b29d4986SJoe Thornber if (r) { 1909b29d4986SJoe Thornber DMERR("register failed (as cleaner) %d", r); 1910b29d4986SJoe Thornber goto out_cleaner; 19119ed84698SJoe Thornber } 19129ed84698SJoe Thornber 1913bccab6a0SMike Snitzer r = dm_cache_policy_register(&default_policy_type); 1914bccab6a0SMike Snitzer if (r) { 1915bccab6a0SMike Snitzer DMERR("register failed (as default) %d", r); 1916b29d4986SJoe Thornber goto out_default; 1917bccab6a0SMike Snitzer } 1918bccab6a0SMike Snitzer 191966a63635SJoe Thornber return 0; 1920b29d4986SJoe Thornber 1921b29d4986SJoe Thornber out_default: 1922b29d4986SJoe Thornber dm_cache_policy_unregister(&cleaner_policy_type); 1923b29d4986SJoe Thornber out_cleaner: 1924b29d4986SJoe Thornber dm_cache_policy_unregister(&mq_policy_type); 1925b29d4986SJoe Thornber out_mq: 1926b29d4986SJoe Thornber dm_cache_policy_unregister(&smq_policy_type); 1927b29d4986SJoe Thornber 1928b29d4986SJoe Thornber return -ENOMEM; 192966a63635SJoe Thornber } 193066a63635SJoe Thornber 193166a63635SJoe Thornber static void __exit smq_exit(void) 193266a63635SJoe Thornber { 1933b29d4986SJoe Thornber dm_cache_policy_unregister(&cleaner_policy_type); 193466a63635SJoe Thornber dm_cache_policy_unregister(&smq_policy_type); 19359ed84698SJoe Thornber dm_cache_policy_unregister(&mq_policy_type); 1936bccab6a0SMike Snitzer dm_cache_policy_unregister(&default_policy_type); 193766a63635SJoe Thornber } 193866a63635SJoe Thornber 193966a63635SJoe Thornber module_init(smq_init); 194066a63635SJoe Thornber module_exit(smq_exit); 194166a63635SJoe Thornber 194266a63635SJoe Thornber MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>"); 194366a63635SJoe Thornber MODULE_LICENSE("GPL"); 194466a63635SJoe Thornber MODULE_DESCRIPTION("smq cache policy"); 194534dd0517SYi Zhang 194634dd0517SYi Zhang MODULE_ALIAS("dm-cache-default"); 19479ed84698SJoe Thornber MODULE_ALIAS("dm-cache-mq"); 1948b29d4986SJoe Thornber MODULE_ALIAS("dm-cache-cleaner"); 1949