• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Institute of Parallel And Distributed Systems (IPADS), Shanghai Jiao Tong University (SJTU)
3  * Licensed under the Mulan PSL v2.
4  * You can use this software according to the terms and conditions of the Mulan PSL v2.
5  * You may obtain a copy of Mulan PSL v2 at:
6  *     http://license.coscl.org.cn/MulanPSL2
7  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
8  * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
9  * PURPOSE.
10  * See the Mulan PSL v2 for more details.
11  */
12 #ifndef FS_PAGE_CACHE_H
13 #define FS_PAGE_CACHE_H
14 
15 #include <sys/types.h>
16 #include <chcore/container/list.h>
17 #include <chcore/container/radix.h>
18 #include <pthread.h>
19 
20 #define PAGE_CACHE_DEBUG 0
21 
22 #if PAGE_CACHE_DEBUG == 1
23 #define page_cache_debug(fmt, ...)                            \
24     printf(COLOR_YELLOW "<%s:%s:%d>: " COLOR_DEFAULT " " fmt, \
25            __FILE__,                                          \
26            __func__,                                          \
27            __LINE__,                                          \
28            ##__VA_ARGS__)
29 #else
30 #define page_cache_debug(fmt, ...) \
31     do {                           \
32     } while (0)
33 #endif
34 
35 #define CACHED_PAGE_SIZE  4096
36 #define CACHED_BLOCK_SIZE 512
37 #define BLOCK_PER_PAGE    (CACHED_PAGE_SIZE / CACHED_BLOCK_SIZE)
38 
39 #define ACTIVE_LIST_MAX   (1 << 14)
40 #define INACTIVE_LIST_MAX (1 << 14)
41 #define MAX_PINNED_PAGE   512
42 #define MAX_PAGE_CACHE_PAGE \
43     (ACTIVE_LIST_MAX + INACTIVE_LIST_MAX + MAX_PINNED_PAGE)
44 
45 #define WRITE_BACK_CYCLE 300
46 
47 typedef off_t pidx_t;
48 
49 /* List types. */
50 typedef enum {
51     UNKNOWN_LIST = 0,
52     ACTIVE_LIST,
53     INACTIVE_LIST,
54     PINNED_PAGES_LIST,
55     INODE_PAGES_LIST,
56 } PAGE_CACHE_LIST_TYPE;
57 
58 typedef enum {
59     DIRECT = 0,
60     WRITE_THROUGH,
61     WRITE_BACK,
62 } PAGE_CACHE_STRATEGY;
63 
64 typedef enum {
65     READ = 0,
66     WRITE,
67 } PAGE_CACHE_OPERATION_TYPE;
68 
69 struct cached_page {
70     /* Owner page_cache_entity_of_inode. */
71     struct page_cache_entity_of_inode *owner;
72 
73     /* Which file page it caches. */
74     pidx_t file_page_idx;
75 
76     /* Dirty flag for each block. */
77     bool dirty[BLOCK_PER_PAGE];
78 
79     /* Cached contents. */
80     char *content;
81 
82     /*
83      * Indicates which list this page locates at.
84      * A page should always be in two types of list.
85      * One must be INODE_PAGES_LIST and the other can be
86      * ACTIVE_LIST, INACTIVE_LIST or PINNED_PAGES_LIST.
87      * The possible values of this field are:
88      * UNKNOWN_LIST, ACTIVE_LIST, INACTIVE_LIST,
89      * PINNED_PAGES_LIST, INODE_PAGES_LIST.
90      * Note: INODE_PAGES_LIST means that this page is only in
91      * INODE_PAGES_LIST. The other types mean that this page is
92      * both in INODE_PAGES_LIST and this type.
93      */
94     PAGE_CACHE_LIST_TYPE in_which_list;
95 
96     /* Used for 2-list strategy. */
97     struct list_head two_list_node;
98 
99     /* Used for pinned pages. */
100     struct list_head pinned_pages_node;
101 
102     /* Used for page_cache_entity_of_inode. */
103     struct list_head inode_pages_node;
104 
105     /* Page lock. */
106     pthread_rwlock_t page_rwlock;
107 };
108 
109 struct cached_pages_list {
110     /* Used for easily traversing all pages. */
111     struct list_head queue;
112 
113     /* List size. */
114     int size;
115 };
116 
117 struct page_cache_entity_of_inode {
118     /* Owner inode index. */
119     ino_t host_idx;
120 
121     /*
122      * Used for easily traversing all pages and
123      * quickly finding a specific page.
124      */
125     struct cached_pages_list pages;
126 
127     /* Used for quickly finding a specific page. */
128     struct radix idx2page;
129 
130     /* Number of pages */
131     int pages_cnt;
132 
133     /* Private data used for file read/write functions. */
134     void *private_data;
135 };
136 
137 /* Read a specific page from file. */
138 typedef int (*file_reader_t)(char *buf, pidx_t file_page_idx,
139                              void *private_data);
140 
141 /* Write a specific block or page(when page_block_idx == -1) to file. */
142 typedef int (*file_writer_t)(char *buf, pidx_t file_page_idx,
143                              int page_block_idx, void *private_data);
144 
145 typedef int (*event_handler_t)(void *private);
146 
147 struct user_defined_funcs {
148     /*
149      * (Essential)
150      * read/write behavior defined by module user
151      */
152     file_reader_t file_read;
153     file_writer_t file_write;
154 
155     /*
156      * (Optional, NULL if not used)
157      * User may do something when pce turns empty to non-empty (combined
158      * with a private)
159      */
160     event_handler_t handler_pce_turns_nonempty;
161     event_handler_t handler_pce_turns_empty;
162 };
163 
164 struct fs_page_cache {
165     /*
166      * Using two-list strategy to maintain caches.
167      * Once a cold block is accessed, append it to second list. If a block
168      * in second list is accessed, boost to first list. Both lists use LRU.
169      */
170     struct cached_pages_list active_list;
171     struct cached_pages_list inactive_list;
172 
173     /* pinned pages list. */
174     struct cached_pages_list pinned_pages_list;
175 
176     /* Each inode can use different cache strategy. */
177     PAGE_CACHE_STRATEGY cache_strategy;
178 
179     /* functions supported by page cache user */
180     struct user_defined_funcs user_func;
181 
182     pthread_mutex_t page_cache_lock;
183 };
184 
185 /*
186  * Initialize fs_page_cache.
187  */
188 void fs_page_cache_init(PAGE_CACHE_STRATEGY strategy,
189                         struct user_defined_funcs *user_func);
190 
191 /*
192  * Alloc a new page_cache_entity_of_inode.
193  */
194 struct page_cache_entity_of_inode *
195 new_page_cache_entity_of_inode(ino_t host_idx, void *private_data);
196 
197 /*
198  * Switch page cache strategy.
199  * Return: if succeed, return current strategy,
200  *	 if failed, return -1.
201  */
202 int page_cache_switch_strategy(PAGE_CACHE_STRATEGY new_strategy);
203 
204 /*
205  * Check if a specific page has been cached.
206  * Return: if page exists, return 1,
207  *	 if page doesn't exists, return 0.
208  */
209 int page_cache_check_page(struct page_cache_entity_of_inode *pce,
210                           pidx_t file_page_idx);
211 
212 /*
213  * Get a block or a page from corresponding page cache.
214  * If page_block_idx == -1, read a page,
215  * else read a block.
216  * Return: if failed, return NULL.
217  */
218 char *page_cache_get_block_or_page(struct page_cache_entity_of_inode *pce,
219                                    pidx_t file_page_idx, int page_block_idx,
220                                    PAGE_CACHE_OPERATION_TYPE op_type);
221 
222 /*
223  * Put a block or a page to corresponding page cache.
224  */
225 void page_cache_put_block_or_page(struct page_cache_entity_of_inode *pce,
226                                   pidx_t file_page_idx, int page_block_idx,
227                                   PAGE_CACHE_OPERATION_TYPE op_type);
228 
229 /*
230  * Flush a block or a page in corresponding page cache,
231  * the cache still remains.
232  * If page_block_idx == -1, flush a page,
233  * else flush a block.
234  * Return: if succeed, return 0,
235  *	 if failed, return -1.
236  */
237 int page_cache_flush_block_or_page(struct page_cache_entity_of_inode *pce,
238                                    pidx_t file_page_idx, int page_block_idx);
239 
240 /*
241  * Flush all dirty pages that belongs to an inode,
242  * the cache still remains.
243  * Return: if succeed, return 0,
244  *	 if failed, return -1.
245  */
246 int page_cache_flush_pages_of_inode(struct page_cache_entity_of_inode *pce);
247 
248 /*
249  * Flush all dirty pages.
250  * the cache still remains.
251  * Return: if succeed, return 0,
252  *	 if failed, return -1.
253  */
254 int page_cache_flush_all_pages(void);
255 
256 /*
257  * Pin a single page.
258  * Return: if succeed, return 0,
259  *	 if failed, return -1.
260  */
261 int page_cache_pin_single_page(struct page_cache_entity_of_inode *pce,
262                                pidx_t file_page_idx);
263 
264 /*
265  * Unpin a single page.
266  * Return: if succeed, return 0,
267  *	 if failed, return -1.
268  */
269 int page_cache_unpin_single_page(struct page_cache_entity_of_inode *pce,
270                                  pidx_t file_page_idx);
271 
272 /*
273  * Pin multiple pages (@page_num pages from @file_page_idx).
274  * Return: if succeed, return 0,
275  *	 if failed, return -1.
276  */
277 int page_cache_pin_multiple_pages(struct page_cache_entity_of_inode *pce,
278                                   pidx_t file_page_idx, u64 page_num);
279 
280 /*
281  * Unpin multiple pages (@page_num pages from @file_page_idx).
282  * Return: if succeed, return 0,
283  *	 if failed, return -1.
284  */
285 int page_cache_unpin_multiple_pages(struct page_cache_entity_of_inode *pce,
286                                     pidx_t file_page_idx, u64 page_num);
287 
288 /*
289  * Evict a single page.
290  * Return: if succeed, return 0,
291  *	 if failed, return -1.
292  */
293 int page_cache_evict_single_page(struct page_cache_entity_of_inode *pce,
294                                  pidx_t file_page_idx);
295 
296 /*
297  * Evict all pages of inode.
298  * Return: if succeed, return 0,
299  *	 if failed, return -1.
300  */
301 int page_cache_evict_pages_of_inode(struct page_cache_entity_of_inode *pce);
302 
303 /*
304  * Delete a single page without flushing.
305  * Return: if succeed, return 0,
306  *	 if failed, return -1.
307  */
308 int page_cache_delete_single_page(struct page_cache_entity_of_inode *pce,
309                                   pidx_t file_page_idx);
310 
311 /*
312  * Delete all pages of inode without flushing.
313  * Return: if succeed, return 0,
314  *	 if failed, return -1.
315  */
316 int page_cache_delete_pages_of_inode(struct page_cache_entity_of_inode *pce);
317 
318 #if PAGE_CACHE_DEBUG == 1
319 /* debug */
320 void print_all_list(void);
321 void test_fs_page_cache(void);
322 #endif
323 
324 #endif /* FS_PAGE_CACHE_H */