246 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2017 Red Hat. All rights reserved.4 *5 * This file is released under the GPL.6 */7 8#include "dm-cache-background-tracker.h"9 10/*----------------------------------------------------------------*/11 12#define DM_MSG_PREFIX "dm-background-tracker"13 14struct background_tracker {15 unsigned int max_work;16 atomic_t pending_promotes;17 atomic_t pending_writebacks;18 atomic_t pending_demotes;19 20 struct list_head issued;21 struct list_head queued;22 struct rb_root pending;23};24 25struct kmem_cache *btracker_work_cache = NULL;26 27struct background_tracker *btracker_create(unsigned int max_work)28{29 struct background_tracker *b = kmalloc(sizeof(*b), GFP_KERNEL);30 31 if (!b) {32 DMERR("couldn't create background_tracker");33 return NULL;34 }35 36 b->max_work = max_work;37 atomic_set(&b->pending_promotes, 0);38 atomic_set(&b->pending_writebacks, 0);39 atomic_set(&b->pending_demotes, 0);40 41 INIT_LIST_HEAD(&b->issued);42 INIT_LIST_HEAD(&b->queued);43 44 b->pending = RB_ROOT;45 46 return b;47}48EXPORT_SYMBOL_GPL(btracker_create);49 50void btracker_destroy(struct background_tracker *b)51{52 struct bt_work *w, *tmp;53 54 BUG_ON(!list_empty(&b->issued));55 list_for_each_entry_safe (w, tmp, &b->queued, list) {56 list_del(&w->list);57 kmem_cache_free(btracker_work_cache, w);58 }59 60 kfree(b);61}62EXPORT_SYMBOL_GPL(btracker_destroy);63 64static int cmp_oblock(dm_oblock_t lhs, dm_oblock_t rhs)65{66 if (from_oblock(lhs) < from_oblock(rhs))67 return -1;68 69 if (from_oblock(rhs) < from_oblock(lhs))70 return 1;71 72 return 0;73}74 75static bool __insert_pending(struct background_tracker *b,76 struct bt_work *nw)77{78 int cmp;79 struct bt_work *w;80 struct rb_node **new = &b->pending.rb_node, *parent = NULL;81 82 while (*new) {83 w = container_of(*new, struct bt_work, node);84 85 parent = *new;86 cmp = cmp_oblock(w->work.oblock, nw->work.oblock);87 if (cmp < 0)88 new = &((*new)->rb_left);89 90 else if (cmp > 0)91 new = &((*new)->rb_right);92 93 else94 /* already present */95 return false;96 }97 98 rb_link_node(&nw->node, parent, new);99 rb_insert_color(&nw->node, &b->pending);100 101 return true;102}103 104static struct bt_work *__find_pending(struct background_tracker *b,105 dm_oblock_t oblock)106{107 int cmp;108 struct bt_work *w;109 struct rb_node **new = &b->pending.rb_node;110 111 while (*new) {112 w = container_of(*new, struct bt_work, node);113 114 cmp = cmp_oblock(w->work.oblock, oblock);115 if (cmp < 0)116 new = &((*new)->rb_left);117 118 else if (cmp > 0)119 new = &((*new)->rb_right);120 121 else122 break;123 }124 125 return *new ? w : NULL;126}127 128 129static void update_stats(struct background_tracker *b, struct policy_work *w, int delta)130{131 switch (w->op) {132 case POLICY_PROMOTE:133 atomic_add(delta, &b->pending_promotes);134 break;135 136 case POLICY_DEMOTE:137 atomic_add(delta, &b->pending_demotes);138 break;139 140 case POLICY_WRITEBACK:141 atomic_add(delta, &b->pending_writebacks);142 break;143 }144}145 146unsigned int btracker_nr_writebacks_queued(struct background_tracker *b)147{148 return atomic_read(&b->pending_writebacks);149}150EXPORT_SYMBOL_GPL(btracker_nr_writebacks_queued);151 152unsigned int btracker_nr_demotions_queued(struct background_tracker *b)153{154 return atomic_read(&b->pending_demotes);155}156EXPORT_SYMBOL_GPL(btracker_nr_demotions_queued);157 158static bool max_work_reached(struct background_tracker *b)159{160 return atomic_read(&b->pending_promotes) +161 atomic_read(&b->pending_writebacks) +162 atomic_read(&b->pending_demotes) >= b->max_work;163}164 165static struct bt_work *alloc_work(struct background_tracker *b)166{167 if (max_work_reached(b))168 return NULL;169 170 return kmem_cache_alloc(btracker_work_cache, GFP_NOWAIT);171}172 173int btracker_queue(struct background_tracker *b,174 struct policy_work *work,175 struct policy_work **pwork)176{177 struct bt_work *w;178 179 if (pwork)180 *pwork = NULL;181 182 w = alloc_work(b);183 if (!w)184 return -ENOMEM;185 186 memcpy(&w->work, work, sizeof(*work));187 188 if (!__insert_pending(b, w)) {189 /*190 * There was a race, we'll just ignore this second191 * bit of work for the same oblock.192 */193 kmem_cache_free(btracker_work_cache, w);194 return -EINVAL;195 }196 197 if (pwork) {198 *pwork = &w->work;199 list_add(&w->list, &b->issued);200 } else201 list_add(&w->list, &b->queued);202 update_stats(b, &w->work, 1);203 204 return 0;205}206EXPORT_SYMBOL_GPL(btracker_queue);207 208/*209 * Returns -ENODATA if there's no work.210 */211int btracker_issue(struct background_tracker *b, struct policy_work **work)212{213 struct bt_work *w;214 215 if (list_empty(&b->queued))216 return -ENODATA;217 218 w = list_first_entry(&b->queued, struct bt_work, list);219 list_move(&w->list, &b->issued);220 *work = &w->work;221 222 return 0;223}224EXPORT_SYMBOL_GPL(btracker_issue);225 226void btracker_complete(struct background_tracker *b,227 struct policy_work *op)228{229 struct bt_work *w = container_of(op, struct bt_work, work);230 231 update_stats(b, &w->work, -1);232 rb_erase(&w->node, &b->pending);233 list_del(&w->list);234 kmem_cache_free(btracker_work_cache, w);235}236EXPORT_SYMBOL_GPL(btracker_complete);237 238bool btracker_promotion_already_present(struct background_tracker *b,239 dm_oblock_t oblock)240{241 return __find_pending(b, oblock) != NULL;242}243EXPORT_SYMBOL_GPL(btracker_promotion_already_present);244 245/*----------------------------------------------------------------*/246