f2fs: split discard command in prior to block layer
Chao Yu
maintainerPATCH v2 1/1 f2fs: split discard command in prior to block layer
From: Chao Yu <[email protected]>
Some devices has small max_{hw,}discard_sectors, so that in __blkdev_issue_discard(), one big size discard bio can be split into multiple small size discard bios, result in heavy load in IO scheduler and device, which can hang other sync IO for long time.
Now, f2fs is trying to control discard commands more elaboratively, in order to make less conflict in between discard IO and user IO to enhance application's performance, so in this patch, we will split discard bio in f2fs in prior to in block layer to reduce issuing multiple discard bios in a short time.
---
v2:
- change to split discard command entry before submission.
fs/f2fs/f2fs.h | 13 +++--- fs/f2fs/segment.c | 117 +++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 86 insertions(+), 44 deletions(-)
fs/f2fs/f2fs.h +6 -7
@@ -178,7 +178,6 @@ enum {#define MAX_DISCARD_BLOCKS (sbi) BLKS_PER_SEC(sbi)#define DEF_MAX_DISCARD_REQUEST 8/* issue 8 discards per round */ -#define DEF_MAX_DISCARD_LEN 512/* Max. 2MB per discard */ #define DEF_MIN_DISCARD_ISSUE_TIME 50/* 50 ms, if exists */ #define DEF_MID_DISCARD_ISSUE_TIME 500/* 500 ms, if device busy */ #define DEF_MAX_DISCARD_ISSUE_TIME 60000/* 60 s, if no candidates */
@@ -709,22 +708,22 @@ static inline void set_extent_info(struct extent_info *ei, unsigned int fofs, }static inline bool __is_discard_mergeable (struct discard_info *back, -struct discard_info *front) +struct discard_info *front,unsigned int max_len) {return (back->lstart + back->len == front->lstart) && - (back->len + front->len <DEF_MAX_DISCARD_LEN ); + (back->len + front->len <= max_len); }static inline bool __is_discard_back_mergeable (struct discard_info *cur, -struct discard_info *back) +struct discard_info *back,unsigned int max_len) { -return __is_discard_mergeable (back, cur); +return __is_discard_mergeable (back, cur, max_len); }static inline bool __is_discard_front_mergeable (struct discard_info *cur, -struct discard_info *front) +struct discard_info *front,unsigned int max_len) { -return __is_discard_mergeable (cur, front); +return __is_discard_mergeable (cur, front, max_len); } static inline bool __is_extent_mergeable(struct extent_info *back,
fs/f2fs/segment.c +80 -37
193 lines not on this page · raw message
Signature · 1 line
2.16.2.17.g38e79b1fd
Jaegeuk Kim
maintainerQuoted 7 lines · Chao Yu
From: Chao Yu <[email protected]>
Some devices has small max_{hw,}discard_sectors, so that in
__blkdev_issue_discard(), one big size discard bio can be split into multiple small size discard bios, result in heavy load in IO scheduler and device, which can hang other sync IO for long time.
Quoted 5 lines
Now, f2fs is trying to control discard commands more elaboratively, in order to make less conflict in between discard IO and user IO to enhance application's performance, so in this patch, we will split discard bio in f2fs in prior to in block layer to reduce issuing multiple discard bios in a short time.
Can we just change __submit_discard_cmd() to submit the large candidate partially and adjust lstart in the original candidate?
Quoted 259 lines
Signed-off-by Chao Yu <[email protected]>---
v2:
- change to split discard command entry before submission.fs/f2fs/f2fs.h | 13 +++--- fs/f2fs/segment.c | 117 +++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 86 insertions(+), 44 deletions(-)fs/f2fs/f2fs.h +6
-7@@ -178,7 +178,6 @@ enum {#define MAX_DISCARD_BLOCKS (sbi) BLKS_PER_SEC(sbi)#define DEF_MAX_DISCARD_REQUEST 8/* issue 8 discards per round */ -#define DEF_MAX_DISCARD_LEN 512/* Max. 2MB per discard */ #define DEF_MIN_DISCARD_ISSUE_TIME 50/* 50 ms, if exists */ #define DEF_MID_DISCARD_ISSUE_TIME 500/* 500 ms, if device busy */ #define DEF_MAX_DISCARD_ISSUE_TIME 60000/* 60 s, if no candidates */ @@ -709,22 +708,22 @@ static inline void set_extent_info(struct extent_info *ei, unsigned int fofs, }static inline bool __is_discard_mergeable (struct discard_info *back, -struct discard_info *front) +struct discard_info *front,unsigned int max_len) {return (back->lstart + back->len == front->lstart) && - (back->len + front->len <DEF_MAX_DISCARD_LEN ); + (back->len + front->len <= max_len); }static inline bool __is_discard_back_mergeable (struct discard_info *cur, -struct discard_info *back) +struct discard_info *back,unsigned int max_len) { -return __is_discard_mergeable (back, cur); +return __is_discard_mergeable (back, cur, max_len); }static inline bool __is_discard_front_mergeable (struct discard_info *cur, -struct discard_info *front) +struct discard_info *front,unsigned int max_len) { -return __is_discard_mergeable (cur, front); +return __is_discard_mergeable (cur, front, max_len); } static inline bool __is_extent_mergeable(struct extent_info *back,fs/f2fs/segment.c +80
-37@@ -966,17 +966,24 @@ static void __init_discard_policy(struct f2fs_sb_info *sbi, } } - +static void __update_discard_tree_range (struct f2fs_sb_info *sbi, +struct block_device *bdev,block_t lstart, +block_t start,block_t len);/* this function is copied from blkdev_issue_discard from block/blk-lib.c */ static void __submit_discard_cmd (struct f2fs_sb_info *sbi,struct discard_policy *dpolicy, -struct discard_cmd *dc) +struct discard_cmd *dc, +unsigned int *issued) { +struct block_device *bdev = dc->bdev; +struct request_queue *q =bdev_get_queue (bdev); +unsigned int max_discard_blocks = +SECTOR_TO_BLOCK (q->limits.max_discard_sectors);struct discard_cmd_control *dcc =SM_I (sbi)->dcc_info;struct list_head *wait_list = (dpolicy->type ==DPOLICY_FSTRIM ) ? &(dcc->fstrim_list) : &(dcc->wait_list); -struct bio *bio =NULL ;int flag = dpolicy->sync ?REQ_SYNC :0 ; +block_t lstart, start, len, total_len;if (dc->state !=D_PREP )return ;@@ -984,30 +991,63 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi,if (is_sbi_flag_set (sbi,SBI_NEED_FSCK ))return ; -trace_f2fs_issue_discard (dc->bdev, dc->start, dc->len); - - dc->error =__blkdev_issue_discard (dc->bdev, -SECTOR_FROM_BLOCK (dc->start), -SECTOR_FROM_BLOCK (dc->len), -GFP_NOFS ,0 , &bio); -if (!dc->error) { -/* should keep before submission to avoid D_DONE right away */ - dc->state =D_SUBMIT ; -atomic_inc (&dcc->issued_discard); -atomic_inc (&dcc->issing_discard); -if (bio) { - bio->bi_private = dc; - bio->bi_end_io = f2fs_submit_discard_endio; - bio->bi_opf |= flag; -submit_bio (bio); -list_move_tail (&dc->list, wait_list); -__check_sit_bitmap (sbi, dc->start, dc->start + dc->len); - -f2fs_update_iostat (sbi,FS_DISCARD ,1 ); +trace_f2fs_issue_discard (bdev, dc->start, dc->len); + + lstart = dc->lstart; + start = dc->start; + len = dc->len; + total_len = len; + +while (total_len && *issued < dpolicy->max_requests) { +struct bio *bio =NULL ; + +if (len > max_discard_blocks) + len = max_discard_blocks; + + dc->error =__blkdev_issue_discard (bdev, +SECTOR_FROM_BLOCK (start), +SECTOR_FROM_BLOCK (len), +GFP_NOFS ,0 , &bio); +if (!dc->error) { +/* +* should keep before submission to avoid D_DONE +* right away +*/ + dc->state =D_SUBMIT ; + dc->len = len; +atomic_inc (&dcc->issued_discard); +atomic_inc (&dcc->issing_discard); +if (bio) { + bio->bi_private = dc; + bio->bi_end_io = f2fs_submit_discard_endio; + bio->bi_opf |= flag; +submit_bio (bio); +list_move_tail (&dc->list, wait_list); +__check_sit_bitmap (sbi, dc->start, + dc->start + dc->len); + +f2fs_update_iostat (sbi,FS_DISCARD ,1 ); + } + }else { +__remove_discard_cmd (sbi, dc); +return ; + } + lstart += len; + start += len; + total_len -= len; + len = total_len; + (*issued)++; + +if (len) { +__update_discard_tree_range (sbi, bdev, lstart, + start, len); + dc = (struct discard_cmd *)f2fs_lookup_rb_tree ( + &dcc->root,NULL , lstart); +f2fs_bug_on (sbi, !dc); } - }else { -__remove_discard_cmd (sbi, dc); } + +return ; } static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi,@@ -1088,10 +1128,11 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,struct discard_cmd *dc;struct discard_info di = {0 };struct rb_node **insert_p =NULL , *insert_parent =NULL ; +struct request_queue *q =bdev_get_queue (bdev); +unsigned int max_discard_blocks = +SECTOR_TO_BLOCK (q->limits.max_discard_sectors);block_t end = lstart + len; -mutex_lock (&dcc->cmd_lock); - dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root, NULL, lstart, (struct rb_entry **)&prev_dc,@@ -1131,7 +1172,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,if (prev_dc && prev_dc->state ==D_PREP && prev_dc->bdev == bdev && -__is_discard_back_mergeable (&di, &prev_dc->di)) { +__is_discard_back_mergeable (&di, &prev_dc->di, + max_discard_blocks)) { prev_dc->di.len += di.len; dcc->undiscard_blks += di.len;__relocate_discard_cmd (dcc, prev_dc);@@ -1142,7 +1184,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,if (next_dc && next_dc->state ==D_PREP && next_dc->bdev == bdev && -__is_discard_front_mergeable (&di, &next_dc->di)) { +__is_discard_front_mergeable (&di, &next_dc->di, + max_discard_blocks)) { next_dc->di.lstart = di.lstart; next_dc->di.len += di.len; next_dc->di.start = di.start;@@ -1165,8 +1208,6 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi, node =rb_next (&prev_dc->rb_node); next_dc = rb_entry_safe(node, struct discard_cmd, rb_node); } - -mutex_unlock (&dcc->cmd_lock); } static int __queue_discard_cmd(struct f2fs_sb_info *sbi,@@ -1181,7 +1222,9 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, blkstart -=FDEV (devi).start_blk; } +mutex_lock (&SM_I (sbi)->dcc_info->cmd_lock);__update_discard_tree_range (sbi, bdev, lblkstart, blkstart, blklen); +mutex_unlock (&SM_I (sbi)->dcc_info->cmd_lock);return 0 ; }@@ -1220,9 +1263,9 @@ static unsigned int __issue_discard_cmd_orderly(struct f2fs_sb_info *sbi, } dcc->next_pos = dc->lstart + dc->len; -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued); -if (++issued >= dpolicy->max_requests) +if (issued >= dpolicy->max_requests)break ; next: node =rb_next (&dc->rb_node);@@ -1277,9 +1320,9 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi,break ; } -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued); -if (++issued >= dpolicy->max_requests) +if (issued >= dpolicy->max_requests)break ; }blk_finish_plug (&plug);@@ -2475,9 +2518,9 @@ static unsigned int __issue_discard_cmd_range(struct f2fs_sb_info *sbi, goto skip; } -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued); -if (++issued >= dpolicy->max_requests) { +if (issued >= dpolicy->max_requests) { start = dc->lstart + dc->len;blk_finish_plug (&plug);Signature · 1 line
2.16.2.17.g38e79b1fd
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Chao Yu
maintainerHi Jaegeuk,
Quoted 8 lines · Jaegeuk Kim
Quoted 7 lines · Chao Yu
From: Chao Yu <[email protected]>
Some devices has small max_{hw,}discard_sectors, so that in
__blkdev_issue_discard(), one big size discard bio can be split into multiple small size discard bios, result in heavy load in IO scheduler and device, which can hang other sync IO for long time.
Quoted 8 lines
Quoted 5 lines
Now, f2fs is trying to control discard commands more elaboratively, in order to make less conflict in between discard IO and user IO to enhance application's performance, so in this patch, we will split discard bio in f2fs in prior to in block layer to reduce issuing multiple discard bios in a short time.
Can we just change
__submit_discard_cmd()to submit the large candidate partially and adjust lstart in the original candidate?
Yep, could you check __submit_discard_cmd() implementation below? Does that match the thoughts from you?
Thanks,
Quoted 253 lines
Quoted 253 lines
Signed-off-by Chao Yu <[email protected]>---
v2:
- change to split discard command entry before submission.fs/f2fs/f2fs.h | 13 +++--- fs/f2fs/segment.c | 117 +++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 86 insertions(+), 44 deletions(-)fs/f2fs/f2fs.h +6
-7@@ -178,7 +178,6 @@ enum {#define MAX_DISCARD_BLOCKS (sbi) BLKS_PER_SEC(sbi)#define DEF_MAX_DISCARD_REQUEST 8/* issue 8 discards per round */ -#define DEF_MAX_DISCARD_LEN 512/* Max. 2MB per discard */ #define DEF_MIN_DISCARD_ISSUE_TIME 50/* 50 ms, if exists */ #define DEF_MID_DISCARD_ISSUE_TIME 500/* 500 ms, if device busy */ #define DEF_MAX_DISCARD_ISSUE_TIME 60000/* 60 s, if no candidates */ @@ -709,22 +708,22 @@ static inline void set_extent_info(struct extent_info *ei, unsigned int fofs, }static inline bool __is_discard_mergeable (struct discard_info *back, -struct discard_info *front) +struct discard_info *front,unsigned int max_len) {return (back->lstart + back->len == front->lstart) && - (back->len + front->len <DEF_MAX_DISCARD_LEN ); + (back->len + front->len <= max_len); }static inline bool __is_discard_back_mergeable (struct discard_info *cur, -struct discard_info *back) +struct discard_info *back,unsigned int max_len) { -return __is_discard_mergeable (back, cur); +return __is_discard_mergeable (back, cur, max_len); }static inline bool __is_discard_front_mergeable (struct discard_info *cur, -struct discard_info *front) +struct discard_info *front,unsigned int max_len) { -return __is_discard_mergeable (cur, front); +return __is_discard_mergeable (cur, front, max_len); } static inline bool __is_extent_mergeable(struct extent_info *back,fs/f2fs/segment.c +79
-36@@ -966,17 +966,24 @@ static void __init_discard_policy(struct f2fs_sb_info *sbi, } } - +static void __update_discard_tree_range (struct f2fs_sb_info *sbi, +struct block_device *bdev,block_t lstart, +block_t start,block_t len);/* this function is copied from blkdev_issue_discard from block/blk-lib.c */ static void __submit_discard_cmd (struct f2fs_sb_info *sbi,struct discard_policy *dpolicy, -struct discard_cmd *dc) +struct discard_cmd *dc, +unsigned int *issued) { +struct block_device *bdev = dc->bdev; +struct request_queue *q =bdev_get_queue (bdev); +unsigned int max_discard_blocks = +SECTOR_TO_BLOCK (q->limits.max_discard_sectors);struct discard_cmd_control *dcc =SM_I (sbi)->dcc_info;struct list_head *wait_list = (dpolicy->type ==DPOLICY_FSTRIM ) ? &(dcc->fstrim_list) : &(dcc->wait_list); -struct bio *bio =NULL ;int flag = dpolicy->sync ?REQ_SYNC :0 ; +block_t lstart, start, len, total_len;if (dc->state !=D_PREP )return ;@@ -984,30 +991,63 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi,if (is_sbi_flag_set (sbi,SBI_NEED_FSCK ))return ; -trace_f2fs_issue_discard (dc->bdev, dc->start, dc->len); - - dc->error =__blkdev_issue_discard (dc->bdev, -SECTOR_FROM_BLOCK (dc->start), -SECTOR_FROM_BLOCK (dc->len), -GFP_NOFS ,0 , &bio); -if (!dc->error) { -/* should keep before submission to avoid D_DONE right away */ - dc->state =D_SUBMIT ; -atomic_inc (&dcc->issued_discard); -atomic_inc (&dcc->issing_discard); -if (bio) { - bio->bi_private = dc; - bio->bi_end_io = f2fs_submit_discard_endio; - bio->bi_opf |= flag; -submit_bio (bio); -list_move_tail (&dc->list, wait_list); -__check_sit_bitmap (sbi, dc->start, dc->start + dc->len); - -f2fs_update_iostat (sbi,FS_DISCARD ,1 ); +trace_f2fs_issue_discard (bdev, dc->start, dc->len); + + lstart = dc->lstart; + start = dc->start; + len = dc->len; + total_len = len; + +while (total_len && *issued < dpolicy->max_requests) { +struct bio *bio =NULL ; + +if (len > max_discard_blocks) + len = max_discard_blocks; + + dc->error =__blkdev_issue_discard (bdev, +SECTOR_FROM_BLOCK (start), +SECTOR_FROM_BLOCK (len), +GFP_NOFS ,0 , &bio); +if (!dc->error) { +/* +* should keep before submission to avoid D_DONE +* right away +*/ + dc->state =D_SUBMIT ; + dc->len = len; +atomic_inc (&dcc->issued_discard); +atomic_inc (&dcc->issing_discard); +if (bio) { + bio->bi_private = dc; + bio->bi_end_io = f2fs_submit_discard_endio; + bio->bi_opf |= flag; +submit_bio (bio); +list_move_tail (&dc->list, wait_list); +__check_sit_bitmap (sbi, dc->start, + dc->start + dc->len); + +f2fs_update_iostat (sbi,FS_DISCARD ,1 ); + } + }else { +__remove_discard_cmd (sbi, dc); +return ; + } + lstart += len; + start += len; + total_len -= len; + len = total_len; + (*issued)++; + +if (len) { +__update_discard_tree_range (sbi, bdev, lstart, + start, len); + dc = (struct discard_cmd *)f2fs_lookup_rb_tree ( + &dcc->root,NULL , lstart); +f2fs_bug_on (sbi, !dc); } - }else { -__remove_discard_cmd (sbi, dc); } + +return ; } static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi,@@ -1088,10 +1128,11 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,struct discard_cmd *dc;struct discard_info di = {0 };struct rb_node **insert_p =NULL , *insert_parent =NULL ; +struct request_queue *q =bdev_get_queue (bdev); +unsigned int max_discard_blocks = +SECTOR_TO_BLOCK (q->limits.max_discard_sectors);block_t end = lstart + len; -mutex_lock (&dcc->cmd_lock); - dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root, NULL, lstart, (struct rb_entry **)&prev_dc,@@ -1131,7 +1172,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,if (prev_dc && prev_dc->state ==D_PREP && prev_dc->bdev == bdev && -__is_discard_back_mergeable (&di, &prev_dc->di)) { +__is_discard_back_mergeable (&di, &prev_dc->di, + max_discard_blocks)) { prev_dc->di.len += di.len; dcc->undiscard_blks += di.len;__relocate_discard_cmd (dcc, prev_dc);@@ -1142,7 +1184,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,if (next_dc && next_dc->state ==D_PREP && next_dc->bdev == bdev && -__is_discard_front_mergeable (&di, &next_dc->di)) { +__is_discard_front_mergeable (&di, &next_dc->di, + max_discard_blocks)) { next_dc->di.lstart = di.lstart; next_dc->di.len += di.len; next_dc->di.start = di.start;@@ -1165,8 +1208,6 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi, node =rb_next (&prev_dc->rb_node); next_dc = rb_entry_safe(node, struct discard_cmd, rb_node); } - -mutex_unlock (&dcc->cmd_lock); } static int __queue_discard_cmd(struct f2fs_sb_info *sbi,@@ -1181,7 +1222,9 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, blkstart -=FDEV (devi).start_blk; } +mutex_lock (&SM_I (sbi)->dcc_info->cmd_lock);__update_discard_tree_range (sbi, bdev, lblkstart, blkstart, blklen); +mutex_unlock (&SM_I (sbi)->dcc_info->cmd_lock);return 0 ; }@@ -1220,9 +1263,9 @@ static unsigned int __issue_discard_cmd_orderly(struct f2fs_sb_info *sbi, } dcc->next_pos = dc->lstart + dc->len; -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued); -if (++issued >= dpolicy->max_requests) +if (issued >= dpolicy->max_requests)break ; next: node =rb_next (&dc->rb_node);@@ -1277,9 +1320,9 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi,break ; } -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued); -if (++issued >= dpolicy->max_requests) +if (issued >= dpolicy->max_requests)break ; }blk_finish_plug (&plug);@@ -2475,9 +2518,9 @@ static unsigned int __issue_discard_cmd_range(struct f2fs_sb_info *sbi, goto skip; } -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued);
Quoted 7 lines
Quoted 7 lines
fs/f2fs/segment.c +1
-1-if (++issued >= dpolicy->max_requests) { +if (issued >= dpolicy->max_requests) { start = dc->lstart + dc->len;blk_finish_plug (&plug);Signature · 1 line
2.16.2.17.g38e79b1fd
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Jaegeuk Kim
maintainerQuoted 84 lines · Chao Yu
Hi Jaegeuk,
Quoted 8 lines · Jaegeuk Kim
Quoted 7 lines · Chao Yu
From: Chao Yu <[email protected]>
Some devices has small max_{hw,}discard_sectors, so that in
__blkdev_issue_discard(), one big size discard bio can be split into multiple small size discard bios, result in heavy load in IO scheduler and device, which can hang other sync IO for long time.Quoted 8 lines
Quoted 5 lines
Now, f2fs is trying to control discard commands more elaboratively, in order to make less conflict in between discard IO and user IO to enhance application's performance, so in this patch, we will split discard bio in f2fs in prior to in block layer to reduce issuing multiple discard bios in a short time.
Can we just change
__submit_discard_cmd()to submit the large candidate partially and adjust lstart in the original candidate?Yep, could you check
__submit_discard_cmd()implementation below? Does that match the thoughts from you?Thanks,
Quoted 59 lines
Quoted 59 lines
Signed-off-by Chao Yu <[email protected]>---
v2:
- change to split discard command entry before submission.fs/f2fs/f2fs.h | 13 +++--- fs/f2fs/segment.c | 117 +++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 86 insertions(+), 44 deletions(-)fs/f2fs/f2fs.h +6
-7@@ -178,7 +178,6 @@ enum {#define MAX_DISCARD_BLOCKS (sbi) BLKS_PER_SEC(sbi)#define DEF_MAX_DISCARD_REQUEST 8/* issue 8 discards per round */ -#define DEF_MAX_DISCARD_LEN 512/* Max. 2MB per discard */ #define DEF_MIN_DISCARD_ISSUE_TIME 50/* 50 ms, if exists */ #define DEF_MID_DISCARD_ISSUE_TIME 500/* 500 ms, if device busy */ #define DEF_MAX_DISCARD_ISSUE_TIME 60000/* 60 s, if no candidates */ @@ -709,22 +708,22 @@ static inline void set_extent_info(struct extent_info *ei, unsigned int fofs, }static inline bool __is_discard_mergeable (struct discard_info *back, -struct discard_info *front) +struct discard_info *front,unsigned int max_len) {return (back->lstart + back->len == front->lstart) && - (back->len + front->len <DEF_MAX_DISCARD_LEN ); + (back->len + front->len <= max_len); }static inline bool __is_discard_back_mergeable (struct discard_info *cur, -struct discard_info *back) +struct discard_info *back,unsigned int max_len) { -return __is_discard_mergeable (back, cur); +return __is_discard_mergeable (back, cur, max_len); }static inline bool __is_discard_front_mergeable (struct discard_info *cur, -struct discard_info *front) +struct discard_info *front,unsigned int max_len) { -return __is_discard_mergeable (cur, front); +return __is_discard_mergeable (cur, front, max_len); } static inline bool __is_extent_mergeable(struct extent_info *back,fs/f2fs/segment.c +0
-0@@ -966,17 +966,24 @@ static void __init_discard_policy(struct f2fs_sb_info *sbi, } }
Quoted 1 line
Quoted 1 line
fs/f2fs/segment.c static void __init_discard_policy(struct f2fs_sb_info *sbi, · line 969
Quoted 1 line
fs/f2fs/segment.c +0
-1-
===================== BEGIN
Quoted 78 lines
Quoted 78 lines
Quoted 78 lines
fs/f2fs/segment.c +41
-22+static void __update_discard_tree_range (struct f2fs_sb_info *sbi, +struct block_device *bdev,block_t lstart, +block_t start,block_t len);/* this function is copied from blkdev_issue_discard from block/blk-lib.c */ static void __submit_discard_cmd (struct f2fs_sb_info *sbi,struct discard_policy *dpolicy, -struct discard_cmd *dc) +struct discard_cmd *dc, +unsigned int *issued) { +struct block_device *bdev = dc->bdev; +struct request_queue *q =bdev_get_queue (bdev); +unsigned int max_discard_blocks = +SECTOR_TO_BLOCK (q->limits.max_discard_sectors);struct discard_cmd_control *dcc =SM_I (sbi)->dcc_info;struct list_head *wait_list = (dpolicy->type ==DPOLICY_FSTRIM ) ? &(dcc->fstrim_list) : &(dcc->wait_list); -struct bio *bio =NULL ;int flag = dpolicy->sync ?REQ_SYNC :0 ; +block_t lstart, start, len, total_len;if (dc->state !=D_PREP )return ;@@ -984,30 +991,63 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi,if (is_sbi_flag_set (sbi,SBI_NEED_FSCK ))return ; -trace_f2fs_issue_discard (dc->bdev, dc->start, dc->len); - - dc->error =__blkdev_issue_discard (dc->bdev, -SECTOR_FROM_BLOCK (dc->start), -SECTOR_FROM_BLOCK (dc->len), -GFP_NOFS ,0 , &bio); -if (!dc->error) { -/* should keep before submission to avoid D_DONE right away */ - dc->state =D_SUBMIT ; -atomic_inc (&dcc->issued_discard); -atomic_inc (&dcc->issing_discard); -if (bio) { - bio->bi_private = dc; - bio->bi_end_io = f2fs_submit_discard_endio; - bio->bi_opf |= flag; -submit_bio (bio); -list_move_tail (&dc->list, wait_list); -__check_sit_bitmap (sbi, dc->start, dc->start + dc->len); - -f2fs_update_iostat (sbi,FS_DISCARD ,1 ); +trace_f2fs_issue_discard (bdev, dc->start, dc->len); + + lstart = dc->lstart; + start = dc->start; + len = dc->len; + total_len = len; + +while (total_len && *issued < dpolicy->max_requests) { +struct bio *bio =NULL ; + +if (len > max_discard_blocks) + len = max_discard_blocks; + + dc->error =__blkdev_issue_discard (bdev, +SECTOR_FROM_BLOCK (start), +SECTOR_FROM_BLOCK (len), +GFP_NOFS ,0 , &bio); +if (!dc->error) { +/* +* should keep before submission to avoid D_DONE +* right away +*/ + dc->state =D_SUBMIT ; + dc->len = len; +atomic_inc (&dcc->issued_discard); +atomic_inc (&dcc->issing_discard); +if (bio) { + bio->bi_private = dc; + bio->bi_end_io = f2fs_submit_discard_endio; + bio->bi_opf |= flag; +submit_bio (bio);
Quoted 8 lines
Quoted 8 lines
fs/f2fs/segment.c static void __submit_discard_cmd(struct f2fs_sb_info *sbi, · line 1032
Quoted 8 lines
fs/f2fs/segment.c +8
-0+list_move_tail (&dc->list, wait_list); +__check_sit_bitmap (sbi, dc->start, + dc->start + dc->len); + +f2fs_update_iostat (sbi,FS_DISCARD ,1 ); + } + }else { +__remove_discard_cmd (sbi, dc);
Keep the original one, since it's partially failed?
Quoted 11 lines
Quoted 11 lines
fs/f2fs/segment.c static void __submit_discard_cmd(struct f2fs_sb_info *sbi, · line 1043
Quoted 11 lines
fs/f2fs/segment.c +11
-0+return ; + } + lstart += len; + start += len; + total_len -= len; + len = total_len; + (*issued)++; + +if (len) { +__update_discard_tree_range (sbi, bdev, lstart, + start, len);
Do we really need this? We already grabbed one candidate which was not merged well.
Quoted 10 lines
Quoted 10 lines
fs/f2fs/segment.c static void __submit_discard_cmd(struct f2fs_sb_info *sbi, · line 1051
Quoted 10 lines
fs/f2fs/segment.c +5
-2+ dc = (struct discard_cmd *)f2fs_lookup_rb_tree ( + &dcc->root,NULL , lstart); +f2fs_bug_on (sbi, !dc); } - }else { -__remove_discard_cmd (sbi, dc); } + +return ; }
======================= END
Quoted 93 lines
Quoted 93 lines
Quoted 93 lines
fs/f2fs/segment.c +15
-12static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi,@@ -1088,10 +1128,11 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,struct discard_cmd *dc;struct discard_info di = {0 };struct rb_node **insert_p =NULL , *insert_parent =NULL ; +struct request_queue *q =bdev_get_queue (bdev); +unsigned int max_discard_blocks = +SECTOR_TO_BLOCK (q->limits.max_discard_sectors);block_t end = lstart + len; -mutex_lock (&dcc->cmd_lock); - dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root, NULL, lstart, (struct rb_entry **)&prev_dc,@@ -1131,7 +1172,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,if (prev_dc && prev_dc->state ==D_PREP && prev_dc->bdev == bdev && -__is_discard_back_mergeable (&di, &prev_dc->di)) { +__is_discard_back_mergeable (&di, &prev_dc->di, + max_discard_blocks)) { prev_dc->di.len += di.len; dcc->undiscard_blks += di.len;__relocate_discard_cmd (dcc, prev_dc);@@ -1142,7 +1184,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,if (next_dc && next_dc->state ==D_PREP && next_dc->bdev == bdev && -__is_discard_front_mergeable (&di, &next_dc->di)) { +__is_discard_front_mergeable (&di, &next_dc->di, + max_discard_blocks)) { next_dc->di.lstart = di.lstart; next_dc->di.len += di.len; next_dc->di.start = di.start;@@ -1165,8 +1208,6 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi, node =rb_next (&prev_dc->rb_node); next_dc = rb_entry_safe(node, struct discard_cmd, rb_node); } - -mutex_unlock (&dcc->cmd_lock); } static int __queue_discard_cmd(struct f2fs_sb_info *sbi,@@ -1181,7 +1222,9 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, blkstart -=FDEV (devi).start_blk; } +mutex_lock (&SM_I (sbi)->dcc_info->cmd_lock);__update_discard_tree_range (sbi, bdev, lblkstart, blkstart, blklen); +mutex_unlock (&SM_I (sbi)->dcc_info->cmd_lock);return 0 ; }@@ -1220,9 +1263,9 @@ static unsigned int __issue_discard_cmd_orderly(struct f2fs_sb_info *sbi, } dcc->next_pos = dc->lstart + dc->len; -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued); -if (++issued >= dpolicy->max_requests) +if (issued >= dpolicy->max_requests)break ; next: node =rb_next (&dc->rb_node);@@ -1277,9 +1320,9 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi,break ; } -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued); -if (++issued >= dpolicy->max_requests) +if (issued >= dpolicy->max_requests)break ; }blk_finish_plug (&plug);@@ -2475,9 +2518,9 @@ static unsigned int __issue_discard_cmd_range(struct f2fs_sb_info *sbi, goto skip; } -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued); -if (++issued >= dpolicy->max_requests) { +if (issued >= dpolicy->max_requests) { start = dc->lstart + dc->len;blk_finish_plug (&plug);Signature · 1 line
2.16.2.17.g38e79b1fd
Chao Yu
maintainerQuoted 169 lines · Jaegeuk Kim
Quoted 84 lines · Chao Yu
Hi Jaegeuk,
Quoted 8 lines · Jaegeuk Kim
Quoted 7 lines · Chao Yu
From: Chao Yu <[email protected]>
Some devices has small max_{hw,}discard_sectors, so that in
__blkdev_issue_discard(), one big size discard bio can be split into multiple small size discard bios, result in heavy load in IO scheduler and device, which can hang other sync IO for long time.Quoted 8 lines
Quoted 5 lines
Now, f2fs is trying to control discard commands more elaboratively, in order to make less conflict in between discard IO and user IO to enhance application's performance, so in this patch, we will split discard bio in f2fs in prior to in block layer to reduce issuing multiple discard bios in a short time.
Can we just change
__submit_discard_cmd()to submit the large candidate partially and adjust lstart in the original candidate?Yep, could you check
__submit_discard_cmd()implementation below? Does that match the thoughts from you?Thanks,
Quoted 59 lines
Quoted 59 lines
Signed-off-by Chao Yu <[email protected]>---
v2:
- change to split discard command entry before submission.fs/f2fs/f2fs.h | 13 +++--- fs/f2fs/segment.c | 117 +++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 86 insertions(+), 44 deletions(-)fs/f2fs/f2fs.h +6
-7@@ -178,7 +178,6 @@ enum {#define MAX_DISCARD_BLOCKS (sbi) BLKS_PER_SEC(sbi)#define DEF_MAX_DISCARD_REQUEST 8/* issue 8 discards per round */ -#define DEF_MAX_DISCARD_LEN 512/* Max. 2MB per discard */ #define DEF_MIN_DISCARD_ISSUE_TIME 50/* 50 ms, if exists */ #define DEF_MID_DISCARD_ISSUE_TIME 500/* 500 ms, if device busy */ #define DEF_MAX_DISCARD_ISSUE_TIME 60000/* 60 s, if no candidates */ @@ -709,22 +708,22 @@ static inline void set_extent_info(struct extent_info *ei, unsigned int fofs, }static inline bool __is_discard_mergeable (struct discard_info *back, -struct discard_info *front) +struct discard_info *front,unsigned int max_len) {return (back->lstart + back->len == front->lstart) && - (back->len + front->len <DEF_MAX_DISCARD_LEN ); + (back->len + front->len <= max_len); }static inline bool __is_discard_back_mergeable (struct discard_info *cur, -struct discard_info *back) +struct discard_info *back,unsigned int max_len) { -return __is_discard_mergeable (back, cur); +return __is_discard_mergeable (back, cur, max_len); }static inline bool __is_discard_front_mergeable (struct discard_info *cur, -struct discard_info *front) +struct discard_info *front,unsigned int max_len) { -return __is_discard_mergeable (cur, front); +return __is_discard_mergeable (cur, front, max_len); } static inline bool __is_extent_mergeable(struct extent_info *back,fs/f2fs/segment.c +0
-0@@ -966,17 +966,24 @@ static void __init_discard_policy(struct f2fs_sb_info *sbi, } }Quoted 1 line
Quoted 1 line
fs/f2fs/segment.c static void __init_discard_policy(struct f2fs_sb_info *sbi, · line 969
Quoted 1 line
fs/f2fs/segment.c +0
-1-===================== BEGIN
Quoted 80 lines
Quoted 80 lines
Quoted 80 lines
fs/f2fs/segment.c +43
-22+static void __update_discard_tree_range (struct f2fs_sb_info *sbi, +struct block_device *bdev,block_t lstart, +block_t start,block_t len);/* this function is copied from blkdev_issue_discard from block/blk-lib.c */ static void __submit_discard_cmd (struct f2fs_sb_info *sbi,struct discard_policy *dpolicy, -struct discard_cmd *dc) +struct discard_cmd *dc, +unsigned int *issued) { +struct block_device *bdev = dc->bdev; +struct request_queue *q =bdev_get_queue (bdev); +unsigned int max_discard_blocks = +SECTOR_TO_BLOCK (q->limits.max_discard_sectors);struct discard_cmd_control *dcc =SM_I (sbi)->dcc_info;struct list_head *wait_list = (dpolicy->type ==DPOLICY_FSTRIM ) ? &(dcc->fstrim_list) : &(dcc->wait_list); -struct bio *bio =NULL ;int flag = dpolicy->sync ?REQ_SYNC :0 ; +block_t lstart, start, len, total_len;if (dc->state !=D_PREP )return ;@@ -984,30 +991,63 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi,if (is_sbi_flag_set (sbi,SBI_NEED_FSCK ))return ; -trace_f2fs_issue_discard (dc->bdev, dc->start, dc->len); - - dc->error =__blkdev_issue_discard (dc->bdev, -SECTOR_FROM_BLOCK (dc->start), -SECTOR_FROM_BLOCK (dc->len), -GFP_NOFS ,0 , &bio); -if (!dc->error) { -/* should keep before submission to avoid D_DONE right away */ - dc->state =D_SUBMIT ; -atomic_inc (&dcc->issued_discard); -atomic_inc (&dcc->issing_discard); -if (bio) { - bio->bi_private = dc; - bio->bi_end_io = f2fs_submit_discard_endio; - bio->bi_opf |= flag; -submit_bio (bio); -list_move_tail (&dc->list, wait_list); -__check_sit_bitmap (sbi, dc->start, dc->start + dc->len); - -f2fs_update_iostat (sbi,FS_DISCARD ,1 ); +trace_f2fs_issue_discard (bdev, dc->start, dc->len); + + lstart = dc->lstart; + start = dc->start; + len = dc->len; + total_len = len; + +while (total_len && *issued < dpolicy->max_requests) { +struct bio *bio =NULL ; + +if (len > max_discard_blocks) + len = max_discard_blocks; + + dc->error =__blkdev_issue_discard (bdev, +SECTOR_FROM_BLOCK (start), +SECTOR_FROM_BLOCK (len), +GFP_NOFS ,0 , &bio); +if (!dc->error) { +/* +* should keep before submission to avoid D_DONE +* right away +*/ + dc->state =D_SUBMIT ; + dc->len = len; +atomic_inc (&dcc->issued_discard); +atomic_inc (&dcc->issing_discard); +if (bio) { + bio->bi_private = dc; + bio->bi_end_io = f2fs_submit_discard_endio; + bio->bi_opf |= flag; +submit_bio (bio); +list_move_tail (&dc->list, wait_list); + __check_sit_bitmap(sbi, dc->start,
Quoted 8 lines
Quoted 6 lines
Quoted 6 lines
fs/f2fs/segment.c static void __submit_discard_cmd(struct f2fs_sb_info *sbi, · line 1032
Quoted 6 lines
fs/f2fs/segment.c +6
-0+ dc->start + dc->len); + +f2fs_update_iostat (sbi,FS_DISCARD ,1 ); + } + }else { +__remove_discard_cmd (sbi, dc);Keep the original one, since it's partially failed?
During __submit_discard_cmd(), we are trying to split original dc into multiple dc, and once we failed to submit some of them, we will remove corresponding dc, the removal logic is the same as before, not being changed...
If we want to keep failed dc and do retry, we'd better add another patch for that, right?
Quoted 15 lines
Quoted 12 lines
Quoted 12 lines
fs/f2fs/segment.c static void __submit_discard_cmd(struct f2fs_sb_info *sbi, · line 1043
Quoted 12 lines
fs/f2fs/segment.c +11
-0+return ; + } + lstart += len; + start += len; + total_len -= len; + len = total_len; + (*issued)++; + +if (len) { +__update_discard_tree_range (sbi, bdev, lstart, + start, len);Do we really need this? We already grabbed one candidate which was not merged well.
You mean submitting multiple bios based on one large size dc?
Previously, one bio references to one dc as below, in end_io, it will change dc->state which can be detected by its own waiter.
bio->bi_private = dc; bio->bi_end_io = f2fs_submit_discard_endio; bio->bi_opf |= flag;
If multiple bios reference to one dc, it will be more complicated to handle that. How do you think?
Thanks,
Quoted 101 lines
Quoted 11 lines
Quoted 11 lines
fs/f2fs/segment.c static void __submit_discard_cmd(struct f2fs_sb_info *sbi, · line 1051
Quoted 11 lines
fs/f2fs/segment.c +5
-2+ dc = (struct discard_cmd *)f2fs_lookup_rb_tree ( + &dcc->root,NULL , lstart); +f2fs_bug_on (sbi, !dc); } - }else { -__remove_discard_cmd (sbi, dc); } + +return ; }======================= END
Quoted 86 lines
Quoted 86 lines
Quoted 86 lines
fs/f2fs/segment.c +14
-11static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi,@@ -1088,10 +1128,11 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,struct discard_cmd *dc;struct discard_info di = {0 };struct rb_node **insert_p =NULL , *insert_parent =NULL ; +struct request_queue *q =bdev_get_queue (bdev); +unsigned int max_discard_blocks = +SECTOR_TO_BLOCK (q->limits.max_discard_sectors);block_t end = lstart + len; -mutex_lock (&dcc->cmd_lock); - dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root, NULL, lstart, (struct rb_entry **)&prev_dc,@@ -1131,7 +1172,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,if (prev_dc && prev_dc->state ==D_PREP && prev_dc->bdev == bdev && -__is_discard_back_mergeable (&di, &prev_dc->di)) { +__is_discard_back_mergeable (&di, &prev_dc->di, + max_discard_blocks)) { prev_dc->di.len += di.len; dcc->undiscard_blks += di.len;__relocate_discard_cmd (dcc, prev_dc);@@ -1142,7 +1184,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,if (next_dc && next_dc->state ==D_PREP && next_dc->bdev == bdev && -__is_discard_front_mergeable (&di, &next_dc->di)) { +__is_discard_front_mergeable (&di, &next_dc->di, + max_discard_blocks)) { next_dc->di.lstart = di.lstart; next_dc->di.len += di.len; next_dc->di.start = di.start;@@ -1165,8 +1208,6 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi, node =rb_next (&prev_dc->rb_node); next_dc = rb_entry_safe(node, struct discard_cmd, rb_node); } - -mutex_unlock (&dcc->cmd_lock); } static int __queue_discard_cmd(struct f2fs_sb_info *sbi,@@ -1181,7 +1222,9 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, blkstart -=FDEV (devi).start_blk; } +mutex_lock (&SM_I (sbi)->dcc_info->cmd_lock);__update_discard_tree_range (sbi, bdev, lblkstart, blkstart, blklen); +mutex_unlock (&SM_I (sbi)->dcc_info->cmd_lock);return 0 ; }@@ -1220,9 +1263,9 @@ static unsigned int __issue_discard_cmd_orderly(struct f2fs_sb_info *sbi, } dcc->next_pos = dc->lstart + dc->len; -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued); -if (++issued >= dpolicy->max_requests) +if (issued >= dpolicy->max_requests)break ; next: node =rb_next (&dc->rb_node);@@ -1277,9 +1320,9 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi,break ; } -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued); -if (++issued >= dpolicy->max_requests) +if (issued >= dpolicy->max_requests)break ; }blk_finish_plug (&plug);@@ -2475,9 +2518,9 @@ static unsigned int __issue_discard_cmd_range(struct f2fs_sb_info *sbi, goto skip; } -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued);
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
fs/f2fs/segment.c +1
-1-if (++issued >= dpolicy->max_requests) { +if (issued >= dpolicy->max_requests) { start = dc->lstart + dc->len;blk_finish_plug (&plug);Signature · 1 line
2.16.2.17.g38e79b1fd
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot
Jaegeuk Kim
maintainerQuoted 179 lines · Chao Yu
Quoted 169 lines · Jaegeuk Kim
Quoted 84 lines · Chao Yu
Hi Jaegeuk,
Quoted 8 lines · Jaegeuk Kim
Quoted 7 lines · Chao Yu
From: Chao Yu <[email protected]>
Some devices has small max_{hw,}discard_sectors, so that in
__blkdev_issue_discard(), one big size discard bio can be split into multiple small size discard bios, result in heavy load in IO scheduler and device, which can hang other sync IO for long time.Quoted 8 lines
Quoted 5 lines
Now, f2fs is trying to control discard commands more elaboratively, in order to make less conflict in between discard IO and user IO to enhance application's performance, so in this patch, we will split discard bio in f2fs in prior to in block layer to reduce issuing multiple discard bios in a short time.
Can we just change
__submit_discard_cmd()to submit the large candidate partially and adjust lstart in the original candidate?Yep, could you check
__submit_discard_cmd()implementation below? Does that match the thoughts from you?Thanks,
Quoted 59 lines
Quoted 59 lines
Signed-off-by Chao Yu <[email protected]>---
v2:
- change to split discard command entry before submission.fs/f2fs/f2fs.h | 13 +++--- fs/f2fs/segment.c | 117 +++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 86 insertions(+), 44 deletions(-)fs/f2fs/f2fs.h +6
-7@@ -178,7 +178,6 @@ enum {#define MAX_DISCARD_BLOCKS (sbi) BLKS_PER_SEC(sbi)#define DEF_MAX_DISCARD_REQUEST 8/* issue 8 discards per round */ -#define DEF_MAX_DISCARD_LEN 512/* Max. 2MB per discard */ #define DEF_MIN_DISCARD_ISSUE_TIME 50/* 50 ms, if exists */ #define DEF_MID_DISCARD_ISSUE_TIME 500/* 500 ms, if device busy */ #define DEF_MAX_DISCARD_ISSUE_TIME 60000/* 60 s, if no candidates */ @@ -709,22 +708,22 @@ static inline void set_extent_info(struct extent_info *ei, unsigned int fofs, }static inline bool __is_discard_mergeable (struct discard_info *back, -struct discard_info *front) +struct discard_info *front,unsigned int max_len) {return (back->lstart + back->len == front->lstart) && - (back->len + front->len <DEF_MAX_DISCARD_LEN ); + (back->len + front->len <= max_len); }static inline bool __is_discard_back_mergeable (struct discard_info *cur, -struct discard_info *back) +struct discard_info *back,unsigned int max_len) { -return __is_discard_mergeable (back, cur); +return __is_discard_mergeable (back, cur, max_len); }static inline bool __is_discard_front_mergeable (struct discard_info *cur, -struct discard_info *front) +struct discard_info *front,unsigned int max_len) { -return __is_discard_mergeable (cur, front); +return __is_discard_mergeable (cur, front, max_len); } static inline bool __is_extent_mergeable(struct extent_info *back,fs/f2fs/segment.c +0
-0@@ -966,17 +966,24 @@ static void __init_discard_policy(struct f2fs_sb_info *sbi, } }Quoted 1 line
Quoted 1 line
fs/f2fs/segment.c static void __init_discard_policy(struct f2fs_sb_info *sbi, · line 969
Quoted 1 line
fs/f2fs/segment.c +0
-1-===================== BEGIN
Quoted 80 lines
Quoted 80 lines
Quoted 80 lines
fs/f2fs/segment.c +43
-22+static void __update_discard_tree_range (struct f2fs_sb_info *sbi, +struct block_device *bdev,block_t lstart, +block_t start,block_t len);/* this function is copied from blkdev_issue_discard from block/blk-lib.c */ static void __submit_discard_cmd (struct f2fs_sb_info *sbi,struct discard_policy *dpolicy, -struct discard_cmd *dc) +struct discard_cmd *dc, +unsigned int *issued) { +struct block_device *bdev = dc->bdev; +struct request_queue *q =bdev_get_queue (bdev); +unsigned int max_discard_blocks = +SECTOR_TO_BLOCK (q->limits.max_discard_sectors);struct discard_cmd_control *dcc =SM_I (sbi)->dcc_info;struct list_head *wait_list = (dpolicy->type ==DPOLICY_FSTRIM ) ? &(dcc->fstrim_list) : &(dcc->wait_list); -struct bio *bio =NULL ;int flag = dpolicy->sync ?REQ_SYNC :0 ; +block_t lstart, start, len, total_len;if (dc->state !=D_PREP )return ;@@ -984,30 +991,63 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi,if (is_sbi_flag_set (sbi,SBI_NEED_FSCK ))return ; -trace_f2fs_issue_discard (dc->bdev, dc->start, dc->len); - - dc->error =__blkdev_issue_discard (dc->bdev, -SECTOR_FROM_BLOCK (dc->start), -SECTOR_FROM_BLOCK (dc->len), -GFP_NOFS ,0 , &bio); -if (!dc->error) { -/* should keep before submission to avoid D_DONE right away */ - dc->state =D_SUBMIT ; -atomic_inc (&dcc->issued_discard); -atomic_inc (&dcc->issing_discard); -if (bio) { - bio->bi_private = dc; - bio->bi_end_io = f2fs_submit_discard_endio; - bio->bi_opf |= flag; -submit_bio (bio); -list_move_tail (&dc->list, wait_list); -__check_sit_bitmap (sbi, dc->start, dc->start + dc->len); - -f2fs_update_iostat (sbi,FS_DISCARD ,1 ); +trace_f2fs_issue_discard (bdev, dc->start, dc->len); + + lstart = dc->lstart; + start = dc->start; + len = dc->len; + total_len = len; + +while (total_len && *issued < dpolicy->max_requests) { +struct bio *bio =NULL ; + +if (len > max_discard_blocks) + len = max_discard_blocks; + + dc->error =__blkdev_issue_discard (bdev, +SECTOR_FROM_BLOCK (start), +SECTOR_FROM_BLOCK (len), +GFP_NOFS ,0 , &bio); +if (!dc->error) { +/* +* should keep before submission to avoid D_DONE +* right away +*/ + dc->state =D_SUBMIT ; + dc->len = len; +atomic_inc (&dcc->issued_discard); +atomic_inc (&dcc->issing_discard); +if (bio) { + bio->bi_private = dc; + bio->bi_end_io = f2fs_submit_discard_endio; + bio->bi_opf |= flag; +submit_bio (bio); +list_move_tail (&dc->list, wait_list); + __check_sit_bitmap(sbi, dc->start,Quoted 8 lines
Quoted 6 lines
Quoted 6 lines
fs/f2fs/segment.c static void __submit_discard_cmd(struct f2fs_sb_info *sbi, · line 1032
Quoted 6 lines
fs/f2fs/segment.c +6
-0+ dc->start + dc->len); + +f2fs_update_iostat (sbi,FS_DISCARD ,1 ); + } + }else { +__remove_discard_cmd (sbi, dc);Keep the original one, since it's partially failed?
Quoted 6 lines
During
__submit_discard_cmd(), we are trying to split original dc into multiple dc, and once we failed to submit some of them, we will remove corresponding dc, the removal logic is the same as before, not being changed...If we want to keep failed dc and do retry, we'd better add another patch for that, right?
I thought removing failed portion of the dc.
Quoted 17 lines
Quoted 16 lines
Quoted 13 lines
Quoted 13 lines
fs/f2fs/segment.c static void __submit_discard_cmd(struct f2fs_sb_info *sbi, · line 1043
Quoted 13 lines
fs/f2fs/segment.c +11
-0+return ; + } + lstart += len; + start += len; + total_len -= len; + len = total_len; + (*issued)++; + +if (len) { +__update_discard_tree_range (sbi, bdev, lstart, + start, len);Do we really need this? We already grabbed one candidate which was not merged well.
Quoted 1 line
You mean submitting multiple bios based on one large size dc?
Yes.
Quoted 10 lines
Previously, one bio references to one dc as below, in
end_io, it will changedc->statewhich can be detected by its own waiter.bio->bi_private = dc; bio->bi_end_io = f2fs_submit_discard_endio; bio->bi_opf |= flag;If multiple bios reference to one dc, it will be more complicated to handle that. How do you think?
I was concerned about too many entries in the lists. How about adding a refcnt to count how many bios were issued on the dc?
Thanks,
Quoted 111 lines
Thanks,
Quoted 108 lines
Quoted 11 lines
Quoted 11 lines
fs/f2fs/segment.c static void __submit_discard_cmd(struct f2fs_sb_info *sbi, · line 1051
Quoted 11 lines
fs/f2fs/segment.c +5
-2+ dc = (struct discard_cmd *)f2fs_lookup_rb_tree ( + &dcc->root,NULL , lstart); +f2fs_bug_on (sbi, !dc); } - }else { -__remove_discard_cmd (sbi, dc); } + +return ; }======================= END
Quoted 93 lines
Quoted 93 lines
Quoted 93 lines
fs/f2fs/segment.c +15
-12static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi,@@ -1088,10 +1128,11 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,struct discard_cmd *dc;struct discard_info di = {0 };struct rb_node **insert_p =NULL , *insert_parent =NULL ; +struct request_queue *q =bdev_get_queue (bdev); +unsigned int max_discard_blocks = +SECTOR_TO_BLOCK (q->limits.max_discard_sectors);block_t end = lstart + len; -mutex_lock (&dcc->cmd_lock); - dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root, NULL, lstart, (struct rb_entry **)&prev_dc,@@ -1131,7 +1172,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,if (prev_dc && prev_dc->state ==D_PREP && prev_dc->bdev == bdev && -__is_discard_back_mergeable (&di, &prev_dc->di)) { +__is_discard_back_mergeable (&di, &prev_dc->di, + max_discard_blocks)) { prev_dc->di.len += di.len; dcc->undiscard_blks += di.len;__relocate_discard_cmd (dcc, prev_dc);@@ -1142,7 +1184,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,if (next_dc && next_dc->state ==D_PREP && next_dc->bdev == bdev && -__is_discard_front_mergeable (&di, &next_dc->di)) { +__is_discard_front_mergeable (&di, &next_dc->di, + max_discard_blocks)) { next_dc->di.lstart = di.lstart; next_dc->di.len += di.len; next_dc->di.start = di.start;@@ -1165,8 +1208,6 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi, node =rb_next (&prev_dc->rb_node); next_dc = rb_entry_safe(node, struct discard_cmd, rb_node); } - -mutex_unlock (&dcc->cmd_lock); } static int __queue_discard_cmd(struct f2fs_sb_info *sbi,@@ -1181,7 +1222,9 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, blkstart -=FDEV (devi).start_blk; } +mutex_lock (&SM_I (sbi)->dcc_info->cmd_lock);__update_discard_tree_range (sbi, bdev, lblkstart, blkstart, blklen); +mutex_unlock (&SM_I (sbi)->dcc_info->cmd_lock);return 0 ; }@@ -1220,9 +1263,9 @@ static unsigned int __issue_discard_cmd_orderly(struct f2fs_sb_info *sbi, } dcc->next_pos = dc->lstart + dc->len; -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued); -if (++issued >= dpolicy->max_requests) +if (issued >= dpolicy->max_requests)break ; next: node =rb_next (&dc->rb_node);@@ -1277,9 +1320,9 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi,break ; } -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued); -if (++issued >= dpolicy->max_requests) +if (issued >= dpolicy->max_requests)break ; }blk_finish_plug (&plug);@@ -2475,9 +2518,9 @@ static unsigned int __issue_discard_cmd_range(struct f2fs_sb_info *sbi, goto skip; } -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued); -if (++issued >= dpolicy->max_requests) { +if (issued >= dpolicy->max_requests) { start = dc->lstart + dc->len;blk_finish_plug (&plug);Signature · 1 line
2.16.2.17.g38e79b1fd
Chao Yu
maintainerQuoted 218 lines · Jaegeuk Kim
Quoted 179 lines · Chao Yu
Quoted 169 lines · Jaegeuk Kim
Quoted 84 lines · Chao Yu
Hi Jaegeuk,
Quoted 8 lines · Jaegeuk Kim
Quoted 7 lines · Chao Yu
From: Chao Yu <[email protected]>
Some devices has small max_{hw,}discard_sectors, so that in
__blkdev_issue_discard(), one big size discard bio can be split into multiple small size discard bios, result in heavy load in IO scheduler and device, which can hang other sync IO for long time.Quoted 8 lines
Quoted 5 lines
Now, f2fs is trying to control discard commands more elaboratively, in order to make less conflict in between discard IO and user IO to enhance application's performance, so in this patch, we will split discard bio in f2fs in prior to in block layer to reduce issuing multiple discard bios in a short time.
Can we just change
__submit_discard_cmd()to submit the large candidate partially and adjust lstart in the original candidate?Yep, could you check
__submit_discard_cmd()implementation below? Does that match the thoughts from you?Thanks,
Quoted 59 lines
Quoted 59 lines
Signed-off-by Chao Yu <[email protected]>---
v2:
- change to split discard command entry before submission.fs/f2fs/f2fs.h | 13 +++--- fs/f2fs/segment.c | 117 +++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 86 insertions(+), 44 deletions(-)fs/f2fs/f2fs.h +6
-7@@ -178,7 +178,6 @@ enum {#define MAX_DISCARD_BLOCKS (sbi) BLKS_PER_SEC(sbi)#define DEF_MAX_DISCARD_REQUEST 8/* issue 8 discards per round */ -#define DEF_MAX_DISCARD_LEN 512/* Max. 2MB per discard */ #define DEF_MIN_DISCARD_ISSUE_TIME 50/* 50 ms, if exists */ #define DEF_MID_DISCARD_ISSUE_TIME 500/* 500 ms, if device busy */ #define DEF_MAX_DISCARD_ISSUE_TIME 60000/* 60 s, if no candidates */ @@ -709,22 +708,22 @@ static inline void set_extent_info(struct extent_info *ei, unsigned int fofs, }static inline bool __is_discard_mergeable (struct discard_info *back, -struct discard_info *front) +struct discard_info *front,unsigned int max_len) {return (back->lstart + back->len == front->lstart) && - (back->len + front->len <DEF_MAX_DISCARD_LEN ); + (back->len + front->len <= max_len); }static inline bool __is_discard_back_mergeable (struct discard_info *cur, -struct discard_info *back) +struct discard_info *back,unsigned int max_len) { -return __is_discard_mergeable (back, cur); +return __is_discard_mergeable (back, cur, max_len); }static inline bool __is_discard_front_mergeable (struct discard_info *cur, -struct discard_info *front) +struct discard_info *front,unsigned int max_len) { -return __is_discard_mergeable (cur, front); +return __is_discard_mergeable (cur, front, max_len); } static inline bool __is_extent_mergeable(struct extent_info *back,fs/f2fs/segment.c +0
-0@@ -966,17 +966,24 @@ static void __init_discard_policy(struct f2fs_sb_info *sbi, } }Quoted 1 line
Quoted 1 line
fs/f2fs/segment.c static void __init_discard_policy(struct f2fs_sb_info *sbi, · line 969
Quoted 1 line
fs/f2fs/segment.c +0
-1-===================== BEGIN
Quoted 80 lines
Quoted 80 lines
Quoted 80 lines
fs/f2fs/segment.c +43
-22+static void __update_discard_tree_range (struct f2fs_sb_info *sbi, +struct block_device *bdev,block_t lstart, +block_t start,block_t len);/* this function is copied from blkdev_issue_discard from block/blk-lib.c */ static void __submit_discard_cmd (struct f2fs_sb_info *sbi,struct discard_policy *dpolicy, -struct discard_cmd *dc) +struct discard_cmd *dc, +unsigned int *issued) { +struct block_device *bdev = dc->bdev; +struct request_queue *q =bdev_get_queue (bdev); +unsigned int max_discard_blocks = +SECTOR_TO_BLOCK (q->limits.max_discard_sectors);struct discard_cmd_control *dcc =SM_I (sbi)->dcc_info;struct list_head *wait_list = (dpolicy->type ==DPOLICY_FSTRIM ) ? &(dcc->fstrim_list) : &(dcc->wait_list); -struct bio *bio =NULL ;int flag = dpolicy->sync ?REQ_SYNC :0 ; +block_t lstart, start, len, total_len;if (dc->state !=D_PREP )return ;@@ -984,30 +991,63 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi,if (is_sbi_flag_set (sbi,SBI_NEED_FSCK ))return ; -trace_f2fs_issue_discard (dc->bdev, dc->start, dc->len); - - dc->error =__blkdev_issue_discard (dc->bdev, -SECTOR_FROM_BLOCK (dc->start), -SECTOR_FROM_BLOCK (dc->len), -GFP_NOFS ,0 , &bio); -if (!dc->error) { -/* should keep before submission to avoid D_DONE right away */ - dc->state =D_SUBMIT ; -atomic_inc (&dcc->issued_discard); -atomic_inc (&dcc->issing_discard); -if (bio) { - bio->bi_private = dc; - bio->bi_end_io = f2fs_submit_discard_endio; - bio->bi_opf |= flag; -submit_bio (bio); -list_move_tail (&dc->list, wait_list); -__check_sit_bitmap (sbi, dc->start, dc->start + dc->len); - -f2fs_update_iostat (sbi,FS_DISCARD ,1 ); +trace_f2fs_issue_discard (bdev, dc->start, dc->len); + + lstart = dc->lstart; + start = dc->start; + len = dc->len; + total_len = len; + +while (total_len && *issued < dpolicy->max_requests) { +struct bio *bio =NULL ; + +if (len > max_discard_blocks) + len = max_discard_blocks; + + dc->error =__blkdev_issue_discard (bdev, +SECTOR_FROM_BLOCK (start), +SECTOR_FROM_BLOCK (len), +GFP_NOFS ,0 , &bio); +if (!dc->error) { +/* +* should keep before submission to avoid D_DONE +* right away +*/ + dc->state =D_SUBMIT ; + dc->len = len; +atomic_inc (&dcc->issued_discard); +atomic_inc (&dcc->issing_discard); +if (bio) { + bio->bi_private = dc; + bio->bi_end_io = f2fs_submit_discard_endio; + bio->bi_opf |= flag; +submit_bio (bio); +list_move_tail (&dc->list, wait_list); + __check_sit_bitmap(sbi, dc->start,Quoted 8 lines
Quoted 6 lines
Quoted 6 lines
fs/f2fs/segment.c static void __submit_discard_cmd(struct f2fs_sb_info *sbi, · line 1032
Quoted 6 lines
fs/f2fs/segment.c +6
-0+ dc->start + dc->len); + +f2fs_update_iostat (sbi,FS_DISCARD ,1 ); + } + }else { +__remove_discard_cmd (sbi, dc);Keep the original one, since it's partially failed?
Quoted 6 lines
During
__submit_discard_cmd(), we are trying to split original dc into multiple dc, and once we failed to submit some of them, we will remove corresponding dc, the removal logic is the same as before, not being changed...If we want to keep failed dc and do retry, we'd better add another patch for that, right?
I thought removing failed portion of the dc.
Quoted 17 lines
Quoted 16 lines
Quoted 13 lines
Quoted 13 lines
fs/f2fs/segment.c static void __submit_discard_cmd(struct f2fs_sb_info *sbi, · line 1043
Quoted 13 lines
fs/f2fs/segment.c +11
-0+return ; + } + lstart += len; + start += len; + total_len -= len; + len = total_len; + (*issued)++; + +if (len) { +__update_discard_tree_range (sbi, bdev, lstart, + start, len);Do we really need this? We already grabbed one candidate which was not merged well.
Quoted 1 line
You mean submitting multiple bios based on one large size dc?
Yes.
Quoted 8 lines
Previously, one bio references to one dc as below, in
end_io, it will changedc->statewhich can be detected by its own waiter.bio->bi_private = dc; bio->bi_end_io = f2fs_submit_discard_endio; bio->bi_opf |= flag;
Quoted 5 lines
Quoted 2 lines
If multiple bios reference to one dc, it will be more complicated to handle that. How do you think?
I was concerned about too many entries in the lists. How about adding a refcnt to count how many bios were issued on the dc?
Got you, let me try to refactor a bit for that.
Thanks,
Quoted 123 lines
Thanks,
Quoted 104 lines
Thanks,
Quoted 101 lines
Quoted 11 lines
Quoted 11 lines
fs/f2fs/segment.c static void __submit_discard_cmd(struct f2fs_sb_info *sbi, · line 1051
Quoted 11 lines
fs/f2fs/segment.c +5
-2+ dc = (struct discard_cmd *)f2fs_lookup_rb_tree ( + &dcc->root,NULL , lstart); +f2fs_bug_on (sbi, !dc); } - }else { -__remove_discard_cmd (sbi, dc); } + +return ; }======================= END
Quoted 86 lines
Quoted 86 lines
Quoted 86 lines
fs/f2fs/segment.c +14
-11static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi,@@ -1088,10 +1128,11 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,struct discard_cmd *dc;struct discard_info di = {0 };struct rb_node **insert_p =NULL , *insert_parent =NULL ; +struct request_queue *q =bdev_get_queue (bdev); +unsigned int max_discard_blocks = +SECTOR_TO_BLOCK (q->limits.max_discard_sectors);block_t end = lstart + len; -mutex_lock (&dcc->cmd_lock); - dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root, NULL, lstart, (struct rb_entry **)&prev_dc,@@ -1131,7 +1172,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,if (prev_dc && prev_dc->state ==D_PREP && prev_dc->bdev == bdev && -__is_discard_back_mergeable (&di, &prev_dc->di)) { +__is_discard_back_mergeable (&di, &prev_dc->di, + max_discard_blocks)) { prev_dc->di.len += di.len; dcc->undiscard_blks += di.len;__relocate_discard_cmd (dcc, prev_dc);@@ -1142,7 +1184,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,if (next_dc && next_dc->state ==D_PREP && next_dc->bdev == bdev && -__is_discard_front_mergeable (&di, &next_dc->di)) { +__is_discard_front_mergeable (&di, &next_dc->di, + max_discard_blocks)) { next_dc->di.lstart = di.lstart; next_dc->di.len += di.len; next_dc->di.start = di.start;@@ -1165,8 +1208,6 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi, node =rb_next (&prev_dc->rb_node); next_dc = rb_entry_safe(node, struct discard_cmd, rb_node); } - -mutex_unlock (&dcc->cmd_lock); } static int __queue_discard_cmd(struct f2fs_sb_info *sbi,@@ -1181,7 +1222,9 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi, blkstart -=FDEV (devi).start_blk; } +mutex_lock (&SM_I (sbi)->dcc_info->cmd_lock);__update_discard_tree_range (sbi, bdev, lblkstart, blkstart, blklen); +mutex_unlock (&SM_I (sbi)->dcc_info->cmd_lock);return 0 ; }@@ -1220,9 +1263,9 @@ static unsigned int __issue_discard_cmd_orderly(struct f2fs_sb_info *sbi, } dcc->next_pos = dc->lstart + dc->len; -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued); -if (++issued >= dpolicy->max_requests) +if (issued >= dpolicy->max_requests)break ; next: node =rb_next (&dc->rb_node);@@ -1277,9 +1320,9 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi,break ; } -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued); -if (++issued >= dpolicy->max_requests) +if (issued >= dpolicy->max_requests)break ; }blk_finish_plug (&plug);@@ -2475,9 +2518,9 @@ static unsigned int __issue_discard_cmd_range(struct f2fs_sb_info *sbi, goto skip; } -__submit_discard_cmd (sbi, dpolicy, dc); +__submit_discard_cmd (sbi, dpolicy, dc, &issued);Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
fs/f2fs/segment.c +1
-1-if (++issued >= dpolicy->max_requests) { +if (issued >= dpolicy->max_requests) { start = dc->lstart + dc->len;blk_finish_plug (&plug);Signature · 1 line
2.16.2.17.g38e79b1fd------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Linux-f2fs-devel mailing list [email protected]
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel