summaryrefslogtreecommitdiff
path: root/fs/f2fs/cache.h
blob: 5408727dd321a1f3dd46b81284e2db9dc2c5ad7a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/* 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 */