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 */