|
@@ -136,6 +136,7 @@ enum {
|
|
|
Opt_alloc,
|
|
|
Opt_fsync,
|
|
|
Opt_test_dummy_encryption,
|
|
|
+ Opt_checkpoint,
|
|
|
Opt_err,
|
|
|
};
|
|
|
|
|
@@ -194,6 +195,7 @@ static match_table_t f2fs_tokens = {
|
|
|
{Opt_alloc, "alloc_mode=%s"},
|
|
|
{Opt_fsync, "fsync_mode=%s"},
|
|
|
{Opt_test_dummy_encryption, "test_dummy_encryption"},
|
|
|
+ {Opt_checkpoint, "checkpoint=%s"},
|
|
|
{Opt_err, NULL},
|
|
|
};
|
|
|
|
|
@@ -769,6 +771,23 @@ static int parse_options(struct super_block *sb, char *options)
|
|
|
"Test dummy encryption mount option ignored");
|
|
|
#endif
|
|
|
break;
|
|
|
+ case Opt_checkpoint:
|
|
|
+ name = match_strdup(&args[0]);
|
|
|
+ if (!name)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ if (strlen(name) == 6 &&
|
|
|
+ !strncmp(name, "enable", 6)) {
|
|
|
+ clear_opt(sbi, DISABLE_CHECKPOINT);
|
|
|
+ } else if (strlen(name) == 7 &&
|
|
|
+ !strncmp(name, "disable", 7)) {
|
|
|
+ set_opt(sbi, DISABLE_CHECKPOINT);
|
|
|
+ } else {
|
|
|
+ kfree(name);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ kfree(name);
|
|
|
+ break;
|
|
|
default:
|
|
|
f2fs_msg(sb, KERN_ERR,
|
|
|
"Unrecognized mount option \"%s\" or missing value",
|
|
@@ -827,6 +846,12 @@ static int parse_options(struct super_block *sb, char *options)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (test_opt(sbi, DISABLE_CHECKPOINT) && test_opt(sbi, LFS)) {
|
|
|
+ f2fs_msg(sb, KERN_ERR,
|
|
|
+ "LFS not compatible with checkpoint=disable\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
/* Not pass down write hints if the number of active logs is lesser
|
|
|
* than NR_CURSEG_TYPE.
|
|
|
*/
|
|
@@ -1014,8 +1039,8 @@ static void f2fs_put_super(struct super_block *sb)
|
|
|
* But, the previous checkpoint was not done by umount, it needs to do
|
|
|
* clean checkpoint again.
|
|
|
*/
|
|
|
- if (is_sbi_flag_set(sbi, SBI_IS_DIRTY) ||
|
|
|
- !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) {
|
|
|
+ if ((is_sbi_flag_set(sbi, SBI_IS_DIRTY) ||
|
|
|
+ !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG))) {
|
|
|
struct cp_control cpc = {
|
|
|
.reason = CP_UMOUNT,
|
|
|
};
|
|
@@ -1087,6 +1112,8 @@ int f2fs_sync_fs(struct super_block *sb, int sync)
|
|
|
|
|
|
if (unlikely(f2fs_cp_error(sbi)))
|
|
|
return 0;
|
|
|
+ if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
|
|
|
+ return 0;
|
|
|
|
|
|
trace_f2fs_sync_fs(sb, sync);
|
|
|
|
|
@@ -1186,6 +1213,11 @@ static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|
|
buf->f_blocks = total_count - start_count;
|
|
|
buf->f_bfree = user_block_count - valid_user_blocks(sbi) -
|
|
|
sbi->current_reserved_blocks;
|
|
|
+ if (unlikely(buf->f_bfree <= sbi->unusable_block_count))
|
|
|
+ buf->f_bfree = 0;
|
|
|
+ else
|
|
|
+ buf->f_bfree -= sbi->unusable_block_count;
|
|
|
+
|
|
|
if (buf->f_bfree > F2FS_OPTION(sbi).root_reserved_blocks)
|
|
|
buf->f_bavail = buf->f_bfree -
|
|
|
F2FS_OPTION(sbi).root_reserved_blocks;
|
|
@@ -1365,6 +1397,9 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
|
|
|
else if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_REUSE)
|
|
|
seq_printf(seq, ",alloc_mode=%s", "reuse");
|
|
|
|
|
|
+ if (test_opt(sbi, DISABLE_CHECKPOINT))
|
|
|
+ seq_puts(seq, ",checkpoint=disable");
|
|
|
+
|
|
|
if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_POSIX)
|
|
|
seq_printf(seq, ",fsync_mode=%s", "posix");
|
|
|
else if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT)
|
|
@@ -1392,6 +1427,7 @@ static void default_options(struct f2fs_sb_info *sbi)
|
|
|
set_opt(sbi, INLINE_DENTRY);
|
|
|
set_opt(sbi, EXTENT_CACHE);
|
|
|
set_opt(sbi, NOHEAP);
|
|
|
+ clear_opt(sbi, DISABLE_CHECKPOINT);
|
|
|
sbi->sb->s_flags |= SB_LAZYTIME;
|
|
|
set_opt(sbi, FLUSH_MERGE);
|
|
|
set_opt(sbi, DISCARD);
|
|
@@ -1413,6 +1449,57 @@ static void default_options(struct f2fs_sb_info *sbi)
|
|
|
#ifdef CONFIG_QUOTA
|
|
|
static int f2fs_enable_quotas(struct super_block *sb);
|
|
|
#endif
|
|
|
+
|
|
|
+static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
|
|
|
+{
|
|
|
+ struct cp_control cpc;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ sbi->sb->s_flags |= SB_ACTIVE;
|
|
|
+
|
|
|
+ mutex_lock(&sbi->gc_mutex);
|
|
|
+ f2fs_update_time(sbi, DISABLE_TIME);
|
|
|
+
|
|
|
+ while (!f2fs_time_over(sbi, DISABLE_TIME)) {
|
|
|
+ err = f2fs_gc(sbi, true, false, NULL_SEGNO);
|
|
|
+ if (err == -ENODATA)
|
|
|
+ break;
|
|
|
+ if (err && err != -EAGAIN) {
|
|
|
+ mutex_unlock(&sbi->gc_mutex);
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ mutex_unlock(&sbi->gc_mutex);
|
|
|
+
|
|
|
+ err = sync_filesystem(sbi->sb);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (f2fs_disable_cp_again(sbi))
|
|
|
+ return -EAGAIN;
|
|
|
+
|
|
|
+ mutex_lock(&sbi->gc_mutex);
|
|
|
+ cpc.reason = CP_PAUSE;
|
|
|
+ set_sbi_flag(sbi, SBI_CP_DISABLED);
|
|
|
+ f2fs_write_checkpoint(sbi, &cpc);
|
|
|
+
|
|
|
+ sbi->unusable_block_count = 0;
|
|
|
+ mutex_unlock(&sbi->gc_mutex);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void f2fs_enable_checkpoint(struct f2fs_sb_info *sbi)
|
|
|
+{
|
|
|
+ mutex_lock(&sbi->gc_mutex);
|
|
|
+ f2fs_dirty_to_prefree(sbi);
|
|
|
+
|
|
|
+ clear_sbi_flag(sbi, SBI_CP_DISABLED);
|
|
|
+ set_sbi_flag(sbi, SBI_IS_DIRTY);
|
|
|
+ mutex_unlock(&sbi->gc_mutex);
|
|
|
+
|
|
|
+ f2fs_sync_fs(sbi->sb, 1);
|
|
|
+}
|
|
|
+
|
|
|
static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|
|
{
|
|
|
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
|
@@ -1422,6 +1509,8 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|
|
bool need_restart_gc = false;
|
|
|
bool need_stop_gc = false;
|
|
|
bool no_extent_cache = !test_opt(sbi, EXTENT_CACHE);
|
|
|
+ bool disable_checkpoint = test_opt(sbi, DISABLE_CHECKPOINT);
|
|
|
+ bool checkpoint_changed;
|
|
|
#ifdef CONFIG_QUOTA
|
|
|
int i, j;
|
|
|
#endif
|
|
@@ -1466,6 +1555,8 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|
|
err = parse_options(sb, data);
|
|
|
if (err)
|
|
|
goto restore_opts;
|
|
|
+ checkpoint_changed =
|
|
|
+ disable_checkpoint != test_opt(sbi, DISABLE_CHECKPOINT);
|
|
|
|
|
|
/*
|
|
|
* Previous and new state of filesystem is RO,
|
|
@@ -1479,7 +1570,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|
|
err = dquot_suspend(sb, -1);
|
|
|
if (err < 0)
|
|
|
goto restore_opts;
|
|
|
- } else if (f2fs_readonly(sb) && !(*flags & MS_RDONLY)) {
|
|
|
+ } else if (f2fs_readonly(sb) && !(*flags & SB_RDONLY)) {
|
|
|
/* dquot_resume needs RW */
|
|
|
sb->s_flags &= ~SB_RDONLY;
|
|
|
if (sb_any_quota_suspended(sb)) {
|
|
@@ -1499,6 +1590,13 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|
|
goto restore_opts;
|
|
|
}
|
|
|
|
|
|
+ if ((*flags & SB_RDONLY) && test_opt(sbi, DISABLE_CHECKPOINT)) {
|
|
|
+ err = -EINVAL;
|
|
|
+ f2fs_msg(sbi->sb, KERN_WARNING,
|
|
|
+ "disabling checkpoint not compatible with read-only");
|
|
|
+ goto restore_opts;
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* We stop the GC thread if FS is mounted as RO
|
|
|
* or if background_gc = off is passed in mount
|
|
@@ -1527,6 +1625,16 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
|
|
clear_sbi_flag(sbi, SBI_IS_CLOSE);
|
|
|
}
|
|
|
|
|
|
+ if (checkpoint_changed) {
|
|
|
+ if (test_opt(sbi, DISABLE_CHECKPOINT)) {
|
|
|
+ err = f2fs_disable_checkpoint(sbi);
|
|
|
+ if (err)
|
|
|
+ goto restore_gc;
|
|
|
+ } else {
|
|
|
+ f2fs_enable_checkpoint(sbi);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* We stop issue flush thread if FS is mounted as RO
|
|
|
* or if flush_merge is not passed in mount option.
|
|
@@ -2485,6 +2593,7 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
|
|
|
sbi->interval_time[REQ_TIME] = DEF_IDLE_INTERVAL;
|
|
|
sbi->interval_time[DISCARD_TIME] = DEF_IDLE_INTERVAL;
|
|
|
sbi->interval_time[GC_TIME] = DEF_IDLE_INTERVAL;
|
|
|
+ sbi->interval_time[DISABLE_TIME] = DEF_DISABLE_INTERVAL;
|
|
|
clear_sbi_flag(sbi, SBI_NEED_FSCK);
|
|
|
|
|
|
for (i = 0; i < NR_COUNT_TYPE; i++)
|
|
@@ -3093,6 +3202,9 @@ try_onemore:
|
|
|
if (err)
|
|
|
goto free_meta;
|
|
|
|
|
|
+ if (unlikely(is_set_ckpt_flags(sbi, CP_DISABLED_FLAG)))
|
|
|
+ goto skip_recovery;
|
|
|
+
|
|
|
/* recover fsynced data */
|
|
|
if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) {
|
|
|
/*
|
|
@@ -3132,6 +3244,14 @@ skip_recovery:
|
|
|
/* f2fs_recover_fsync_data() cleared this already */
|
|
|
clear_sbi_flag(sbi, SBI_POR_DOING);
|
|
|
|
|
|
+ if (test_opt(sbi, DISABLE_CHECKPOINT)) {
|
|
|
+ err = f2fs_disable_checkpoint(sbi);
|
|
|
+ if (err)
|
|
|
+ goto free_meta;
|
|
|
+ } else if (is_set_ckpt_flags(sbi, CP_DISABLED_FLAG)) {
|
|
|
+ f2fs_enable_checkpoint(sbi);
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* If filesystem is not mounted as read-only then
|
|
|
* do start the gc_thread.
|