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 #include <malloc.h>
13 #include <pthread.h>
14 #include <stdlib.h>
15 #include <chcore/defs.h>
16 #include <chcore/memory.h>
17 #include <chcore/syscall.h>
18 #include <chcore-internal/fs_debug.h>
19
20 #include "fs_page_cache.h"
21 #include "fs_wrapper_defs.h"
22
23 static struct fs_page_cache page_cache = {0};
24
25 /* +++++++++++++++++++++++++++ Helper functions +++++++++++++++++++++++++++++ */
26
27 /*
28 * if page_block_idx == -1, check a page's dirty flag,
29 * else check a block's dirty flag.
30 */
is_block_or_page_dirty(struct cached_page * p,int page_block_idx)31 static bool is_block_or_page_dirty(struct cached_page *p, int page_block_idx)
32 {
33 int i;
34
35 if (page_block_idx != -1)
36 return p->dirty[page_block_idx];
37 else
38 for (i = 0; i < BLOCK_PER_PAGE; ++i)
39 if (p->dirty[i])
40 return true;
41 return false;
42 }
43
44 /*
45 * if page_block_idx == -1, set a page's dirty flag as @is_dirty,
46 * else set a block's dirty flag as @is_dirtyy.
47 */
set_block_or_page_dirty(struct cached_page * p,int page_block_idx,bool is_dirty)48 static void set_block_or_page_dirty(struct cached_page *p, int page_block_idx,
49 bool is_dirty)
50 {
51 int i;
52
53 if (page_block_idx != -1)
54 p->dirty[page_block_idx] = is_dirty;
55 else
56 for (i = 0; i < BLOCK_PER_PAGE; ++i)
57 p->dirty[i] = is_dirty;
58 }
59
60 /* ++++++++++++++++++++++++++ Page list operations ++++++++++++++++++++++++++ */
cached_pages_list_init(struct cached_pages_list * list)61 static inline void cached_pages_list_init(struct cached_pages_list *list)
62 {
63 init_list_head(&list->queue);
64 list->size = 0;
65 }
66
67 /* Return the head of the list. */
68 static struct cached_page *
cached_pages_list_top_node(struct cached_pages_list * list,PAGE_CACHE_LIST_TYPE list_type)69 cached_pages_list_top_node(struct cached_pages_list *list,
70 PAGE_CACHE_LIST_TYPE list_type)
71 {
72 if (list_empty(&list->queue))
73 return NULL;
74 if (list_type == ACTIVE_LIST || list_type == INACTIVE_LIST)
75 return list_entry(list->queue.next, struct cached_page, two_list_node);
76 else if (list_type == INODE_PAGES_LIST)
77 return list_entry(
78 list->queue.next, struct cached_page, inode_pages_node);
79 else if (list_type == PINNED_PAGES_LIST)
80 return list_entry(
81 list->queue.next, struct cached_page, pinned_pages_node);
82
83 BUG("Unknown list type.");
84 return NULL;
85 }
86
87 /* Find corresponding page from the page_cache_entity_of_inode. */
88 static inline struct cached_page *
get_node_from_page_cache_entity(struct page_cache_entity_of_inode * pce,pidx_t file_page_idx)89 get_node_from_page_cache_entity(struct page_cache_entity_of_inode *pce,
90 pidx_t file_page_idx)
91 {
92 return radix_get(&pce->idx2page, file_page_idx);
93 }
94
95 /*
96 * Append node to the tail of the list.
97 */
cached_pages_list_append_node(struct cached_pages_list * list,struct cached_page * p,PAGE_CACHE_LIST_TYPE list_type)98 static void cached_pages_list_append_node(struct cached_pages_list *list,
99 struct cached_page *p,
100 PAGE_CACHE_LIST_TYPE list_type)
101 {
102 switch (list_type) {
103 case ACTIVE_LIST:
104 case INACTIVE_LIST:
105 list_append(&p->two_list_node, &list->queue);
106 break;
107 case INODE_PAGES_LIST:
108 list_append(&p->inode_pages_node, &list->queue);
109 break;
110 case PINNED_PAGES_LIST:
111 list_append(&p->pinned_pages_node, &list->queue);
112 break;
113 default:
114 BUG("Unknown list type.");
115 break;
116 }
117
118 p->in_which_list = list_type;
119 list->size++;
120 }
121
122 /*
123 * Remove head node, don't forget to free the data outside this function.
124 */
cached_pages_list_delete_node(struct cached_pages_list * list,struct cached_page * p,PAGE_CACHE_LIST_TYPE list_type)125 static void cached_pages_list_delete_node(struct cached_pages_list *list,
126 struct cached_page *p,
127 PAGE_CACHE_LIST_TYPE list_type)
128 {
129 switch (list_type) {
130 case ACTIVE_LIST:
131 case INACTIVE_LIST:
132 list_del(&p->two_list_node);
133 p->in_which_list = INODE_PAGES_LIST;
134 break;
135 case INODE_PAGES_LIST:
136 list_del(&p->inode_pages_node);
137 p->in_which_list = UNKNOWN_LIST;
138 break;
139 case PINNED_PAGES_LIST:
140 list_del(&p->pinned_pages_node);
141 p->in_which_list = INODE_PAGES_LIST;
142 break;
143 default:
144 BUG("Unknown list type.");
145 break;
146 }
147
148 list->size--;
149 BUG_ON(list->size < 0);
150 }
151
152 /* +++++++++++++++++++++++++ cached_page operations +++++++++++++++++++++++++ */
153 /* The caller should guarantee page_block_idx is in legal range. */
flush_single_block(struct cached_page * p,int page_block_idx)154 static int flush_single_block(struct cached_page *p, int page_block_idx)
155 {
156 int ret = 0;
157
158 BUG_ON(page_block_idx < 0);
159
160 if (is_block_or_page_dirty(p, page_block_idx)) {
161 page_cache_debug(
162 "[write_back_routine] %d:%d:%d in active list is dirty, write it back.\n",
163 p->owner->host_idx,
164 p->file_page_idx,
165 page_block_idx);
166 if (page_cache.user_func.file_write(
167 p->content + page_block_idx * CACHED_BLOCK_SIZE,
168 p->file_page_idx,
169 page_block_idx,
170 p->owner->private_data)
171 <= 0) {
172 BUG("[write_back_all_pages] file_write failed\n");
173 ret = -1;
174 goto out;
175 }
176 set_block_or_page_dirty(p, page_block_idx, false);
177 }
178
179 out:
180 return ret;
181 }
182
flush_single_page(struct cached_page * p)183 static int flush_single_page(struct cached_page *p)
184 {
185 int i, ret = 0;
186
187 BUG_ON(p == NULL);
188
189 for (i = 0; i < BLOCK_PER_PAGE; ++i) {
190 if (flush_single_block(p, i) != 0) {
191 ret = -1;
192 goto out;
193 }
194 }
195 out:
196 return ret;
197 }
198
free_page(struct cached_page * p)199 static void free_page(struct cached_page *p)
200 {
201 BUG_ON(p == NULL);
202
203 free(p->content);
204
205 /* Delete from ACTIVE_LIST, INACTIVE_LIST or PINNED_PAGES_LIST. */
206 switch (p->in_which_list) {
207 case ACTIVE_LIST:
208 cached_pages_list_delete_node(&page_cache.active_list, p, ACTIVE_LIST);
209 break;
210 case INACTIVE_LIST:
211 cached_pages_list_delete_node(
212 &page_cache.inactive_list, p, INACTIVE_LIST);
213 break;
214 case PINNED_PAGES_LIST:
215 cached_pages_list_delete_node(
216 &page_cache.pinned_pages_list, p, PINNED_PAGES_LIST);
217 break;
218 default:
219 BUG("Try to free a page that is not in ACTIVE_LIST, INACTIVE_LIST or PINNED_PAGES_LIST.\n");
220 }
221
222 /* Delete from INODE_PAGES_LIST. */
223 cached_pages_list_delete_node(&p->owner->pages, p, INODE_PAGES_LIST);
224 radix_del(&p->owner->idx2page, p->file_page_idx, 0);
225
226 /* Once pages_cnt in a pce reached 0, trigger handler */
227 if (--(p->owner->pages_cnt) == 0
228 && page_cache.user_func.handler_pce_turns_empty) {
229 page_cache.user_func.handler_pce_turns_empty(p->owner->private_data);
230 }
231
232 pthread_rwlock_destroy(&p->page_rwlock);
233
234 free(p);
235 }
236
flush_and_free_page(struct cached_page * p)237 static void flush_and_free_page(struct cached_page *p)
238 {
239 BUG_ON(p == NULL);
240
241 /* Can't free a page that's being used. */
242 pthread_rwlock_wrlock(&p->page_rwlock);
243
244 /*
245 * pages with empty contents is marked not dirty.
246 * So it won't be flushed.
247 */
248 if (is_block_or_page_dirty(p, -1))
249 flush_single_page(p);
250
251 free_page(p);
252 }
253
new_page(struct page_cache_entity_of_inode * owner,pidx_t file_page_idx)254 static struct cached_page *new_page(struct page_cache_entity_of_inode *owner,
255 pidx_t file_page_idx)
256 {
257 struct cached_page *p;
258
259 p = (struct cached_page *)calloc(1, sizeof(struct cached_page));
260 if (!p)
261 return NULL;
262
263 p->owner = owner;
264 p->file_page_idx = file_page_idx;
265
266 init_list_head(&p->two_list_node);
267 init_list_head(&p->inode_pages_node);
268 init_list_head(&p->pinned_pages_node);
269
270 pthread_rwlock_init(&p->page_rwlock, NULL);
271
272 p->content = aligned_alloc(CACHED_PAGE_SIZE, CACHED_PAGE_SIZE);
273 BUG_ON(!p->content);
274 memset(p->content, 0, CACHED_PAGE_SIZE);
275
276 /* Append to INODE_PAGES_LIST. */
277 cached_pages_list_append_node(&owner->pages, p, INODE_PAGES_LIST);
278 radix_add(&owner->idx2page, file_page_idx, p);
279
280 /* Contribute 1 ref to pce for each cached_page */
281 if (owner->pages_cnt++ == 0
282 && page_cache.user_func.handler_pce_turns_nonempty) {
283 page_cache.user_func.handler_pce_turns_nonempty(owner->private_data);
284 }
285
286 return p;
287 }
288
evict_head_page(struct cached_pages_list * list,PAGE_CACHE_LIST_TYPE list_type)289 static inline void evict_head_page(struct cached_pages_list *list,
290 PAGE_CACHE_LIST_TYPE list_type)
291 {
292 struct cached_page *p;
293
294 /* Evict head page. */
295 if (list_type == ACTIVE_LIST || list_type == INACTIVE_LIST) {
296 p = cached_pages_list_top_node(list, list_type);
297 page_cache_debug("[evict_head_page] Evict %d:%d in list %d.\n",
298 p->owner->host_idx,
299 p->file_page_idx,
300 list_type);
301 flush_and_free_page(p);
302 } else {
303 BUG("Try to evict a page that is not in two list.\n");
304 }
305 }
306
307 /*
308 * Boost a node in active list. Remove the node and append it at tail.
309 */
boost_active_page(struct cached_page * p)310 static inline void boost_active_page(struct cached_page *p)
311 {
312 /* Delete node from active list. */
313 cached_pages_list_delete_node(&page_cache.active_list, p, ACTIVE_LIST);
314
315 /* Append it to the tail. */
316 cached_pages_list_append_node(&page_cache.active_list, p, ACTIVE_LIST);
317 }
318
319 /*
320 * Boost a node in inactive list.
321 * Remove the node and append it to the tail of the active list.
322 */
boost_inactive_page(struct cached_page * p)323 static inline void boost_inactive_page(struct cached_page *p)
324 {
325 struct cached_page *head_page;
326 /* Delete node from inactive list */
327 cached_pages_list_delete_node(&page_cache.inactive_list, p, INACTIVE_LIST);
328
329 /* Add node to active list */
330 cached_pages_list_append_node(&page_cache.active_list, p, ACTIVE_LIST);
331
332 if (page_cache.active_list.size > ACTIVE_LIST_MAX) {
333 /*
334 * If active list is full,
335 * move head unpinned page to inactive list.
336 */
337 head_page =
338 cached_pages_list_top_node(&page_cache.active_list, ACTIVE_LIST);
339 /**
340 * It should be impossible for head_page == NULL but klocwork
341 * reported a bug here, maybe due to precision of static
342 * analysis
343 */
344 if (head_page == NULL) {
345 WARN("head_page == NULL");
346 return;
347 }
348 cached_pages_list_delete_node(
349 &page_cache.active_list, head_page, ACTIVE_LIST);
350 cached_pages_list_append_node(
351 &page_cache.inactive_list, head_page, INACTIVE_LIST);
352
353 if (page_cache.inactive_list.size > INACTIVE_LIST_MAX) {
354 /*
355 * if inactive list is full,
356 * evict head unpinned page.
357 */
358 evict_head_page(&page_cache.inactive_list, INACTIVE_LIST);
359 }
360 }
361 }
362
363 /* Caller should hold per page_cache mutex lock to protect list operations */
boost_page(struct cached_page * p)364 static inline void boost_page(struct cached_page *p)
365 {
366 if (p->in_which_list == ACTIVE_LIST)
367 boost_active_page(p);
368 else if (p->in_which_list == INACTIVE_LIST)
369 boost_inactive_page(p);
370 else
371 BUG("Try to boost page that is not in two lists!\n");
372 }
373
374 /*
375 * Find a page from active_list, inactive_list and pinned_pages_list, if page
376 * not found, create a new one and insert it to inactive list and pages list.
377 * Notice: The caller should hold per page_cache mutex lock!
378 * return value:
379 * @which_list: ACTIVE_LIST or INACTIVE_LIST or PINNED_PAGES_LIST.
380 * @is_new: if page is newly allocated, is_new = true, else is_new = false.
381 */
find_or_new_page(struct page_cache_entity_of_inode * pce,pidx_t file_page_idx,bool * is_new)382 struct cached_page *find_or_new_page(struct page_cache_entity_of_inode *pce,
383 pidx_t file_page_idx, bool *is_new)
384 {
385 struct cached_page *p;
386 bool temp_is_new;
387
388 p = get_node_from_page_cache_entity(pce, file_page_idx);
389 if (p == NULL) {
390 #ifdef TEST_COUNT_PAGE_CACHE
391 count.miss = count.miss + 1;
392 #endif
393 /*
394 * Cache miss, create a new page
395 * and fill it with corresponding contents.
396 */
397 page_cache_debug(
398 "[find_or_new_page] page cache %d:%d not found, alloc a new one\n",
399 pce->host_idx,
400 file_page_idx);
401 p = new_page(pce, file_page_idx);
402 if (p == NULL) {
403 BUG("[find_or_new_page] new_page failed\n");
404 goto fail;
405 }
406
407 /*
408 * According to two list strategy, a new page should
409 * be inserted into inactive list.
410 */
411 cached_pages_list_append_node(
412 &page_cache.inactive_list, p, INACTIVE_LIST);
413 if (page_cache.inactive_list.size > INACTIVE_LIST_MAX) {
414 /*
415 * if inactive list is full,
416 * evict head unpinned page.
417 */
418 evict_head_page(&page_cache.inactive_list, INACTIVE_LIST);
419 }
420
421 /* Fill it with corresponding contents. */
422 page_cache.user_func.file_read(
423 p->content, file_page_idx, pce->private_data);
424
425 temp_is_new = true;
426 } else {
427 /* Cache hit. */
428 #ifdef TEST_COUNT_PAGE_CACHE
429 count.hit = count.hit + 1;
430 #endif
431 page_cache_debug("[find_or_new_page] page cache %d:%d found in %d\n",
432 pce->host_idx,
433 file_page_idx,
434 p->in_which_list);
435 temp_is_new = false;
436 }
437
438 if (is_new)
439 *is_new = temp_is_new;
440 return p;
441
442 fail:
443 if (p)
444 flush_and_free_page(p);
445 return NULL;
446 }
447
448 /* The caller should hold per page_cache mutex lock! */
write_back_all_pages(void)449 void write_back_all_pages(void)
450 {
451 struct cached_page *p;
452
453 /* Write back pages in active list. */
454 for_each_in_list (
455 p, struct cached_page, two_list_node, &page_cache.active_list.queue) {
456 pthread_rwlock_rdlock(&p->page_rwlock);
457 flush_single_page(p);
458 pthread_rwlock_unlock(&p->page_rwlock);
459 }
460 /* Write back pages in inactive list. */
461 for_each_in_list (
462 p, struct cached_page, two_list_node, &page_cache.inactive_list.queue) {
463 pthread_rwlock_rdlock(&p->page_rwlock);
464 flush_single_page(p);
465 pthread_rwlock_unlock(&p->page_rwlock);
466 }
467 /* Write back pages in pinned pages list. */
468 for_each_in_list (p,
469 struct cached_page,
470 pinned_pages_node,
471 &page_cache.pinned_pages_list.queue) {
472 pthread_rwlock_rdlock(&p->page_rwlock);
473 flush_single_page(p);
474 pthread_rwlock_unlock(&p->page_rwlock);
475 }
476 }
477
478 /* Write back all dirty pages periodically. */
write_back_routine(void * args)479 void *write_back_routine(void *args)
480 {
481 struct timespec ts;
482
483 while (1) {
484 pthread_mutex_lock(&page_cache.page_cache_lock);
485 if (page_cache.cache_strategy == WRITE_BACK) {
486 page_cache_debug(
487 "[write_back_routine] write back routine started.\n");
488 write_back_all_pages();
489 page_cache_debug(
490 "[write_back_routine] write back routine completed.\n");
491 }
492 pthread_mutex_unlock(&page_cache.page_cache_lock);
493
494 ts.tv_sec = WRITE_BACK_CYCLE;
495 ts.tv_nsec = 0;
496 nanosleep(&ts, NULL);
497 }
498
499 return NULL;
500 }
501
502 /* +++++++++++++++++++++++++ Exposed Functions+++++++++++++++++++++++++++ */
503
fs_page_cache_init(PAGE_CACHE_STRATEGY strategy,struct user_defined_funcs * uf)504 void fs_page_cache_init(PAGE_CACHE_STRATEGY strategy,
505 struct user_defined_funcs *uf)
506 {
507 pthread_t thread;
508
509 memcpy(&page_cache.user_func, uf, sizeof(*uf));
510
511 cached_pages_list_init(&page_cache.active_list);
512 cached_pages_list_init(&page_cache.inactive_list);
513 cached_pages_list_init(&page_cache.pinned_pages_list);
514
515 page_cache.cache_strategy = strategy;
516 pthread_create(&thread, 0, write_back_routine, NULL);
517
518 pthread_mutex_init(&page_cache.page_cache_lock, NULL);
519
520 page_cache_debug("fs page cache init finished.\n");
521 }
522
523 struct page_cache_entity_of_inode *
new_page_cache_entity_of_inode(ino_t host_idx,void * private_data)524 new_page_cache_entity_of_inode(ino_t host_idx, void *private_data)
525 {
526 struct page_cache_entity_of_inode *pce;
527
528 pce = (struct page_cache_entity_of_inode *)calloc(
529 1, sizeof(struct page_cache_entity_of_inode));
530 if (pce == NULL)
531 goto out;
532
533 pce->host_idx = host_idx;
534 pce->private_data = private_data;
535
536 cached_pages_list_init(&pce->pages);
537 init_radix(&pce->idx2page);
538
539 out:
540 return pce;
541 }
542
page_cache_switch_strategy(PAGE_CACHE_STRATEGY new_strategy)543 int page_cache_switch_strategy(PAGE_CACHE_STRATEGY new_strategy)
544 {
545 if (page_cache.cache_strategy == new_strategy)
546 return 0;
547
548 pthread_mutex_lock(&page_cache.page_cache_lock);
549
550 if (page_cache.cache_strategy == WRITE_BACK) {
551 /* Write back all the pages. */
552 write_back_all_pages();
553 }
554
555 page_cache_debug(
556 "[page_cache_switch_strategy] switch cache strategy from %d to %d\n",
557 page_cache.cache_strategy,
558 new_strategy);
559 page_cache.cache_strategy = new_strategy;
560
561 pthread_mutex_unlock(&page_cache.page_cache_lock);
562
563 return 0;
564 }
565
page_cache_check_page(struct page_cache_entity_of_inode * pce,pidx_t file_page_idx)566 int page_cache_check_page(struct page_cache_entity_of_inode *pce,
567 pidx_t file_page_idx)
568 {
569 struct cached_page *p;
570
571 pthread_mutex_lock(&page_cache.page_cache_lock);
572
573 /* Find corresponding page from pce list. */
574 p = get_node_from_page_cache_entity(pce, file_page_idx);
575
576 pthread_mutex_unlock(&page_cache.page_cache_lock);
577
578 if (p == NULL)
579 return 0;
580 else
581 return 1;
582 }
583
584 /* Only boost_page in page_cache_get_block_or_page. */
page_cache_put_block_or_page(struct page_cache_entity_of_inode * pce,pidx_t file_page_idx,int page_block_idx,PAGE_CACHE_OPERATION_TYPE op_type)585 void page_cache_put_block_or_page(struct page_cache_entity_of_inode *pce,
586 pidx_t file_page_idx, int page_block_idx,
587 PAGE_CACHE_OPERATION_TYPE op_type)
588 {
589 struct cached_page *p;
590 bool is_new;
591
592 BUG_ON(page_block_idx < -1 || page_block_idx >= BLOCK_PER_PAGE);
593 BUG_ON(op_type != READ && op_type != WRITE);
594
595 pthread_mutex_lock(&page_cache.page_cache_lock);
596
597 p = find_or_new_page(pce, file_page_idx, &is_new);
598 if (p == NULL)
599 goto out;
600
601 /* Target page must be existed. */
602 BUG_ON(is_new == true);
603
604 /* Nothing need to be done when handling read operations. */
605 if (op_type == READ) {
606 pthread_rwlock_unlock(&p->page_rwlock);
607 goto out;
608 }
609
610 /* Handling write operation. */
611 switch (page_cache.cache_strategy) {
612 case DIRECT:
613 /* Directly write block or page to disk. */
614 if (page_block_idx != -1) {
615 set_block_or_page_dirty(p, page_block_idx, true);
616 if (flush_single_block(p, page_block_idx) != 0)
617 BUG("[pc_set_block_or_page] file_write failed.\n");
618 } else {
619 set_block_or_page_dirty(p, -1, true);
620 if (flush_single_page(p) != 0)
621 BUG("[pc_set_block_or_page] file_write failed.\n");
622 }
623
624 /* Must unlock before freeing. */
625 pthread_rwlock_unlock(&p->page_rwlock);
626
627 /*
628 * Invalidate and free outdated cache.
629 * There won't be any use-after-free error because
630 * page_cache_lock protect us from this error.
631 */
632 flush_and_free_page(p);
633 break;
634
635 case WRITE_BACK:
636 /* Mark this block or page as dirty. */
637 set_block_or_page_dirty(p, page_block_idx, true);
638
639 pthread_rwlock_unlock(&p->page_rwlock);
640 break;
641
642 case WRITE_THROUGH:
643 /* Directly write block or page to disk. */
644 if (page_block_idx != -1) {
645 set_block_or_page_dirty(p, page_block_idx, true);
646 if (flush_single_block(p, page_block_idx) != 0)
647 BUG("[pc_set_block_or_page] file_write failed.\n");
648 } else {
649 set_block_or_page_dirty(p, -1, true);
650 if (flush_single_page(p) != 0)
651 BUG("[pc_set_block_or_page] file_write failed.\n");
652 }
653
654 pthread_rwlock_unlock(&p->page_rwlock);
655 break;
656 }
657
658 out:
659 pthread_mutex_unlock(&page_cache.page_cache_lock);
660 }
661
662 /* Only boost_page when calling page_cache_get_block_or_page. */
page_cache_get_block_or_page(struct page_cache_entity_of_inode * pce,pidx_t file_page_idx,int page_block_idx,PAGE_CACHE_OPERATION_TYPE op_type)663 char *page_cache_get_block_or_page(struct page_cache_entity_of_inode *pce,
664 pidx_t file_page_idx, int page_block_idx,
665 PAGE_CACHE_OPERATION_TYPE op_type)
666 {
667 struct cached_page *p;
668 bool is_new;
669 char *buf;
670
671 BUG_ON(page_block_idx < -1 || page_block_idx >= BLOCK_PER_PAGE);
672 BUG_ON(op_type != READ && op_type != WRITE);
673
674 pthread_mutex_lock(&page_cache.page_cache_lock);
675
676 p = find_or_new_page(pce, file_page_idx, &is_new);
677 if (p == NULL) {
678 buf = NULL;
679 goto out;
680 }
681
682 /* Grab read or write lock of this page. */
683 if (op_type == READ)
684 pthread_rwlock_rdlock(&p->page_rwlock);
685 else
686 pthread_rwlock_wrlock(&p->page_rwlock);
687
688 /* Read from corresponding cached page. */
689 if (page_block_idx != -1)
690 buf = p->content + page_block_idx * CACHED_BLOCK_SIZE;
691 else
692 buf = p->content;
693
694 /* New allocated pages should not be boosted. */
695 if (is_new == false)
696 boost_page(p);
697
698 out:
699 pthread_mutex_unlock(&page_cache.page_cache_lock);
700 return buf;
701 }
702
page_cache_flush_block_or_page(struct page_cache_entity_of_inode * pce,pidx_t file_page_idx,int page_block_idx)703 int page_cache_flush_block_or_page(struct page_cache_entity_of_inode *pce,
704 pidx_t file_page_idx, int page_block_idx)
705 {
706 struct cached_page *p;
707 int ret = 0;
708
709 BUG_ON(page_block_idx < -1 || page_block_idx >= BLOCK_PER_PAGE);
710
711 pthread_mutex_lock(&page_cache.page_cache_lock);
712
713 p = get_node_from_page_cache_entity(pce, file_page_idx);
714 if (p == NULL) {
715 page_cache_debug("Corresponding page cache does not exist!\n");
716 ret = -1;
717 goto out;
718 }
719
720 pthread_rwlock_rdlock(&p->page_rwlock);
721 if (page_block_idx != -1)
722 flush_single_block(p, page_block_idx);
723 else
724 flush_single_page(p);
725
726 out:
727 if (p)
728 pthread_rwlock_unlock(&p->page_rwlock);
729 pthread_mutex_unlock(&page_cache.page_cache_lock);
730 return ret;
731 }
732
page_cache_flush_pages_of_inode(struct page_cache_entity_of_inode * pce)733 int page_cache_flush_pages_of_inode(struct page_cache_entity_of_inode *pce)
734 {
735 struct cached_page *p;
736 int ret = 0;
737
738 pthread_mutex_lock(&page_cache.page_cache_lock);
739
740 page_cache_debug(
741 "[page_cache_flush_pages_of_inode] write back inode pages started.\n");
742
743 /* Write back pages that belongs to an inode. */
744 for_each_in_list (
745 p, struct cached_page, inode_pages_node, &pce->pages.queue) {
746 pthread_rwlock_rdlock(&p->page_rwlock);
747 if (flush_single_page(p) != 0) {
748 ret = -1;
749 goto fail_unlock;
750 }
751 pthread_rwlock_unlock(&p->page_rwlock);
752 }
753
754 page_cache_debug(
755 "[page_cache_flush_pages_of_inode] write back inode pages finished.\n");
756 goto out;
757
758 fail_unlock:
759 pthread_rwlock_unlock(&p->page_rwlock);
760 out:
761 pthread_mutex_unlock(&page_cache.page_cache_lock);
762 return ret;
763 }
764
page_cache_flush_all_pages(void)765 int page_cache_flush_all_pages(void)
766 {
767 pthread_mutex_lock(&page_cache.page_cache_lock);
768
769 page_cache_debug("[page_cache_flush_all_pages] flush all pages started.\n");
770
771 write_back_all_pages();
772
773 page_cache_debug(
774 "[page_cache_flush_all_pages] flush all pages finished.\n");
775
776 pthread_mutex_unlock(&page_cache.page_cache_lock);
777 return 0;
778 }
779
page_cache_pin_single_page(struct page_cache_entity_of_inode * pce,pidx_t file_page_idx)780 int page_cache_pin_single_page(struct page_cache_entity_of_inode *pce,
781 pidx_t file_page_idx)
782 {
783 struct cached_page *p;
784 int ret = 0;
785
786 pthread_mutex_lock(&page_cache.page_cache_lock);
787
788 /* Check max pinned pages. */
789 if (page_cache.pinned_pages_list.size == MAX_PINNED_PAGE) {
790 page_cache_debug("pinned pages number reach limits.\n");
791 ret = -1;
792 goto out;
793 }
794
795 /* Find target page, if not found, create one. */
796 p = find_or_new_page(pce, file_page_idx, NULL);
797 if (p == NULL) {
798 ret = -1;
799 goto out;
800 }
801
802 /* Remove this page from two lists. */
803 if (p->in_which_list == ACTIVE_LIST) {
804 cached_pages_list_delete_node(&page_cache.active_list, p, ACTIVE_LIST);
805 } else if (p->in_which_list == INACTIVE_LIST) {
806 cached_pages_list_delete_node(
807 &page_cache.inactive_list, p, INACTIVE_LIST);
808 } else if (p->in_which_list == PINNED_PAGES_LIST) {
809 /* Page already pinned. */
810 ret = 0;
811 goto out;
812 } else {
813 BUG("Invalid list type.\n");
814 ret = -1;
815 goto out;
816 }
817
818 /* Insert this page to pinned pages list. */
819 cached_pages_list_append_node(
820 &page_cache.pinned_pages_list, p, PINNED_PAGES_LIST);
821
822 out:
823 pthread_mutex_unlock(&page_cache.page_cache_lock);
824 return ret;
825 }
826
page_cache_unpin_single_page(struct page_cache_entity_of_inode * pce,pidx_t file_page_idx)827 int page_cache_unpin_single_page(struct page_cache_entity_of_inode *pce,
828 pidx_t file_page_idx)
829 {
830 struct cached_page *p;
831 int ret = 0;
832
833 pthread_mutex_lock(&page_cache.page_cache_lock);
834
835 /* Ensuring target page is already existed. */
836 p = get_node_from_page_cache_entity(pce, file_page_idx);
837 if (p == NULL) {
838 page_cache_debug("page not found.\n");
839 ret = -1;
840 goto out;
841 }
842
843 /* Ensuring target page is already pinned. */
844 if (p->in_which_list != PINNED_PAGES_LIST) {
845 page_cache_debug("page not pinned.\n");
846 ret = -1;
847 goto out;
848 }
849
850 /*
851 * Because we are unlikely to access this page again after we unpin it,
852 * we just flush and free this page.
853 */
854 flush_and_free_page(p);
855
856 out:
857 pthread_mutex_unlock(&page_cache.page_cache_lock);
858 return ret;
859 }
860
page_cache_pin_multiple_pages(struct page_cache_entity_of_inode * pce,pidx_t file_page_idx,u64 page_num)861 int page_cache_pin_multiple_pages(struct page_cache_entity_of_inode *pce,
862 pidx_t file_page_idx, u64 page_num)
863 {
864 u64 i;
865
866 for (i = file_page_idx; i < file_page_idx + page_num; ++i)
867 if (page_cache_pin_single_page(pce, i) != 0)
868 return -1;
869
870 return 0;
871 }
872
page_cache_unpin_multiple_pages(struct page_cache_entity_of_inode * pce,pidx_t file_page_idx,u64 page_num)873 int page_cache_unpin_multiple_pages(struct page_cache_entity_of_inode *pce,
874 pidx_t file_page_idx, u64 page_num)
875 {
876 u64 i;
877
878 for (i = file_page_idx; i < file_page_idx + page_num; ++i)
879 if (page_cache_unpin_single_page(pce, i) != 0)
880 return -1;
881
882 return 0;
883 }
884
page_cache_evict_single_page(struct page_cache_entity_of_inode * pce,pidx_t file_page_idx)885 int page_cache_evict_single_page(struct page_cache_entity_of_inode *pce,
886 pidx_t file_page_idx)
887 {
888 struct cached_page *p;
889 int ret = 0;
890
891 pthread_mutex_lock(&page_cache.page_cache_lock);
892
893 /* Ensuring target page is already existing. */
894 p = get_node_from_page_cache_entity(pce, file_page_idx);
895 if (p == NULL) {
896 page_cache_debug(
897 "[page_cache_evict_single_page] page does not exist.\n");
898 ret = -1;
899 goto out;
900 }
901
902 page_cache_debug("Evict %d:%d in list %d\n",
903 p->owner->host_idx,
904 p->file_page_idx,
905 p->in_which_list);
906 flush_and_free_page(p);
907
908 out:
909 pthread_mutex_unlock(&page_cache.page_cache_lock);
910 return ret;
911 }
912
page_cache_evict_pages_of_inode(struct page_cache_entity_of_inode * pce)913 int page_cache_evict_pages_of_inode(struct page_cache_entity_of_inode *pce)
914 {
915 struct cached_page **p_array;
916 struct cached_page *p;
917 int queue_size, i = 0;
918
919 pthread_mutex_lock(&page_cache.page_cache_lock);
920
921 queue_size = pce->pages.size;
922 p_array = (struct cached_page **)calloc(pce->pages.size,
923 sizeof(struct cached_page *));
924 BUG_ON(p_array == NULL);
925
926 /* Find all target pages. */
927 for_each_in_list (
928 p, struct cached_page, inode_pages_node, &pce->pages.queue)
929 p_array[i++] = p;
930 BUG_ON(i != queue_size);
931
932 for (i = 0; i < queue_size; ++i) {
933 page_cache_debug("Evict %d:%d in list %d\n",
934 p_array[i]->owner->host_idx,
935 p_array[i]->file_page_idx,
936 p_array[i]->in_which_list);
937 flush_and_free_page(p_array[i]);
938 }
939
940 free(p_array);
941
942 pthread_mutex_unlock(&page_cache.page_cache_lock);
943 return 0;
944 }
945
page_cache_delete_single_page(struct page_cache_entity_of_inode * pce,pidx_t file_page_idx)946 int page_cache_delete_single_page(struct page_cache_entity_of_inode *pce,
947 pidx_t file_page_idx)
948 {
949 struct cached_page *p;
950 int ret = 0;
951
952 pthread_mutex_lock(&page_cache.page_cache_lock);
953
954 /* Ensuring target page is already existing. */
955 p = get_node_from_page_cache_entity(pce, file_page_idx);
956 if (p == NULL) {
957 page_cache_debug(
958 "[page_cache_delete_single_page] page does not exist.\n");
959 ret = -1;
960 goto out;
961 }
962
963 page_cache_debug("Delete %d:%d in list %d\n",
964 p->owner->host_idx,
965 p->file_page_idx,
966 p->in_which_list);
967 free_page(p);
968
969 out:
970 pthread_mutex_unlock(&page_cache.page_cache_lock);
971 return ret;
972 }
973
page_cache_delete_pages_of_inode(struct page_cache_entity_of_inode * pce)974 int page_cache_delete_pages_of_inode(struct page_cache_entity_of_inode *pce)
975 {
976 struct cached_page **p_array;
977 struct cached_page *p;
978 int queue_size, i = 0;
979
980 pthread_mutex_lock(&page_cache.page_cache_lock);
981
982 queue_size = pce->pages.size;
983 p_array = (struct cached_page **)calloc(pce->pages.size,
984 sizeof(struct cached_page *));
985 BUG_ON(p_array == NULL);
986
987 /* Find all target pages. */
988 for_each_in_list (
989 p, struct cached_page, inode_pages_node, &pce->pages.queue)
990 p_array[i++] = p;
991 BUG_ON(i != queue_size);
992
993 for (i = 0; i < queue_size; ++i) {
994 page_cache_debug("Delete %d:%d in list %d\n",
995 p_array[i]->owner->host_idx,
996 p_array[i]->file_page_idx,
997 p_array[i]->in_which_list);
998 free_page(p_array[i]);
999 }
1000
1001 free(p_array);
1002
1003 pthread_mutex_unlock(&page_cache.page_cache_lock);
1004 return 0;
1005 }
1006