Commit 4d3aed70 authored by Daniel Rosenberg's avatar Daniel Rosenberg Committed by Jaegeuk Kim

f2fs: Add option to limit required GC for checkpoint=disable

This extends the checkpoint option to allow checkpoint=disable:%u[%]
This allows you to specify what how much of the disk you are willing
to lose access to while mounting with checkpoint=disable. If the amount
lost would be higher, the mount will return -EAGAIN. This can be given
as a percent of total space, or in blocks.

Currently, we need to run garbage collection until the amount of holes
is smaller than the OVP space. With the new option, f2fs can mark
space as unusable up front instead of requiring garbage collection until
the number of holes is small enough.
Signed-off-by: default avatarDaniel Rosenberg <drosen@google.com>
Reviewed-by: default avatarChao Yu <yuchao0@huawei.com>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent a4c3ecaa
...@@ -243,3 +243,11 @@ Description: ...@@ -243,3 +243,11 @@ Description:
- Del: echo '[h/c]!extension' > /sys/fs/f2fs/<disk>/extension_list - Del: echo '[h/c]!extension' > /sys/fs/f2fs/<disk>/extension_list
- [h] means add/del hot file extension - [h] means add/del hot file extension
- [c] means add/del cold file extension - [c] means add/del cold file extension
What: /sys/fs/f2fs/<disk>/unusable
Date April 2019
Contact: "Daniel Rosenberg" <drosen@google.com>
Description:
If checkpoint=disable, it displays the number of blocks that are unusable.
If checkpoint=enable it displays the enumber of blocks that would be unusable
if checkpoint=disable were to be set.
...@@ -214,11 +214,22 @@ fsync_mode=%s Control the policy of fsync. Currently supports "posix", ...@@ -214,11 +214,22 @@ fsync_mode=%s Control the policy of fsync. Currently supports "posix",
non-atomic files likewise "nobarrier" mount option. non-atomic files likewise "nobarrier" mount option.
test_dummy_encryption Enable dummy encryption, which provides a fake fscrypt test_dummy_encryption Enable dummy encryption, which provides a fake fscrypt
context. The fake fscrypt context is used by xfstests. context. The fake fscrypt context is used by xfstests.
checkpoint=%s Set to "disable" to turn off checkpointing. Set to "enable" checkpoint=%s[:%u[%]] Set to "disable" to turn off checkpointing. Set to "enable"
to reenable checkpointing. Is enabled by default. While to reenable checkpointing. Is enabled by default. While
disabled, any unmounting or unexpected shutdowns will cause disabled, any unmounting or unexpected shutdowns will cause
the filesystem contents to appear as they did when the the filesystem contents to appear as they did when the
filesystem was mounted with that option. filesystem was mounted with that option.
While mounting with checkpoint=disabled, the filesystem must
run garbage collection to ensure that all available space can
be used. If this takes too much time, the mount may return
EAGAIN. You may optionally add a value to indicate how much
of the disk you would be willing to temporarily give up to
avoid additional garbage collection. This can be given as a
number of blocks, or as a percent. For instance, mounting
with checkpoint=disable:100% would always succeed, but it may
hide up to all remaining free space. The actual space that
would be unusable can be viewed at /sys/fs/f2fs/<disk>/unusable
This space is reclaimed once checkpoint=enable.
================================================================================ ================================================================================
DEBUGFS ENTRIES DEBUGFS ENTRIES
...@@ -396,6 +407,12 @@ Files in /sys/fs/f2fs/<devname> ...@@ -396,6 +407,12 @@ Files in /sys/fs/f2fs/<devname>
current_reserved_blocks This shows # of blocks currently reserved. current_reserved_blocks This shows # of blocks currently reserved.
unusable If checkpoint=disable, this shows the number of
blocks that are unusable.
If checkpoint=enable it shows the number of blocks
that would be unusable if checkpoint=disable were
to be set.
================================================================================ ================================================================================
USAGE USAGE
================================================================================ ================================================================================
......
...@@ -136,6 +136,9 @@ struct f2fs_mount_info { ...@@ -136,6 +136,9 @@ struct f2fs_mount_info {
int alloc_mode; /* segment allocation policy */ int alloc_mode; /* segment allocation policy */
int fsync_mode; /* fsync policy */ int fsync_mode; /* fsync policy */
bool test_dummy_encryption; /* test dummy encryption */ bool test_dummy_encryption; /* test dummy encryption */
block_t unusable_cap; /* Amount of space allowed to be
* unusable when disabling checkpoint
*/
}; };
#define F2FS_FEATURE_ENCRYPT 0x0001 #define F2FS_FEATURE_ENCRYPT 0x0001
...@@ -3085,7 +3088,8 @@ bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi); ...@@ -3085,7 +3088,8 @@ bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi);
void f2fs_clear_prefree_segments(struct f2fs_sb_info *sbi, void f2fs_clear_prefree_segments(struct f2fs_sb_info *sbi,
struct cp_control *cpc); struct cp_control *cpc);
void f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi); void f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi);
int f2fs_disable_cp_again(struct f2fs_sb_info *sbi); block_t f2fs_get_unusable_blocks(struct f2fs_sb_info *sbi);
int f2fs_disable_cp_again(struct f2fs_sb_info *sbi, block_t unusable);
void f2fs_release_discard_addrs(struct f2fs_sb_info *sbi); void f2fs_release_discard_addrs(struct f2fs_sb_info *sbi);
int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra); int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra);
void f2fs_allocate_new_segments(struct f2fs_sb_info *sbi); void f2fs_allocate_new_segments(struct f2fs_sb_info *sbi);
......
...@@ -873,13 +873,14 @@ void f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi) ...@@ -873,13 +873,14 @@ void f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi)
mutex_unlock(&dirty_i->seglist_lock); mutex_unlock(&dirty_i->seglist_lock);
} }
int f2fs_disable_cp_again(struct f2fs_sb_info *sbi) block_t f2fs_get_unusable_blocks(struct f2fs_sb_info *sbi)
{ {
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
int ovp_hole_segs = int ovp_hole_segs =
(overprovision_segments(sbi) - reserved_segments(sbi)); (overprovision_segments(sbi) - reserved_segments(sbi));
block_t ovp_holes = ovp_hole_segs << sbi->log_blocks_per_seg; block_t ovp_holes = ovp_hole_segs << sbi->log_blocks_per_seg;
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
block_t holes[2] = {0, 0}; /* DATA and NODE */ block_t holes[2] = {0, 0}; /* DATA and NODE */
block_t unusable;
struct seg_entry *se; struct seg_entry *se;
unsigned int segno; unsigned int segno;
...@@ -893,7 +894,17 @@ int f2fs_disable_cp_again(struct f2fs_sb_info *sbi) ...@@ -893,7 +894,17 @@ int f2fs_disable_cp_again(struct f2fs_sb_info *sbi)
} }
mutex_unlock(&dirty_i->seglist_lock); mutex_unlock(&dirty_i->seglist_lock);
if (holes[DATA] > ovp_holes || holes[NODE] > ovp_holes) unusable = holes[DATA] > holes[NODE] ? holes[DATA] : holes[NODE];
if (unusable > ovp_holes)
return unusable - ovp_holes;
return 0;
}
int f2fs_disable_cp_again(struct f2fs_sb_info *sbi, block_t unusable)
{
int ovp_hole_segs =
(overprovision_segments(sbi) - reserved_segments(sbi));
if (unusable > F2FS_OPTION(sbi).unusable_cap)
return -EAGAIN; return -EAGAIN;
if (is_sbi_flag_set(sbi, SBI_CP_DISABLED_QUICK) && if (is_sbi_flag_set(sbi, SBI_CP_DISABLED_QUICK) &&
dirty_segments(sbi) > ovp_hole_segs) dirty_segments(sbi) > ovp_hole_segs)
......
...@@ -136,7 +136,10 @@ enum { ...@@ -136,7 +136,10 @@ enum {
Opt_alloc, Opt_alloc,
Opt_fsync, Opt_fsync,
Opt_test_dummy_encryption, Opt_test_dummy_encryption,
Opt_checkpoint, Opt_checkpoint_disable,
Opt_checkpoint_disable_cap,
Opt_checkpoint_disable_cap_perc,
Opt_checkpoint_enable,
Opt_err, Opt_err,
}; };
...@@ -195,7 +198,10 @@ static match_table_t f2fs_tokens = { ...@@ -195,7 +198,10 @@ static match_table_t f2fs_tokens = {
{Opt_alloc, "alloc_mode=%s"}, {Opt_alloc, "alloc_mode=%s"},
{Opt_fsync, "fsync_mode=%s"}, {Opt_fsync, "fsync_mode=%s"},
{Opt_test_dummy_encryption, "test_dummy_encryption"}, {Opt_test_dummy_encryption, "test_dummy_encryption"},
{Opt_checkpoint, "checkpoint=%s"}, {Opt_checkpoint_disable, "checkpoint=disable"},
{Opt_checkpoint_disable_cap, "checkpoint=disable:%u"},
{Opt_checkpoint_disable_cap_perc, "checkpoint=disable:%u%%"},
{Opt_checkpoint_enable, "checkpoint=enable"},
{Opt_err, NULL}, {Opt_err, NULL},
}; };
...@@ -772,22 +778,30 @@ static int parse_options(struct super_block *sb, char *options) ...@@ -772,22 +778,30 @@ static int parse_options(struct super_block *sb, char *options)
"Test dummy encryption mount option ignored"); "Test dummy encryption mount option ignored");
#endif #endif
break; break;
case Opt_checkpoint: case Opt_checkpoint_disable_cap_perc:
name = match_strdup(&args[0]); if (args->from && match_int(args, &arg))
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 {
kvfree(name);
return -EINVAL; return -EINVAL;
} if (arg < 0 || arg > 100)
kvfree(name); return -EINVAL;
if (arg == 100)
F2FS_OPTION(sbi).unusable_cap =
sbi->user_block_count;
else
F2FS_OPTION(sbi).unusable_cap =
(sbi->user_block_count / 100) * arg;
set_opt(sbi, DISABLE_CHECKPOINT);
break;
case Opt_checkpoint_disable_cap:
if (args->from && match_int(args, &arg))
return -EINVAL;
F2FS_OPTION(sbi).unusable_cap = arg;
set_opt(sbi, DISABLE_CHECKPOINT);
break;
case Opt_checkpoint_disable:
set_opt(sbi, DISABLE_CHECKPOINT);
break;
case Opt_checkpoint_enable:
clear_opt(sbi, DISABLE_CHECKPOINT);
break; break;
default: default:
f2fs_msg(sb, KERN_ERR, f2fs_msg(sb, KERN_ERR,
...@@ -1412,8 +1426,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) ...@@ -1412,8 +1426,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
seq_printf(seq, ",alloc_mode=%s", "reuse"); seq_printf(seq, ",alloc_mode=%s", "reuse");
if (test_opt(sbi, DISABLE_CHECKPOINT)) if (test_opt(sbi, DISABLE_CHECKPOINT))
seq_puts(seq, ",checkpoint=disable"); seq_printf(seq, ",checkpoint=disable:%u",
F2FS_OPTION(sbi).unusable_cap);
if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_POSIX) if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_POSIX)
seq_printf(seq, ",fsync_mode=%s", "posix"); seq_printf(seq, ",fsync_mode=%s", "posix");
else if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT) else if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT)
...@@ -1442,6 +1456,7 @@ static void default_options(struct f2fs_sb_info *sbi) ...@@ -1442,6 +1456,7 @@ static void default_options(struct f2fs_sb_info *sbi)
set_opt(sbi, EXTENT_CACHE); set_opt(sbi, EXTENT_CACHE);
set_opt(sbi, NOHEAP); set_opt(sbi, NOHEAP);
clear_opt(sbi, DISABLE_CHECKPOINT); clear_opt(sbi, DISABLE_CHECKPOINT);
F2FS_OPTION(sbi).unusable_cap = 0;
sbi->sb->s_flags |= SB_LAZYTIME; sbi->sb->s_flags |= SB_LAZYTIME;
set_opt(sbi, FLUSH_MERGE); set_opt(sbi, FLUSH_MERGE);
set_opt(sbi, DISCARD); set_opt(sbi, DISCARD);
...@@ -1470,6 +1485,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) ...@@ -1470,6 +1485,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
struct cp_control cpc; struct cp_control cpc;
int err = 0; int err = 0;
int ret; int ret;
block_t unusable;
if (s_flags & SB_RDONLY) { if (s_flags & SB_RDONLY) {
f2fs_msg(sbi->sb, KERN_ERR, f2fs_msg(sbi->sb, KERN_ERR,
...@@ -1497,7 +1513,8 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) ...@@ -1497,7 +1513,8 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
goto restore_flag; goto restore_flag;
} }
if (f2fs_disable_cp_again(sbi)) { unusable = f2fs_get_unusable_blocks(sbi);
if (f2fs_disable_cp_again(sbi, unusable)) {
err = -EAGAIN; err = -EAGAIN;
goto restore_flag; goto restore_flag;
} }
...@@ -1510,7 +1527,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) ...@@ -1510,7 +1527,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
goto out_unlock; goto out_unlock;
spin_lock(&sbi->stat_lock); spin_lock(&sbi->stat_lock);
sbi->unusable_block_count = 0; sbi->unusable_block_count = unusable;
spin_unlock(&sbi->stat_lock); spin_unlock(&sbi->stat_lock);
out_unlock: out_unlock:
......
...@@ -68,6 +68,20 @@ static ssize_t dirty_segments_show(struct f2fs_attr *a, ...@@ -68,6 +68,20 @@ static ssize_t dirty_segments_show(struct f2fs_attr *a,
(unsigned long long)(dirty_segments(sbi))); (unsigned long long)(dirty_segments(sbi)));
} }
static ssize_t unusable_show(struct f2fs_attr *a,
struct f2fs_sb_info *sbi, char *buf)
{
block_t unusable;
if (test_opt(sbi, DISABLE_CHECKPOINT))
unusable = sbi->unusable_block_count;
else
unusable = f2fs_get_unusable_blocks(sbi);
return snprintf(buf, PAGE_SIZE, "%llu\n",
(unsigned long long)unusable);
}
static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a, static ssize_t lifetime_write_kbytes_show(struct f2fs_attr *a,
struct f2fs_sb_info *sbi, char *buf) struct f2fs_sb_info *sbi, char *buf)
{ {
...@@ -440,6 +454,7 @@ F2FS_GENERAL_RO_ATTR(dirty_segments); ...@@ -440,6 +454,7 @@ F2FS_GENERAL_RO_ATTR(dirty_segments);
F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes); F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes);
F2FS_GENERAL_RO_ATTR(features); F2FS_GENERAL_RO_ATTR(features);
F2FS_GENERAL_RO_ATTR(current_reserved_blocks); F2FS_GENERAL_RO_ATTR(current_reserved_blocks);
F2FS_GENERAL_RO_ATTR(unusable);
#ifdef CONFIG_FS_ENCRYPTION #ifdef CONFIG_FS_ENCRYPTION
F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO); F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO);
...@@ -495,6 +510,7 @@ static struct attribute *f2fs_attrs[] = { ...@@ -495,6 +510,7 @@ static struct attribute *f2fs_attrs[] = {
ATTR_LIST(inject_type), ATTR_LIST(inject_type),
#endif #endif
ATTR_LIST(dirty_segments), ATTR_LIST(dirty_segments),
ATTR_LIST(unusable),
ATTR_LIST(lifetime_write_kbytes), ATTR_LIST(lifetime_write_kbytes),
ATTR_LIST(features), ATTR_LIST(features),
ATTR_LIST(reserved_blocks), ATTR_LIST(reserved_blocks),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment