aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJaegeuk Kim <jaegeuk@kernel.org>2016-12-07 19:23:32 -0500
committerJaegeuk Kim <jaegeuk@kernel.org>2016-12-07 21:56:50 -0500
commit5eba8c5d1fb3af28b2073ba5228d4998196c1bcc (patch)
treec873248bed74d949213de8a3c42438b2b8dfef14
parenta2125ff7dd1ed3a2a53cdc1f8f9c9cec9cfaa7ab (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.h2
-rw-r--r--fs/f2fs/segment.c33
-rw-r--r--fs/f2fs/super.c5
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);
2103void f2fs_balance_fs_bg(struct f2fs_sb_info *); 2103void f2fs_balance_fs_bg(struct f2fs_sb_info *);
2104int f2fs_issue_flush(struct f2fs_sb_info *); 2104int f2fs_issue_flush(struct f2fs_sb_info *);
2105int create_flush_cmd_control(struct f2fs_sb_info *); 2105int create_flush_cmd_control(struct f2fs_sb_info *);
2106void destroy_flush_cmd_control(struct f2fs_sb_info *); 2106void destroy_flush_cmd_control(struct f2fs_sb_info *, bool);
2107void invalidate_blocks(struct f2fs_sb_info *, block_t); 2107void invalidate_blocks(struct f2fs_sb_info *, block_t);
2108bool is_checkpointed_data(struct f2fs_sb_info *, block_t); 2108bool is_checkpointed_data(struct f2fs_sb_info *, block_t);
2109void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t); 2109void 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;
521init_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
523void destroy_flush_cmd_control(struct f2fs_sb_info *sbi) 534void 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
533static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, 550static 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;