| // SPDX-License-Identifier: GPL-2.0-or-later |
| #include "cache.h" |
| #include "backing_dev.h" |
| #include "cache_dev.h" |
| #include "dm_pcache.h" |
| |
| struct pcache_cache_kset_onmedia pcache_empty_kset = { 0 }; |
| |
| void cache_key_init(struct pcache_cache_tree *cache_tree, struct pcache_cache_key *key) |
| { |
| kref_init(&key->ref); |
| key->cache_tree = cache_tree; |
| INIT_LIST_HEAD(&key->list_node); |
| RB_CLEAR_NODE(&key->rb_node); |
| } |
| |
| struct pcache_cache_key *cache_key_alloc(struct pcache_cache_tree *cache_tree, gfp_t gfp_mask) |
| { |
| struct pcache_cache_key *key; |
| |
| key = mempool_alloc(&cache_tree->key_pool, gfp_mask); |
| if (!key) |
| return NULL; |
| |
| memset(key, 0, sizeof(struct pcache_cache_key)); |
| cache_key_init(cache_tree, key); |
| |
| return key; |
| } |
| |
| /** |
| * cache_key_get - Increment the reference count of a cache key. |
| * @key: Pointer to the pcache_cache_key structure. |
| * |
| * This function increments the reference count of the specified cache key, |
| * ensuring that it is not freed while still in use. |
| */ |
| void cache_key_get(struct pcache_cache_key *key) |
| { |
| kref_get(&key->ref); |
| } |
| |
| /** |
| * cache_key_destroy - Free a cache key structure when its reference count drops to zero. |
| * @ref: Pointer to the kref structure. |
| * |
| * This function is called when the reference count of the cache key reaches zero. |
| * It frees the allocated cache key back to the slab cache. |
| */ |
| static void cache_key_destroy(struct kref *ref) |
| { |
| struct pcache_cache_key *key = container_of(ref, struct pcache_cache_key, ref); |
| struct pcache_cache_tree *cache_tree = key->cache_tree; |
| |
| mempool_free(key, &cache_tree->key_pool); |
| } |
| |
| void cache_key_put(struct pcache_cache_key *key) |
| { |
| kref_put(&key->ref, cache_key_destroy); |
| } |
| |
| void cache_pos_advance(struct pcache_cache_pos *pos, u32 len) |
| { |
| /* Ensure enough space remains in the current segment */ |
| BUG_ON(cache_seg_remain(pos) < len); |
| |
| pos->seg_off += len; |
| } |
| |
| static void cache_key_encode(struct pcache_cache *cache, |
| struct pcache_cache_key_onmedia *key_onmedia, |
| struct pcache_cache_key *key) |
| { |
| key_onmedia->off = key->off; |
| key_onmedia->len = key->len; |
| |
| key_onmedia->cache_seg_id = key->cache_pos.cache_seg->cache_seg_id; |
| key_onmedia->cache_seg_off = key->cache_pos.seg_off; |
| |
| key_onmedia->seg_gen = key->seg_gen; |
| key_onmedia->flags = key->flags; |
| |
| if (cache_data_crc_on(cache)) |
| key_onmedia->data_crc = cache_key_data_crc(key); |
| } |
| |
| int cache_key_decode(struct pcache_cache *cache, |
| struct pcache_cache_key_onmedia *key_onmedia, |
| struct pcache_cache_key *key) |
| { |
| struct dm_pcache *pcache = CACHE_TO_PCACHE(cache); |
| |
| key->off = key_onmedia->off; |
| key->len = key_onmedia->len; |
| |
| key->cache_pos.cache_seg = &cache->segments[key_onmedia->cache_seg_id]; |
| key->cache_pos.seg_off = key_onmedia->cache_seg_off; |
| |
| key->seg_gen = key_onmedia->seg_gen; |
| key->flags = key_onmedia->flags; |
| |
| if (cache_data_crc_on(cache) && |
| key_onmedia->data_crc != cache_key_data_crc(key)) { |
| pcache_dev_err(pcache, "key: %llu:%u seg %u:%u data_crc error: %x, expected: %x\n", |
| key->off, key->len, key->cache_pos.cache_seg->cache_seg_id, |
| key->cache_pos.seg_off, cache_key_data_crc(key), key_onmedia->data_crc); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static void append_last_kset(struct pcache_cache *cache, u32 next_seg) |
| { |
| struct pcache_cache_kset_onmedia kset_onmedia = { 0 }; |
| |
| kset_onmedia.flags |= PCACHE_KSET_FLAGS_LAST; |
| kset_onmedia.next_cache_seg_id = next_seg; |
| kset_onmedia.magic = PCACHE_KSET_MAGIC; |
| kset_onmedia.crc = cache_kset_crc(&kset_onmedia); |
| |
| memcpy_flushcache(get_key_head_addr(cache), &kset_onmedia, sizeof(struct pcache_cache_kset_onmedia)); |
| pmem_wmb(); |
| cache_pos_advance(&cache->key_head, sizeof(struct pcache_cache_kset_onmedia)); |
| } |
| |
| int cache_kset_close(struct pcache_cache *cache, struct pcache_cache_kset *kset) |
| { |
| struct pcache_cache_kset_onmedia *kset_onmedia; |
| u32 kset_onmedia_size; |
| int ret; |
| |
| kset_onmedia = &kset->kset_onmedia; |
| |
| if (!kset_onmedia->key_num) |
| return 0; |
| |
| kset_onmedia_size = struct_size(kset_onmedia, data, kset_onmedia->key_num); |
| |
| spin_lock(&cache->key_head_lock); |
| again: |
| /* Reserve space for the last kset */ |
| if (cache_seg_remain(&cache->key_head) < kset_onmedia_size + sizeof(struct pcache_cache_kset_onmedia)) { |
| struct pcache_cache_segment *next_seg; |
| |
| next_seg = get_cache_segment(cache); |
| if (!next_seg) { |
| ret = -EBUSY; |
| goto out; |
| } |
| |
| /* clear outdated kset in next seg */ |
| memcpy_flushcache(next_seg->segment.data, &pcache_empty_kset, |
| sizeof(struct pcache_cache_kset_onmedia)); |
| append_last_kset(cache, next_seg->cache_seg_id); |
| cache->key_head.cache_seg = next_seg; |
| cache->key_head.seg_off = 0; |
| goto again; |
| } |
| |
| kset_onmedia->magic = PCACHE_KSET_MAGIC; |
| kset_onmedia->crc = cache_kset_crc(kset_onmedia); |
| |
| /* clear outdated kset after current kset */ |
| memcpy_flushcache(get_key_head_addr(cache) + kset_onmedia_size, &pcache_empty_kset, |
| sizeof(struct pcache_cache_kset_onmedia)); |
| /* write current kset into segment */ |
| memcpy_flushcache(get_key_head_addr(cache), kset_onmedia, kset_onmedia_size); |
| pmem_wmb(); |
| |
| /* reset kset_onmedia */ |
| memset(kset_onmedia, 0, sizeof(struct pcache_cache_kset_onmedia)); |
| cache_pos_advance(&cache->key_head, kset_onmedia_size); |
| |
| ret = 0; |
| out: |
| spin_unlock(&cache->key_head_lock); |
| |
| return ret; |
| } |
| |
| /** |
| * cache_key_append - Append a cache key to the related kset. |
| * @cache: Pointer to the pcache_cache structure. |
| * @key: Pointer to the cache key structure to append. |
| * @force_close: Need to close current kset if true. |
| * |
| * This function appends a cache key to the appropriate kset. If the kset |
| * is full, it closes the kset. If not, it queues a flush work to write |
| * the kset to media. |
| * |
| * Returns 0 on success, or a negative error code on failure. |
| */ |
| int cache_key_append(struct pcache_cache *cache, struct pcache_cache_key *key, bool force_close) |
| { |
| struct pcache_cache_kset *kset; |
| struct pcache_cache_kset_onmedia *kset_onmedia; |
| struct pcache_cache_key_onmedia *key_onmedia; |
| u32 kset_id = get_kset_id(cache, key->off); |
| int ret = 0; |
| |
| kset = get_kset(cache, kset_id); |
| kset_onmedia = &kset->kset_onmedia; |
| |
| spin_lock(&kset->kset_lock); |
| key_onmedia = &kset_onmedia->data[kset_onmedia->key_num]; |
| cache_key_encode(cache, key_onmedia, key); |
| |
| /* Check if the current kset has reached the maximum number of keys */ |
| if (++kset_onmedia->key_num == PCACHE_KSET_KEYS_MAX || force_close) { |
| /* If full, close the kset */ |
| ret = cache_kset_close(cache, kset); |
| if (ret) { |
| kset_onmedia->key_num--; |
| goto out; |
| } |
| } else { |
| /* If not full, queue a delayed work to flush the kset */ |
| queue_delayed_work(cache_get_wq(cache), &kset->flush_work, 1 * HZ); |
| } |
| out: |
| spin_unlock(&kset->kset_lock); |
| |
| return ret; |
| } |
| |
| /** |
| * cache_subtree_walk - Traverse the cache tree. |
| * @ctx: Pointer to the context structure for traversal. |
| * |
| * This function traverses the cache tree starting from the specified node. |
| * It calls the appropriate callback functions based on the relationships |
| * between the keys in the cache tree. |
| * |
| * Returns 0 on success, or a negative error code on failure. |
| */ |
| int cache_subtree_walk(struct pcache_cache_subtree_walk_ctx *ctx) |
| { |
| struct pcache_cache_key *key_tmp, *key; |
| struct rb_node *node_tmp; |
| int ret = SUBTREE_WALK_RET_OK; |
| |
| key = ctx->key; |
| node_tmp = ctx->start_node; |
| |
| while (node_tmp) { |
| if (ctx->walk_done && ctx->walk_done(ctx)) |
| break; |
| |
| key_tmp = CACHE_KEY(node_tmp); |
| /* |
| * If key_tmp ends before the start of key, continue to the next node. |
| * |----------| |
| * |=====| |
| */ |
| if (cache_key_lend(key_tmp) <= cache_key_lstart(key)) { |
| if (ctx->after) { |
| ret = ctx->after(key, key_tmp, ctx); |
| if (ret) |
| goto out; |
| } |
| goto next; |
| } |
| |
| /* |
| * If key_tmp starts after the end of key, stop traversing. |
| * |--------| |
| * |====| |
| */ |
| if (cache_key_lstart(key_tmp) >= cache_key_lend(key)) { |
| if (ctx->before) { |
| ret = ctx->before(key, key_tmp, ctx); |
| if (ret) |
| goto out; |
| } |
| break; |
| } |
| |
| /* Handle overlapping keys */ |
| if (cache_key_lstart(key_tmp) >= cache_key_lstart(key)) { |
| /* |
| * If key_tmp encompasses key. |
| * |----------------| key_tmp |
| * |===========| key |
| */ |
| if (cache_key_lend(key_tmp) >= cache_key_lend(key)) { |
| if (ctx->overlap_tail) { |
| ret = ctx->overlap_tail(key, key_tmp, ctx); |
| if (ret) |
| goto out; |
| } |
| break; |
| } |
| |
| /* |
| * If key_tmp is contained within key. |
| * |----| key_tmp |
| * |==========| key |
| */ |
| if (ctx->overlap_contain) { |
| ret = ctx->overlap_contain(key, key_tmp, ctx); |
| if (ret) |
| goto out; |
| } |
| |
| goto next; |
| } |
| |
| /* |
| * If key_tmp starts before key ends but ends after key. |
| * |-----------| key_tmp |
| * |====| key |
| */ |
| if (cache_key_lend(key_tmp) > cache_key_lend(key)) { |
| if (ctx->overlap_contained) { |
| ret = ctx->overlap_contained(key, key_tmp, ctx); |
| if (ret) |
| goto out; |
| } |
| break; |
| } |
| |
| /* |
| * If key_tmp starts before key and ends within key. |
| * |--------| key_tmp |
| * |==========| key |
| */ |
| if (ctx->overlap_head) { |
| ret = ctx->overlap_head(key, key_tmp, ctx); |
| if (ret) |
| goto out; |
| } |
| next: |
| node_tmp = rb_next(node_tmp); |
| } |
| |
| out: |
| if (ctx->walk_finally) |
| ret = ctx->walk_finally(ctx, ret); |
| |
| return ret; |
| } |
| |
| /** |
| * cache_subtree_search - Search for a key in the cache tree. |
| * @cache_subtree: Pointer to the cache tree structure. |
| * @key: Pointer to the cache key to search for. |
| * @parentp: Pointer to store the parent node of the found node. |
| * @newp: Pointer to store the location where the new node should be inserted. |
| * @delete_key_list: List to collect invalid keys for deletion. |
| * |
| * This function searches the cache tree for a specific key and returns |
| * the node that is the predecessor of the key, or first node if the key is |
| * less than all keys in the tree. If any invalid keys are found during |
| * the search, they are added to the delete_key_list for later cleanup. |
| * |
| * Returns a pointer to the previous node. |
| */ |
| struct rb_node *cache_subtree_search(struct pcache_cache_subtree *cache_subtree, struct pcache_cache_key *key, |
| struct rb_node **parentp, struct rb_node ***newp, |
| struct list_head *delete_key_list) |
| { |
| struct rb_node **new, *parent = NULL; |
| struct pcache_cache_key *key_tmp; |
| struct rb_node *prev_node = NULL; |
| |
| new = &(cache_subtree->root.rb_node); |
| while (*new) { |
| key_tmp = container_of(*new, struct pcache_cache_key, rb_node); |
| if (cache_key_invalid(key_tmp)) |
| list_add(&key_tmp->list_node, delete_key_list); |
| |
| parent = *new; |
| if (key_tmp->off >= key->off) { |
| new = &((*new)->rb_left); |
| } else { |
| prev_node = *new; |
| new = &((*new)->rb_right); |
| } |
| } |
| |
| if (!prev_node) |
| prev_node = rb_first(&cache_subtree->root); |
| |
| if (parentp) |
| *parentp = parent; |
| |
| if (newp) |
| *newp = new; |
| |
| return prev_node; |
| } |
| |
| static struct pcache_cache_key *get_pre_alloc_key(struct pcache_cache_subtree_walk_ctx *ctx) |
| { |
| struct pcache_cache_key *key; |
| |
| if (ctx->pre_alloc_key) { |
| key = ctx->pre_alloc_key; |
| ctx->pre_alloc_key = NULL; |
| |
| return key; |
| } |
| |
| return cache_key_alloc(ctx->cache_tree, GFP_NOWAIT); |
| } |
| |
| /** |
| * fixup_overlap_tail - Adjust the key when it overlaps at the tail. |
| * @key: Pointer to the new cache key being inserted. |
| * @key_tmp: Pointer to the existing key that overlaps. |
| * @ctx: Pointer to the context for walking the cache tree. |
| * |
| * This function modifies the existing key (key_tmp) when there is an |
| * overlap at the tail with the new key. If the modified key becomes |
| * empty, it is deleted. |
| */ |
| static int fixup_overlap_tail(struct pcache_cache_key *key, |
| struct pcache_cache_key *key_tmp, |
| struct pcache_cache_subtree_walk_ctx *ctx) |
| { |
| /* |
| * |----------------| key_tmp |
| * |===========| key |
| */ |
| BUG_ON(cache_key_empty(key)); |
| if (cache_key_empty(key_tmp)) { |
| cache_key_delete(key_tmp); |
| return SUBTREE_WALK_RET_RESEARCH; |
| } |
| |
| cache_key_cutfront(key_tmp, cache_key_lend(key) - cache_key_lstart(key_tmp)); |
| if (key_tmp->len == 0) { |
| cache_key_delete(key_tmp); |
| return SUBTREE_WALK_RET_RESEARCH; |
| } |
| |
| return SUBTREE_WALK_RET_OK; |
| } |
| |
| /** |
| * fixup_overlap_contain - Handle case where new key completely contains an existing key. |
| * @key: Pointer to the new cache key being inserted. |
| * @key_tmp: Pointer to the existing key that is being contained. |
| * @ctx: Pointer to the context for walking the cache tree. |
| * |
| * This function deletes the existing key (key_tmp) when the new key |
| * completely contains it. It returns SUBTREE_WALK_RET_RESEARCH to indicate that the |
| * tree structure may have changed, necessitating a re-insertion of |
| * the new key. |
| */ |
| static int fixup_overlap_contain(struct pcache_cache_key *key, |
| struct pcache_cache_key *key_tmp, |
| struct pcache_cache_subtree_walk_ctx *ctx) |
| { |
| /* |
| * |----| key_tmp |
| * |==========| key |
| */ |
| BUG_ON(cache_key_empty(key)); |
| cache_key_delete(key_tmp); |
| |
| return SUBTREE_WALK_RET_RESEARCH; |
| } |
| |
| /** |
| * fixup_overlap_contained - Handle overlap when a new key is contained in an existing key. |
| * @key: The new cache key being inserted. |
| * @key_tmp: The existing cache key that overlaps with the new key. |
| * @ctx: Context for the cache tree walk. |
| * |
| * This function adjusts the existing key if the new key is contained |
| * within it. If the existing key is empty, it indicates a placeholder key |
| * that was inserted during a miss read. This placeholder will later be |
| * updated with real data from the backing_dev, making it no longer an empty key. |
| * |
| * If we delete key or insert a key, the structure of the entire cache tree may change, |
| * requiring a full research of the tree to find a new insertion point. |
| */ |
| static int fixup_overlap_contained(struct pcache_cache_key *key, |
| struct pcache_cache_key *key_tmp, struct pcache_cache_subtree_walk_ctx *ctx) |
| { |
| struct pcache_cache_tree *cache_tree = ctx->cache_tree; |
| |
| /* |
| * |-----------| key_tmp |
| * |====| key |
| */ |
| BUG_ON(cache_key_empty(key)); |
| if (cache_key_empty(key_tmp)) { |
| /* If key_tmp is empty, don't split it; |
| * it's a placeholder key for miss reads that will be updated later. |
| */ |
| cache_key_cutback(key_tmp, cache_key_lend(key_tmp) - cache_key_lstart(key)); |
| if (key_tmp->len == 0) { |
| cache_key_delete(key_tmp); |
| return SUBTREE_WALK_RET_RESEARCH; |
| } |
| } else { |
| struct pcache_cache_key *key_fixup; |
| bool need_research = false; |
| |
| key_fixup = get_pre_alloc_key(ctx); |
| if (!key_fixup) |
| return SUBTREE_WALK_RET_NEED_KEY; |
| |
| cache_key_copy(key_fixup, key_tmp); |
| |
| /* Split key_tmp based on the new key's range */ |
| cache_key_cutback(key_tmp, cache_key_lend(key_tmp) - cache_key_lstart(key)); |
| if (key_tmp->len == 0) { |
| cache_key_delete(key_tmp); |
| need_research = true; |
| } |
| |
| /* Create a new portion for key_fixup */ |
| cache_key_cutfront(key_fixup, cache_key_lend(key) - cache_key_lstart(key_tmp)); |
| if (key_fixup->len == 0) { |
| cache_key_put(key_fixup); |
| } else { |
| /* Insert the new key into the cache */ |
| cache_key_insert(cache_tree, key_fixup, false); |
| need_research = true; |
| } |
| |
| if (need_research) |
| return SUBTREE_WALK_RET_RESEARCH; |
| } |
| |
| return SUBTREE_WALK_RET_OK; |
| } |
| |
| /** |
| * fixup_overlap_head - Handle overlap when a new key overlaps with the head of an existing key. |
| * @key: The new cache key being inserted. |
| * @key_tmp: The existing cache key that overlaps with the new key. |
| * @ctx: Context for the cache tree walk. |
| * |
| * This function adjusts the existing key if the new key overlaps |
| * with the beginning of it. If the resulting key length is zero |
| * after the adjustment, the key is deleted. This indicates that |
| * the key no longer holds valid data and requires the tree to be |
| * re-researched for a new insertion point. |
| */ |
| static int fixup_overlap_head(struct pcache_cache_key *key, |
| struct pcache_cache_key *key_tmp, struct pcache_cache_subtree_walk_ctx *ctx) |
| { |
| /* |
| * |--------| key_tmp |
| * |==========| key |
| */ |
| BUG_ON(cache_key_empty(key)); |
| /* Adjust key_tmp by cutting back based on the new key's start */ |
| cache_key_cutback(key_tmp, cache_key_lend(key_tmp) - cache_key_lstart(key)); |
| if (key_tmp->len == 0) { |
| /* If the adjusted key_tmp length is zero, delete it */ |
| cache_key_delete(key_tmp); |
| return SUBTREE_WALK_RET_RESEARCH; |
| } |
| |
| return SUBTREE_WALK_RET_OK; |
| } |
| |
| /** |
| * cache_key_insert - Insert a new cache key into the cache tree. |
| * @cache_tree: Pointer to the cache_tree structure. |
| * @key: The cache key to insert. |
| * @fixup: Indicates if this is a new key being inserted. |
| * |
| * This function searches for the appropriate location to insert |
| * a new cache key into the cache tree. It handles key overlaps |
| * and ensures any invalid keys are removed before insertion. |
| */ |
| void cache_key_insert(struct pcache_cache_tree *cache_tree, struct pcache_cache_key *key, bool fixup) |
| { |
| struct pcache_cache *cache = cache_tree->cache; |
| struct pcache_cache_subtree_walk_ctx walk_ctx = { 0 }; |
| struct rb_node **new, *parent = NULL; |
| struct pcache_cache_subtree *cache_subtree; |
| struct pcache_cache_key *key_tmp = NULL, *key_next; |
| struct rb_node *prev_node = NULL; |
| LIST_HEAD(delete_key_list); |
| int ret; |
| |
| cache_subtree = get_subtree(cache_tree, key->off); |
| key->cache_subtree = cache_subtree; |
| search: |
| prev_node = cache_subtree_search(cache_subtree, key, &parent, &new, &delete_key_list); |
| if (!list_empty(&delete_key_list)) { |
| /* Remove invalid keys from the delete list */ |
| list_for_each_entry_safe(key_tmp, key_next, &delete_key_list, list_node) { |
| list_del_init(&key_tmp->list_node); |
| cache_key_delete(key_tmp); |
| } |
| goto search; |
| } |
| |
| if (fixup) { |
| /* Set up the context with the cache, start node, and new key */ |
| walk_ctx.cache_tree = cache_tree; |
| walk_ctx.start_node = prev_node; |
| walk_ctx.key = key; |
| |
| /* Assign overlap handling functions for different scenarios */ |
| walk_ctx.overlap_tail = fixup_overlap_tail; |
| walk_ctx.overlap_head = fixup_overlap_head; |
| walk_ctx.overlap_contain = fixup_overlap_contain; |
| walk_ctx.overlap_contained = fixup_overlap_contained; |
| |
| ret = cache_subtree_walk(&walk_ctx); |
| switch (ret) { |
| case SUBTREE_WALK_RET_OK: |
| break; |
| case SUBTREE_WALK_RET_RESEARCH: |
| goto search; |
| case SUBTREE_WALK_RET_NEED_KEY: |
| spin_unlock(&cache_subtree->tree_lock); |
| pcache_dev_debug(CACHE_TO_PCACHE(cache), "allocate pre_alloc_key with GFP_NOIO"); |
| walk_ctx.pre_alloc_key = cache_key_alloc(cache_tree, GFP_NOIO); |
| spin_lock(&cache_subtree->tree_lock); |
| goto search; |
| default: |
| BUG(); |
| } |
| } |
| |
| if (walk_ctx.pre_alloc_key) |
| cache_key_put(walk_ctx.pre_alloc_key); |
| |
| /* Link and insert the new key into the red-black tree */ |
| rb_link_node(&key->rb_node, parent, new); |
| rb_insert_color(&key->rb_node, &cache_subtree->root); |
| } |
| |
| /** |
| * clean_fn - Cleanup function to remove invalid keys from the cache tree. |
| * @work: Pointer to the work_struct associated with the cleanup. |
| * |
| * This function cleans up invalid keys from the cache tree in the background |
| * after a cache segment has been invalidated during cache garbage collection. |
| * It processes a maximum of PCACHE_CLEAN_KEYS_MAX keys per iteration and holds |
| * the tree lock to ensure thread safety. |
| */ |
| void clean_fn(struct work_struct *work) |
| { |
| struct pcache_cache *cache = container_of(work, struct pcache_cache, clean_work); |
| struct pcache_cache_subtree *cache_subtree; |
| struct rb_node *node; |
| struct pcache_cache_key *key; |
| int i, count; |
| |
| for (i = 0; i < cache->req_key_tree.n_subtrees; i++) { |
| cache_subtree = &cache->req_key_tree.subtrees[i]; |
| |
| again: |
| if (pcache_is_stopping(CACHE_TO_PCACHE(cache))) |
| return; |
| |
| /* Delete up to PCACHE_CLEAN_KEYS_MAX keys in one iteration */ |
| count = 0; |
| spin_lock(&cache_subtree->tree_lock); |
| node = rb_first(&cache_subtree->root); |
| while (node) { |
| key = CACHE_KEY(node); |
| node = rb_next(node); |
| if (cache_key_invalid(key)) { |
| count++; |
| cache_key_delete(key); |
| } |
| |
| if (count >= PCACHE_CLEAN_KEYS_MAX) { |
| /* Unlock and pause before continuing cleanup */ |
| spin_unlock(&cache_subtree->tree_lock); |
| usleep_range(1000, 2000); |
| goto again; |
| } |
| } |
| spin_unlock(&cache_subtree->tree_lock); |
| } |
| } |
| |
| /* |
| * kset_flush_fn - Flush work for a cache kset. |
| * |
| * This function is called when a kset flush work is queued from |
| * cache_key_append(). If the kset is full, it will be closed |
| * immediately. If not, the flush work will be queued for later closure. |
| * |
| * If cache_kset_close detects that a new segment is required to store |
| * the kset and there are no available segments, it will return an error. |
| * In this scenario, a retry will be attempted. |
| */ |
| void kset_flush_fn(struct work_struct *work) |
| { |
| struct pcache_cache_kset *kset = container_of(work, struct pcache_cache_kset, flush_work.work); |
| struct pcache_cache *cache = kset->cache; |
| int ret; |
| |
| if (pcache_is_stopping(CACHE_TO_PCACHE(cache))) |
| return; |
| |
| spin_lock(&kset->kset_lock); |
| ret = cache_kset_close(cache, kset); |
| spin_unlock(&kset->kset_lock); |
| |
| if (ret) { |
| /* Failed to flush kset, schedule a retry. */ |
| queue_delayed_work(cache_get_wq(cache), &kset->flush_work, msecs_to_jiffies(100)); |
| } |
| } |
| |
| static int kset_replay(struct pcache_cache *cache, struct pcache_cache_kset_onmedia *kset_onmedia) |
| { |
| struct pcache_cache_key_onmedia *key_onmedia; |
| struct pcache_cache_subtree *cache_subtree; |
| struct pcache_cache_key *key; |
| int ret; |
| int i; |
| |
| for (i = 0; i < kset_onmedia->key_num; i++) { |
| key_onmedia = &kset_onmedia->data[i]; |
| |
| key = cache_key_alloc(&cache->req_key_tree, GFP_NOIO); |
| ret = cache_key_decode(cache, key_onmedia, key); |
| if (ret) { |
| cache_key_put(key); |
| goto err; |
| } |
| |
| __set_bit(key->cache_pos.cache_seg->cache_seg_id, cache->seg_map); |
| |
| /* Check if the segment generation is valid for insertion. */ |
| if (key->seg_gen < key->cache_pos.cache_seg->gen) { |
| cache_key_put(key); |
| } else { |
| cache_subtree = get_subtree(&cache->req_key_tree, key->off); |
| spin_lock(&cache_subtree->tree_lock); |
| cache_key_insert(&cache->req_key_tree, key, true); |
| spin_unlock(&cache_subtree->tree_lock); |
| } |
| |
| cache_seg_get(key->cache_pos.cache_seg); |
| } |
| |
| return 0; |
| err: |
| return ret; |
| } |
| |
| int cache_replay(struct pcache_cache *cache) |
| { |
| struct dm_pcache *pcache = CACHE_TO_PCACHE(cache); |
| struct pcache_cache_pos pos_tail; |
| struct pcache_cache_pos *pos; |
| struct pcache_cache_kset_onmedia *kset_onmedia; |
| u32 to_copy, count = 0; |
| int ret = 0; |
| |
| kset_onmedia = kzalloc(PCACHE_KSET_ONMEDIA_SIZE_MAX, GFP_KERNEL); |
| if (!kset_onmedia) |
| return -ENOMEM; |
| |
| cache_pos_copy(&pos_tail, &cache->key_tail); |
| pos = &pos_tail; |
| |
| /* |
| * In cache replaying stage, there is no other one will access |
| * cache->seg_map, so we can set bit here without cache->seg_map_lock. |
| */ |
| __set_bit(pos->cache_seg->cache_seg_id, cache->seg_map); |
| |
| while (true) { |
| to_copy = min(PCACHE_KSET_ONMEDIA_SIZE_MAX, PCACHE_SEG_SIZE - pos->seg_off); |
| ret = copy_mc_to_kernel(kset_onmedia, cache_pos_addr(pos), to_copy); |
| if (ret) { |
| ret = -EIO; |
| goto out; |
| } |
| |
| if (kset_onmedia->magic != PCACHE_KSET_MAGIC || |
| kset_onmedia->crc != cache_kset_crc(kset_onmedia)) { |
| break; |
| } |
| |
| /* Process the last kset and prepare for the next segment. */ |
| if (kset_onmedia->flags & PCACHE_KSET_FLAGS_LAST) { |
| struct pcache_cache_segment *next_seg; |
| |
| pcache_dev_debug(pcache, "last kset replay, next: %u\n", kset_onmedia->next_cache_seg_id); |
| |
| next_seg = &cache->segments[kset_onmedia->next_cache_seg_id]; |
| |
| pos->cache_seg = next_seg; |
| pos->seg_off = 0; |
| |
| __set_bit(pos->cache_seg->cache_seg_id, cache->seg_map); |
| continue; |
| } |
| |
| /* Replay the kset and check for errors. */ |
| ret = kset_replay(cache, kset_onmedia); |
| if (ret) |
| goto out; |
| |
| /* Advance the position after processing the kset. */ |
| cache_pos_advance(pos, get_kset_onmedia_size(kset_onmedia)); |
| if (++count > 512) { |
| cond_resched(); |
| count = 0; |
| } |
| } |
| |
| /* Update the key_head position after replaying. */ |
| spin_lock(&cache->key_head_lock); |
| cache_pos_copy(&cache->key_head, pos); |
| spin_unlock(&cache->key_head_lock); |
| out: |
| kfree(kset_onmedia); |
| return ret; |
| } |
| |
| int cache_tree_init(struct pcache_cache *cache, struct pcache_cache_tree *cache_tree, u32 n_subtrees) |
| { |
| int ret; |
| u32 i; |
| |
| cache_tree->cache = cache; |
| cache_tree->n_subtrees = n_subtrees; |
| |
| ret = mempool_init_slab_pool(&cache_tree->key_pool, 1024, key_cache); |
| if (ret) |
| goto err; |
| |
| /* |
| * Allocate and initialize the subtrees array. |
| * Each element is a cache tree structure that contains |
| * an RB tree root and a spinlock for protecting its contents. |
| */ |
| cache_tree->subtrees = kvcalloc(cache_tree->n_subtrees, sizeof(struct pcache_cache_subtree), GFP_KERNEL); |
| if (!cache_tree->subtrees) { |
| ret = -ENOMEM; |
| goto key_pool_exit; |
| } |
| |
| for (i = 0; i < cache_tree->n_subtrees; i++) { |
| struct pcache_cache_subtree *cache_subtree = &cache_tree->subtrees[i]; |
| |
| cache_subtree->root = RB_ROOT; |
| spin_lock_init(&cache_subtree->tree_lock); |
| } |
| |
| return 0; |
| |
| key_pool_exit: |
| mempool_exit(&cache_tree->key_pool); |
| err: |
| return ret; |
| } |
| |
| void cache_tree_clear(struct pcache_cache_tree *cache_tree) |
| { |
| struct pcache_cache_subtree *cache_subtree; |
| struct rb_node *node; |
| struct pcache_cache_key *key; |
| u32 i; |
| |
| for (i = 0; i < cache_tree->n_subtrees; i++) { |
| cache_subtree = &cache_tree->subtrees[i]; |
| |
| spin_lock(&cache_subtree->tree_lock); |
| node = rb_first(&cache_subtree->root); |
| while (node) { |
| key = CACHE_KEY(node); |
| node = rb_next(node); |
| |
| cache_key_delete(key); |
| } |
| spin_unlock(&cache_subtree->tree_lock); |
| } |
| } |
| |
| void cache_tree_exit(struct pcache_cache_tree *cache_tree) |
| { |
| cache_tree_clear(cache_tree); |
| kvfree(cache_tree->subtrees); |
| mempool_exit(&cache_tree->key_pool); |
| } |