blob: 5408727dd321a1f3dd46b81284e2db9dc2c5ad7a [file] [edit]
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2026 Google LLC
* Author: Chao Yu <chaseyu@google.com>
*/
#ifndef _LINUX_F2FS_CACHE_H
#define _LINUX_F2FS_CACHE_H
#include <linux/pagemap.h>
#include <linux/mm.h>
#include <linux/list.h>
#include <linux/radix-tree.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/types.h>
struct f2fs_rwsem;
struct f2fs_io_info;
enum page_type;
/* Represents a single cached block (meta, node or compress) */
struct f2fs_cached_block {
struct list_head list; /* LRU list head */
struct f2fs_cached_block_list *cache; /* parent cache list */
union {
struct f2fs_cached_block *next_entry;/* chain for merged BIO */
nid_t ino; /* inode number for compress cache */
};
unsigned long index; /* key in radix tree, (meta/compress: pba, node: nid) */
unsigned long state; /* cache entry state (e.g., Dirty, UpToDate) */
void *data; /* blocksize-aligned memory (4KB or 16KB) */
atomic_t refcount; /* reference count */
};
struct f2fs_sb_info;
enum f2fs_cache_type {
F2FS_META_CACHE,
F2FS_NODE_CACHE,
F2FS_COMPRESS_CACHE,
NR_CACHE_TYPES,
};
/* Main cache control structure (per sb_info) */
struct f2fs_cached_block_list {
struct f2fs_sb_info *sbi; /* Pointer to f2fs_sb_info */
struct radix_tree_root root; /* Radix tree for cache lookup */
spinlock_t tree_lock; /* Lock for radix tree */
struct list_head lru_list; /* Single global LRU list */
spinlock_t list_lock; /* Lock for LRU list */
enum f2fs_cache_type type; /* Cache type (Node, Meta, Compress) */
unsigned long num_entries; /* Current number of entries */
};
#define IS_META_CACHE(cache) ((cache)->type == F2FS_META_CACHE)
#define IS_NODE_CACHE(cache) ((cache)->type == F2FS_NODE_CACHE)
#define IS_COMPRESS_CACHE(cache) ((cache)->type == F2FS_COMPRESS_CACHE)
/* Flags for f2fs_cached_block state */
enum f2fs_cached_state {
F2FS_BLOCK_LOCKED, /* cache entry is locked */
F2FS_BLOCK_UPTODATE, /* cache data is valid */
F2FS_BLOCK_DIRTY, /* cache data is dirty, need to writeback the data */
F2FS_BLOCK_WRITEBACK, /* cache data is writeback state */
F2FS_BLOCK_REFERENCED, /* cache was accessed recently, shrinker will skip it for once */
F2FS_BLOCK_INLINE_DATA, /* indicate inline data */
};
enum {
__F2FS_CACHE_CREATE, /* create the cache if there is no cache entry */
__F2FS_CACHE_LOCK, /* get and lock the cache entry */
__F2FS_CACHE_NOFAIL, /* do not allow failure */
__F2FS_CACHE_ACCESS, /* give a chance to add referenced tag */
};
enum f2fs_cache_request_flag {
F2FS_CACHE_CREATE = 1 << __F2FS_CACHE_CREATE,
F2FS_CACHE_LOCK = 1 << __F2FS_CACHE_LOCK,
F2FS_CACHE_NOFAIL = 1 << __F2FS_CACHE_NOFAIL,
F2FS_CACHE_ACCESS = 1 << __F2FS_CACHE_ACCESS,
};
#define F2FS_CACHE_LOCK_CREATE (F2FS_CACHE_LOCK | F2FS_CACHE_CREATE)
#define F2FS_ONSTACK_CACHES (32)
#define METADATA_CACHE_SHRINK_NUMBER (128)
#define F2FS_CACHE_FLAG_TEST_FUNC(name, flagname) \
static inline bool f2fs_cache_test_##name( \
const struct f2fs_cached_block *entry) \
{ \
return test_bit(F2FS_BLOCK_##flagname, &entry->state); \
} \
#define F2FS_CACHE_FLAG_SET_FUNC(name, flagname) \
static inline void f2fs_cache_set_##name( \
struct f2fs_cached_block *entry) \
{ \
set_bit(F2FS_BLOCK_##flagname, &entry->state); \
} \
#define F2FS_CACHE_FLAG_CLEAR_FUNC(name, flagname) \
static inline void f2fs_cache_clear_##name( \
struct f2fs_cached_block *entry) \
{ \
clear_bit(F2FS_BLOCK_##flagname, &entry->state); \
} \
#define F2FS_CACHE_FLAG_TEST_AND_SET_FUNC(name, flagname) \
static inline bool f2fs_cache_test_and_set_##name( \
struct f2fs_cached_block *entry) \
{ \
return test_and_set_bit(F2FS_BLOCK_##flagname, &entry->state); \
} \
#define F2FS_CACHE_FLAG_TEST_AND_CLEAR_FUNC(name, flagname) \
static inline bool f2fs_cache_test_and_clear_##name( \
struct f2fs_cached_block *entry) \
{ \
return test_and_clear_bit(F2FS_BLOCK_##flagname, &entry->state);\
} \
F2FS_CACHE_FLAG_TEST_FUNC(locked, LOCKED);
F2FS_CACHE_FLAG_SET_FUNC(locked, LOCKED);
F2FS_CACHE_FLAG_CLEAR_FUNC(locked, LOCKED);
F2FS_CACHE_FLAG_TEST_FUNC(uptodate, UPTODATE);
F2FS_CACHE_FLAG_SET_FUNC(uptodate, UPTODATE);
F2FS_CACHE_FLAG_CLEAR_FUNC(uptodate, UPTODATE);
F2FS_CACHE_FLAG_TEST_FUNC(dirty, DIRTY);
F2FS_CACHE_FLAG_SET_FUNC(dirty, DIRTY);
F2FS_CACHE_FLAG_CLEAR_FUNC(dirty, DIRTY);
F2FS_CACHE_FLAG_TEST_AND_SET_FUNC(dirty, DIRTY);
F2FS_CACHE_FLAG_TEST_AND_CLEAR_FUNC(dirty, DIRTY);
F2FS_CACHE_FLAG_TEST_FUNC(writeback, WRITEBACK);
F2FS_CACHE_FLAG_SET_FUNC(writeback, WRITEBACK);
F2FS_CACHE_FLAG_CLEAR_FUNC(writeback, WRITEBACK);
F2FS_CACHE_FLAG_TEST_FUNC(inline, INLINE_DATA);
F2FS_CACHE_FLAG_SET_FUNC(inline, INLINE_DATA);
F2FS_CACHE_FLAG_CLEAR_FUNC(inline, INLINE_DATA);
F2FS_CACHE_FLAG_TEST_FUNC(referenced, REFERENCED);
F2FS_CACHE_FLAG_SET_FUNC(referenced, REFERENCED);
F2FS_CACHE_FLAG_TEST_AND_CLEAR_FUNC(referenced, REFERENCED);
static inline void *cache_address(const struct f2fs_cached_block *entry)
{
return entry->data;
}
#define CACHED_NODE(entry) ((struct f2fs_node *)(cache_address(entry)))
static inline struct folio *cache_folio(const struct f2fs_cached_block *entry)
{
return virt_to_folio(entry->data);
}
int f2fs_init_cache(struct f2fs_sb_info *sbi,
struct f2fs_cached_block_list *cache,
enum f2fs_cache_type type);
void f2fs_destroy_cache(struct f2fs_cached_block_list *cache);
void f2fs_cache_get(struct f2fs_cached_block *entry);
struct f2fs_cached_block *f2fs_find_cache(
struct f2fs_cached_block_list *cache,
unsigned long index,
enum f2fs_cache_request_flag rflag);
#define F2FS_CACHE_TAG_NONE 0
#define F2FS_CACHE_TAG_DIRTY 1
#define F2FS_CACHE_TAG_WRITEBACK 2
bool f2fs_trylock_cache(struct f2fs_cached_block *entry);
void f2fs_lock_cache(struct f2fs_cached_block *entry);
void f2fs_unlock_cache(struct f2fs_cached_block *entry);
bool f2fs_put_cache(struct f2fs_cached_block *entry, bool unlock);
bool f2fs_mark_cache_dirty(struct f2fs_cached_block *entry);
void f2fs_drop_cache_dirty(struct f2fs_cached_block *entry);
void f2fs_start_cache_writeback(struct f2fs_cached_block *entry);
void f2fs_end_cache_writeback(struct f2fs_cached_block *entry);
unsigned int f2fs_cache_gang_lookup(struct f2fs_cached_block_list *cache,
struct f2fs_cached_block **entries,
pgoff_t *index, unsigned long end);
unsigned int f2fs_cache_gang_lookup_tag(struct f2fs_cached_block_list *cache,
struct f2fs_cached_block **results, pgoff_t *first_index,
unsigned int max_items, int tag);
void f2fs_cache_gang_release(struct f2fs_cached_block **entries,
unsigned int nr_entries);
void f2fs_cache_wait_on_all_writeback(struct f2fs_cached_block_list *cache);
void f2fs_cache_wait_writeback_cond(struct f2fs_cached_block *entry,
enum page_type type);
void f2fs_cache_wait_writeback(struct f2fs_cached_block *entry);
void f2fs_cache_update_tag(struct f2fs_cached_block *entry,
unsigned int clear_from, unsigned int set_to);
struct f2fs_cached_block *f2fs_grab_cache(struct f2fs_cached_block_list *cache,
unsigned long index, int flags);
void f2fs_truncate_locked_cache(struct f2fs_cached_block *entry,
bool drop_dirty);
void f2fs_truncate_cache(struct f2fs_cached_block *entry, bool drop_dirty);
void f2fs_drop_cache_range(struct f2fs_cached_block_list *cache,
unsigned long start, unsigned long len, bool drop_dirty);
#define META_CACHE(sbi) (&(sbi)->meta_blocks)
#define NODE_CACHE(sbi) (&(sbi)->node_blocks)
#define COMPRESS_CACHE(sbi) (&(sbi)->compress_blocks)
#define f2fs_find_meta_cache(sbi, blkaddr) \
f2fs_find_cache(META_CACHE(sbi), blkaddr, 0)
#define f2fs_invalidate_meta_caches(sbi, start, len) \
f2fs_drop_cache_range(META_CACHE(sbi), start, len, false)
#define f2fs_truncate_meta_caches(sbi, start, len) \
f2fs_drop_cache_range(META_CACHE(sbi), start, len, true)
#define f2fs_grab_node_cache(sbi, blkaddr) \
f2fs_grab_cache(NODE_CACHE(sbi), blkaddr, \
F2FS_CACHE_LOCK_CREATE)
#define f2fs_find_node_cache(sbi, blkaddr) \
f2fs_find_cache(NODE_CACHE(sbi), blkaddr, F2FS_CACHE_ACCESS)
#define f2fs_invalidate_node_cache(sbi, blkaddr) \
f2fs_drop_cache_range(NODE_CACHE(sbi), blkaddr, 1, false)
#define f2fs_truncate_node_caches(sbi, start, len) \
f2fs_drop_cache_range(NODE_CACHE(sbi), start, len, true)
unsigned long f2fs_shrink_cache(struct f2fs_sb_info *sbi,
unsigned long nr_to_scan);
unsigned long f2fs_shrink_meta_cache(struct f2fs_sb_info *sbi,
unsigned long nr_to_scan);
unsigned long f2fs_shrink_node_cache(struct f2fs_sb_info *sbi,
unsigned long nr_to_scan);
#define DEF_DIRTY_CACHE_TIMEOUT 5000
#define MIN_DIRTY_CACHE_TIMEOUT 100
#define MAX_DIRTY_CACHE_TIMEOUT 30000
#define DEF_CACHE_WB_DIRTY_THRESH (8192)
struct f2fs_cache_kthread {
struct task_struct *cache_wb_task;
wait_queue_head_t cache_wb_wq;
unsigned int cache_wb_interval;
unsigned int cache_wb_dirty_threshold;
unsigned int cache_wb_total_threshold;
bool cache_wb_throttled; /* last round made no progress */
};
int f2fs_start_cache_wb_thread(struct f2fs_sb_info *sbi);
void f2fs_stop_cache_wb_thread(struct f2fs_sb_info *sbi);
void f2fs_wake_up_cache_wb(struct f2fs_sb_info *sbi);
#endif /* _LINUX_F2FS_CACHE_H */