18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 38c2ecf20Sopenharmony_ci#include <linux/slab.h> 48c2ecf20Sopenharmony_ci#include <linux/list.h> 58c2ecf20Sopenharmony_ci#include <linux/list_bl.h> 68c2ecf20Sopenharmony_ci#include <linux/module.h> 78c2ecf20Sopenharmony_ci#include <linux/sched.h> 88c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 98c2ecf20Sopenharmony_ci#include <linux/mbcache.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* 128c2ecf20Sopenharmony_ci * Mbcache is a simple key-value store. Keys need not be unique, however 138c2ecf20Sopenharmony_ci * key-value pairs are expected to be unique (we use this fact in 148c2ecf20Sopenharmony_ci * mb_cache_entry_delete_or_get()). 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Ext2 and ext4 use this cache for deduplication of extended attribute blocks. 178c2ecf20Sopenharmony_ci * Ext4 also uses it for deduplication of xattr values stored in inodes. 188c2ecf20Sopenharmony_ci * They use hash of data as a key and provide a value that may represent a 198c2ecf20Sopenharmony_ci * block or inode number. That's why keys need not be unique (hash of different 208c2ecf20Sopenharmony_ci * data may be the same). However user provided value always uniquely 218c2ecf20Sopenharmony_ci * identifies a cache entry. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * We provide functions for creation and removal of entries, search by key, 248c2ecf20Sopenharmony_ci * and a special "delete entry with given key-value pair" operation. Fixed 258c2ecf20Sopenharmony_ci * size hash table is used for fast key lookups. 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct mb_cache { 298c2ecf20Sopenharmony_ci /* Hash table of entries */ 308c2ecf20Sopenharmony_ci struct hlist_bl_head *c_hash; 318c2ecf20Sopenharmony_ci /* log2 of hash table size */ 328c2ecf20Sopenharmony_ci int c_bucket_bits; 338c2ecf20Sopenharmony_ci /* Maximum entries in cache to avoid degrading hash too much */ 348c2ecf20Sopenharmony_ci unsigned long c_max_entries; 358c2ecf20Sopenharmony_ci /* Protects c_list, c_entry_count */ 368c2ecf20Sopenharmony_ci spinlock_t c_list_lock; 378c2ecf20Sopenharmony_ci struct list_head c_list; 388c2ecf20Sopenharmony_ci /* Number of entries in cache */ 398c2ecf20Sopenharmony_ci unsigned long c_entry_count; 408c2ecf20Sopenharmony_ci struct shrinker c_shrink; 418c2ecf20Sopenharmony_ci /* Work for shrinking when the cache has too many entries */ 428c2ecf20Sopenharmony_ci struct work_struct c_shrink_work; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic struct kmem_cache *mb_entry_cache; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic unsigned long mb_cache_shrink(struct mb_cache *cache, 488c2ecf20Sopenharmony_ci unsigned long nr_to_scan); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic inline struct hlist_bl_head *mb_cache_entry_head(struct mb_cache *cache, 518c2ecf20Sopenharmony_ci u32 key) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci return &cache->c_hash[hash_32(key, cache->c_bucket_bits)]; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* 578c2ecf20Sopenharmony_ci * Number of entries to reclaim synchronously when there are too many entries 588c2ecf20Sopenharmony_ci * in cache 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci#define SYNC_SHRINK_BATCH 64 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* 638c2ecf20Sopenharmony_ci * mb_cache_entry_create - create entry in cache 648c2ecf20Sopenharmony_ci * @cache - cache where the entry should be created 658c2ecf20Sopenharmony_ci * @mask - gfp mask with which the entry should be allocated 668c2ecf20Sopenharmony_ci * @key - key of the entry 678c2ecf20Sopenharmony_ci * @value - value of the entry 688c2ecf20Sopenharmony_ci * @reusable - is the entry reusable by others? 698c2ecf20Sopenharmony_ci * 708c2ecf20Sopenharmony_ci * Creates entry in @cache with key @key and value @value. The function returns 718c2ecf20Sopenharmony_ci * -EBUSY if entry with the same key and value already exists in cache. 728c2ecf20Sopenharmony_ci * Otherwise 0 is returned. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ciint mb_cache_entry_create(struct mb_cache *cache, gfp_t mask, u32 key, 758c2ecf20Sopenharmony_ci u64 value, bool reusable) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct mb_cache_entry *entry, *dup; 788c2ecf20Sopenharmony_ci struct hlist_bl_node *dup_node; 798c2ecf20Sopenharmony_ci struct hlist_bl_head *head; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* Schedule background reclaim if there are too many entries */ 828c2ecf20Sopenharmony_ci if (cache->c_entry_count >= cache->c_max_entries) 838c2ecf20Sopenharmony_ci schedule_work(&cache->c_shrink_work); 848c2ecf20Sopenharmony_ci /* Do some sync reclaim if background reclaim cannot keep up */ 858c2ecf20Sopenharmony_ci if (cache->c_entry_count >= 2*cache->c_max_entries) 868c2ecf20Sopenharmony_ci mb_cache_shrink(cache, SYNC_SHRINK_BATCH); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci entry = kmem_cache_alloc(mb_entry_cache, mask); 898c2ecf20Sopenharmony_ci if (!entry) 908c2ecf20Sopenharmony_ci return -ENOMEM; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&entry->e_list); 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * We create entry with two references. One reference is kept by the 958c2ecf20Sopenharmony_ci * hash table, the other reference is used to protect us from 968c2ecf20Sopenharmony_ci * mb_cache_entry_delete_or_get() until the entry is fully setup. This 978c2ecf20Sopenharmony_ci * avoids nesting of cache->c_list_lock into hash table bit locks which 988c2ecf20Sopenharmony_ci * is problematic for RT. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ci atomic_set(&entry->e_refcnt, 2); 1018c2ecf20Sopenharmony_ci entry->e_key = key; 1028c2ecf20Sopenharmony_ci entry->e_value = value; 1038c2ecf20Sopenharmony_ci entry->e_flags = 0; 1048c2ecf20Sopenharmony_ci if (reusable) 1058c2ecf20Sopenharmony_ci set_bit(MBE_REUSABLE_B, &entry->e_flags); 1068c2ecf20Sopenharmony_ci head = mb_cache_entry_head(cache, key); 1078c2ecf20Sopenharmony_ci hlist_bl_lock(head); 1088c2ecf20Sopenharmony_ci hlist_bl_for_each_entry(dup, dup_node, head, e_hash_list) { 1098c2ecf20Sopenharmony_ci if (dup->e_key == key && dup->e_value == value) { 1108c2ecf20Sopenharmony_ci hlist_bl_unlock(head); 1118c2ecf20Sopenharmony_ci kmem_cache_free(mb_entry_cache, entry); 1128c2ecf20Sopenharmony_ci return -EBUSY; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci hlist_bl_add_head(&entry->e_hash_list, head); 1168c2ecf20Sopenharmony_ci hlist_bl_unlock(head); 1178c2ecf20Sopenharmony_ci spin_lock(&cache->c_list_lock); 1188c2ecf20Sopenharmony_ci list_add_tail(&entry->e_list, &cache->c_list); 1198c2ecf20Sopenharmony_ci cache->c_entry_count++; 1208c2ecf20Sopenharmony_ci spin_unlock(&cache->c_list_lock); 1218c2ecf20Sopenharmony_ci mb_cache_entry_put(cache, entry); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mb_cache_entry_create); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_civoid __mb_cache_entry_free(struct mb_cache *cache, struct mb_cache_entry *entry) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct hlist_bl_head *head; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci head = mb_cache_entry_head(cache, entry->e_key); 1328c2ecf20Sopenharmony_ci hlist_bl_lock(head); 1338c2ecf20Sopenharmony_ci hlist_bl_del(&entry->e_hash_list); 1348c2ecf20Sopenharmony_ci hlist_bl_unlock(head); 1358c2ecf20Sopenharmony_ci kmem_cache_free(mb_entry_cache, entry); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__mb_cache_entry_free); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* 1408c2ecf20Sopenharmony_ci * mb_cache_entry_wait_unused - wait to be the last user of the entry 1418c2ecf20Sopenharmony_ci * 1428c2ecf20Sopenharmony_ci * @entry - entry to work on 1438c2ecf20Sopenharmony_ci * 1448c2ecf20Sopenharmony_ci * Wait to be the last user of the entry. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_civoid mb_cache_entry_wait_unused(struct mb_cache_entry *entry) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci wait_var_event(&entry->e_refcnt, atomic_read(&entry->e_refcnt) <= 2); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mb_cache_entry_wait_unused); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic struct mb_cache_entry *__entry_find(struct mb_cache *cache, 1538c2ecf20Sopenharmony_ci struct mb_cache_entry *entry, 1548c2ecf20Sopenharmony_ci u32 key) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct mb_cache_entry *old_entry = entry; 1578c2ecf20Sopenharmony_ci struct hlist_bl_node *node; 1588c2ecf20Sopenharmony_ci struct hlist_bl_head *head; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci head = mb_cache_entry_head(cache, key); 1618c2ecf20Sopenharmony_ci hlist_bl_lock(head); 1628c2ecf20Sopenharmony_ci if (entry && !hlist_bl_unhashed(&entry->e_hash_list)) 1638c2ecf20Sopenharmony_ci node = entry->e_hash_list.next; 1648c2ecf20Sopenharmony_ci else 1658c2ecf20Sopenharmony_ci node = hlist_bl_first(head); 1668c2ecf20Sopenharmony_ci while (node) { 1678c2ecf20Sopenharmony_ci entry = hlist_bl_entry(node, struct mb_cache_entry, 1688c2ecf20Sopenharmony_ci e_hash_list); 1698c2ecf20Sopenharmony_ci if (entry->e_key == key && 1708c2ecf20Sopenharmony_ci test_bit(MBE_REUSABLE_B, &entry->e_flags) && 1718c2ecf20Sopenharmony_ci atomic_inc_not_zero(&entry->e_refcnt)) 1728c2ecf20Sopenharmony_ci goto out; 1738c2ecf20Sopenharmony_ci node = node->next; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci entry = NULL; 1768c2ecf20Sopenharmony_ciout: 1778c2ecf20Sopenharmony_ci hlist_bl_unlock(head); 1788c2ecf20Sopenharmony_ci if (old_entry) 1798c2ecf20Sopenharmony_ci mb_cache_entry_put(cache, old_entry); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return entry; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/* 1858c2ecf20Sopenharmony_ci * mb_cache_entry_find_first - find the first reusable entry with the given key 1868c2ecf20Sopenharmony_ci * @cache: cache where we should search 1878c2ecf20Sopenharmony_ci * @key: key to look for 1888c2ecf20Sopenharmony_ci * 1898c2ecf20Sopenharmony_ci * Search in @cache for a reusable entry with key @key. Grabs reference to the 1908c2ecf20Sopenharmony_ci * first reusable entry found and returns the entry. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_cistruct mb_cache_entry *mb_cache_entry_find_first(struct mb_cache *cache, 1938c2ecf20Sopenharmony_ci u32 key) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci return __entry_find(cache, NULL, key); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mb_cache_entry_find_first); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* 2008c2ecf20Sopenharmony_ci * mb_cache_entry_find_next - find next reusable entry with the same key 2018c2ecf20Sopenharmony_ci * @cache: cache where we should search 2028c2ecf20Sopenharmony_ci * @entry: entry to start search from 2038c2ecf20Sopenharmony_ci * 2048c2ecf20Sopenharmony_ci * Finds next reusable entry in the hash chain which has the same key as @entry. 2058c2ecf20Sopenharmony_ci * If @entry is unhashed (which can happen when deletion of entry races with the 2068c2ecf20Sopenharmony_ci * search), finds the first reusable entry in the hash chain. The function drops 2078c2ecf20Sopenharmony_ci * reference to @entry and returns with a reference to the found entry. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_cistruct mb_cache_entry *mb_cache_entry_find_next(struct mb_cache *cache, 2108c2ecf20Sopenharmony_ci struct mb_cache_entry *entry) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci return __entry_find(cache, entry, entry->e_key); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mb_cache_entry_find_next); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* 2178c2ecf20Sopenharmony_ci * mb_cache_entry_get - get a cache entry by value (and key) 2188c2ecf20Sopenharmony_ci * @cache - cache we work with 2198c2ecf20Sopenharmony_ci * @key - key 2208c2ecf20Sopenharmony_ci * @value - value 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_cistruct mb_cache_entry *mb_cache_entry_get(struct mb_cache *cache, u32 key, 2238c2ecf20Sopenharmony_ci u64 value) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct hlist_bl_node *node; 2268c2ecf20Sopenharmony_ci struct hlist_bl_head *head; 2278c2ecf20Sopenharmony_ci struct mb_cache_entry *entry; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci head = mb_cache_entry_head(cache, key); 2308c2ecf20Sopenharmony_ci hlist_bl_lock(head); 2318c2ecf20Sopenharmony_ci hlist_bl_for_each_entry(entry, node, head, e_hash_list) { 2328c2ecf20Sopenharmony_ci if (entry->e_key == key && entry->e_value == value && 2338c2ecf20Sopenharmony_ci atomic_inc_not_zero(&entry->e_refcnt)) 2348c2ecf20Sopenharmony_ci goto out; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci entry = NULL; 2378c2ecf20Sopenharmony_ciout: 2388c2ecf20Sopenharmony_ci hlist_bl_unlock(head); 2398c2ecf20Sopenharmony_ci return entry; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mb_cache_entry_get); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/* mb_cache_entry_delete - try to remove a cache entry 2448c2ecf20Sopenharmony_ci * @cache - cache we work with 2458c2ecf20Sopenharmony_ci * @key - key 2468c2ecf20Sopenharmony_ci * @value - value 2478c2ecf20Sopenharmony_ci * 2488c2ecf20Sopenharmony_ci * Remove entry from cache @cache with key @key and value @value. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_civoid mb_cache_entry_delete(struct mb_cache *cache, u32 key, u64 value) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct hlist_bl_node *node; 2538c2ecf20Sopenharmony_ci struct hlist_bl_head *head; 2548c2ecf20Sopenharmony_ci struct mb_cache_entry *entry; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci head = mb_cache_entry_head(cache, key); 2578c2ecf20Sopenharmony_ci hlist_bl_lock(head); 2588c2ecf20Sopenharmony_ci hlist_bl_for_each_entry(entry, node, head, e_hash_list) { 2598c2ecf20Sopenharmony_ci if (entry->e_key == key && entry->e_value == value) { 2608c2ecf20Sopenharmony_ci /* We keep hash list reference to keep entry alive */ 2618c2ecf20Sopenharmony_ci hlist_bl_del_init(&entry->e_hash_list); 2628c2ecf20Sopenharmony_ci hlist_bl_unlock(head); 2638c2ecf20Sopenharmony_ci spin_lock(&cache->c_list_lock); 2648c2ecf20Sopenharmony_ci if (!list_empty(&entry->e_list)) { 2658c2ecf20Sopenharmony_ci list_del_init(&entry->e_list); 2668c2ecf20Sopenharmony_ci if (!WARN_ONCE(cache->c_entry_count == 0, 2678c2ecf20Sopenharmony_ci "mbcache: attempt to decrement c_entry_count past zero")) 2688c2ecf20Sopenharmony_ci cache->c_entry_count--; 2698c2ecf20Sopenharmony_ci atomic_dec(&entry->e_refcnt); 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci spin_unlock(&cache->c_list_lock); 2728c2ecf20Sopenharmony_ci mb_cache_entry_put(cache, entry); 2738c2ecf20Sopenharmony_ci return; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci hlist_bl_unlock(head); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mb_cache_entry_delete); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/* mb_cache_entry_delete_or_get - remove a cache entry if it has no users 2818c2ecf20Sopenharmony_ci * @cache - cache we work with 2828c2ecf20Sopenharmony_ci * @key - key 2838c2ecf20Sopenharmony_ci * @value - value 2848c2ecf20Sopenharmony_ci * 2858c2ecf20Sopenharmony_ci * Remove entry from cache @cache with key @key and value @value. The removal 2868c2ecf20Sopenharmony_ci * happens only if the entry is unused. The function returns NULL in case the 2878c2ecf20Sopenharmony_ci * entry was successfully removed or there's no entry in cache. Otherwise the 2888c2ecf20Sopenharmony_ci * function grabs reference of the entry that we failed to delete because it 2898c2ecf20Sopenharmony_ci * still has users and return it. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_cistruct mb_cache_entry *mb_cache_entry_delete_or_get(struct mb_cache *cache, 2928c2ecf20Sopenharmony_ci u32 key, u64 value) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct mb_cache_entry *entry; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci entry = mb_cache_entry_get(cache, key, value); 2978c2ecf20Sopenharmony_ci if (!entry) 2988c2ecf20Sopenharmony_ci return NULL; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* 3018c2ecf20Sopenharmony_ci * Drop the ref we got from mb_cache_entry_get() and the initial hash 3028c2ecf20Sopenharmony_ci * ref if we are the last user 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_ci if (atomic_cmpxchg(&entry->e_refcnt, 2, 0) != 2) 3058c2ecf20Sopenharmony_ci return entry; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci spin_lock(&cache->c_list_lock); 3088c2ecf20Sopenharmony_ci if (!list_empty(&entry->e_list)) 3098c2ecf20Sopenharmony_ci list_del_init(&entry->e_list); 3108c2ecf20Sopenharmony_ci cache->c_entry_count--; 3118c2ecf20Sopenharmony_ci spin_unlock(&cache->c_list_lock); 3128c2ecf20Sopenharmony_ci __mb_cache_entry_free(cache, entry); 3138c2ecf20Sopenharmony_ci return NULL; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mb_cache_entry_delete_or_get); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci/* mb_cache_entry_touch - cache entry got used 3188c2ecf20Sopenharmony_ci * @cache - cache the entry belongs to 3198c2ecf20Sopenharmony_ci * @entry - entry that got used 3208c2ecf20Sopenharmony_ci * 3218c2ecf20Sopenharmony_ci * Marks entry as used to give hit higher chances of surviving in cache. 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_civoid mb_cache_entry_touch(struct mb_cache *cache, 3248c2ecf20Sopenharmony_ci struct mb_cache_entry *entry) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci set_bit(MBE_REFERENCED_B, &entry->e_flags); 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mb_cache_entry_touch); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic unsigned long mb_cache_count(struct shrinker *shrink, 3318c2ecf20Sopenharmony_ci struct shrink_control *sc) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct mb_cache *cache = container_of(shrink, struct mb_cache, 3348c2ecf20Sopenharmony_ci c_shrink); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return cache->c_entry_count; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci/* Shrink number of entries in cache */ 3408c2ecf20Sopenharmony_cistatic unsigned long mb_cache_shrink(struct mb_cache *cache, 3418c2ecf20Sopenharmony_ci unsigned long nr_to_scan) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct mb_cache_entry *entry; 3448c2ecf20Sopenharmony_ci unsigned long shrunk = 0; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci spin_lock(&cache->c_list_lock); 3478c2ecf20Sopenharmony_ci while (nr_to_scan-- && !list_empty(&cache->c_list)) { 3488c2ecf20Sopenharmony_ci entry = list_first_entry(&cache->c_list, 3498c2ecf20Sopenharmony_ci struct mb_cache_entry, e_list); 3508c2ecf20Sopenharmony_ci /* Drop initial hash reference if there is no user */ 3518c2ecf20Sopenharmony_ci if (test_bit(MBE_REFERENCED_B, &entry->e_flags) || 3528c2ecf20Sopenharmony_ci atomic_cmpxchg(&entry->e_refcnt, 1, 0) != 1) { 3538c2ecf20Sopenharmony_ci clear_bit(MBE_REFERENCED_B, &entry->e_flags); 3548c2ecf20Sopenharmony_ci list_move_tail(&entry->e_list, &cache->c_list); 3558c2ecf20Sopenharmony_ci continue; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci list_del_init(&entry->e_list); 3588c2ecf20Sopenharmony_ci cache->c_entry_count--; 3598c2ecf20Sopenharmony_ci spin_unlock(&cache->c_list_lock); 3608c2ecf20Sopenharmony_ci __mb_cache_entry_free(cache, entry); 3618c2ecf20Sopenharmony_ci shrunk++; 3628c2ecf20Sopenharmony_ci cond_resched(); 3638c2ecf20Sopenharmony_ci spin_lock(&cache->c_list_lock); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci spin_unlock(&cache->c_list_lock); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return shrunk; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic unsigned long mb_cache_scan(struct shrinker *shrink, 3718c2ecf20Sopenharmony_ci struct shrink_control *sc) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct mb_cache *cache = container_of(shrink, struct mb_cache, 3748c2ecf20Sopenharmony_ci c_shrink); 3758c2ecf20Sopenharmony_ci return mb_cache_shrink(cache, sc->nr_to_scan); 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci/* We shrink 1/X of the cache when we have too many entries in it */ 3798c2ecf20Sopenharmony_ci#define SHRINK_DIVISOR 16 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic void mb_cache_shrink_worker(struct work_struct *work) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct mb_cache *cache = container_of(work, struct mb_cache, 3848c2ecf20Sopenharmony_ci c_shrink_work); 3858c2ecf20Sopenharmony_ci mb_cache_shrink(cache, cache->c_max_entries / SHRINK_DIVISOR); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci/* 3898c2ecf20Sopenharmony_ci * mb_cache_create - create cache 3908c2ecf20Sopenharmony_ci * @bucket_bits: log2 of the hash table size 3918c2ecf20Sopenharmony_ci * 3928c2ecf20Sopenharmony_ci * Create cache for keys with 2^bucket_bits hash entries. 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_cistruct mb_cache *mb_cache_create(int bucket_bits) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct mb_cache *cache; 3978c2ecf20Sopenharmony_ci unsigned long bucket_count = 1UL << bucket_bits; 3988c2ecf20Sopenharmony_ci unsigned long i; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci cache = kzalloc(sizeof(struct mb_cache), GFP_KERNEL); 4018c2ecf20Sopenharmony_ci if (!cache) 4028c2ecf20Sopenharmony_ci goto err_out; 4038c2ecf20Sopenharmony_ci cache->c_bucket_bits = bucket_bits; 4048c2ecf20Sopenharmony_ci cache->c_max_entries = bucket_count << 4; 4058c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cache->c_list); 4068c2ecf20Sopenharmony_ci spin_lock_init(&cache->c_list_lock); 4078c2ecf20Sopenharmony_ci cache->c_hash = kmalloc_array(bucket_count, 4088c2ecf20Sopenharmony_ci sizeof(struct hlist_bl_head), 4098c2ecf20Sopenharmony_ci GFP_KERNEL); 4108c2ecf20Sopenharmony_ci if (!cache->c_hash) { 4118c2ecf20Sopenharmony_ci kfree(cache); 4128c2ecf20Sopenharmony_ci goto err_out; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci for (i = 0; i < bucket_count; i++) 4158c2ecf20Sopenharmony_ci INIT_HLIST_BL_HEAD(&cache->c_hash[i]); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci cache->c_shrink.count_objects = mb_cache_count; 4188c2ecf20Sopenharmony_ci cache->c_shrink.scan_objects = mb_cache_scan; 4198c2ecf20Sopenharmony_ci cache->c_shrink.seeks = DEFAULT_SEEKS; 4208c2ecf20Sopenharmony_ci if (register_shrinker(&cache->c_shrink)) { 4218c2ecf20Sopenharmony_ci kfree(cache->c_hash); 4228c2ecf20Sopenharmony_ci kfree(cache); 4238c2ecf20Sopenharmony_ci goto err_out; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci INIT_WORK(&cache->c_shrink_work, mb_cache_shrink_worker); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return cache; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cierr_out: 4318c2ecf20Sopenharmony_ci return NULL; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mb_cache_create); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci/* 4368c2ecf20Sopenharmony_ci * mb_cache_destroy - destroy cache 4378c2ecf20Sopenharmony_ci * @cache: the cache to destroy 4388c2ecf20Sopenharmony_ci * 4398c2ecf20Sopenharmony_ci * Free all entries in cache and cache itself. Caller must make sure nobody 4408c2ecf20Sopenharmony_ci * (except shrinker) can reach @cache when calling this. 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_civoid mb_cache_destroy(struct mb_cache *cache) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct mb_cache_entry *entry, *next; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci unregister_shrinker(&cache->c_shrink); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* 4498c2ecf20Sopenharmony_ci * We don't bother with any locking. Cache must not be used at this 4508c2ecf20Sopenharmony_ci * point. 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_ci list_for_each_entry_safe(entry, next, &cache->c_list, e_list) { 4538c2ecf20Sopenharmony_ci list_del(&entry->e_list); 4548c2ecf20Sopenharmony_ci WARN_ON(atomic_read(&entry->e_refcnt) != 1); 4558c2ecf20Sopenharmony_ci mb_cache_entry_put(cache, entry); 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci kfree(cache->c_hash); 4588c2ecf20Sopenharmony_ci kfree(cache); 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mb_cache_destroy); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic int __init mbcache_init(void) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci mb_entry_cache = kmem_cache_create("mbcache", 4658c2ecf20Sopenharmony_ci sizeof(struct mb_cache_entry), 0, 4668c2ecf20Sopenharmony_ci SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL); 4678c2ecf20Sopenharmony_ci if (!mb_entry_cache) 4688c2ecf20Sopenharmony_ci return -ENOMEM; 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic void __exit mbcache_exit(void) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci kmem_cache_destroy(mb_entry_cache); 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cimodule_init(mbcache_init) 4788c2ecf20Sopenharmony_cimodule_exit(mbcache_exit) 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jan Kara <jack@suse.cz>"); 4818c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Meta block cache (for extended attributes)"); 4828c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 483