Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

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

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

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

Signed-off-by: Joe Thornber <ejt@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>

authored by

Joe Thornber and committed by
Mike Snitzer
e65ff870 fdecee32

+93 -1
+93 -1
drivers/md/dm-cache-policy-mq.c
··· 8 8 #include "dm.h" 9 9 10 10 #include <linux/hash.h> 11 + #include <linux/jiffies.h> 11 12 #include <linux/module.h> 12 13 #include <linux/mutex.h> 13 14 #include <linux/slab.h> ··· 127 126 #define NR_QUEUE_LEVELS 16u 128 127 #define NR_SENTINELS NR_QUEUE_LEVELS * 3 129 128 129 + #define WRITEBACK_PERIOD HZ 130 + 130 131 struct queue { 131 132 unsigned nr_elts; 133 + bool current_writeback_sentinels; 134 + unsigned long next_writeback; 132 135 struct list_head qs[NR_QUEUE_LEVELS]; 133 136 struct list_head sentinels[NR_SENTINELS]; 134 137 }; ··· 142 137 unsigned i; 143 138 144 139 q->nr_elts = 0; 140 + q->current_writeback_sentinels = false; 141 + q->next_writeback = 0; 145 142 for (i = 0; i < NR_QUEUE_LEVELS; i++) { 146 143 INIT_LIST_HEAD(q->qs + i); 147 144 INIT_LIST_HEAD(q->sentinels + i); 145 + INIT_LIST_HEAD(q->sentinels + NR_QUEUE_LEVELS + i); 146 + INIT_LIST_HEAD(q->sentinels + (2 * NR_QUEUE_LEVELS) + i); 148 147 } 148 + } 149 + 150 + static unsigned queue_size(struct queue *q) 151 + { 152 + return q->nr_elts; 149 153 } 150 154 151 155 static bool queue_empty(struct queue *q) ··· 211 197 return r; 212 198 } 213 199 200 + /* 201 + * Pops an entry from a level that is not past a sentinel. 202 + */ 203 + static struct list_head *queue_pop_old(struct queue *q) 204 + { 205 + unsigned level; 206 + struct list_head *h; 207 + 208 + for (level = 0; level < NR_QUEUE_LEVELS; level++) 209 + list_for_each(h, q->qs + level) { 210 + if (is_sentinel(q, h)) 211 + break; 212 + 213 + q->nr_elts--; 214 + list_del(h); 215 + return h; 216 + } 217 + 218 + return NULL; 219 + } 220 + 214 221 static struct list_head *list_pop(struct list_head *lh) 215 222 { 216 223 struct list_head *r = lh->next; ··· 240 205 list_del_init(r); 241 206 242 207 return r; 208 + } 209 + 210 + static struct list_head *writeback_sentinel(struct queue *q, unsigned level) 211 + { 212 + if (q->current_writeback_sentinels) 213 + return q->sentinels + NR_QUEUE_LEVELS + level; 214 + else 215 + return q->sentinels + 2 * NR_QUEUE_LEVELS + level; 216 + } 217 + 218 + static void queue_update_writeback_sentinels(struct queue *q) 219 + { 220 + unsigned i; 221 + struct list_head *h; 222 + 223 + if (time_after(jiffies, q->next_writeback)) { 224 + for (i = 0; i < NR_QUEUE_LEVELS; i++) { 225 + h = writeback_sentinel(q, i); 226 + list_del(h); 227 + list_add_tail(h, q->qs + i); 228 + } 229 + 230 + q->next_writeback = jiffies + WRITEBACK_PERIOD; 231 + q->current_writeback_sentinels = !q->current_writeback_sentinels; 232 + } 243 233 } 244 234 245 235 /* ··· 590 530 { 591 531 struct entry *e; 592 532 struct list_head *h = queue_pop(q); 533 + 534 + if (!h) 535 + return NULL; 536 + 537 + e = container_of(h, struct entry, list); 538 + hash_remove(e); 539 + 540 + return e; 541 + } 542 + 543 + static struct entry *pop_old(struct mq_policy *mq, struct queue *q) 544 + { 545 + struct entry *e; 546 + struct list_head *h = queue_pop_old(q); 593 547 594 548 if (!h) 595 549 return NULL; ··· 1006 932 queue_tick(&mq->pre_cache); 1007 933 queue_tick(&mq->cache_dirty); 1008 934 queue_tick(&mq->cache_clean); 935 + queue_update_writeback_sentinels(&mq->cache_dirty); 1009 936 spin_unlock_irqrestore(&mq->tick_lock, flags); 1010 937 } 1011 938 ··· 1187 1112 return r; 1188 1113 } 1189 1114 1115 + #define CLEAN_TARGET_PERCENTAGE 25 1116 + 1117 + static bool clean_target_met(struct mq_policy *mq) 1118 + { 1119 + /* 1120 + * Cache entries may not be populated. So we're cannot rely on the 1121 + * size of the clean queue. 1122 + */ 1123 + unsigned nr_clean = from_cblock(mq->cache_size) - queue_size(&mq->cache_dirty); 1124 + unsigned target = from_cblock(mq->cache_size) * CLEAN_TARGET_PERCENTAGE / 100; 1125 + 1126 + return nr_clean >= target; 1127 + } 1128 + 1190 1129 static int __mq_writeback_work(struct mq_policy *mq, dm_oblock_t *oblock, 1191 1130 dm_cblock_t *cblock) 1192 1131 { 1193 - struct entry *e = pop(mq, &mq->cache_dirty); 1132 + struct entry *e = pop_old(mq, &mq->cache_dirty); 1133 + 1134 + if (!e && !clean_target_met(mq)) 1135 + e = pop(mq, &mq->cache_dirty); 1194 1136 1195 1137 if (!e) 1196 1138 return -ENODATA;