18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci lru_cache.c 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci This file is part of DRBD by Philipp Reisner and Lars Ellenberg. 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci Copyright (C) 2003-2008, LINBIT Information Technologies GmbH. 88c2ecf20Sopenharmony_ci Copyright (C) 2003-2008, Philipp Reisner <philipp.reisner@linbit.com>. 98c2ecf20Sopenharmony_ci Copyright (C) 2003-2008, Lars Ellenberg <lars.ellenberg@linbit.com>. 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/bitops.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/string.h> /* for memset */ 188c2ecf20Sopenharmony_ci#include <linux/seq_file.h> /* for seq_printf */ 198c2ecf20Sopenharmony_ci#include <linux/lru_cache.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Philipp Reisner <phil@linbit.com>, " 228c2ecf20Sopenharmony_ci "Lars Ellenberg <lars@linbit.com>"); 238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("lru_cache - Track sets of hot objects"); 248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* this is developers aid only. 278c2ecf20Sopenharmony_ci * it catches concurrent access (lack of locking on the users part) */ 288c2ecf20Sopenharmony_ci#define PARANOIA_ENTRY() do { \ 298c2ecf20Sopenharmony_ci BUG_ON(!lc); \ 308c2ecf20Sopenharmony_ci BUG_ON(!lc->nr_elements); \ 318c2ecf20Sopenharmony_ci BUG_ON(test_and_set_bit(__LC_PARANOIA, &lc->flags)); \ 328c2ecf20Sopenharmony_ci} while (0) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define RETURN(x...) do { \ 358c2ecf20Sopenharmony_ci clear_bit_unlock(__LC_PARANOIA, &lc->flags); \ 368c2ecf20Sopenharmony_ci return x ; } while (0) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* BUG() if e is not one of the elements tracked by lc */ 398c2ecf20Sopenharmony_ci#define PARANOIA_LC_ELEMENT(lc, e) do { \ 408c2ecf20Sopenharmony_ci struct lru_cache *lc_ = (lc); \ 418c2ecf20Sopenharmony_ci struct lc_element *e_ = (e); \ 428c2ecf20Sopenharmony_ci unsigned i = e_->lc_index; \ 438c2ecf20Sopenharmony_ci BUG_ON(i >= lc_->nr_elements); \ 448c2ecf20Sopenharmony_ci BUG_ON(lc_->lc_element[i] != e_); } while (0) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* We need to atomically 488c2ecf20Sopenharmony_ci * - try to grab the lock (set LC_LOCKED) 498c2ecf20Sopenharmony_ci * - only if there is no pending transaction 508c2ecf20Sopenharmony_ci * (neither LC_DIRTY nor LC_STARVING is set) 518c2ecf20Sopenharmony_ci * Because of PARANOIA_ENTRY() above abusing lc->flags as well, 528c2ecf20Sopenharmony_ci * it is not sufficient to just say 538c2ecf20Sopenharmony_ci * return 0 == cmpxchg(&lc->flags, 0, LC_LOCKED); 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_ciint lc_try_lock(struct lru_cache *lc) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci unsigned long val; 588c2ecf20Sopenharmony_ci do { 598c2ecf20Sopenharmony_ci val = cmpxchg(&lc->flags, 0, LC_LOCKED); 608c2ecf20Sopenharmony_ci } while (unlikely (val == LC_PARANOIA)); 618c2ecf20Sopenharmony_ci /* Spin until no-one is inside a PARANOIA_ENTRY()/RETURN() section. */ 628c2ecf20Sopenharmony_ci return 0 == val; 638c2ecf20Sopenharmony_ci#if 0 648c2ecf20Sopenharmony_ci /* Alternative approach, spin in case someone enters or leaves a 658c2ecf20Sopenharmony_ci * PARANOIA_ENTRY()/RETURN() section. */ 668c2ecf20Sopenharmony_ci unsigned long old, new, val; 678c2ecf20Sopenharmony_ci do { 688c2ecf20Sopenharmony_ci old = lc->flags & LC_PARANOIA; 698c2ecf20Sopenharmony_ci new = old | LC_LOCKED; 708c2ecf20Sopenharmony_ci val = cmpxchg(&lc->flags, old, new); 718c2ecf20Sopenharmony_ci } while (unlikely (val == (old ^ LC_PARANOIA))); 728c2ecf20Sopenharmony_ci return old == val; 738c2ecf20Sopenharmony_ci#endif 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/** 778c2ecf20Sopenharmony_ci * lc_create - prepares to track objects in an active set 788c2ecf20Sopenharmony_ci * @name: descriptive name only used in lc_seq_printf_stats and lc_seq_dump_details 798c2ecf20Sopenharmony_ci * @max_pending_changes: maximum changes to accumulate until a transaction is required 808c2ecf20Sopenharmony_ci * @e_count: number of elements allowed to be active simultaneously 818c2ecf20Sopenharmony_ci * @e_size: size of the tracked objects 828c2ecf20Sopenharmony_ci * @e_off: offset to the &struct lc_element member in a tracked object 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * Returns a pointer to a newly initialized struct lru_cache on success, 858c2ecf20Sopenharmony_ci * or NULL on (allocation) failure. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_cistruct lru_cache *lc_create(const char *name, struct kmem_cache *cache, 888c2ecf20Sopenharmony_ci unsigned max_pending_changes, 898c2ecf20Sopenharmony_ci unsigned e_count, size_t e_size, size_t e_off) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct hlist_head *slot = NULL; 928c2ecf20Sopenharmony_ci struct lc_element **element = NULL; 938c2ecf20Sopenharmony_ci struct lru_cache *lc; 948c2ecf20Sopenharmony_ci struct lc_element *e; 958c2ecf20Sopenharmony_ci unsigned cache_obj_size = kmem_cache_size(cache); 968c2ecf20Sopenharmony_ci unsigned i; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci WARN_ON(cache_obj_size < e_size); 998c2ecf20Sopenharmony_ci if (cache_obj_size < e_size) 1008c2ecf20Sopenharmony_ci return NULL; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* e_count too big; would probably fail the allocation below anyways. 1038c2ecf20Sopenharmony_ci * for typical use cases, e_count should be few thousand at most. */ 1048c2ecf20Sopenharmony_ci if (e_count > LC_MAX_ACTIVE) 1058c2ecf20Sopenharmony_ci return NULL; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci slot = kcalloc(e_count, sizeof(struct hlist_head), GFP_KERNEL); 1088c2ecf20Sopenharmony_ci if (!slot) 1098c2ecf20Sopenharmony_ci goto out_fail; 1108c2ecf20Sopenharmony_ci element = kcalloc(e_count, sizeof(struct lc_element *), GFP_KERNEL); 1118c2ecf20Sopenharmony_ci if (!element) 1128c2ecf20Sopenharmony_ci goto out_fail; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci lc = kzalloc(sizeof(*lc), GFP_KERNEL); 1158c2ecf20Sopenharmony_ci if (!lc) 1168c2ecf20Sopenharmony_ci goto out_fail; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&lc->in_use); 1198c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&lc->lru); 1208c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&lc->free); 1218c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&lc->to_be_changed); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci lc->name = name; 1248c2ecf20Sopenharmony_ci lc->element_size = e_size; 1258c2ecf20Sopenharmony_ci lc->element_off = e_off; 1268c2ecf20Sopenharmony_ci lc->nr_elements = e_count; 1278c2ecf20Sopenharmony_ci lc->max_pending_changes = max_pending_changes; 1288c2ecf20Sopenharmony_ci lc->lc_cache = cache; 1298c2ecf20Sopenharmony_ci lc->lc_element = element; 1308c2ecf20Sopenharmony_ci lc->lc_slot = slot; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* preallocate all objects */ 1338c2ecf20Sopenharmony_ci for (i = 0; i < e_count; i++) { 1348c2ecf20Sopenharmony_ci void *p = kmem_cache_alloc(cache, GFP_KERNEL); 1358c2ecf20Sopenharmony_ci if (!p) 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci memset(p, 0, lc->element_size); 1388c2ecf20Sopenharmony_ci e = p + e_off; 1398c2ecf20Sopenharmony_ci e->lc_index = i; 1408c2ecf20Sopenharmony_ci e->lc_number = LC_FREE; 1418c2ecf20Sopenharmony_ci e->lc_new_number = LC_FREE; 1428c2ecf20Sopenharmony_ci list_add(&e->list, &lc->free); 1438c2ecf20Sopenharmony_ci element[i] = e; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci if (i == e_count) 1468c2ecf20Sopenharmony_ci return lc; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* else: could not allocate all elements, give up */ 1498c2ecf20Sopenharmony_ci for (i--; i; i--) { 1508c2ecf20Sopenharmony_ci void *p = element[i]; 1518c2ecf20Sopenharmony_ci kmem_cache_free(cache, p - e_off); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci kfree(lc); 1548c2ecf20Sopenharmony_ciout_fail: 1558c2ecf20Sopenharmony_ci kfree(element); 1568c2ecf20Sopenharmony_ci kfree(slot); 1578c2ecf20Sopenharmony_ci return NULL; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic void lc_free_by_index(struct lru_cache *lc, unsigned i) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci void *p = lc->lc_element[i]; 1638c2ecf20Sopenharmony_ci WARN_ON(!p); 1648c2ecf20Sopenharmony_ci if (p) { 1658c2ecf20Sopenharmony_ci p -= lc->element_off; 1668c2ecf20Sopenharmony_ci kmem_cache_free(lc->lc_cache, p); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/** 1718c2ecf20Sopenharmony_ci * lc_destroy - frees memory allocated by lc_create() 1728c2ecf20Sopenharmony_ci * @lc: the lru cache to destroy 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_civoid lc_destroy(struct lru_cache *lc) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci unsigned i; 1778c2ecf20Sopenharmony_ci if (!lc) 1788c2ecf20Sopenharmony_ci return; 1798c2ecf20Sopenharmony_ci for (i = 0; i < lc->nr_elements; i++) 1808c2ecf20Sopenharmony_ci lc_free_by_index(lc, i); 1818c2ecf20Sopenharmony_ci kfree(lc->lc_element); 1828c2ecf20Sopenharmony_ci kfree(lc->lc_slot); 1838c2ecf20Sopenharmony_ci kfree(lc); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/** 1878c2ecf20Sopenharmony_ci * lc_reset - does a full reset for @lc and the hash table slots. 1888c2ecf20Sopenharmony_ci * @lc: the lru cache to operate on 1898c2ecf20Sopenharmony_ci * 1908c2ecf20Sopenharmony_ci * It is roughly the equivalent of re-allocating a fresh lru_cache object, 1918c2ecf20Sopenharmony_ci * basically a short cut to lc_destroy(lc); lc = lc_create(...); 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_civoid lc_reset(struct lru_cache *lc) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci unsigned i; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&lc->in_use); 1988c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&lc->lru); 1998c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&lc->free); 2008c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&lc->to_be_changed); 2018c2ecf20Sopenharmony_ci lc->used = 0; 2028c2ecf20Sopenharmony_ci lc->hits = 0; 2038c2ecf20Sopenharmony_ci lc->misses = 0; 2048c2ecf20Sopenharmony_ci lc->starving = 0; 2058c2ecf20Sopenharmony_ci lc->locked = 0; 2068c2ecf20Sopenharmony_ci lc->changed = 0; 2078c2ecf20Sopenharmony_ci lc->pending_changes = 0; 2088c2ecf20Sopenharmony_ci lc->flags = 0; 2098c2ecf20Sopenharmony_ci memset(lc->lc_slot, 0, sizeof(struct hlist_head) * lc->nr_elements); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci for (i = 0; i < lc->nr_elements; i++) { 2128c2ecf20Sopenharmony_ci struct lc_element *e = lc->lc_element[i]; 2138c2ecf20Sopenharmony_ci void *p = e; 2148c2ecf20Sopenharmony_ci p -= lc->element_off; 2158c2ecf20Sopenharmony_ci memset(p, 0, lc->element_size); 2168c2ecf20Sopenharmony_ci /* re-init it */ 2178c2ecf20Sopenharmony_ci e->lc_index = i; 2188c2ecf20Sopenharmony_ci e->lc_number = LC_FREE; 2198c2ecf20Sopenharmony_ci e->lc_new_number = LC_FREE; 2208c2ecf20Sopenharmony_ci list_add(&e->list, &lc->free); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/** 2258c2ecf20Sopenharmony_ci * lc_seq_printf_stats - print stats about @lc into @seq 2268c2ecf20Sopenharmony_ci * @seq: the seq_file to print into 2278c2ecf20Sopenharmony_ci * @lc: the lru cache to print statistics of 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_civoid lc_seq_printf_stats(struct seq_file *seq, struct lru_cache *lc) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci /* NOTE: 2328c2ecf20Sopenharmony_ci * total calls to lc_get are 2338c2ecf20Sopenharmony_ci * (starving + hits + misses) 2348c2ecf20Sopenharmony_ci * misses include "locked" count (update from an other thread in 2358c2ecf20Sopenharmony_ci * progress) and "changed", when this in fact lead to an successful 2368c2ecf20Sopenharmony_ci * update of the cache. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci seq_printf(seq, "\t%s: used:%u/%u hits:%lu misses:%lu starving:%lu locked:%lu changed:%lu\n", 2398c2ecf20Sopenharmony_ci lc->name, lc->used, lc->nr_elements, 2408c2ecf20Sopenharmony_ci lc->hits, lc->misses, lc->starving, lc->locked, lc->changed); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic struct hlist_head *lc_hash_slot(struct lru_cache *lc, unsigned int enr) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci return lc->lc_slot + (enr % lc->nr_elements); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic struct lc_element *__lc_find(struct lru_cache *lc, unsigned int enr, 2508c2ecf20Sopenharmony_ci bool include_changing) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct lc_element *e; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci BUG_ON(!lc); 2558c2ecf20Sopenharmony_ci BUG_ON(!lc->nr_elements); 2568c2ecf20Sopenharmony_ci hlist_for_each_entry(e, lc_hash_slot(lc, enr), colision) { 2578c2ecf20Sopenharmony_ci /* "about to be changed" elements, pending transaction commit, 2588c2ecf20Sopenharmony_ci * are hashed by their "new number". "Normal" elements have 2598c2ecf20Sopenharmony_ci * lc_number == lc_new_number. */ 2608c2ecf20Sopenharmony_ci if (e->lc_new_number != enr) 2618c2ecf20Sopenharmony_ci continue; 2628c2ecf20Sopenharmony_ci if (e->lc_new_number == e->lc_number || include_changing) 2638c2ecf20Sopenharmony_ci return e; 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci return NULL; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/** 2708c2ecf20Sopenharmony_ci * lc_find - find element by label, if present in the hash table 2718c2ecf20Sopenharmony_ci * @lc: The lru_cache object 2728c2ecf20Sopenharmony_ci * @enr: element number 2738c2ecf20Sopenharmony_ci * 2748c2ecf20Sopenharmony_ci * Returns the pointer to an element, if the element with the requested 2758c2ecf20Sopenharmony_ci * "label" or element number is present in the hash table, 2768c2ecf20Sopenharmony_ci * or NULL if not found. Does not change the refcnt. 2778c2ecf20Sopenharmony_ci * Ignores elements that are "about to be used", i.e. not yet in the active 2788c2ecf20Sopenharmony_ci * set, but still pending transaction commit. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_cistruct lc_element *lc_find(struct lru_cache *lc, unsigned int enr) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci return __lc_find(lc, enr, 0); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/** 2868c2ecf20Sopenharmony_ci * lc_is_used - find element by label 2878c2ecf20Sopenharmony_ci * @lc: The lru_cache object 2888c2ecf20Sopenharmony_ci * @enr: element number 2898c2ecf20Sopenharmony_ci * 2908c2ecf20Sopenharmony_ci * Returns true, if the element with the requested "label" or element number is 2918c2ecf20Sopenharmony_ci * present in the hash table, and is used (refcnt > 0). 2928c2ecf20Sopenharmony_ci * Also finds elements that are not _currently_ used but only "about to be 2938c2ecf20Sopenharmony_ci * used", i.e. on the "to_be_changed" list, pending transaction commit. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_cibool lc_is_used(struct lru_cache *lc, unsigned int enr) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct lc_element *e = __lc_find(lc, enr, 1); 2988c2ecf20Sopenharmony_ci return e && e->refcnt; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/** 3028c2ecf20Sopenharmony_ci * lc_del - removes an element from the cache 3038c2ecf20Sopenharmony_ci * @lc: The lru_cache object 3048c2ecf20Sopenharmony_ci * @e: The element to remove 3058c2ecf20Sopenharmony_ci * 3068c2ecf20Sopenharmony_ci * @e must be unused (refcnt == 0). Moves @e from "lru" to "free" list, 3078c2ecf20Sopenharmony_ci * sets @e->enr to %LC_FREE. 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_civoid lc_del(struct lru_cache *lc, struct lc_element *e) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci PARANOIA_ENTRY(); 3128c2ecf20Sopenharmony_ci PARANOIA_LC_ELEMENT(lc, e); 3138c2ecf20Sopenharmony_ci BUG_ON(e->refcnt); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci e->lc_number = e->lc_new_number = LC_FREE; 3168c2ecf20Sopenharmony_ci hlist_del_init(&e->colision); 3178c2ecf20Sopenharmony_ci list_move(&e->list, &lc->free); 3188c2ecf20Sopenharmony_ci RETURN(); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic struct lc_element *lc_prepare_for_change(struct lru_cache *lc, unsigned new_number) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct list_head *n; 3248c2ecf20Sopenharmony_ci struct lc_element *e; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (!list_empty(&lc->free)) 3278c2ecf20Sopenharmony_ci n = lc->free.next; 3288c2ecf20Sopenharmony_ci else if (!list_empty(&lc->lru)) 3298c2ecf20Sopenharmony_ci n = lc->lru.prev; 3308c2ecf20Sopenharmony_ci else 3318c2ecf20Sopenharmony_ci return NULL; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci e = list_entry(n, struct lc_element, list); 3348c2ecf20Sopenharmony_ci PARANOIA_LC_ELEMENT(lc, e); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci e->lc_new_number = new_number; 3378c2ecf20Sopenharmony_ci if (!hlist_unhashed(&e->colision)) 3388c2ecf20Sopenharmony_ci __hlist_del(&e->colision); 3398c2ecf20Sopenharmony_ci hlist_add_head(&e->colision, lc_hash_slot(lc, new_number)); 3408c2ecf20Sopenharmony_ci list_move(&e->list, &lc->to_be_changed); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return e; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic int lc_unused_element_available(struct lru_cache *lc) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci if (!list_empty(&lc->free)) 3488c2ecf20Sopenharmony_ci return 1; /* something on the free list */ 3498c2ecf20Sopenharmony_ci if (!list_empty(&lc->lru)) 3508c2ecf20Sopenharmony_ci return 1; /* something to evict */ 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return 0; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/* used as internal flags to __lc_get */ 3568c2ecf20Sopenharmony_cienum { 3578c2ecf20Sopenharmony_ci LC_GET_MAY_CHANGE = 1, 3588c2ecf20Sopenharmony_ci LC_GET_MAY_USE_UNCOMMITTED = 2, 3598c2ecf20Sopenharmony_ci}; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic struct lc_element *__lc_get(struct lru_cache *lc, unsigned int enr, unsigned int flags) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct lc_element *e; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci PARANOIA_ENTRY(); 3668c2ecf20Sopenharmony_ci if (lc->flags & LC_STARVING) { 3678c2ecf20Sopenharmony_ci ++lc->starving; 3688c2ecf20Sopenharmony_ci RETURN(NULL); 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci e = __lc_find(lc, enr, 1); 3728c2ecf20Sopenharmony_ci /* if lc_new_number != lc_number, 3738c2ecf20Sopenharmony_ci * this enr is currently being pulled in already, 3748c2ecf20Sopenharmony_ci * and will be available once the pending transaction 3758c2ecf20Sopenharmony_ci * has been committed. */ 3768c2ecf20Sopenharmony_ci if (e) { 3778c2ecf20Sopenharmony_ci if (e->lc_new_number != e->lc_number) { 3788c2ecf20Sopenharmony_ci /* It has been found above, but on the "to_be_changed" 3798c2ecf20Sopenharmony_ci * list, not yet committed. Don't pull it in twice, 3808c2ecf20Sopenharmony_ci * wait for the transaction, then try again... 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_ci if (!(flags & LC_GET_MAY_USE_UNCOMMITTED)) 3838c2ecf20Sopenharmony_ci RETURN(NULL); 3848c2ecf20Sopenharmony_ci /* ... unless the caller is aware of the implications, 3858c2ecf20Sopenharmony_ci * probably preparing a cumulative transaction. */ 3868c2ecf20Sopenharmony_ci ++e->refcnt; 3878c2ecf20Sopenharmony_ci ++lc->hits; 3888c2ecf20Sopenharmony_ci RETURN(e); 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci /* else: lc_new_number == lc_number; a real hit. */ 3918c2ecf20Sopenharmony_ci ++lc->hits; 3928c2ecf20Sopenharmony_ci if (e->refcnt++ == 0) 3938c2ecf20Sopenharmony_ci lc->used++; 3948c2ecf20Sopenharmony_ci list_move(&e->list, &lc->in_use); /* Not evictable... */ 3958c2ecf20Sopenharmony_ci RETURN(e); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci /* e == NULL */ 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci ++lc->misses; 4008c2ecf20Sopenharmony_ci if (!(flags & LC_GET_MAY_CHANGE)) 4018c2ecf20Sopenharmony_ci RETURN(NULL); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* To avoid races with lc_try_lock(), first, mark us dirty 4048c2ecf20Sopenharmony_ci * (using test_and_set_bit, as it implies memory barriers), ... */ 4058c2ecf20Sopenharmony_ci test_and_set_bit(__LC_DIRTY, &lc->flags); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* ... only then check if it is locked anyways. If lc_unlock clears 4088c2ecf20Sopenharmony_ci * the dirty bit again, that's not a problem, we will come here again. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_ci if (test_bit(__LC_LOCKED, &lc->flags)) { 4118c2ecf20Sopenharmony_ci ++lc->locked; 4128c2ecf20Sopenharmony_ci RETURN(NULL); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* In case there is nothing available and we can not kick out 4168c2ecf20Sopenharmony_ci * the LRU element, we have to wait ... 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_ci if (!lc_unused_element_available(lc)) { 4198c2ecf20Sopenharmony_ci __set_bit(__LC_STARVING, &lc->flags); 4208c2ecf20Sopenharmony_ci RETURN(NULL); 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* It was not present in the active set. We are going to recycle an 4248c2ecf20Sopenharmony_ci * unused (or even "free") element, but we won't accumulate more than 4258c2ecf20Sopenharmony_ci * max_pending_changes changes. */ 4268c2ecf20Sopenharmony_ci if (lc->pending_changes >= lc->max_pending_changes) 4278c2ecf20Sopenharmony_ci RETURN(NULL); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci e = lc_prepare_for_change(lc, enr); 4308c2ecf20Sopenharmony_ci BUG_ON(!e); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci clear_bit(__LC_STARVING, &lc->flags); 4338c2ecf20Sopenharmony_ci BUG_ON(++e->refcnt != 1); 4348c2ecf20Sopenharmony_ci lc->used++; 4358c2ecf20Sopenharmony_ci lc->pending_changes++; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci RETURN(e); 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci/** 4418c2ecf20Sopenharmony_ci * lc_get - get element by label, maybe change the active set 4428c2ecf20Sopenharmony_ci * @lc: the lru cache to operate on 4438c2ecf20Sopenharmony_ci * @enr: the label to look up 4448c2ecf20Sopenharmony_ci * 4458c2ecf20Sopenharmony_ci * Finds an element in the cache, increases its usage count, 4468c2ecf20Sopenharmony_ci * "touches" and returns it. 4478c2ecf20Sopenharmony_ci * 4488c2ecf20Sopenharmony_ci * In case the requested number is not present, it needs to be added to the 4498c2ecf20Sopenharmony_ci * cache. Therefore it is possible that an other element becomes evicted from 4508c2ecf20Sopenharmony_ci * the cache. In either case, the user is notified so he is able to e.g. keep 4518c2ecf20Sopenharmony_ci * a persistent log of the cache changes, and therefore the objects in use. 4528c2ecf20Sopenharmony_ci * 4538c2ecf20Sopenharmony_ci * Return values: 4548c2ecf20Sopenharmony_ci * NULL 4558c2ecf20Sopenharmony_ci * The cache was marked %LC_STARVING, 4568c2ecf20Sopenharmony_ci * or the requested label was not in the active set 4578c2ecf20Sopenharmony_ci * and a changing transaction is still pending (@lc was marked %LC_DIRTY). 4588c2ecf20Sopenharmony_ci * Or no unused or free element could be recycled (@lc will be marked as 4598c2ecf20Sopenharmony_ci * %LC_STARVING, blocking further lc_get() operations). 4608c2ecf20Sopenharmony_ci * 4618c2ecf20Sopenharmony_ci * pointer to the element with the REQUESTED element number. 4628c2ecf20Sopenharmony_ci * In this case, it can be used right away 4638c2ecf20Sopenharmony_ci * 4648c2ecf20Sopenharmony_ci * pointer to an UNUSED element with some different element number, 4658c2ecf20Sopenharmony_ci * where that different number may also be %LC_FREE. 4668c2ecf20Sopenharmony_ci * 4678c2ecf20Sopenharmony_ci * In this case, the cache is marked %LC_DIRTY, 4688c2ecf20Sopenharmony_ci * so lc_try_lock() will no longer succeed. 4698c2ecf20Sopenharmony_ci * The returned element pointer is moved to the "to_be_changed" list, 4708c2ecf20Sopenharmony_ci * and registered with the new element number on the hash collision chains, 4718c2ecf20Sopenharmony_ci * so it is possible to pick it up from lc_is_used(). 4728c2ecf20Sopenharmony_ci * Up to "max_pending_changes" (see lc_create()) can be accumulated. 4738c2ecf20Sopenharmony_ci * The user now should do whatever housekeeping is necessary, 4748c2ecf20Sopenharmony_ci * typically serialize on lc_try_lock_for_transaction(), then call 4758c2ecf20Sopenharmony_ci * lc_committed(lc) and lc_unlock(), to finish the change. 4768c2ecf20Sopenharmony_ci * 4778c2ecf20Sopenharmony_ci * NOTE: The user needs to check the lc_number on EACH use, so he recognizes 4788c2ecf20Sopenharmony_ci * any cache set change. 4798c2ecf20Sopenharmony_ci */ 4808c2ecf20Sopenharmony_cistruct lc_element *lc_get(struct lru_cache *lc, unsigned int enr) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci return __lc_get(lc, enr, LC_GET_MAY_CHANGE); 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci/** 4868c2ecf20Sopenharmony_ci * lc_get_cumulative - like lc_get; also finds to-be-changed elements 4878c2ecf20Sopenharmony_ci * @lc: the lru cache to operate on 4888c2ecf20Sopenharmony_ci * @enr: the label to look up 4898c2ecf20Sopenharmony_ci * 4908c2ecf20Sopenharmony_ci * Unlike lc_get this also returns the element for @enr, if it is belonging to 4918c2ecf20Sopenharmony_ci * a pending transaction, so the return values are like for lc_get(), 4928c2ecf20Sopenharmony_ci * plus: 4938c2ecf20Sopenharmony_ci * 4948c2ecf20Sopenharmony_ci * pointer to an element already on the "to_be_changed" list. 4958c2ecf20Sopenharmony_ci * In this case, the cache was already marked %LC_DIRTY. 4968c2ecf20Sopenharmony_ci * 4978c2ecf20Sopenharmony_ci * Caller needs to make sure that the pending transaction is completed, 4988c2ecf20Sopenharmony_ci * before proceeding to actually use this element. 4998c2ecf20Sopenharmony_ci */ 5008c2ecf20Sopenharmony_cistruct lc_element *lc_get_cumulative(struct lru_cache *lc, unsigned int enr) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci return __lc_get(lc, enr, LC_GET_MAY_CHANGE|LC_GET_MAY_USE_UNCOMMITTED); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci/** 5068c2ecf20Sopenharmony_ci * lc_try_get - get element by label, if present; do not change the active set 5078c2ecf20Sopenharmony_ci * @lc: the lru cache to operate on 5088c2ecf20Sopenharmony_ci * @enr: the label to look up 5098c2ecf20Sopenharmony_ci * 5108c2ecf20Sopenharmony_ci * Finds an element in the cache, increases its usage count, 5118c2ecf20Sopenharmony_ci * "touches" and returns it. 5128c2ecf20Sopenharmony_ci * 5138c2ecf20Sopenharmony_ci * Return values: 5148c2ecf20Sopenharmony_ci * NULL 5158c2ecf20Sopenharmony_ci * The cache was marked %LC_STARVING, 5168c2ecf20Sopenharmony_ci * or the requested label was not in the active set 5178c2ecf20Sopenharmony_ci * 5188c2ecf20Sopenharmony_ci * pointer to the element with the REQUESTED element number. 5198c2ecf20Sopenharmony_ci * In this case, it can be used right away 5208c2ecf20Sopenharmony_ci */ 5218c2ecf20Sopenharmony_cistruct lc_element *lc_try_get(struct lru_cache *lc, unsigned int enr) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci return __lc_get(lc, enr, 0); 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci/** 5278c2ecf20Sopenharmony_ci * lc_committed - tell @lc that pending changes have been recorded 5288c2ecf20Sopenharmony_ci * @lc: the lru cache to operate on 5298c2ecf20Sopenharmony_ci * 5308c2ecf20Sopenharmony_ci * User is expected to serialize on explicit lc_try_lock_for_transaction() 5318c2ecf20Sopenharmony_ci * before the transaction is started, and later needs to lc_unlock() explicitly 5328c2ecf20Sopenharmony_ci * as well. 5338c2ecf20Sopenharmony_ci */ 5348c2ecf20Sopenharmony_civoid lc_committed(struct lru_cache *lc) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct lc_element *e, *tmp; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci PARANOIA_ENTRY(); 5398c2ecf20Sopenharmony_ci list_for_each_entry_safe(e, tmp, &lc->to_be_changed, list) { 5408c2ecf20Sopenharmony_ci /* count number of changes, not number of transactions */ 5418c2ecf20Sopenharmony_ci ++lc->changed; 5428c2ecf20Sopenharmony_ci e->lc_number = e->lc_new_number; 5438c2ecf20Sopenharmony_ci list_move(&e->list, &lc->in_use); 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci lc->pending_changes = 0; 5468c2ecf20Sopenharmony_ci RETURN(); 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci/** 5518c2ecf20Sopenharmony_ci * lc_put - give up refcnt of @e 5528c2ecf20Sopenharmony_ci * @lc: the lru cache to operate on 5538c2ecf20Sopenharmony_ci * @e: the element to put 5548c2ecf20Sopenharmony_ci * 5558c2ecf20Sopenharmony_ci * If refcnt reaches zero, the element is moved to the lru list, 5568c2ecf20Sopenharmony_ci * and a %LC_STARVING (if set) is cleared. 5578c2ecf20Sopenharmony_ci * Returns the new (post-decrement) refcnt. 5588c2ecf20Sopenharmony_ci */ 5598c2ecf20Sopenharmony_ciunsigned int lc_put(struct lru_cache *lc, struct lc_element *e) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci PARANOIA_ENTRY(); 5628c2ecf20Sopenharmony_ci PARANOIA_LC_ELEMENT(lc, e); 5638c2ecf20Sopenharmony_ci BUG_ON(e->refcnt == 0); 5648c2ecf20Sopenharmony_ci BUG_ON(e->lc_number != e->lc_new_number); 5658c2ecf20Sopenharmony_ci if (--e->refcnt == 0) { 5668c2ecf20Sopenharmony_ci /* move it to the front of LRU. */ 5678c2ecf20Sopenharmony_ci list_move(&e->list, &lc->lru); 5688c2ecf20Sopenharmony_ci lc->used--; 5698c2ecf20Sopenharmony_ci clear_bit_unlock(__LC_STARVING, &lc->flags); 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci RETURN(e->refcnt); 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci/** 5758c2ecf20Sopenharmony_ci * lc_element_by_index 5768c2ecf20Sopenharmony_ci * @lc: the lru cache to operate on 5778c2ecf20Sopenharmony_ci * @i: the index of the element to return 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_cistruct lc_element *lc_element_by_index(struct lru_cache *lc, unsigned i) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci BUG_ON(i >= lc->nr_elements); 5828c2ecf20Sopenharmony_ci BUG_ON(lc->lc_element[i] == NULL); 5838c2ecf20Sopenharmony_ci BUG_ON(lc->lc_element[i]->lc_index != i); 5848c2ecf20Sopenharmony_ci return lc->lc_element[i]; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci/** 5888c2ecf20Sopenharmony_ci * lc_index_of 5898c2ecf20Sopenharmony_ci * @lc: the lru cache to operate on 5908c2ecf20Sopenharmony_ci * @e: the element to query for its index position in lc->element 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_ciunsigned int lc_index_of(struct lru_cache *lc, struct lc_element *e) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci PARANOIA_LC_ELEMENT(lc, e); 5958c2ecf20Sopenharmony_ci return e->lc_index; 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci/** 5998c2ecf20Sopenharmony_ci * lc_set - associate index with label 6008c2ecf20Sopenharmony_ci * @lc: the lru cache to operate on 6018c2ecf20Sopenharmony_ci * @enr: the label to set 6028c2ecf20Sopenharmony_ci * @index: the element index to associate label with. 6038c2ecf20Sopenharmony_ci * 6048c2ecf20Sopenharmony_ci * Used to initialize the active set to some previously recorded state. 6058c2ecf20Sopenharmony_ci */ 6068c2ecf20Sopenharmony_civoid lc_set(struct lru_cache *lc, unsigned int enr, int index) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci struct lc_element *e; 6098c2ecf20Sopenharmony_ci struct list_head *lh; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (index < 0 || index >= lc->nr_elements) 6128c2ecf20Sopenharmony_ci return; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci e = lc_element_by_index(lc, index); 6158c2ecf20Sopenharmony_ci BUG_ON(e->lc_number != e->lc_new_number); 6168c2ecf20Sopenharmony_ci BUG_ON(e->refcnt != 0); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci e->lc_number = e->lc_new_number = enr; 6198c2ecf20Sopenharmony_ci hlist_del_init(&e->colision); 6208c2ecf20Sopenharmony_ci if (enr == LC_FREE) 6218c2ecf20Sopenharmony_ci lh = &lc->free; 6228c2ecf20Sopenharmony_ci else { 6238c2ecf20Sopenharmony_ci hlist_add_head(&e->colision, lc_hash_slot(lc, enr)); 6248c2ecf20Sopenharmony_ci lh = &lc->lru; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci list_move(&e->list, lh); 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci/** 6308c2ecf20Sopenharmony_ci * lc_dump - Dump a complete LRU cache to seq in textual form. 6318c2ecf20Sopenharmony_ci * @lc: the lru cache to operate on 6328c2ecf20Sopenharmony_ci * @seq: the &struct seq_file pointer to seq_printf into 6338c2ecf20Sopenharmony_ci * @utext: user supplied additional "heading" or other info 6348c2ecf20Sopenharmony_ci * @detail: function pointer the user may provide to dump further details 6358c2ecf20Sopenharmony_ci * of the object the lc_element is embedded in. May be NULL. 6368c2ecf20Sopenharmony_ci * Note: a leading space ' ' and trailing newline '\n' is implied. 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_civoid lc_seq_dump_details(struct seq_file *seq, struct lru_cache *lc, char *utext, 6398c2ecf20Sopenharmony_ci void (*detail) (struct seq_file *, struct lc_element *)) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci unsigned int nr_elements = lc->nr_elements; 6428c2ecf20Sopenharmony_ci struct lc_element *e; 6438c2ecf20Sopenharmony_ci int i; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci seq_printf(seq, "\tnn: lc_number (new nr) refcnt %s\n ", utext); 6468c2ecf20Sopenharmony_ci for (i = 0; i < nr_elements; i++) { 6478c2ecf20Sopenharmony_ci e = lc_element_by_index(lc, i); 6488c2ecf20Sopenharmony_ci if (e->lc_number != e->lc_new_number) 6498c2ecf20Sopenharmony_ci seq_printf(seq, "\t%5d: %6d %8d %6d ", 6508c2ecf20Sopenharmony_ci i, e->lc_number, e->lc_new_number, e->refcnt); 6518c2ecf20Sopenharmony_ci else 6528c2ecf20Sopenharmony_ci seq_printf(seq, "\t%5d: %6d %-8s %6d ", 6538c2ecf20Sopenharmony_ci i, e->lc_number, "-\"-", e->refcnt); 6548c2ecf20Sopenharmony_ci if (detail) 6558c2ecf20Sopenharmony_ci detail(seq, e); 6568c2ecf20Sopenharmony_ci seq_putc(seq, '\n'); 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_create); 6618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_reset); 6628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_destroy); 6638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_set); 6648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_del); 6658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_try_get); 6668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_find); 6678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_get); 6688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_put); 6698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_committed); 6708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_element_by_index); 6718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_index_of); 6728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_seq_printf_stats); 6738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_seq_dump_details); 6748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_try_lock); 6758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_is_used); 6768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(lc_get_cumulative); 677