Skip to content

Commit 275b66b

Browse files
chaseyuJaegeuk Kim
authored andcommitted
f2fs: support async discard
Like most filesystems, f2fs will issue discard command synchronously, so when user trigger fstrim through ioctl, multiple discard commands will be issued serially with sync mode, which makes poor performance. In this patch we try to support async discard, so that all discard commands can be issued and be waited for endio in batch to improve performance. Signed-off-by: Chao Yu <[email protected]> Signed-off-by: Jaegeuk Kim <[email protected]>
1 parent 167451e commit 275b66b

File tree

4 files changed

+99
-2
lines changed

4 files changed

+99
-2
lines changed

fs/f2fs/checkpoint.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,7 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
11971197
f2fs_bug_on(sbi, prefree_segments(sbi));
11981198
flush_sit_entries(sbi, cpc);
11991199
clear_prefree_segments(sbi, cpc);
1200+
f2fs_wait_all_discard_bio(sbi);
12001201
unblock_operations(sbi);
12011202
goto out;
12021203
}
@@ -1216,6 +1217,8 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
12161217
/* unlock all the fs_lock[] in do_checkpoint() */
12171218
err = do_checkpoint(sbi, cpc);
12181219

1220+
f2fs_wait_all_discard_bio(sbi);
1221+
12191222
unblock_operations(sbi);
12201223
stat_inc_cp_count(sbi->stat_info);
12211224

fs/f2fs/f2fs.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,13 @@ struct discard_entry {
211211
int len; /* # of consecutive blocks of the discard */
212212
};
213213

214+
struct bio_entry {
215+
struct list_head list;
216+
struct bio *bio;
217+
struct completion event;
218+
int error;
219+
};
220+
214221
/* for the list of fsync inodes, used only during recovery */
215222
struct fsync_inode_entry {
216223
struct list_head list; /* list head */
@@ -645,6 +652,7 @@ struct f2fs_sm_info {
645652

646653
/* for small discard management */
647654
struct list_head discard_list; /* 4KB discard list */
655+
struct list_head wait_list; /* linked with issued discard bio */
648656
int nr_discards; /* # of discards in the list */
649657
int max_discards; /* max. discards to be issued */
650658

@@ -2026,6 +2034,7 @@ void destroy_flush_cmd_control(struct f2fs_sb_info *);
20262034
void invalidate_blocks(struct f2fs_sb_info *, block_t);
20272035
bool is_checkpointed_data(struct f2fs_sb_info *, block_t);
20282036
void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t);
2037+
void f2fs_wait_all_discard_bio(struct f2fs_sb_info *);
20292038
void clear_prefree_segments(struct f2fs_sb_info *, struct cp_control *);
20302039
void release_discard_addrs(struct f2fs_sb_info *);
20312040
bool discard_next_dnode(struct f2fs_sb_info *, block_t);

fs/f2fs/recovery.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,8 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
636636
invalidate = true;
637637
}
638638

639+
f2fs_wait_all_discard_bio(sbi);
640+
639641
/* Flush all the NAT/SIT pages */
640642
while (get_pages(sbi, F2FS_DIRTY_META))
641643
sync_meta_pages(sbi, META, LONG_MAX);

fs/f2fs/segment.c

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#define __reverse_ffz(x) __reverse_ffs(~(x))
2727

2828
static struct kmem_cache *discard_entry_slab;
29+
static struct kmem_cache *bio_entry_slab;
2930
static struct kmem_cache *sit_entry_set_slab;
3031
static struct kmem_cache *inmem_entry_slab;
3132

@@ -580,6 +581,74 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
580581
mutex_unlock(&dirty_i->seglist_lock);
581582
}
582583

584+
static struct bio_entry *__add_bio_entry(struct f2fs_sb_info *sbi,
585+
struct bio *bio)
586+
{
587+
struct list_head *wait_list = &(SM_I(sbi)->wait_list);
588+
struct bio_entry *be = f2fs_kmem_cache_alloc(bio_entry_slab, GFP_NOFS);
589+
590+
INIT_LIST_HEAD(&be->list);
591+
be->bio = bio;
592+
init_completion(&be->event);
593+
list_add_tail(&be->list, wait_list);
594+
595+
return be;
596+
}
597+
598+
void f2fs_wait_all_discard_bio(struct f2fs_sb_info *sbi)
599+
{
600+
struct list_head *wait_list = &(SM_I(sbi)->wait_list);
601+
struct bio_entry *be, *tmp;
602+
603+
list_for_each_entry_safe(be, tmp, wait_list, list) {
604+
struct bio *bio = be->bio;
605+
int err;
606+
607+
wait_for_completion_io(&be->event);
608+
err = be->error;
609+
if (err == -EOPNOTSUPP)
610+
err = 0;
611+
612+
if (err)
613+
f2fs_msg(sbi->sb, KERN_INFO,
614+
"Issue discard failed, ret: %d", err);
615+
616+
bio_put(bio);
617+
list_del(&be->list);
618+
kmem_cache_free(bio_entry_slab, be);
619+
}
620+
}
621+
622+
static void f2fs_submit_bio_wait_endio(struct bio *bio)
623+
{
624+
struct bio_entry *be = (struct bio_entry *)bio->bi_private;
625+
626+
be->error = bio->bi_error;
627+
complete(&be->event);
628+
}
629+
630+
/* this function is copied from blkdev_issue_discard from block/blk-lib.c */
631+
int __f2fs_issue_discard_async(struct f2fs_sb_info *sbi, sector_t sector,
632+
sector_t nr_sects, gfp_t gfp_mask, unsigned long flags)
633+
{
634+
struct block_device *bdev = sbi->sb->s_bdev;
635+
struct bio *bio = NULL;
636+
int err;
637+
638+
err = __blkdev_issue_discard(bdev, sector, nr_sects, gfp_mask, flags,
639+
&bio);
640+
if (!err && bio) {
641+
struct bio_entry *be = __add_bio_entry(sbi, bio);
642+
643+
bio->bi_private = be;
644+
bio->bi_end_io = f2fs_submit_bio_wait_endio;
645+
bio->bi_opf |= REQ_SYNC;
646+
submit_bio(bio);
647+
}
648+
649+
return err;
650+
}
651+
583652
static int f2fs_issue_discard(struct f2fs_sb_info *sbi,
584653
block_t blkstart, block_t blklen)
585654
{
@@ -597,7 +666,7 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi,
597666
sbi->discard_blks--;
598667
}
599668
trace_f2fs_issue_discard(sbi->sb, blkstart, blklen);
600-
return blkdev_issue_discard(sbi->sb->s_bdev, start, len, GFP_NOFS, 0);
669+
return __f2fs_issue_discard_async(sbi, start, len, GFP_NOFS, 0);
601670
}
602671

603672
bool discard_next_dnode(struct f2fs_sb_info *sbi, block_t blkaddr)
@@ -719,11 +788,14 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc)
719788
struct list_head *head = &(SM_I(sbi)->discard_list);
720789
struct discard_entry *entry, *this;
721790
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
791+
struct blk_plug plug;
722792
unsigned long *prefree_map = dirty_i->dirty_segmap[PRE];
723793
unsigned int start = 0, end = -1;
724794
unsigned int secno, start_segno;
725795
bool force = (cpc->reason == CP_DISCARD);
726796

797+
blk_start_plug(&plug);
798+
727799
mutex_lock(&dirty_i->seglist_lock);
728800

729801
while (1) {
@@ -772,6 +844,8 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc)
772844
SM_I(sbi)->nr_discards -= entry->len;
773845
kmem_cache_free(discard_entry_slab, entry);
774846
}
847+
848+
blk_finish_plug(&plug);
775849
}
776850

777851
static bool __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno)
@@ -2457,6 +2531,7 @@ int build_segment_manager(struct f2fs_sb_info *sbi)
24572531
sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS;
24582532

24592533
INIT_LIST_HEAD(&sm_info->discard_list);
2534+
INIT_LIST_HEAD(&sm_info->wait_list);
24602535
sm_info->nr_discards = 0;
24612536
sm_info->max_discards = 0;
24622537

@@ -2600,10 +2675,15 @@ int __init create_segment_manager_caches(void)
26002675
if (!discard_entry_slab)
26012676
goto fail;
26022677

2678+
bio_entry_slab = f2fs_kmem_cache_create("bio_entry",
2679+
sizeof(struct bio_entry));
2680+
if (!bio_entry_slab)
2681+
goto destory_discard_entry;
2682+
26032683
sit_entry_set_slab = f2fs_kmem_cache_create("sit_entry_set",
26042684
sizeof(struct sit_entry_set));
26052685
if (!sit_entry_set_slab)
2606-
goto destory_discard_entry;
2686+
goto destroy_bio_entry;
26072687

26082688
inmem_entry_slab = f2fs_kmem_cache_create("inmem_page_entry",
26092689
sizeof(struct inmem_pages));
@@ -2613,6 +2693,8 @@ int __init create_segment_manager_caches(void)
26132693

26142694
destroy_sit_entry_set:
26152695
kmem_cache_destroy(sit_entry_set_slab);
2696+
destroy_bio_entry:
2697+
kmem_cache_destroy(bio_entry_slab);
26162698
destory_discard_entry:
26172699
kmem_cache_destroy(discard_entry_slab);
26182700
fail:
@@ -2622,6 +2704,7 @@ int __init create_segment_manager_caches(void)
26222704
void destroy_segment_manager_caches(void)
26232705
{
26242706
kmem_cache_destroy(sit_entry_set_slab);
2707+
kmem_cache_destroy(bio_entry_slab);
26252708
kmem_cache_destroy(discard_entry_slab);
26262709
kmem_cache_destroy(inmem_entry_slab);
26272710
}

0 commit comments

Comments
 (0)