Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253/* 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 */