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