diff options
author | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2013-02-04 01:11:17 -0500 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2013-02-11 17:15:02 -0500 |
commit | 437275272f9e635673f065300e5d95226a25cb06 (patch) | |
tree | bb0b8945d70971c200d098106b8879c1f84e109a /fs/f2fs/gc.c | |
parent | b1f1daf8c72d615b64163e26488d8effeed29b60 (diff) |
f2fs: clarify and enhance the f2fs_gc flow
This patch makes clearer the ambiguous f2fs_gc flow as follows.
1. Remove intermediate checkpoint condition during f2fs_gc
(i.e., should_do_checkpoint() and GC_BLOCKED)
2. Remove unnecessary return values of f2fs_gc because of #1.
(i.e., GC_NODE, GC_OK, etc)
3. Simplify write_checkpoint() because of #2.
4. Clarify the main f2fs_gc flow.
o monitor how many freed sections during one iteration of do_garbage_collect().
o do GC more without checkpoints if we can't get enough free sections.
o do checkpoint once we've got enough free sections through forground GCs.
5. Adopt thread-logging (Slack-Space-Recycle) scheme more aggressively on data
log types. See. get_ssr_segement()
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
Diffstat (limited to 'fs/f2fs/gc.c')
-rw-r--r-- | fs/f2fs/gc.c | 107 |
1 files changed, 41 insertions, 66 deletions
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 16fdec355201..52d3a391b922 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c | |||
@@ -78,7 +78,8 @@ static int gc_thread_func(void *data) | |||
78 | 78 | ||
79 | sbi->bg_gc++; | 79 | sbi->bg_gc++; |
80 | 80 | ||
81 | if (f2fs_gc(sbi) == GC_NONE) | 81 | /* if return value is not zero, no victim was selected */ |
82 | if (f2fs_gc(sbi)) | ||
82 | wait_ms = GC_THREAD_NOGC_SLEEP_TIME; | 83 | wait_ms = GC_THREAD_NOGC_SLEEP_TIME; |
83 | else if (wait_ms == GC_THREAD_NOGC_SLEEP_TIME) | 84 | else if (wait_ms == GC_THREAD_NOGC_SLEEP_TIME) |
84 | wait_ms = GC_THREAD_MAX_SLEEP_TIME; | 85 | wait_ms = GC_THREAD_MAX_SLEEP_TIME; |
@@ -360,7 +361,7 @@ static int check_valid_map(struct f2fs_sb_info *sbi, | |||
360 | sentry = get_seg_entry(sbi, segno); | 361 | sentry = get_seg_entry(sbi, segno); |
361 | ret = f2fs_test_bit(offset, sentry->cur_valid_map); | 362 | ret = f2fs_test_bit(offset, sentry->cur_valid_map); |
362 | mutex_unlock(&sit_i->sentry_lock); | 363 | mutex_unlock(&sit_i->sentry_lock); |
363 | return ret ? GC_OK : GC_NEXT; | 364 | return ret; |
364 | } | 365 | } |
365 | 366 | ||
366 | /* | 367 | /* |
@@ -368,7 +369,7 @@ static int check_valid_map(struct f2fs_sb_info *sbi, | |||
368 | * On validity, copy that node with cold status, otherwise (invalid node) | 369 | * On validity, copy that node with cold status, otherwise (invalid node) |
369 | * ignore that. | 370 | * ignore that. |
370 | */ | 371 | */ |
371 | static int gc_node_segment(struct f2fs_sb_info *sbi, | 372 | static void gc_node_segment(struct f2fs_sb_info *sbi, |
372 | struct f2fs_summary *sum, unsigned int segno, int gc_type) | 373 | struct f2fs_summary *sum, unsigned int segno, int gc_type) |
373 | { | 374 | { |
374 | bool initial = true; | 375 | bool initial = true; |
@@ -380,21 +381,12 @@ next_step: | |||
380 | for (off = 0; off < sbi->blocks_per_seg; off++, entry++) { | 381 | for (off = 0; off < sbi->blocks_per_seg; off++, entry++) { |
381 | nid_t nid = le32_to_cpu(entry->nid); | 382 | nid_t nid = le32_to_cpu(entry->nid); |
382 | struct page *node_page; | 383 | struct page *node_page; |
383 | int err; | ||
384 | 384 | ||
385 | /* | 385 | /* stop BG_GC if there is not enough free sections. */ |
386 | * It makes sure that free segments are able to write | 386 | if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) |
387 | * all the dirty node pages before CP after this CP. | 387 | return; |
388 | * So let's check the space of dirty node pages. | ||
389 | */ | ||
390 | if (should_do_checkpoint(sbi)) { | ||
391 | mutex_lock(&sbi->cp_mutex); | ||
392 | block_operations(sbi); | ||
393 | return GC_BLOCKED; | ||
394 | } | ||
395 | 388 | ||
396 | err = check_valid_map(sbi, segno, off); | 389 | if (check_valid_map(sbi, segno, off) == 0) |
397 | if (err == GC_NEXT) | ||
398 | continue; | 390 | continue; |
399 | 391 | ||
400 | if (initial) { | 392 | if (initial) { |
@@ -424,7 +416,6 @@ next_step: | |||
424 | }; | 416 | }; |
425 | sync_node_pages(sbi, 0, &wbc); | 417 | sync_node_pages(sbi, 0, &wbc); |
426 | } | 418 | } |
427 | return GC_DONE; | ||
428 | } | 419 | } |
429 | 420 | ||
430 | /* | 421 | /* |
@@ -467,13 +458,13 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, | |||
467 | 458 | ||
468 | node_page = get_node_page(sbi, nid); | 459 | node_page = get_node_page(sbi, nid); |
469 | if (IS_ERR(node_page)) | 460 | if (IS_ERR(node_page)) |
470 | return GC_NEXT; | 461 | return 0; |
471 | 462 | ||
472 | get_node_info(sbi, nid, dni); | 463 | get_node_info(sbi, nid, dni); |
473 | 464 | ||
474 | if (sum->version != dni->version) { | 465 | if (sum->version != dni->version) { |
475 | f2fs_put_page(node_page, 1); | 466 | f2fs_put_page(node_page, 1); |
476 | return GC_NEXT; | 467 | return 0; |
477 | } | 468 | } |
478 | 469 | ||
479 | *nofs = ofs_of_node(node_page); | 470 | *nofs = ofs_of_node(node_page); |
@@ -481,8 +472,8 @@ static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, | |||
481 | f2fs_put_page(node_page, 1); | 472 | f2fs_put_page(node_page, 1); |
482 | 473 | ||
483 | if (source_blkaddr != blkaddr) | 474 | if (source_blkaddr != blkaddr) |
484 | return GC_NEXT; | 475 | return 0; |
485 | return GC_OK; | 476 | return 1; |
486 | } | 477 | } |
487 | 478 | ||
488 | static void move_data_page(struct inode *inode, struct page *page, int gc_type) | 479 | static void move_data_page(struct inode *inode, struct page *page, int gc_type) |
@@ -523,13 +514,13 @@ out: | |||
523 | * If the parent node is not valid or the data block address is different, | 514 | * If the parent node is not valid or the data block address is different, |
524 | * the victim data block is ignored. | 515 | * the victim data block is ignored. |
525 | */ | 516 | */ |
526 | static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, | 517 | static void gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, |
527 | struct list_head *ilist, unsigned int segno, int gc_type) | 518 | struct list_head *ilist, unsigned int segno, int gc_type) |
528 | { | 519 | { |
529 | struct super_block *sb = sbi->sb; | 520 | struct super_block *sb = sbi->sb; |
530 | struct f2fs_summary *entry; | 521 | struct f2fs_summary *entry; |
531 | block_t start_addr; | 522 | block_t start_addr; |
532 | int err, off; | 523 | int off; |
533 | int phase = 0; | 524 | int phase = 0; |
534 | 525 | ||
535 | start_addr = START_BLOCK(sbi, segno); | 526 | start_addr = START_BLOCK(sbi, segno); |
@@ -543,20 +534,11 @@ next_step: | |||
543 | unsigned int ofs_in_node, nofs; | 534 | unsigned int ofs_in_node, nofs; |
544 | block_t start_bidx; | 535 | block_t start_bidx; |
545 | 536 | ||
546 | /* | 537 | /* stop BG_GC if there is not enough free sections. */ |
547 | * It makes sure that free segments are able to write | 538 | if (gc_type == BG_GC && has_not_enough_free_secs(sbi, 0)) |
548 | * all the dirty node pages before CP after this CP. | 539 | return; |
549 | * So let's check the space of dirty node pages. | ||
550 | */ | ||
551 | if (should_do_checkpoint(sbi)) { | ||
552 | mutex_lock(&sbi->cp_mutex); | ||
553 | block_operations(sbi); | ||
554 | err = GC_BLOCKED; | ||
555 | goto stop; | ||
556 | } | ||
557 | 540 | ||
558 | err = check_valid_map(sbi, segno, off); | 541 | if (check_valid_map(sbi, segno, off) == 0) |
559 | if (err == GC_NEXT) | ||
560 | continue; | 542 | continue; |
561 | 543 | ||
562 | if (phase == 0) { | 544 | if (phase == 0) { |
@@ -565,8 +547,7 @@ next_step: | |||
565 | } | 547 | } |
566 | 548 | ||
567 | /* Get an inode by ino with checking validity */ | 549 | /* Get an inode by ino with checking validity */ |
568 | err = check_dnode(sbi, entry, &dni, start_addr + off, &nofs); | 550 | if (check_dnode(sbi, entry, &dni, start_addr + off, &nofs) == 0) |
569 | if (err == GC_NEXT) | ||
570 | continue; | 551 | continue; |
571 | 552 | ||
572 | if (phase == 1) { | 553 | if (phase == 1) { |
@@ -606,11 +587,9 @@ next_iput: | |||
606 | } | 587 | } |
607 | if (++phase < 4) | 588 | if (++phase < 4) |
608 | goto next_step; | 589 | goto next_step; |
609 | err = GC_DONE; | 590 | |
610 | stop: | ||
611 | if (gc_type == FG_GC) | 591 | if (gc_type == FG_GC) |
612 | f2fs_submit_bio(sbi, DATA, true); | 592 | f2fs_submit_bio(sbi, DATA, true); |
613 | return err; | ||
614 | } | 593 | } |
615 | 594 | ||
616 | static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, | 595 | static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, |
@@ -624,17 +603,16 @@ static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, | |||
624 | return ret; | 603 | return ret; |
625 | } | 604 | } |
626 | 605 | ||
627 | static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, | 606 | static void do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, |
628 | struct list_head *ilist, int gc_type) | 607 | struct list_head *ilist, int gc_type) |
629 | { | 608 | { |
630 | struct page *sum_page; | 609 | struct page *sum_page; |
631 | struct f2fs_summary_block *sum; | 610 | struct f2fs_summary_block *sum; |
632 | int ret = GC_DONE; | ||
633 | 611 | ||
634 | /* read segment summary of victim */ | 612 | /* read segment summary of victim */ |
635 | sum_page = get_sum_page(sbi, segno); | 613 | sum_page = get_sum_page(sbi, segno); |
636 | if (IS_ERR(sum_page)) | 614 | if (IS_ERR(sum_page)) |
637 | return GC_ERROR; | 615 | return; |
638 | 616 | ||
639 | /* | 617 | /* |
640 | * CP needs to lock sum_page. In this time, we don't need | 618 | * CP needs to lock sum_page. In this time, we don't need |
@@ -646,17 +624,16 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, | |||
646 | 624 | ||
647 | switch (GET_SUM_TYPE((&sum->footer))) { | 625 | switch (GET_SUM_TYPE((&sum->footer))) { |
648 | case SUM_TYPE_NODE: | 626 | case SUM_TYPE_NODE: |
649 | ret = gc_node_segment(sbi, sum->entries, segno, gc_type); | 627 | gc_node_segment(sbi, sum->entries, segno, gc_type); |
650 | break; | 628 | break; |
651 | case SUM_TYPE_DATA: | 629 | case SUM_TYPE_DATA: |
652 | ret = gc_data_segment(sbi, sum->entries, ilist, segno, gc_type); | 630 | gc_data_segment(sbi, sum->entries, ilist, segno, gc_type); |
653 | break; | 631 | break; |
654 | } | 632 | } |
655 | stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer))); | 633 | stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer))); |
656 | stat_inc_call_count(sbi->stat_info); | 634 | stat_inc_call_count(sbi->stat_info); |
657 | 635 | ||
658 | f2fs_put_page(sum_page, 0); | 636 | f2fs_put_page(sum_page, 0); |
659 | return ret; | ||
660 | } | 637 | } |
661 | 638 | ||
662 | int f2fs_gc(struct f2fs_sb_info *sbi) | 639 | int f2fs_gc(struct f2fs_sb_info *sbi) |
@@ -664,40 +641,38 @@ int f2fs_gc(struct f2fs_sb_info *sbi) | |||
664 | struct list_head ilist; | 641 | struct list_head ilist; |
665 | unsigned int segno, i; | 642 | unsigned int segno, i; |
666 | int gc_type = BG_GC; | 643 | int gc_type = BG_GC; |
667 | int gc_status = GC_NONE; | 644 | int nfree = 0; |
645 | int ret = -1; | ||
668 | 646 | ||
669 | INIT_LIST_HEAD(&ilist); | 647 | INIT_LIST_HEAD(&ilist); |
670 | gc_more: | 648 | gc_more: |
671 | if (!(sbi->sb->s_flags & MS_ACTIVE)) | 649 | if (!(sbi->sb->s_flags & MS_ACTIVE)) |
672 | goto stop; | 650 | goto stop; |
673 | 651 | ||
674 | if (gc_type == BG_GC && has_not_enough_free_secs(sbi)) | 652 | if (gc_type == BG_GC && has_not_enough_free_secs(sbi, nfree)) |
675 | gc_type = FG_GC; | 653 | gc_type = FG_GC; |
676 | 654 | ||
677 | if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE)) | 655 | if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE)) |
678 | goto stop; | 656 | goto stop; |
657 | ret = 0; | ||
679 | 658 | ||
680 | for (i = 0; i < sbi->segs_per_sec; i++) { | 659 | for (i = 0; i < sbi->segs_per_sec; i++) |
681 | /* | 660 | do_garbage_collect(sbi, segno + i, &ilist, gc_type); |
682 | * do_garbage_collect will give us three gc_status: | 661 | |
683 | * GC_ERROR, GC_DONE, and GC_BLOCKED. | 662 | if (gc_type == FG_GC && |
684 | * If GC is finished uncleanly, we have to return | 663 | get_valid_blocks(sbi, segno, sbi->segs_per_sec) == 0) |
685 | * the victim to dirty segment list. | 664 | nfree++; |
686 | */ | 665 | |
687 | gc_status = do_garbage_collect(sbi, segno + i, &ilist, gc_type); | 666 | if (has_not_enough_free_secs(sbi, nfree)) |
688 | if (gc_status != GC_DONE) | 667 | goto gc_more; |
689 | break; | 668 | |
690 | } | 669 | if (gc_type == FG_GC) |
691 | if (has_not_enough_free_secs(sbi)) { | 670 | write_checkpoint(sbi, false); |
692 | write_checkpoint(sbi, (gc_status == GC_BLOCKED), false); | ||
693 | if (has_not_enough_free_secs(sbi)) | ||
694 | goto gc_more; | ||
695 | } | ||
696 | stop: | 671 | stop: |
697 | mutex_unlock(&sbi->gc_mutex); | 672 | mutex_unlock(&sbi->gc_mutex); |
698 | 673 | ||
699 | put_gc_inode(&ilist); | 674 | put_gc_inode(&ilist); |
700 | return gc_status; | 675 | return ret; |
701 | } | 676 | } |
702 | 677 | ||
703 | void build_gc_manager(struct f2fs_sb_info *sbi) | 678 | void build_gc_manager(struct f2fs_sb_info *sbi) |