diff options
author | Jaegeuk Kim <jaegeuk@kernel.org> | 2016-12-07 19:23:32 -0500 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk@kernel.org> | 2016-12-07 21:56:50 -0500 |
commit | 5eba8c5d1fb3af28b2073ba5228d4998196c1bcc (patch) | |
tree | c873248bed74d949213de8a3c42438b2b8dfef14 | |
parent | a2125ff7dd1ed3a2a53cdc1f8f9c9cec9cfaa7ab (diff) |
f2fs: fix to access nullified flush_cmd_control pointer
f2fs_sync_file() remount_ro
- f2fs_readonly
- destroy_flush_cmd_control
- f2fs_issue_flush
- no fcc pointer!
So, this patch doesn't free fcc in this case, but just stop its kernel thread
which sends flush commands.
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
-rw-r--r-- | fs/f2fs/f2fs.h | 2 | ||||
-rw-r--r-- | fs/f2fs/segment.c | 33 | ||||
-rw-r--r-- | fs/f2fs/super.c | 5 |
3 files changed, 29 insertions, 11 deletions
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 35dbab157ec3..137bbf0ae4c6 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h | |||
@@ -2103,7 +2103,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *, bool); | |||
2103 | void f2fs_balance_fs_bg(struct f2fs_sb_info *); | 2103 | void f2fs_balance_fs_bg(struct f2fs_sb_info *); |
2104 | int f2fs_issue_flush(struct f2fs_sb_info *); | 2104 | int f2fs_issue_flush(struct f2fs_sb_info *); |
2105 | int create_flush_cmd_control(struct f2fs_sb_info *); | 2105 | int create_flush_cmd_control(struct f2fs_sb_info *); |
2106 | void destroy_flush_cmd_control(struct f2fs_sb_info *); | 2106 | void destroy_flush_cmd_control(struct f2fs_sb_info *, bool); |
2107 | void invalidate_blocks(struct f2fs_sb_info *, block_t); | 2107 | void invalidate_blocks(struct f2fs_sb_info *, block_t); |
2108 | bool is_checkpointed_data(struct f2fs_sb_info *, block_t); | 2108 | bool is_checkpointed_data(struct f2fs_sb_info *, block_t); |
2109 | void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t); | 2109 | void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t); |
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 8affc5621181..d7d5727fe8b2 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c | |||
@@ -489,8 +489,13 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi) | |||
489 | if (!fcc->dispatch_list) | 489 | if (!fcc->dispatch_list) |
490 | wake_up(&fcc->flush_wait_queue); | 490 | wake_up(&fcc->flush_wait_queue); |
491 | 491 | ||
492 | wait_for_completion(&cmd.wait); | 492 | if (fcc->f2fs_issue_flush) { |
493 | atomic_dec(&fcc->submit_flush); | 493 | wait_for_completion(&cmd.wait); |
494 | atomic_dec(&fcc->submit_flush); | ||
495 | } else { | ||
496 | llist_del_all(&fcc->issue_list); | ||
497 | atomic_set(&fcc->submit_flush, 0); | ||
498 | } | ||
494 | 499 | ||
495 | return cmd.ret; | 500 | return cmd.ret; |
496 | } | 501 | } |
@@ -501,6 +506,11 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi) | |||
501 | struct flush_cmd_control *fcc; | 506 | struct flush_cmd_control *fcc; |
502 | int err = 0; | 507 | int err = 0; |
503 | 508 | ||
509 | if (SM_I(sbi)->cmd_control_info) { | ||
510 | fcc = SM_I(sbi)->cmd_control_info; | ||
511 | goto init_thread; | ||
512 | } | ||
513 | |||
504 | fcc = kzalloc(sizeof(struct flush_cmd_control), GFP_KERNEL); | 514 | fcc = kzalloc(sizeof(struct flush_cmd_control), GFP_KERNEL); |
505 | if (!fcc) | 515 | if (!fcc) |
506 | return -ENOMEM; | 516 | return -ENOMEM; |
@@ -508,6 +518,7 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi) | |||
508 | init_waitqueue_head(&fcc->flush_wait_queue); | 518 | init_waitqueue_head(&fcc->flush_wait_queue); |
509 | init_llist_head(&fcc->issue_list); | 519 | init_llist_head(&fcc->issue_list); |
510 | SM_I(sbi)->cmd_control_info = fcc; | 520 | SM_I(sbi)->cmd_control_info = fcc; |
521 | init_thread: | ||
511 | fcc->f2fs_issue_flush = kthread_run(issue_flush_thread, sbi, | 522 | fcc->f2fs_issue_flush = kthread_run(issue_flush_thread, sbi, |
512 | "f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev)); | 523 | "f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev)); |
513 | if (IS_ERR(fcc->f2fs_issue_flush)) { | 524 | if (IS_ERR(fcc->f2fs_issue_flush)) { |
@@ -520,14 +531,20 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi) | |||
520 | return err; | 531 | return err; |
521 | } | 532 | } |
522 | 533 | ||
523 | void destroy_flush_cmd_control(struct f2fs_sb_info *sbi) | 534 | void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free) |
524 | { | 535 | { |
525 | struct flush_cmd_control *fcc = SM_I(sbi)->cmd_control_info; | 536 | struct flush_cmd_control *fcc = SM_I(sbi)->cmd_control_info; |
526 | 537 | ||
527 | if (fcc && fcc->f2fs_issue_flush) | 538 | if (fcc && fcc->f2fs_issue_flush) { |
528 | kthread_stop(fcc->f2fs_issue_flush); | 539 | struct task_struct *flush_thread = fcc->f2fs_issue_flush; |
529 | kfree(fcc); | 540 | |
530 | SM_I(sbi)->cmd_control_info = NULL; | 541 | fcc->f2fs_issue_flush = NULL; |
542 | kthread_stop(flush_thread); | ||
543 | } | ||
544 | if (free) { | ||
545 | kfree(fcc); | ||
546 | SM_I(sbi)->cmd_control_info = NULL; | ||
547 | } | ||
531 | } | 548 | } |
532 | 549 | ||
533 | static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, | 550 | static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, |
@@ -2738,7 +2755,7 @@ void destroy_segment_manager(struct f2fs_sb_info *sbi) | |||
2738 | 2755 | ||
2739 | if (!sm_info) | 2756 | if (!sm_info) |
2740 | return; | 2757 | return; |
2741 | destroy_flush_cmd_control(sbi); | 2758 | destroy_flush_cmd_control(sbi, true); |
2742 | destroy_dirty_segmap(sbi); | 2759 | destroy_dirty_segmap(sbi); |
2743 | destroy_curseg(sbi); | 2760 | destroy_curseg(sbi); |
2744 | destroy_free_segmap(sbi); | 2761 | destroy_free_segmap(sbi); |
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 1a526474b332..b62c10d28e06 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c | |||
@@ -1102,8 +1102,9 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) | |||
1102 | * or if flush_merge is not passed in mount option. | 1102 | * or if flush_merge is not passed in mount option. |
1103 | */ | 1103 | */ |
1104 | if ((*flags & MS_RDONLY) || !test_opt(sbi, FLUSH_MERGE)) { | 1104 | if ((*flags & MS_RDONLY) || !test_opt(sbi, FLUSH_MERGE)) { |
1105 | destroy_flush_cmd_control(sbi); | 1105 | clear_opt(sbi, FLUSH_MERGE); |
1106 | } else if (!SM_I(sbi)->cmd_control_info) { | 1106 | destroy_flush_cmd_control(sbi, false); |
1107 | } else { | ||
1107 | err = create_flush_cmd_control(sbi); | 1108 | err = create_flush_cmd_control(sbi); |
1108 | if (err) | 1109 | if (err) |
1109 | goto restore_gc; | 1110 | goto restore_gc; |