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