Skip to content

Commit 64d8b29

Browse files
Jaegeuk Kimpundiramit
authored andcommitted
f2fs: support async discard based on v4.9
commit 275b66b09e85cf0520dc610dd89706952751a473 upstream. This patch is based on commit 275b66b09e85 (f2fs: support async discard). Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
1 parent cfbece0 commit 64d8b29

3 files changed

Lines changed: 181 additions & 12 deletions

File tree

fs/f2fs/checkpoint.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,7 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
12551255
f2fs_bug_on(sbi, prefree_segments(sbi));
12561256
flush_sit_entries(sbi, cpc);
12571257
clear_prefree_segments(sbi, cpc);
1258+
f2fs_wait_all_discard_bio(sbi);
12581259
unblock_operations(sbi);
12591260
goto out;
12601261
}
@@ -1273,10 +1274,12 @@ int write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
12731274

12741275
/* unlock all the fs_lock[] in do_checkpoint() */
12751276
err = do_checkpoint(sbi, cpc);
1276-
if (err)
1277+
if (err) {
12771278
release_discard_addrs(sbi);
1278-
else
1279+
} else {
12791280
clear_prefree_segments(sbi, cpc);
1281+
f2fs_wait_all_discard_bio(sbi);
1282+
}
12801283

12811284
unblock_operations(sbi);
12821285
stat_inc_cp_count(sbi->stat_info);

fs/f2fs/f2fs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ static inline void bio_set_op_attrs(struct bio *bio, unsigned op,
127127
static inline int wbc_to_write_flags(struct writeback_control *wbc)
128128
{
129129
if (wbc->sync_mode == WB_SYNC_ALL)
130-
return REQ_SYNC;
130+
return REQ_SYNC | REQ_NOIDLE;
131131
return 0;
132132
}
133133

@@ -2174,6 +2174,7 @@ void destroy_flush_cmd_control(struct f2fs_sb_info *, bool);
21742174
void invalidate_blocks(struct f2fs_sb_info *, block_t);
21752175
bool is_checkpointed_data(struct f2fs_sb_info *, block_t);
21762176
void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t);
2177+
void f2fs_wait_all_discard_bio(struct f2fs_sb_info *);
21772178
void clear_prefree_segments(struct f2fs_sb_info *, struct cp_control *);
21782179
void release_discard_addrs(struct f2fs_sb_info *);
21792180
int npages_for_summary_flush(struct f2fs_sb_info *, bool);

fs/f2fs/segment.c

Lines changed: 174 additions & 9 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

@@ -622,6 +623,162 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
622623
mutex_unlock(&dirty_i->seglist_lock);
623624
}
624625

626+
static struct bio_entry *__add_bio_entry(struct f2fs_sb_info *sbi,
627+
struct bio *bio)
628+
{
629+
struct list_head *wait_list = &(SM_I(sbi)->wait_list);
630+
struct bio_entry *be = f2fs_kmem_cache_alloc(bio_entry_slab, GFP_NOFS);
631+
632+
INIT_LIST_HEAD(&be->list);
633+
be->bio = bio;
634+
init_completion(&be->event);
635+
list_add_tail(&be->list, wait_list);
636+
637+
return be;
638+
}
639+
640+
void f2fs_wait_all_discard_bio(struct f2fs_sb_info *sbi)
641+
{
642+
struct list_head *wait_list = &(SM_I(sbi)->wait_list);
643+
struct bio_entry *be, *tmp;
644+
645+
list_for_each_entry_safe(be, tmp, wait_list, list) {
646+
struct bio *bio = be->bio;
647+
int err;
648+
649+
wait_for_completion_io(&be->event);
650+
err = be->error;
651+
if (err == -EOPNOTSUPP)
652+
err = 0;
653+
654+
if (err)
655+
f2fs_msg(sbi->sb, KERN_INFO,
656+
"Issue discard failed, ret: %d", err);
657+
658+
bio_put(bio);
659+
list_del(&be->list);
660+
kmem_cache_free(bio_entry_slab, be);
661+
}
662+
}
663+
664+
static void f2fs_submit_bio_wait_endio(struct bio *bio)
665+
{
666+
struct bio_entry *be = (struct bio_entry *)bio->bi_private;
667+
668+
be->error = bio->bi_error;
669+
complete(&be->event);
670+
}
671+
672+
/* copied from block/blk-lib.c in 4.10-rc1 */
673+
static int __blkdev_issue_discard(struct block_device *bdev, sector_t sector,
674+
sector_t nr_sects, gfp_t gfp_mask, int flags,
675+
struct bio **biop)
676+
{
677+
struct request_queue *q = bdev_get_queue(bdev);
678+
struct bio *bio = *biop;
679+
unsigned int granularity;
680+
int op = REQ_WRITE | REQ_DISCARD;
681+
int alignment;
682+
sector_t bs_mask;
683+
684+
if (!q)
685+
return -ENXIO;
686+
687+
if (!blk_queue_discard(q))
688+
return -EOPNOTSUPP;
689+
690+
if (flags & BLKDEV_DISCARD_SECURE) {
691+
if (!blk_queue_secdiscard(q))
692+
return -EOPNOTSUPP;
693+
op |= REQ_SECURE;
694+
}
695+
696+
bs_mask = (bdev_logical_block_size(bdev) >> 9) - 1;
697+
if ((sector | nr_sects) & bs_mask)
698+
return -EINVAL;
699+
700+
/* Zero-sector (unknown) and one-sector granularities are the same. */
701+
granularity = max(q->limits.discard_granularity >> 9, 1U);
702+
alignment = (bdev_discard_alignment(bdev) >> 9) % granularity;
703+
704+
while (nr_sects) {
705+
unsigned int req_sects;
706+
sector_t end_sect, tmp;
707+
708+
/* Make sure bi_size doesn't overflow */
709+
req_sects = min_t(sector_t, nr_sects, UINT_MAX >> 9);
710+
711+
/**
712+
* If splitting a request, and the next starting sector would be
713+
* misaligned, stop the discard at the previous aligned sector.
714+
*/
715+
end_sect = sector + req_sects;
716+
tmp = end_sect;
717+
if (req_sects < nr_sects &&
718+
sector_div(tmp, granularity) != alignment) {
719+
end_sect = end_sect - alignment;
720+
sector_div(end_sect, granularity);
721+
end_sect = end_sect * granularity + alignment;
722+
req_sects = end_sect - sector;
723+
}
724+
725+
if (bio) {
726+
int ret = submit_bio_wait(0, bio);
727+
bio_put(bio);
728+
if (ret)
729+
return ret;
730+
}
731+
bio = f2fs_bio_alloc(0);
732+
bio->bi_iter.bi_sector = sector;
733+
bio->bi_bdev = bdev;
734+
bio_set_op_attrs(bio, op, 0);
735+
736+
bio->bi_iter.bi_size = req_sects << 9;
737+
nr_sects -= req_sects;
738+
sector = end_sect;
739+
740+
/*
741+
* We can loop for a long time in here, if someone does
742+
* full device discards (like mkfs). Be nice and allow
743+
* us to schedule out to avoid softlocking if preempt
744+
* is disabled.
745+
*/
746+
cond_resched();
747+
}
748+
749+
*biop = bio;
750+
return 0;
751+
}
752+
753+
/* this function is copied from blkdev_issue_discard from block/blk-lib.c */
754+
static int __f2fs_issue_discard_async(struct f2fs_sb_info *sbi,
755+
struct block_device *bdev, block_t blkstart, block_t blklen)
756+
{
757+
struct bio *bio = NULL;
758+
int err;
759+
760+
trace_f2fs_issue_discard(sbi->sb, blkstart, blklen);
761+
762+
if (sbi->s_ndevs) {
763+
int devi = f2fs_target_device_index(sbi, blkstart);
764+
765+
blkstart -= FDEV(devi).start_blk;
766+
}
767+
err = __blkdev_issue_discard(bdev,
768+
SECTOR_FROM_BLOCK(blkstart),
769+
SECTOR_FROM_BLOCK(blklen),
770+
GFP_NOFS, 0, &bio);
771+
if (!err && bio) {
772+
struct bio_entry *be = __add_bio_entry(sbi, bio);
773+
774+
bio->bi_private = be;
775+
bio->bi_end_io = f2fs_submit_bio_wait_endio;
776+
submit_bio(REQ_SYNC, bio);
777+
}
778+
779+
return err;
780+
}
781+
625782
#ifdef CONFIG_BLK_DEV_ZONED
626783
static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi,
627784
struct block_device *bdev, block_t blkstart, block_t blklen)
@@ -655,8 +812,7 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi,
655812
case BLK_ZONE_TYPE_CONVENTIONAL:
656813
if (!blk_queue_discard(bdev_get_queue(bdev)))
657814
return 0;
658-
return blkdev_issue_discard(bdev, sector, nr_sects,
659-
GFP_NOFS, 0);
815+
return __f2fs_issue_discard_async(sbi, bdev, blkstart, blklen);
660816
case BLK_ZONE_TYPE_SEQWRITE_REQ:
661817
case BLK_ZONE_TYPE_SEQWRITE_PREF:
662818
trace_f2fs_issue_reset_zone(sbi->sb, blkstart);
@@ -672,15 +828,12 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi,
672828
static int __issue_discard_async(struct f2fs_sb_info *sbi,
673829
struct block_device *bdev, block_t blkstart, block_t blklen)
674830
{
675-
sector_t start = SECTOR_FROM_BLOCK(blkstart);
676-
sector_t len = SECTOR_FROM_BLOCK(blklen);
677-
678831
#ifdef CONFIG_BLK_DEV_ZONED
679832
if (f2fs_sb_mounted_blkzoned(sbi->sb) &&
680833
bdev_zoned_model(bdev) != BLK_ZONED_NONE)
681834
return __f2fs_issue_discard_zone(sbi, bdev, blkstart, blklen);
682835
#endif
683-
return blkdev_issue_discard(bdev, start, len, GFP_NOFS, 0);
836+
return __f2fs_issue_discard_async(sbi, bdev, blkstart, blklen);
684837
}
685838

686839
static int f2fs_issue_discard(struct f2fs_sb_info *sbi,
@@ -720,8 +873,6 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi,
720873

721874
if (len)
722875
err = __issue_discard_async(sbi, bdev, start, len);
723-
724-
trace_f2fs_issue_discard(sbi->sb, blkstart, blklen);
725876
return err;
726877
}
727878

@@ -822,11 +973,14 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc)
822973
struct list_head *head = &(SM_I(sbi)->discard_list);
823974
struct discard_entry *entry, *this;
824975
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
976+
struct blk_plug plug;
825977
unsigned long *prefree_map = dirty_i->dirty_segmap[PRE];
826978
unsigned int start = 0, end = -1;
827979
unsigned int secno, start_segno;
828980
bool force = (cpc->reason == CP_DISCARD);
829981

982+
blk_start_plug(&plug);
983+
830984
mutex_lock(&dirty_i->seglist_lock);
831985

832986
while (1) {
@@ -875,6 +1029,8 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc)
8751029
SM_I(sbi)->nr_discards -= entry->len;
8761030
kmem_cache_free(discard_entry_slab, entry);
8771031
}
1032+
1033+
blk_finish_plug(&plug);
8781034
}
8791035

8801036
static bool __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno)
@@ -2551,6 +2707,7 @@ int build_segment_manager(struct f2fs_sb_info *sbi)
25512707
sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS;
25522708

25532709
INIT_LIST_HEAD(&sm_info->discard_list);
2710+
INIT_LIST_HEAD(&sm_info->wait_list);
25542711
sm_info->nr_discards = 0;
25552712
sm_info->max_discards = 0;
25562713

@@ -2694,10 +2851,15 @@ int __init create_segment_manager_caches(void)
26942851
if (!discard_entry_slab)
26952852
goto fail;
26962853

2854+
bio_entry_slab = f2fs_kmem_cache_create("bio_entry",
2855+
sizeof(struct bio_entry));
2856+
if (!bio_entry_slab)
2857+
goto destroy_discard_entry;
2858+
26972859
sit_entry_set_slab = f2fs_kmem_cache_create("sit_entry_set",
26982860
sizeof(struct sit_entry_set));
26992861
if (!sit_entry_set_slab)
2700-
goto destroy_discard_entry;
2862+
goto destroy_bio_entry;
27012863

27022864
inmem_entry_slab = f2fs_kmem_cache_create("inmem_page_entry",
27032865
sizeof(struct inmem_pages));
@@ -2707,6 +2869,8 @@ int __init create_segment_manager_caches(void)
27072869

27082870
destroy_sit_entry_set:
27092871
kmem_cache_destroy(sit_entry_set_slab);
2872+
destroy_bio_entry:
2873+
kmem_cache_destroy(bio_entry_slab);
27102874
destroy_discard_entry:
27112875
kmem_cache_destroy(discard_entry_slab);
27122876
fail:
@@ -2716,6 +2880,7 @@ int __init create_segment_manager_caches(void)
27162880
void destroy_segment_manager_caches(void)
27172881
{
27182882
kmem_cache_destroy(sit_entry_set_slab);
2883+
kmem_cache_destroy(bio_entry_slab);
27192884
kmem_cache_destroy(discard_entry_slab);
27202885
kmem_cache_destroy(inmem_entry_slab);
27212886
}

0 commit comments

Comments
 (0)