18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2017 Red Hat. All rights reserved.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This file is released under the GPL.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "dm-cache-background-tracker.h"
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define DM_MSG_PREFIX "dm-background-tracker"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistruct bt_work {
148c2ecf20Sopenharmony_ci	struct list_head list;
158c2ecf20Sopenharmony_ci	struct rb_node node;
168c2ecf20Sopenharmony_ci	struct policy_work work;
178c2ecf20Sopenharmony_ci};
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistruct background_tracker {
208c2ecf20Sopenharmony_ci	unsigned max_work;
218c2ecf20Sopenharmony_ci	atomic_t pending_promotes;
228c2ecf20Sopenharmony_ci	atomic_t pending_writebacks;
238c2ecf20Sopenharmony_ci	atomic_t pending_demotes;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	struct list_head issued;
268c2ecf20Sopenharmony_ci	struct list_head queued;
278c2ecf20Sopenharmony_ci	struct rb_root pending;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	struct kmem_cache *work_cache;
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct background_tracker *btracker_create(unsigned max_work)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct background_tracker *b = kmalloc(sizeof(*b), GFP_KERNEL);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	if (!b) {
378c2ecf20Sopenharmony_ci		DMERR("couldn't create background_tracker");
388c2ecf20Sopenharmony_ci		return NULL;
398c2ecf20Sopenharmony_ci	}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	b->max_work = max_work;
428c2ecf20Sopenharmony_ci	atomic_set(&b->pending_promotes, 0);
438c2ecf20Sopenharmony_ci	atomic_set(&b->pending_writebacks, 0);
448c2ecf20Sopenharmony_ci	atomic_set(&b->pending_demotes, 0);
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&b->issued);
478c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&b->queued);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	b->pending = RB_ROOT;
508c2ecf20Sopenharmony_ci	b->work_cache = KMEM_CACHE(bt_work, 0);
518c2ecf20Sopenharmony_ci	if (!b->work_cache) {
528c2ecf20Sopenharmony_ci		DMERR("couldn't create mempool for background work items");
538c2ecf20Sopenharmony_ci		kfree(b);
548c2ecf20Sopenharmony_ci		b = NULL;
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return b;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(btracker_create);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_civoid btracker_destroy(struct background_tracker *b)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	kmem_cache_destroy(b->work_cache);
648c2ecf20Sopenharmony_ci	kfree(b);
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(btracker_destroy);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int cmp_oblock(dm_oblock_t lhs, dm_oblock_t rhs)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	if (from_oblock(lhs) < from_oblock(rhs))
718c2ecf20Sopenharmony_ci		return -1;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (from_oblock(rhs) < from_oblock(lhs))
748c2ecf20Sopenharmony_ci		return 1;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return 0;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic bool __insert_pending(struct background_tracker *b,
808c2ecf20Sopenharmony_ci			     struct bt_work *nw)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	int cmp;
838c2ecf20Sopenharmony_ci	struct bt_work *w;
848c2ecf20Sopenharmony_ci	struct rb_node **new = &b->pending.rb_node, *parent = NULL;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	while (*new) {
878c2ecf20Sopenharmony_ci		w = container_of(*new, struct bt_work, node);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci		parent = *new;
908c2ecf20Sopenharmony_ci		cmp = cmp_oblock(w->work.oblock, nw->work.oblock);
918c2ecf20Sopenharmony_ci		if (cmp < 0)
928c2ecf20Sopenharmony_ci			new = &((*new)->rb_left);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci		else if (cmp > 0)
958c2ecf20Sopenharmony_ci			new = &((*new)->rb_right);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci		else
988c2ecf20Sopenharmony_ci			/* already present */
998c2ecf20Sopenharmony_ci			return false;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	rb_link_node(&nw->node, parent, new);
1038c2ecf20Sopenharmony_ci	rb_insert_color(&nw->node, &b->pending);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	return true;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic struct bt_work *__find_pending(struct background_tracker *b,
1098c2ecf20Sopenharmony_ci				      dm_oblock_t oblock)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	int cmp;
1128c2ecf20Sopenharmony_ci	struct bt_work *w;
1138c2ecf20Sopenharmony_ci	struct rb_node **new = &b->pending.rb_node;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	while (*new) {
1168c2ecf20Sopenharmony_ci		w = container_of(*new, struct bt_work, node);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci		cmp = cmp_oblock(w->work.oblock, oblock);
1198c2ecf20Sopenharmony_ci		if (cmp < 0)
1208c2ecf20Sopenharmony_ci			new = &((*new)->rb_left);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci		else if (cmp > 0)
1238c2ecf20Sopenharmony_ci			new = &((*new)->rb_right);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci		else
1268c2ecf20Sopenharmony_ci			break;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return *new ? w : NULL;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic void update_stats(struct background_tracker *b, struct policy_work *w, int delta)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	switch (w->op) {
1368c2ecf20Sopenharmony_ci	case POLICY_PROMOTE:
1378c2ecf20Sopenharmony_ci		atomic_add(delta, &b->pending_promotes);
1388c2ecf20Sopenharmony_ci		break;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	case POLICY_DEMOTE:
1418c2ecf20Sopenharmony_ci		atomic_add(delta, &b->pending_demotes);
1428c2ecf20Sopenharmony_ci		break;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	case POLICY_WRITEBACK:
1458c2ecf20Sopenharmony_ci		atomic_add(delta, &b->pending_writebacks);
1468c2ecf20Sopenharmony_ci		break;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ciunsigned btracker_nr_writebacks_queued(struct background_tracker *b)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	return atomic_read(&b->pending_writebacks);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(btracker_nr_writebacks_queued);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ciunsigned btracker_nr_demotions_queued(struct background_tracker *b)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	return atomic_read(&b->pending_demotes);
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(btracker_nr_demotions_queued);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic bool max_work_reached(struct background_tracker *b)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	return atomic_read(&b->pending_promotes) +
1658c2ecf20Sopenharmony_ci		atomic_read(&b->pending_writebacks) +
1668c2ecf20Sopenharmony_ci		atomic_read(&b->pending_demotes) >= b->max_work;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic struct bt_work *alloc_work(struct background_tracker *b)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	if (max_work_reached(b))
1728c2ecf20Sopenharmony_ci		return NULL;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return kmem_cache_alloc(b->work_cache, GFP_NOWAIT);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ciint btracker_queue(struct background_tracker *b,
1788c2ecf20Sopenharmony_ci		   struct policy_work *work,
1798c2ecf20Sopenharmony_ci		   struct policy_work **pwork)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct bt_work *w;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (pwork)
1848c2ecf20Sopenharmony_ci		*pwork = NULL;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	w = alloc_work(b);
1878c2ecf20Sopenharmony_ci	if (!w)
1888c2ecf20Sopenharmony_ci		return -ENOMEM;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	memcpy(&w->work, work, sizeof(*work));
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (!__insert_pending(b, w)) {
1938c2ecf20Sopenharmony_ci		/*
1948c2ecf20Sopenharmony_ci		 * There was a race, we'll just ignore this second
1958c2ecf20Sopenharmony_ci		 * bit of work for the same oblock.
1968c2ecf20Sopenharmony_ci		 */
1978c2ecf20Sopenharmony_ci		kmem_cache_free(b->work_cache, w);
1988c2ecf20Sopenharmony_ci		return -EINVAL;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (pwork) {
2028c2ecf20Sopenharmony_ci		*pwork = &w->work;
2038c2ecf20Sopenharmony_ci		list_add(&w->list, &b->issued);
2048c2ecf20Sopenharmony_ci	} else
2058c2ecf20Sopenharmony_ci		list_add(&w->list, &b->queued);
2068c2ecf20Sopenharmony_ci	update_stats(b, &w->work, 1);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	return 0;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(btracker_queue);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci/*
2138c2ecf20Sopenharmony_ci * Returns -ENODATA if there's no work.
2148c2ecf20Sopenharmony_ci */
2158c2ecf20Sopenharmony_ciint btracker_issue(struct background_tracker *b, struct policy_work **work)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	struct bt_work *w;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (list_empty(&b->queued))
2208c2ecf20Sopenharmony_ci		return -ENODATA;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	w = list_first_entry(&b->queued, struct bt_work, list);
2238c2ecf20Sopenharmony_ci	list_move(&w->list, &b->issued);
2248c2ecf20Sopenharmony_ci	*work = &w->work;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	return 0;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(btracker_issue);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_civoid btracker_complete(struct background_tracker *b,
2318c2ecf20Sopenharmony_ci		       struct policy_work *op)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct bt_work *w = container_of(op, struct bt_work, work);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	update_stats(b, &w->work, -1);
2368c2ecf20Sopenharmony_ci	rb_erase(&w->node, &b->pending);
2378c2ecf20Sopenharmony_ci	list_del(&w->list);
2388c2ecf20Sopenharmony_ci	kmem_cache_free(b->work_cache, w);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(btracker_complete);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cibool btracker_promotion_already_present(struct background_tracker *b,
2438c2ecf20Sopenharmony_ci					dm_oblock_t oblock)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	return __find_pending(b, oblock) != NULL;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(btracker_promotion_already_present);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci/*----------------------------------------------------------------*/
250