|
@@ -250,7 +250,13 @@ retry:
|
|
err = -EAGAIN;
|
|
err = -EAGAIN;
|
|
goto next;
|
|
goto next;
|
|
}
|
|
}
|
|
- f2fs_get_node_info(sbi, dn.nid, &ni);
|
|
|
|
|
|
+
|
|
|
|
+ err = f2fs_get_node_info(sbi, dn.nid, &ni);
|
|
|
|
+ if (err) {
|
|
|
|
+ f2fs_put_dnode(&dn);
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (cur->old_addr == NEW_ADDR) {
|
|
if (cur->old_addr == NEW_ADDR) {
|
|
f2fs_invalidate_blocks(sbi, dn.data_blkaddr);
|
|
f2fs_invalidate_blocks(sbi, dn.data_blkaddr);
|
|
f2fs_update_data_blkaddr(&dn, NEW_ADDR);
|
|
f2fs_update_data_blkaddr(&dn, NEW_ADDR);
|
|
@@ -439,8 +445,10 @@ int f2fs_commit_inmem_pages(struct inode *inode)
|
|
int err;
|
|
int err;
|
|
|
|
|
|
f2fs_balance_fs(sbi, true);
|
|
f2fs_balance_fs(sbi, true);
|
|
- f2fs_lock_op(sbi);
|
|
|
|
|
|
|
|
|
|
+ down_write(&fi->i_gc_rwsem[WRITE]);
|
|
|
|
+
|
|
|
|
+ f2fs_lock_op(sbi);
|
|
set_inode_flag(inode, FI_ATOMIC_COMMIT);
|
|
set_inode_flag(inode, FI_ATOMIC_COMMIT);
|
|
|
|
|
|
mutex_lock(&fi->inmem_lock);
|
|
mutex_lock(&fi->inmem_lock);
|
|
@@ -455,6 +463,8 @@ int f2fs_commit_inmem_pages(struct inode *inode)
|
|
clear_inode_flag(inode, FI_ATOMIC_COMMIT);
|
|
clear_inode_flag(inode, FI_ATOMIC_COMMIT);
|
|
|
|
|
|
f2fs_unlock_op(sbi);
|
|
f2fs_unlock_op(sbi);
|
|
|
|
+ up_write(&fi->i_gc_rwsem[WRITE]);
|
|
|
|
+
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -464,12 +474,10 @@ int f2fs_commit_inmem_pages(struct inode *inode)
|
|
*/
|
|
*/
|
|
void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
|
|
void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
|
|
{
|
|
{
|
|
-#ifdef CONFIG_F2FS_FAULT_INJECTION
|
|
|
|
if (time_to_inject(sbi, FAULT_CHECKPOINT)) {
|
|
if (time_to_inject(sbi, FAULT_CHECKPOINT)) {
|
|
f2fs_show_injection_info(FAULT_CHECKPOINT);
|
|
f2fs_show_injection_info(FAULT_CHECKPOINT);
|
|
f2fs_stop_checkpoint(sbi, false);
|
|
f2fs_stop_checkpoint(sbi, false);
|
|
}
|
|
}
|
|
-#endif
|
|
|
|
|
|
|
|
/* balance_fs_bg is able to be pending */
|
|
/* balance_fs_bg is able to be pending */
|
|
if (need && excess_cached_nats(sbi))
|
|
if (need && excess_cached_nats(sbi))
|
|
@@ -503,7 +511,8 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)
|
|
else
|
|
else
|
|
f2fs_build_free_nids(sbi, false, false);
|
|
f2fs_build_free_nids(sbi, false, false);
|
|
|
|
|
|
- if (!is_idle(sbi) && !excess_dirty_nats(sbi))
|
|
|
|
|
|
+ if (!is_idle(sbi) &&
|
|
|
|
+ (!excess_dirty_nats(sbi) && !excess_dirty_nodes(sbi)))
|
|
return;
|
|
return;
|
|
|
|
|
|
/* checkpoint is the only way to shrink partial cached entries */
|
|
/* checkpoint is the only way to shrink partial cached entries */
|
|
@@ -511,6 +520,7 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)
|
|
!f2fs_available_free_memory(sbi, INO_ENTRIES) ||
|
|
!f2fs_available_free_memory(sbi, INO_ENTRIES) ||
|
|
excess_prefree_segs(sbi) ||
|
|
excess_prefree_segs(sbi) ||
|
|
excess_dirty_nats(sbi) ||
|
|
excess_dirty_nats(sbi) ||
|
|
|
|
+ excess_dirty_nodes(sbi) ||
|
|
f2fs_time_over(sbi, CP_TIME)) {
|
|
f2fs_time_over(sbi, CP_TIME)) {
|
|
if (test_opt(sbi, DATA_FLUSH)) {
|
|
if (test_opt(sbi, DATA_FLUSH)) {
|
|
struct blk_plug plug;
|
|
struct blk_plug plug;
|
|
@@ -831,9 +841,12 @@ static struct discard_cmd *__create_discard_cmd(struct f2fs_sb_info *sbi,
|
|
dc->len = len;
|
|
dc->len = len;
|
|
dc->ref = 0;
|
|
dc->ref = 0;
|
|
dc->state = D_PREP;
|
|
dc->state = D_PREP;
|
|
|
|
+ dc->issuing = 0;
|
|
dc->error = 0;
|
|
dc->error = 0;
|
|
init_completion(&dc->wait);
|
|
init_completion(&dc->wait);
|
|
list_add_tail(&dc->list, pend_list);
|
|
list_add_tail(&dc->list, pend_list);
|
|
|
|
+ spin_lock_init(&dc->lock);
|
|
|
|
+ dc->bio_ref = 0;
|
|
atomic_inc(&dcc->discard_cmd_cnt);
|
|
atomic_inc(&dcc->discard_cmd_cnt);
|
|
dcc->undiscard_blks += len;
|
|
dcc->undiscard_blks += len;
|
|
|
|
|
|
@@ -860,7 +873,7 @@ static void __detach_discard_cmd(struct discard_cmd_control *dcc,
|
|
struct discard_cmd *dc)
|
|
struct discard_cmd *dc)
|
|
{
|
|
{
|
|
if (dc->state == D_DONE)
|
|
if (dc->state == D_DONE)
|
|
- atomic_dec(&dcc->issing_discard);
|
|
|
|
|
|
+ atomic_sub(dc->issuing, &dcc->issing_discard);
|
|
|
|
|
|
list_del(&dc->list);
|
|
list_del(&dc->list);
|
|
rb_erase(&dc->rb_node, &dcc->root);
|
|
rb_erase(&dc->rb_node, &dcc->root);
|
|
@@ -875,9 +888,17 @@ static void __remove_discard_cmd(struct f2fs_sb_info *sbi,
|
|
struct discard_cmd *dc)
|
|
struct discard_cmd *dc)
|
|
{
|
|
{
|
|
struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
|
|
struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
|
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
trace_f2fs_remove_discard(dc->bdev, dc->start, dc->len);
|
|
trace_f2fs_remove_discard(dc->bdev, dc->start, dc->len);
|
|
|
|
|
|
|
|
+ spin_lock_irqsave(&dc->lock, flags);
|
|
|
|
+ if (dc->bio_ref) {
|
|
|
|
+ spin_unlock_irqrestore(&dc->lock, flags);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ spin_unlock_irqrestore(&dc->lock, flags);
|
|
|
|
+
|
|
f2fs_bug_on(sbi, dc->ref);
|
|
f2fs_bug_on(sbi, dc->ref);
|
|
|
|
|
|
if (dc->error == -EOPNOTSUPP)
|
|
if (dc->error == -EOPNOTSUPP)
|
|
@@ -893,10 +914,17 @@ static void __remove_discard_cmd(struct f2fs_sb_info *sbi,
|
|
static void f2fs_submit_discard_endio(struct bio *bio)
|
|
static void f2fs_submit_discard_endio(struct bio *bio)
|
|
{
|
|
{
|
|
struct discard_cmd *dc = (struct discard_cmd *)bio->bi_private;
|
|
struct discard_cmd *dc = (struct discard_cmd *)bio->bi_private;
|
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
dc->error = blk_status_to_errno(bio->bi_status);
|
|
dc->error = blk_status_to_errno(bio->bi_status);
|
|
- dc->state = D_DONE;
|
|
|
|
- complete_all(&dc->wait);
|
|
|
|
|
|
+
|
|
|
|
+ spin_lock_irqsave(&dc->lock, flags);
|
|
|
|
+ dc->bio_ref--;
|
|
|
|
+ if (!dc->bio_ref && dc->state == D_SUBMIT) {
|
|
|
|
+ dc->state = D_DONE;
|
|
|
|
+ complete_all(&dc->wait);
|
|
|
|
+ }
|
|
|
|
+ spin_unlock_irqrestore(&dc->lock, flags);
|
|
bio_put(bio);
|
|
bio_put(bio);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -934,6 +962,7 @@ static void __init_discard_policy(struct f2fs_sb_info *sbi,
|
|
/* common policy */
|
|
/* common policy */
|
|
dpolicy->type = discard_type;
|
|
dpolicy->type = discard_type;
|
|
dpolicy->sync = true;
|
|
dpolicy->sync = true;
|
|
|
|
+ dpolicy->ordered = false;
|
|
dpolicy->granularity = granularity;
|
|
dpolicy->granularity = granularity;
|
|
|
|
|
|
dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST;
|
|
dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST;
|
|
@@ -945,6 +974,7 @@ static void __init_discard_policy(struct f2fs_sb_info *sbi,
|
|
dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME;
|
|
dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME;
|
|
dpolicy->io_aware = true;
|
|
dpolicy->io_aware = true;
|
|
dpolicy->sync = false;
|
|
dpolicy->sync = false;
|
|
|
|
+ dpolicy->ordered = true;
|
|
if (utilization(sbi) > DEF_DISCARD_URGENT_UTIL) {
|
|
if (utilization(sbi) > DEF_DISCARD_URGENT_UTIL) {
|
|
dpolicy->granularity = 1;
|
|
dpolicy->granularity = 1;
|
|
dpolicy->max_interval = DEF_MIN_DISCARD_ISSUE_TIME;
|
|
dpolicy->max_interval = DEF_MIN_DISCARD_ISSUE_TIME;
|
|
@@ -962,48 +992,115 @@ 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 */
|
|
/* this function is copied from blkdev_issue_discard from block/blk-lib.c */
|
|
-static void __submit_discard_cmd(struct f2fs_sb_info *sbi,
|
|
|
|
|
|
+static int __submit_discard_cmd(struct f2fs_sb_info *sbi,
|
|
struct discard_policy *dpolicy,
|
|
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 discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
|
|
struct list_head *wait_list = (dpolicy->type == DPOLICY_FSTRIM) ?
|
|
struct list_head *wait_list = (dpolicy->type == DPOLICY_FSTRIM) ?
|
|
&(dcc->fstrim_list) : &(dcc->wait_list);
|
|
&(dcc->fstrim_list) : &(dcc->wait_list);
|
|
- struct bio *bio = NULL;
|
|
|
|
int flag = dpolicy->sync ? REQ_SYNC : 0;
|
|
int flag = dpolicy->sync ? REQ_SYNC : 0;
|
|
|
|
+ block_t lstart, start, len, total_len;
|
|
|
|
+ int err = 0;
|
|
|
|
|
|
if (dc->state != D_PREP)
|
|
if (dc->state != D_PREP)
|
|
- return;
|
|
|
|
|
|
+ return 0;
|
|
|
|
|
|
if (is_sbi_flag_set(sbi, SBI_NEED_FSCK))
|
|
if (is_sbi_flag_set(sbi, SBI_NEED_FSCK))
|
|
- return;
|
|
|
|
|
|
+ return 0;
|
|
|
|
|
|
- trace_f2fs_issue_discard(dc->bdev, dc->start, dc->len);
|
|
|
|
|
|
+ trace_f2fs_issue_discard(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);
|
|
|
|
|
|
+ lstart = dc->lstart;
|
|
|
|
+ start = dc->start;
|
|
|
|
+ len = dc->len;
|
|
|
|
+ total_len = len;
|
|
|
|
+
|
|
|
|
+ dc->len = 0;
|
|
|
|
+
|
|
|
|
+ while (total_len && *issued < dpolicy->max_requests && !err) {
|
|
|
|
+ struct bio *bio = NULL;
|
|
|
|
+ unsigned long flags;
|
|
|
|
+ bool last = true;
|
|
|
|
+
|
|
|
|
+ if (len > max_discard_blocks) {
|
|
|
|
+ len = max_discard_blocks;
|
|
|
|
+ last = false;
|
|
}
|
|
}
|
|
- } else {
|
|
|
|
- __remove_discard_cmd(sbi, dc);
|
|
|
|
|
|
+
|
|
|
|
+ (*issued)++;
|
|
|
|
+ if (*issued == dpolicy->max_requests)
|
|
|
|
+ last = true;
|
|
|
|
+
|
|
|
|
+ dc->len += len;
|
|
|
|
+
|
|
|
|
+ if (time_to_inject(sbi, FAULT_DISCARD)) {
|
|
|
|
+ f2fs_show_injection_info(FAULT_DISCARD);
|
|
|
|
+ err = -EIO;
|
|
|
|
+ goto submit;
|
|
|
|
+ }
|
|
|
|
+ err = __blkdev_issue_discard(bdev,
|
|
|
|
+ SECTOR_FROM_BLOCK(start),
|
|
|
|
+ SECTOR_FROM_BLOCK(len),
|
|
|
|
+ GFP_NOFS, 0, &bio);
|
|
|
|
+submit:
|
|
|
|
+ if (err) {
|
|
|
|
+ spin_lock_irqsave(&dc->lock, flags);
|
|
|
|
+ if (dc->state == D_PARTIAL)
|
|
|
|
+ dc->state = D_SUBMIT;
|
|
|
|
+ spin_unlock_irqrestore(&dc->lock, flags);
|
|
|
|
+
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ f2fs_bug_on(sbi, !bio);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * should keep before submission to avoid D_DONE
|
|
|
|
+ * right away
|
|
|
|
+ */
|
|
|
|
+ spin_lock_irqsave(&dc->lock, flags);
|
|
|
|
+ if (last)
|
|
|
|
+ dc->state = D_SUBMIT;
|
|
|
|
+ else
|
|
|
|
+ dc->state = D_PARTIAL;
|
|
|
|
+ dc->bio_ref++;
|
|
|
|
+ spin_unlock_irqrestore(&dc->lock, flags);
|
|
|
|
+
|
|
|
|
+ atomic_inc(&dcc->issing_discard);
|
|
|
|
+ dc->issuing++;
|
|
|
|
+ list_move_tail(&dc->list, wait_list);
|
|
|
|
+
|
|
|
|
+ /* sanity check on discard range */
|
|
|
|
+ __check_sit_bitmap(sbi, start, start + len);
|
|
|
|
+
|
|
|
|
+ bio->bi_private = dc;
|
|
|
|
+ bio->bi_end_io = f2fs_submit_discard_endio;
|
|
|
|
+ bio->bi_opf |= flag;
|
|
|
|
+ submit_bio(bio);
|
|
|
|
+
|
|
|
|
+ atomic_inc(&dcc->issued_discard);
|
|
|
|
+
|
|
|
|
+ f2fs_update_iostat(sbi, FS_DISCARD, 1);
|
|
|
|
+
|
|
|
|
+ lstart += len;
|
|
|
|
+ start += len;
|
|
|
|
+ total_len -= len;
|
|
|
|
+ len = total_len;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if (!err && len)
|
|
|
|
+ __update_discard_tree_range(sbi, bdev, lstart, start, len);
|
|
|
|
+ return err;
|
|
}
|
|
}
|
|
|
|
|
|
static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi,
|
|
static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi,
|
|
@@ -1084,10 +1181,11 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,
|
|
struct discard_cmd *dc;
|
|
struct discard_cmd *dc;
|
|
struct discard_info di = {0};
|
|
struct discard_info di = {0};
|
|
struct rb_node **insert_p = NULL, *insert_parent = NULL;
|
|
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;
|
|
block_t end = lstart + len;
|
|
|
|
|
|
- mutex_lock(&dcc->cmd_lock);
|
|
|
|
-
|
|
|
|
dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root,
|
|
dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root,
|
|
NULL, lstart,
|
|
NULL, lstart,
|
|
(struct rb_entry **)&prev_dc,
|
|
(struct rb_entry **)&prev_dc,
|
|
@@ -1127,7 +1225,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,
|
|
|
|
|
|
if (prev_dc && prev_dc->state == D_PREP &&
|
|
if (prev_dc && prev_dc->state == D_PREP &&
|
|
prev_dc->bdev == bdev &&
|
|
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;
|
|
prev_dc->di.len += di.len;
|
|
dcc->undiscard_blks += di.len;
|
|
dcc->undiscard_blks += di.len;
|
|
__relocate_discard_cmd(dcc, prev_dc);
|
|
__relocate_discard_cmd(dcc, prev_dc);
|
|
@@ -1138,7 +1237,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,
|
|
|
|
|
|
if (next_dc && next_dc->state == D_PREP &&
|
|
if (next_dc && next_dc->state == D_PREP &&
|
|
next_dc->bdev == bdev &&
|
|
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.lstart = di.lstart;
|
|
next_dc->di.len += di.len;
|
|
next_dc->di.len += di.len;
|
|
next_dc->di.start = di.start;
|
|
next_dc->di.start = di.start;
|
|
@@ -1161,8 +1261,6 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,
|
|
node = rb_next(&prev_dc->rb_node);
|
|
node = rb_next(&prev_dc->rb_node);
|
|
next_dc = rb_entry_safe(node, struct discard_cmd, 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,
|
|
static int __queue_discard_cmd(struct f2fs_sb_info *sbi,
|
|
@@ -1177,10 +1275,72 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi,
|
|
|
|
|
|
blkstart -= FDEV(devi).start_blk;
|
|
blkstart -= FDEV(devi).start_blk;
|
|
}
|
|
}
|
|
|
|
+ mutex_lock(&SM_I(sbi)->dcc_info->cmd_lock);
|
|
__update_discard_tree_range(sbi, bdev, lblkstart, blkstart, blklen);
|
|
__update_discard_tree_range(sbi, bdev, lblkstart, blkstart, blklen);
|
|
|
|
+ mutex_unlock(&SM_I(sbi)->dcc_info->cmd_lock);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static unsigned int __issue_discard_cmd_orderly(struct f2fs_sb_info *sbi,
|
|
|
|
+ struct discard_policy *dpolicy)
|
|
|
|
+{
|
|
|
|
+ struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
|
|
|
|
+ struct discard_cmd *prev_dc = NULL, *next_dc = NULL;
|
|
|
|
+ struct rb_node **insert_p = NULL, *insert_parent = NULL;
|
|
|
|
+ struct discard_cmd *dc;
|
|
|
|
+ struct blk_plug plug;
|
|
|
|
+ unsigned int pos = dcc->next_pos;
|
|
|
|
+ unsigned int issued = 0;
|
|
|
|
+ bool io_interrupted = false;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&dcc->cmd_lock);
|
|
|
|
+ dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root,
|
|
|
|
+ NULL, pos,
|
|
|
|
+ (struct rb_entry **)&prev_dc,
|
|
|
|
+ (struct rb_entry **)&next_dc,
|
|
|
|
+ &insert_p, &insert_parent, true);
|
|
|
|
+ if (!dc)
|
|
|
|
+ dc = next_dc;
|
|
|
|
+
|
|
|
|
+ blk_start_plug(&plug);
|
|
|
|
+
|
|
|
|
+ while (dc) {
|
|
|
|
+ struct rb_node *node;
|
|
|
|
+ int err = 0;
|
|
|
|
+
|
|
|
|
+ if (dc->state != D_PREP)
|
|
|
|
+ goto next;
|
|
|
|
+
|
|
|
|
+ if (dpolicy->io_aware && !is_idle(sbi)) {
|
|
|
|
+ io_interrupted = true;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dcc->next_pos = dc->lstart + dc->len;
|
|
|
|
+ err = __submit_discard_cmd(sbi, dpolicy, dc, &issued);
|
|
|
|
+
|
|
|
|
+ if (issued >= dpolicy->max_requests)
|
|
|
|
+ break;
|
|
|
|
+next:
|
|
|
|
+ node = rb_next(&dc->rb_node);
|
|
|
|
+ if (err)
|
|
|
|
+ __remove_discard_cmd(sbi, dc);
|
|
|
|
+ dc = rb_entry_safe(node, struct discard_cmd, rb_node);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ blk_finish_plug(&plug);
|
|
|
|
+
|
|
|
|
+ if (!dc)
|
|
|
|
+ dcc->next_pos = 0;
|
|
|
|
+
|
|
|
|
+ mutex_unlock(&dcc->cmd_lock);
|
|
|
|
+
|
|
|
|
+ if (!issued && io_interrupted)
|
|
|
|
+ issued = -1;
|
|
|
|
+
|
|
|
|
+ return issued;
|
|
|
|
+}
|
|
|
|
+
|
|
static int __issue_discard_cmd(struct f2fs_sb_info *sbi,
|
|
static int __issue_discard_cmd(struct f2fs_sb_info *sbi,
|
|
struct discard_policy *dpolicy)
|
|
struct discard_policy *dpolicy)
|
|
{
|
|
{
|
|
@@ -1188,19 +1348,24 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi,
|
|
struct list_head *pend_list;
|
|
struct list_head *pend_list;
|
|
struct discard_cmd *dc, *tmp;
|
|
struct discard_cmd *dc, *tmp;
|
|
struct blk_plug plug;
|
|
struct blk_plug plug;
|
|
- int i, iter = 0, issued = 0;
|
|
|
|
|
|
+ int i, issued = 0;
|
|
bool io_interrupted = false;
|
|
bool io_interrupted = false;
|
|
|
|
|
|
for (i = MAX_PLIST_NUM - 1; i >= 0; i--) {
|
|
for (i = MAX_PLIST_NUM - 1; i >= 0; i--) {
|
|
if (i + 1 < dpolicy->granularity)
|
|
if (i + 1 < dpolicy->granularity)
|
|
break;
|
|
break;
|
|
|
|
+
|
|
|
|
+ if (i < DEFAULT_DISCARD_GRANULARITY && dpolicy->ordered)
|
|
|
|
+ return __issue_discard_cmd_orderly(sbi, dpolicy);
|
|
|
|
+
|
|
pend_list = &dcc->pend_list[i];
|
|
pend_list = &dcc->pend_list[i];
|
|
|
|
|
|
mutex_lock(&dcc->cmd_lock);
|
|
mutex_lock(&dcc->cmd_lock);
|
|
if (list_empty(pend_list))
|
|
if (list_empty(pend_list))
|
|
goto next;
|
|
goto next;
|
|
- f2fs_bug_on(sbi,
|
|
|
|
- !f2fs_check_rb_tree_consistence(sbi, &dcc->root));
|
|
|
|
|
|
+ if (unlikely(dcc->rbtree_check))
|
|
|
|
+ f2fs_bug_on(sbi, !f2fs_check_rb_tree_consistence(sbi,
|
|
|
|
+ &dcc->root));
|
|
blk_start_plug(&plug);
|
|
blk_start_plug(&plug);
|
|
list_for_each_entry_safe(dc, tmp, pend_list, list) {
|
|
list_for_each_entry_safe(dc, tmp, pend_list, list) {
|
|
f2fs_bug_on(sbi, dc->state != D_PREP);
|
|
f2fs_bug_on(sbi, dc->state != D_PREP);
|
|
@@ -1208,20 +1373,19 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi,
|
|
if (dpolicy->io_aware && i < dpolicy->io_aware_gran &&
|
|
if (dpolicy->io_aware && i < dpolicy->io_aware_gran &&
|
|
!is_idle(sbi)) {
|
|
!is_idle(sbi)) {
|
|
io_interrupted = true;
|
|
io_interrupted = true;
|
|
- goto skip;
|
|
|
|
|
|
+ break;
|
|
}
|
|
}
|
|
|
|
|
|
- __submit_discard_cmd(sbi, dpolicy, dc);
|
|
|
|
- issued++;
|
|
|
|
-skip:
|
|
|
|
- if (++iter >= dpolicy->max_requests)
|
|
|
|
|
|
+ __submit_discard_cmd(sbi, dpolicy, dc, &issued);
|
|
|
|
+
|
|
|
|
+ if (issued >= dpolicy->max_requests)
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
blk_finish_plug(&plug);
|
|
blk_finish_plug(&plug);
|
|
next:
|
|
next:
|
|
mutex_unlock(&dcc->cmd_lock);
|
|
mutex_unlock(&dcc->cmd_lock);
|
|
|
|
|
|
- if (iter >= dpolicy->max_requests)
|
|
|
|
|
|
+ if (issued >= dpolicy->max_requests || io_interrupted)
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1319,21 +1483,22 @@ next:
|
|
return trimmed;
|
|
return trimmed;
|
|
}
|
|
}
|
|
|
|
|
|
-static void __wait_all_discard_cmd(struct f2fs_sb_info *sbi,
|
|
|
|
|
|
+static unsigned int __wait_all_discard_cmd(struct f2fs_sb_info *sbi,
|
|
struct discard_policy *dpolicy)
|
|
struct discard_policy *dpolicy)
|
|
{
|
|
{
|
|
struct discard_policy dp;
|
|
struct discard_policy dp;
|
|
|
|
+ unsigned int discard_blks;
|
|
|
|
|
|
- if (dpolicy) {
|
|
|
|
- __wait_discard_cmd_range(sbi, dpolicy, 0, UINT_MAX);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
|
|
+ if (dpolicy)
|
|
|
|
+ return __wait_discard_cmd_range(sbi, dpolicy, 0, UINT_MAX);
|
|
|
|
|
|
/* wait all */
|
|
/* wait all */
|
|
__init_discard_policy(sbi, &dp, DPOLICY_FSTRIM, 1);
|
|
__init_discard_policy(sbi, &dp, DPOLICY_FSTRIM, 1);
|
|
- __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX);
|
|
|
|
|
|
+ discard_blks = __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX);
|
|
__init_discard_policy(sbi, &dp, DPOLICY_UMOUNT, 1);
|
|
__init_discard_policy(sbi, &dp, DPOLICY_UMOUNT, 1);
|
|
- __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX);
|
|
|
|
|
|
+ discard_blks += __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX);
|
|
|
|
+
|
|
|
|
+ return discard_blks;
|
|
}
|
|
}
|
|
|
|
|
|
/* This should be covered by global mutex, &sit_i->sentry_lock */
|
|
/* This should be covered by global mutex, &sit_i->sentry_lock */
|
|
@@ -1386,6 +1551,8 @@ bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi)
|
|
|
|
|
|
/* just to make sure there is no pending discard commands */
|
|
/* just to make sure there is no pending discard commands */
|
|
__wait_all_discard_cmd(sbi, NULL);
|
|
__wait_all_discard_cmd(sbi, NULL);
|
|
|
|
+
|
|
|
|
+ f2fs_bug_on(sbi, atomic_read(&dcc->discard_cmd_cnt));
|
|
return dropped;
|
|
return dropped;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1643,21 +1810,30 @@ void f2fs_clear_prefree_segments(struct f2fs_sb_info *sbi,
|
|
unsigned int start = 0, end = -1;
|
|
unsigned int start = 0, end = -1;
|
|
unsigned int secno, start_segno;
|
|
unsigned int secno, start_segno;
|
|
bool force = (cpc->reason & CP_DISCARD);
|
|
bool force = (cpc->reason & CP_DISCARD);
|
|
|
|
+ bool need_align = test_opt(sbi, LFS) && sbi->segs_per_sec > 1;
|
|
|
|
|
|
mutex_lock(&dirty_i->seglist_lock);
|
|
mutex_lock(&dirty_i->seglist_lock);
|
|
|
|
|
|
while (1) {
|
|
while (1) {
|
|
int i;
|
|
int i;
|
|
|
|
+
|
|
|
|
+ if (need_align && end != -1)
|
|
|
|
+ end--;
|
|
start = find_next_bit(prefree_map, MAIN_SEGS(sbi), end + 1);
|
|
start = find_next_bit(prefree_map, MAIN_SEGS(sbi), end + 1);
|
|
if (start >= MAIN_SEGS(sbi))
|
|
if (start >= MAIN_SEGS(sbi))
|
|
break;
|
|
break;
|
|
end = find_next_zero_bit(prefree_map, MAIN_SEGS(sbi),
|
|
end = find_next_zero_bit(prefree_map, MAIN_SEGS(sbi),
|
|
start + 1);
|
|
start + 1);
|
|
|
|
|
|
- for (i = start; i < end; i++)
|
|
|
|
- clear_bit(i, prefree_map);
|
|
|
|
|
|
+ if (need_align) {
|
|
|
|
+ start = rounddown(start, sbi->segs_per_sec);
|
|
|
|
+ end = roundup(end, sbi->segs_per_sec);
|
|
|
|
+ }
|
|
|
|
|
|
- dirty_i->nr_dirty[PRE] -= end - start;
|
|
|
|
|
|
+ for (i = start; i < end; i++) {
|
|
|
|
+ if (test_and_clear_bit(i, prefree_map))
|
|
|
|
+ dirty_i->nr_dirty[PRE]--;
|
|
|
|
+ }
|
|
|
|
|
|
if (!test_opt(sbi, DISCARD))
|
|
if (!test_opt(sbi, DISCARD))
|
|
continue;
|
|
continue;
|
|
@@ -1751,7 +1927,9 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi)
|
|
dcc->nr_discards = 0;
|
|
dcc->nr_discards = 0;
|
|
dcc->max_discards = MAIN_SEGS(sbi) << sbi->log_blocks_per_seg;
|
|
dcc->max_discards = MAIN_SEGS(sbi) << sbi->log_blocks_per_seg;
|
|
dcc->undiscard_blks = 0;
|
|
dcc->undiscard_blks = 0;
|
|
|
|
+ dcc->next_pos = 0;
|
|
dcc->root = RB_ROOT;
|
|
dcc->root = RB_ROOT;
|
|
|
|
+ dcc->rbtree_check = false;
|
|
|
|
|
|
init_waitqueue_head(&dcc->discard_wait_queue);
|
|
init_waitqueue_head(&dcc->discard_wait_queue);
|
|
SM_I(sbi)->dcc_info = dcc;
|
|
SM_I(sbi)->dcc_info = dcc;
|
|
@@ -1901,6 +2079,8 @@ void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr)
|
|
if (addr == NEW_ADDR)
|
|
if (addr == NEW_ADDR)
|
|
return;
|
|
return;
|
|
|
|
|
|
|
|
+ invalidate_mapping_pages(META_MAPPING(sbi), addr, addr);
|
|
|
|
+
|
|
/* add it into sit main buffer */
|
|
/* add it into sit main buffer */
|
|
down_write(&sit_i->sentry_lock);
|
|
down_write(&sit_i->sentry_lock);
|
|
|
|
|
|
@@ -1919,7 +2099,7 @@ bool f2fs_is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr)
|
|
struct seg_entry *se;
|
|
struct seg_entry *se;
|
|
bool is_cp = false;
|
|
bool is_cp = false;
|
|
|
|
|
|
- if (!is_valid_blkaddr(blkaddr))
|
|
|
|
|
|
+ if (!is_valid_data_blkaddr(sbi, blkaddr))
|
|
return true;
|
|
return true;
|
|
|
|
|
|
down_read(&sit_i->sentry_lock);
|
|
down_read(&sit_i->sentry_lock);
|
|
@@ -1983,7 +2163,7 @@ int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra)
|
|
*/
|
|
*/
|
|
struct page *f2fs_get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno)
|
|
struct page *f2fs_get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno)
|
|
{
|
|
{
|
|
- return f2fs_get_meta_page(sbi, GET_SUM_BLOCK(sbi, segno));
|
|
|
|
|
|
+ return f2fs_get_meta_page_nofail(sbi, GET_SUM_BLOCK(sbi, segno));
|
|
}
|
|
}
|
|
|
|
|
|
void f2fs_update_meta_page(struct f2fs_sb_info *sbi,
|
|
void f2fs_update_meta_page(struct f2fs_sb_info *sbi,
|
|
@@ -2366,7 +2546,7 @@ bool f2fs_exist_trim_candidates(struct f2fs_sb_info *sbi,
|
|
return has_candidate;
|
|
return has_candidate;
|
|
}
|
|
}
|
|
|
|
|
|
-static void __issue_discard_cmd_range(struct f2fs_sb_info *sbi,
|
|
|
|
|
|
+static unsigned int __issue_discard_cmd_range(struct f2fs_sb_info *sbi,
|
|
struct discard_policy *dpolicy,
|
|
struct discard_policy *dpolicy,
|
|
unsigned int start, unsigned int end)
|
|
unsigned int start, unsigned int end)
|
|
{
|
|
{
|
|
@@ -2376,12 +2556,15 @@ static void __issue_discard_cmd_range(struct f2fs_sb_info *sbi,
|
|
struct discard_cmd *dc;
|
|
struct discard_cmd *dc;
|
|
struct blk_plug plug;
|
|
struct blk_plug plug;
|
|
int issued;
|
|
int issued;
|
|
|
|
+ unsigned int trimmed = 0;
|
|
|
|
|
|
next:
|
|
next:
|
|
issued = 0;
|
|
issued = 0;
|
|
|
|
|
|
mutex_lock(&dcc->cmd_lock);
|
|
mutex_lock(&dcc->cmd_lock);
|
|
- f2fs_bug_on(sbi, !f2fs_check_rb_tree_consistence(sbi, &dcc->root));
|
|
|
|
|
|
+ if (unlikely(dcc->rbtree_check))
|
|
|
|
+ f2fs_bug_on(sbi, !f2fs_check_rb_tree_consistence(sbi,
|
|
|
|
+ &dcc->root));
|
|
|
|
|
|
dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root,
|
|
dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root,
|
|
NULL, start,
|
|
NULL, start,
|
|
@@ -2395,6 +2578,7 @@ next:
|
|
|
|
|
|
while (dc && dc->lstart <= end) {
|
|
while (dc && dc->lstart <= end) {
|
|
struct rb_node *node;
|
|
struct rb_node *node;
|
|
|
|
+ int err = 0;
|
|
|
|
|
|
if (dc->len < dpolicy->granularity)
|
|
if (dc->len < dpolicy->granularity)
|
|
goto skip;
|
|
goto skip;
|
|
@@ -2404,19 +2588,24 @@ next:
|
|
goto skip;
|
|
goto skip;
|
|
}
|
|
}
|
|
|
|
|
|
- __submit_discard_cmd(sbi, dpolicy, dc);
|
|
|
|
|
|
+ err = __submit_discard_cmd(sbi, dpolicy, dc, &issued);
|
|
|
|
|
|
- if (++issued >= dpolicy->max_requests) {
|
|
|
|
|
|
+ if (issued >= dpolicy->max_requests) {
|
|
start = dc->lstart + dc->len;
|
|
start = dc->lstart + dc->len;
|
|
|
|
|
|
|
|
+ if (err)
|
|
|
|
+ __remove_discard_cmd(sbi, dc);
|
|
|
|
+
|
|
blk_finish_plug(&plug);
|
|
blk_finish_plug(&plug);
|
|
mutex_unlock(&dcc->cmd_lock);
|
|
mutex_unlock(&dcc->cmd_lock);
|
|
- __wait_all_discard_cmd(sbi, NULL);
|
|
|
|
|
|
+ trimmed += __wait_all_discard_cmd(sbi, NULL);
|
|
congestion_wait(BLK_RW_ASYNC, HZ/50);
|
|
congestion_wait(BLK_RW_ASYNC, HZ/50);
|
|
goto next;
|
|
goto next;
|
|
}
|
|
}
|
|
skip:
|
|
skip:
|
|
node = rb_next(&dc->rb_node);
|
|
node = rb_next(&dc->rb_node);
|
|
|
|
+ if (err)
|
|
|
|
+ __remove_discard_cmd(sbi, dc);
|
|
dc = rb_entry_safe(node, struct discard_cmd, rb_node);
|
|
dc = rb_entry_safe(node, struct discard_cmd, rb_node);
|
|
|
|
|
|
if (fatal_signal_pending(current))
|
|
if (fatal_signal_pending(current))
|
|
@@ -2425,6 +2614,8 @@ skip:
|
|
|
|
|
|
blk_finish_plug(&plug);
|
|
blk_finish_plug(&plug);
|
|
mutex_unlock(&dcc->cmd_lock);
|
|
mutex_unlock(&dcc->cmd_lock);
|
|
|
|
+
|
|
|
|
+ return trimmed;
|
|
}
|
|
}
|
|
|
|
|
|
int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
|
|
int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
|
|
@@ -2437,12 +2628,13 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
|
|
struct discard_policy dpolicy;
|
|
struct discard_policy dpolicy;
|
|
unsigned long long trimmed = 0;
|
|
unsigned long long trimmed = 0;
|
|
int err = 0;
|
|
int err = 0;
|
|
|
|
+ bool need_align = test_opt(sbi, LFS) && sbi->segs_per_sec > 1;
|
|
|
|
|
|
if (start >= MAX_BLKADDR(sbi) || range->len < sbi->blocksize)
|
|
if (start >= MAX_BLKADDR(sbi) || range->len < sbi->blocksize)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
- if (end <= MAIN_BLKADDR(sbi))
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+ if (end < MAIN_BLKADDR(sbi))
|
|
|
|
+ goto out;
|
|
|
|
|
|
if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) {
|
|
if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) {
|
|
f2fs_msg(sbi->sb, KERN_WARNING,
|
|
f2fs_msg(sbi->sb, KERN_WARNING,
|
|
@@ -2454,6 +2646,10 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
|
|
start_segno = (start <= MAIN_BLKADDR(sbi)) ? 0 : GET_SEGNO(sbi, start);
|
|
start_segno = (start <= MAIN_BLKADDR(sbi)) ? 0 : GET_SEGNO(sbi, start);
|
|
end_segno = (end >= MAX_BLKADDR(sbi)) ? MAIN_SEGS(sbi) - 1 :
|
|
end_segno = (end >= MAX_BLKADDR(sbi)) ? MAIN_SEGS(sbi) - 1 :
|
|
GET_SEGNO(sbi, end);
|
|
GET_SEGNO(sbi, end);
|
|
|
|
+ if (need_align) {
|
|
|
|
+ start_segno = rounddown(start_segno, sbi->segs_per_sec);
|
|
|
|
+ end_segno = roundup(end_segno + 1, sbi->segs_per_sec) - 1;
|
|
|
|
+ }
|
|
|
|
|
|
cpc.reason = CP_DISCARD;
|
|
cpc.reason = CP_DISCARD;
|
|
cpc.trim_minlen = max_t(__u64, 1, F2FS_BYTES_TO_BLK(range->minlen));
|
|
cpc.trim_minlen = max_t(__u64, 1, F2FS_BYTES_TO_BLK(range->minlen));
|
|
@@ -2469,24 +2665,27 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
|
|
if (err)
|
|
if (err)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
- start_block = START_BLOCK(sbi, start_segno);
|
|
|
|
- end_block = START_BLOCK(sbi, end_segno + 1);
|
|
|
|
-
|
|
|
|
- __init_discard_policy(sbi, &dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen);
|
|
|
|
- __issue_discard_cmd_range(sbi, &dpolicy, start_block, end_block);
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* We filed discard candidates, but actually we don't need to wait for
|
|
* We filed discard candidates, but actually we don't need to wait for
|
|
* all of them, since they'll be issued in idle time along with runtime
|
|
* all of them, since they'll be issued in idle time along with runtime
|
|
* discard option. User configuration looks like using runtime discard
|
|
* discard option. User configuration looks like using runtime discard
|
|
* or periodic fstrim instead of it.
|
|
* or periodic fstrim instead of it.
|
|
*/
|
|
*/
|
|
- if (!test_opt(sbi, DISCARD)) {
|
|
|
|
- trimmed = __wait_discard_cmd_range(sbi, &dpolicy,
|
|
|
|
|
|
+ if (test_opt(sbi, DISCARD))
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ start_block = START_BLOCK(sbi, start_segno);
|
|
|
|
+ end_block = START_BLOCK(sbi, end_segno + 1);
|
|
|
|
+
|
|
|
|
+ __init_discard_policy(sbi, &dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen);
|
|
|
|
+ trimmed = __issue_discard_cmd_range(sbi, &dpolicy,
|
|
|
|
+ start_block, end_block);
|
|
|
|
+
|
|
|
|
+ trimmed += __wait_discard_cmd_range(sbi, &dpolicy,
|
|
start_block, end_block);
|
|
start_block, end_block);
|
|
- range->len = F2FS_BLK_TO_BYTES(trimmed);
|
|
|
|
- }
|
|
|
|
out:
|
|
out:
|
|
|
|
+ if (!err)
|
|
|
|
+ range->len = F2FS_BLK_TO_BYTES(trimmed);
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2639,8 +2838,8 @@ static int __get_segment_type_6(struct f2fs_io_info *fio)
|
|
return CURSEG_COLD_DATA;
|
|
return CURSEG_COLD_DATA;
|
|
if (file_is_hot(inode) ||
|
|
if (file_is_hot(inode) ||
|
|
is_inode_flag_set(inode, FI_HOT_DATA) ||
|
|
is_inode_flag_set(inode, FI_HOT_DATA) ||
|
|
- is_inode_flag_set(inode, FI_ATOMIC_FILE) ||
|
|
|
|
- is_inode_flag_set(inode, FI_VOLATILE_FILE))
|
|
|
|
|
|
+ f2fs_is_atomic_file(inode) ||
|
|
|
|
+ f2fs_is_volatile_file(inode))
|
|
return CURSEG_HOT_DATA;
|
|
return CURSEG_HOT_DATA;
|
|
return f2fs_rw_hint_to_seg_type(inode->i_write_hint);
|
|
return f2fs_rw_hint_to_seg_type(inode->i_write_hint);
|
|
} else {
|
|
} else {
|
|
@@ -2781,6 +2980,9 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio)
|
|
reallocate:
|
|
reallocate:
|
|
f2fs_allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr,
|
|
f2fs_allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr,
|
|
&fio->new_blkaddr, sum, type, fio, true);
|
|
&fio->new_blkaddr, sum, type, fio, true);
|
|
|
|
+ if (GET_SEGNO(fio->sbi, fio->old_blkaddr) != NULL_SEGNO)
|
|
|
|
+ invalidate_mapping_pages(META_MAPPING(fio->sbi),
|
|
|
|
+ fio->old_blkaddr, fio->old_blkaddr);
|
|
|
|
|
|
/* writeout dirty page into bdev */
|
|
/* writeout dirty page into bdev */
|
|
f2fs_submit_page_write(fio);
|
|
f2fs_submit_page_write(fio);
|
|
@@ -2836,11 +3038,9 @@ void f2fs_outplace_write_data(struct dnode_of_data *dn,
|
|
{
|
|
{
|
|
struct f2fs_sb_info *sbi = fio->sbi;
|
|
struct f2fs_sb_info *sbi = fio->sbi;
|
|
struct f2fs_summary sum;
|
|
struct f2fs_summary sum;
|
|
- struct node_info ni;
|
|
|
|
|
|
|
|
f2fs_bug_on(sbi, dn->data_blkaddr == NULL_ADDR);
|
|
f2fs_bug_on(sbi, dn->data_blkaddr == NULL_ADDR);
|
|
- f2fs_get_node_info(sbi, dn->nid, &ni);
|
|
|
|
- set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
|
|
|
|
|
|
+ set_summary(&sum, dn->nid, dn->ofs_in_node, fio->version);
|
|
do_write_page(&sum, fio);
|
|
do_write_page(&sum, fio);
|
|
f2fs_update_data_blkaddr(dn, fio->new_blkaddr);
|
|
f2fs_update_data_blkaddr(dn, fio->new_blkaddr);
|
|
|
|
|
|
@@ -2937,8 +3137,11 @@ void f2fs_do_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
|
|
|
|
|
if (!recover_curseg || recover_newaddr)
|
|
if (!recover_curseg || recover_newaddr)
|
|
update_sit_entry(sbi, new_blkaddr, 1);
|
|
update_sit_entry(sbi, new_blkaddr, 1);
|
|
- if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO)
|
|
|
|
|
|
+ if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) {
|
|
|
|
+ invalidate_mapping_pages(META_MAPPING(sbi),
|
|
|
|
+ old_blkaddr, old_blkaddr);
|
|
update_sit_entry(sbi, old_blkaddr, -1);
|
|
update_sit_entry(sbi, old_blkaddr, -1);
|
|
|
|
+ }
|
|
|
|
|
|
locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr));
|
|
locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr));
|
|
locate_dirty_segment(sbi, GET_SEGNO(sbi, new_blkaddr));
|
|
locate_dirty_segment(sbi, GET_SEGNO(sbi, new_blkaddr));
|
|
@@ -2992,7 +3195,7 @@ void f2fs_wait_on_block_writeback(struct f2fs_sb_info *sbi, block_t blkaddr)
|
|
{
|
|
{
|
|
struct page *cpage;
|
|
struct page *cpage;
|
|
|
|
|
|
- if (!is_valid_blkaddr(blkaddr))
|
|
|
|
|
|
+ if (!is_valid_data_blkaddr(sbi, blkaddr))
|
|
return;
|
|
return;
|
|
|
|
|
|
cpage = find_lock_page(META_MAPPING(sbi), blkaddr);
|
|
cpage = find_lock_page(META_MAPPING(sbi), blkaddr);
|
|
@@ -3002,7 +3205,7 @@ void f2fs_wait_on_block_writeback(struct f2fs_sb_info *sbi, block_t blkaddr)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-static void read_compacted_summaries(struct f2fs_sb_info *sbi)
|
|
|
|
|
|
+static int read_compacted_summaries(struct f2fs_sb_info *sbi)
|
|
{
|
|
{
|
|
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
|
|
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
|
|
struct curseg_info *seg_i;
|
|
struct curseg_info *seg_i;
|
|
@@ -3014,6 +3217,8 @@ static void read_compacted_summaries(struct f2fs_sb_info *sbi)
|
|
start = start_sum_block(sbi);
|
|
start = start_sum_block(sbi);
|
|
|
|
|
|
page = f2fs_get_meta_page(sbi, start++);
|
|
page = f2fs_get_meta_page(sbi, start++);
|
|
|
|
+ if (IS_ERR(page))
|
|
|
|
+ return PTR_ERR(page);
|
|
kaddr = (unsigned char *)page_address(page);
|
|
kaddr = (unsigned char *)page_address(page);
|
|
|
|
|
|
/* Step 1: restore nat cache */
|
|
/* Step 1: restore nat cache */
|
|
@@ -3054,11 +3259,14 @@ static void read_compacted_summaries(struct f2fs_sb_info *sbi)
|
|
page = NULL;
|
|
page = NULL;
|
|
|
|
|
|
page = f2fs_get_meta_page(sbi, start++);
|
|
page = f2fs_get_meta_page(sbi, start++);
|
|
|
|
+ if (IS_ERR(page))
|
|
|
|
+ return PTR_ERR(page);
|
|
kaddr = (unsigned char *)page_address(page);
|
|
kaddr = (unsigned char *)page_address(page);
|
|
offset = 0;
|
|
offset = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
f2fs_put_page(page, 1);
|
|
f2fs_put_page(page, 1);
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
|
|
static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
|
|
@@ -3070,6 +3278,7 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
|
|
unsigned short blk_off;
|
|
unsigned short blk_off;
|
|
unsigned int segno = 0;
|
|
unsigned int segno = 0;
|
|
block_t blk_addr = 0;
|
|
block_t blk_addr = 0;
|
|
|
|
+ int err = 0;
|
|
|
|
|
|
/* get segment number and block addr */
|
|
/* get segment number and block addr */
|
|
if (IS_DATASEG(type)) {
|
|
if (IS_DATASEG(type)) {
|
|
@@ -3093,6 +3302,8 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
|
|
}
|
|
}
|
|
|
|
|
|
new = f2fs_get_meta_page(sbi, blk_addr);
|
|
new = f2fs_get_meta_page(sbi, blk_addr);
|
|
|
|
+ if (IS_ERR(new))
|
|
|
|
+ return PTR_ERR(new);
|
|
sum = (struct f2fs_summary_block *)page_address(new);
|
|
sum = (struct f2fs_summary_block *)page_address(new);
|
|
|
|
|
|
if (IS_NODESEG(type)) {
|
|
if (IS_NODESEG(type)) {
|
|
@@ -3104,7 +3315,9 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
|
|
ns->ofs_in_node = 0;
|
|
ns->ofs_in_node = 0;
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
- f2fs_restore_node_summary(sbi, segno, sum);
|
|
|
|
|
|
+ err = f2fs_restore_node_summary(sbi, segno, sum);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -3124,8 +3337,9 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
|
|
curseg->alloc_type = ckpt->alloc_type[type];
|
|
curseg->alloc_type = ckpt->alloc_type[type];
|
|
curseg->next_blkoff = blk_off;
|
|
curseg->next_blkoff = blk_off;
|
|
mutex_unlock(&curseg->curseg_mutex);
|
|
mutex_unlock(&curseg->curseg_mutex);
|
|
|
|
+out:
|
|
f2fs_put_page(new, 1);
|
|
f2fs_put_page(new, 1);
|
|
- return 0;
|
|
|
|
|
|
+ return err;
|
|
}
|
|
}
|
|
|
|
|
|
static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
|
|
static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
|
|
@@ -3143,7 +3357,9 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
|
|
META_CP, true);
|
|
META_CP, true);
|
|
|
|
|
|
/* restore for compacted data summary */
|
|
/* restore for compacted data summary */
|
|
- read_compacted_summaries(sbi);
|
|
|
|
|
|
+ err = read_compacted_summaries(sbi);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
type = CURSEG_HOT_NODE;
|
|
type = CURSEG_HOT_NODE;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -3274,7 +3490,7 @@ int f2fs_lookup_journal_in_cursum(struct f2fs_journal *journal, int type,
|
|
static struct page *get_current_sit_page(struct f2fs_sb_info *sbi,
|
|
static struct page *get_current_sit_page(struct f2fs_sb_info *sbi,
|
|
unsigned int segno)
|
|
unsigned int segno)
|
|
{
|
|
{
|
|
- return f2fs_get_meta_page(sbi, current_sit_addr(sbi, segno));
|
|
|
|
|
|
+ return f2fs_get_meta_page_nofail(sbi, current_sit_addr(sbi, segno));
|
|
}
|
|
}
|
|
|
|
|
|
static struct page *get_next_sit_page(struct f2fs_sb_info *sbi,
|
|
static struct page *get_next_sit_page(struct f2fs_sb_info *sbi,
|
|
@@ -3923,6 +4139,7 @@ int f2fs_build_segment_manager(struct f2fs_sb_info *sbi)
|
|
sm_info->ipu_policy = 1 << F2FS_IPU_FSYNC;
|
|
sm_info->ipu_policy = 1 << F2FS_IPU_FSYNC;
|
|
sm_info->min_ipu_util = DEF_MIN_IPU_UTIL;
|
|
sm_info->min_ipu_util = DEF_MIN_IPU_UTIL;
|
|
sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS;
|
|
sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS;
|
|
|
|
+ sm_info->min_seq_blocks = sbi->blocks_per_seg * sbi->segs_per_sec;
|
|
sm_info->min_hot_blocks = DEF_MIN_HOT_BLOCKS;
|
|
sm_info->min_hot_blocks = DEF_MIN_HOT_BLOCKS;
|
|
sm_info->min_ssr_sections = reserved_sections(sbi);
|
|
sm_info->min_ssr_sections = reserved_sections(sbi);
|
|
|
|
|