brintos

brintos / linux-shallow public Read only

0
0
Text · 5.0 KiB · f3051bd Raw
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