lru_cache.c 3.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// SPDX-License-Identifier: GPL-2.0

#include <linux/mm.h>
#include "lru_cache.h"
#include "messages.h"

/*
 * Initialize a cache object.
 *
 * @cache:      The cache.
 * @max_size:   Maximum size (number of entries) for the cache.
 */
void btrfs_lru_cache_init(struct btrfs_lru_cache *cache, unsigned int max_size)
{
	INIT_LIST_HEAD(&cache->lru_list);
	mt_init(&cache->entries);
	cache->size = 0;
	cache->max_size = max_size;
}

21 22 23 24 25 26 27 28 29 30 31 32
static struct btrfs_lru_cache_entry *match_entry(struct list_head *head, u64 key)
{
	struct btrfs_lru_cache_entry *entry;

	list_for_each_entry(entry, head, list) {
		if (entry->key == key)
			return entry;
	}

	return NULL;
}

33 34 35 36 37 38 39 40 41 42 43
/*
 * Lookup for an entry in the cache.
 *
 * @cache:      The cache.
 * @key:        The key of the entry we are looking for.
 *
 * Returns the entry associated with the key or NULL if none found.
 */
struct btrfs_lru_cache_entry *btrfs_lru_cache_lookup(struct btrfs_lru_cache *cache,
						     u64 key)
{
44
	struct list_head *head;
45 46
	struct btrfs_lru_cache_entry *entry;

47 48 49 50 51
	head = mtree_load(&cache->entries, key);
	if (!head)
		return NULL;

	entry = match_entry(head, key);
52 53 54 55 56 57
	if (entry)
		list_move_tail(&entry->lru_list, &cache->lru_list);

	return entry;
}

58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
static void delete_entry(struct btrfs_lru_cache *cache,
			 struct btrfs_lru_cache_entry *entry)
{
	struct list_head *prev = entry->list.prev;

	ASSERT(cache->size > 0);
	ASSERT(!mtree_empty(&cache->entries));

	list_del(&entry->list);
	list_del(&entry->lru_list);

	if (list_empty(prev)) {
		struct list_head *head;

		/*
		 * If previous element in the list entry->list is now empty, it
		 * means it's a head entry not pointing to any cached entries,
		 * so remove it from the maple tree and free it.
		 */
		head = mtree_erase(&cache->entries, entry->key);
		ASSERT(head == prev);
		kfree(head);
	}

	kfree(entry);
	cache->size--;
}

86 87 88 89 90 91 92 93 94 95 96 97
/*
 * Store an entry in the cache.
 *
 * @cache:      The cache.
 * @entry:      The entry to store.
 *
 * Returns 0 on success and < 0 on error.
 */
int btrfs_lru_cache_store(struct btrfs_lru_cache *cache,
			  struct btrfs_lru_cache_entry *new_entry,
			  gfp_t gfp)
{
98 99
	const u64 key = new_entry->key;
	struct list_head *head;
100 101
	int ret;

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
	head = kmalloc(sizeof(*head), gfp);
	if (!head)
		return -ENOMEM;

	ret = mtree_insert(&cache->entries, key, head, gfp);
	if (ret == 0) {
		INIT_LIST_HEAD(head);
		list_add_tail(&new_entry->list, head);
	} else if (ret == -EEXIST) {
		kfree(head);
		head = mtree_load(&cache->entries, key);
		ASSERT(head != NULL);
		if (match_entry(head, key) != NULL)
			return -EEXIST;
		list_add_tail(&new_entry->list, head);
	} else if (ret < 0) {
		kfree(head);
		return ret;
	}

122 123 124 125 126 127
	if (cache->size == cache->max_size) {
		struct btrfs_lru_cache_entry *lru_entry;

		lru_entry = list_first_entry(&cache->lru_list,
					     struct btrfs_lru_cache_entry,
					     lru_list);
128
		delete_entry(cache, lru_entry);
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
	}

	list_add_tail(&new_entry->lru_list, &cache->lru_list);
	cache->size++;

	return 0;
}

/*
 * Empty a cache.
 *
 * @cache:     The cache to empty.
 *
 * Removes all entries from the cache.
 */
void btrfs_lru_cache_clear(struct btrfs_lru_cache *cache)
{
	struct btrfs_lru_cache_entry *entry;
	struct btrfs_lru_cache_entry *tmp;

	list_for_each_entry_safe(entry, tmp, &cache->lru_list, lru_list)
150
		delete_entry(cache, entry);
151

152 153
	ASSERT(cache->size == 0);
	ASSERT(mtree_empty(&cache->entries));
154
}