The first patch adjusts the default allowable holes for checkpointing, and
the next two patches fix underflow issues related to inc_valid_block_count
and inc_valid_node_count. The final one adds a new feature for
checkpointing where the user can specify an acceptable amount of space to
lose access to up front in checkpointing=disable mode instead of requiring
garbage collection.
There is still a question around what to do when the current reserved
space is less than reserved. As it stands, when a block is deleted, if it
was an old block, the space is not actually given back, and is marked as
unusable. But current reserve may still rise towards reserve, which would
make freeing one block result in a net loss of one block, as opposed to no
change. Reserved and unusable serve the same function, so it may make
sense to just handle it as max(current_reserved, unusable), which
effectively removes the double counting. I'm leaving that until later.
Changes from v2:
Adjust threshold for initial unusable blocks
Patches to fix underflows
Added option to set a block limit in addition to a percent for initial
unusable space
Daniel Rosenberg (4):
f2fs: Lower threshold for disable_cp_again
f2fs: Fix root reserved on remount
f2fs: Fix accounting for unusable blocks
f2fs: Add option to limit required GC for checkpoint=disable
Documentation/ABI/testing/sysfs-fs-f2fs | 8 ++++
Documentation/filesystems/f2fs.txt | 19 +++++++-
fs/f2fs/f2fs.h | 22 ++++++---
fs/f2fs/segment.c | 21 +++++++--
fs/f2fs/super.c | 62 ++++++++++++++++---------
fs/f2fs/sysfs.c | 16 +++++++
6 files changed, 115 insertions(+), 33 deletions(-)
--
2.22.0.rc1.257.g3120a18244-goog
The existing threshold for allowable holes at checkpoint=disable time is
too high. The OVP space contains reserved segments, which are always in
the form of free segments. These must be subtracted from the OVP value.
The current threshold is meant to be the maximum value of holes of a
single type we can have and still guarantee that we can fill the disk
without failing to find space for a block of a given type.
If the disk is full, ignoring current reserved, which only helps us,
the amount of unused blocks is equal to the OVP area. Of that, there
are reserved segments, which must be free segments, and the rest of the
ovp area, which can come from either free segments or holes. The maximum
possible amount of holes is OVP-reserved.
Now, consider the disk when mounting with checkpoint=disable.
We must be able to fill all available free space with either data or
node blocks. When we start with checkpoint=disable, holes are locked to
their current type. Say we have H of one type of hole, and H+X of the
other. We can fill H of that space with arbitrary typed blocks via SSR.
For the remaining H+X blocks, we may not have any of a given block type
left at all. For instance, if we were to fill the disk entirely with
blocks of the type with fewer holes, the H+X blocks of the opposite type
would not be used. If H+X > OVP-reserved, there would be more holes than
could possibly exist, and we would have failed to find a suitable block
earlier on, leading to a crash in update_sit_entry.
If H+X <= OVP-reserved, then the holes end up effectively masked by the OVP
region in this case.
Signed-off-by: Daniel Rosenberg <[email protected]>
---
fs/f2fs/segment.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 1a83115284b93..ec59cbd0e661d 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -876,7 +876,9 @@ void f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi)
int f2fs_disable_cp_again(struct f2fs_sb_info *sbi)
{
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
- block_t ovp = overprovision_segments(sbi) << sbi->log_blocks_per_seg;
+ int ovp_hole_segs =
+ (overprovision_segments(sbi) - reserved_segments(sbi));
+ block_t ovp_holes = ovp_hole_segs << sbi->log_blocks_per_seg;
block_t holes[2] = {0, 0}; /* DATA and NODE */
struct seg_entry *se;
unsigned int segno;
@@ -891,10 +893,10 @@ int f2fs_disable_cp_again(struct f2fs_sb_info *sbi)
}
mutex_unlock(&dirty_i->seglist_lock);
- if (holes[DATA] > ovp || holes[NODE] > ovp)
+ if (holes[DATA] > ovp_holes || holes[NODE] > ovp_holes)
return -EAGAIN;
if (is_sbi_flag_set(sbi, SBI_CP_DISABLED_QUICK) &&
- dirty_segments(sbi) > overprovision_segments(sbi))
+ dirty_segments(sbi) > ovp_hole_segs)
return -EAGAIN;
return 0;
}
--
2.22.0.rc1.257.g3120a18244-goog
On a remount, you can currently set root reserved if it was not
previously set. This can cause an underflow if reserved has been set to
a very high value, since then root reserved + current reserved could be
greater than user_block_count. inc_valid_block_count later subtracts out
these values from user_block_count, causing an underflow.
Signed-off-by: Daniel Rosenberg <[email protected]>
---
fs/f2fs/super.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 912e2619d581b..359fd68509d16 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -213,7 +213,8 @@ void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...)
static inline void limit_reserve_root(struct f2fs_sb_info *sbi)
{
- block_t limit = (sbi->user_block_count << 1) / 1000;
+ block_t limit = min((sbi->user_block_count << 1) / 1000,
+ sbi->user_block_count - sbi->reserved_blocks);
/* limit is 0.2% */
if (test_opt(sbi, RESERVE_ROOT) &&
--
2.22.0.rc1.257.g3120a18244-goog
Fixes possible underflows when dealing with unusable blocks.
Signed-off-by: Daniel Rosenberg <[email protected]>
---
fs/f2fs/f2fs.h | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 9b3d9977cd1ef..a39cc4ffeb4b1 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1769,8 +1769,12 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi,
if (!__allow_reserved_blocks(sbi, inode, true))
avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks;
- if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
- avail_user_block_count -= sbi->unusable_block_count;
+ if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
+ if (avail_user_block_count > sbi->unusable_block_count)
+ avail_user_block_count = 0;
+ else
+ avail_user_block_count -= sbi->unusable_block_count;
+ }
if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) {
diff = sbi->total_valid_block_count - avail_user_block_count;
if (diff > *count)
@@ -1970,7 +1974,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
struct inode *inode, bool is_inode)
{
block_t valid_block_count;
- unsigned int valid_node_count;
+ unsigned int valid_node_count, user_block_count;
int err;
if (is_inode) {
@@ -1997,10 +2001,11 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
if (!__allow_reserved_blocks(sbi, inode, false))
valid_block_count += F2FS_OPTION(sbi).root_reserved_blocks;
+ user_block_count = sbi->user_block_count;
if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
- valid_block_count += sbi->unusable_block_count;
+ user_block_count -= sbi->unusable_block_count;
- if (unlikely(valid_block_count > sbi->user_block_count)) {
+ if (unlikely(valid_block_count > user_block_count)) {
spin_unlock(&sbi->stat_lock);
goto enospc;
}
--
2.22.0.rc1.257.g3120a18244-goog
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: Daniel Rosenberg <[email protected]>
---
Documentation/ABI/testing/sysfs-fs-f2fs | 8 ++++
Documentation/filesystems/f2fs.txt | 19 +++++++-
fs/f2fs/f2fs.h | 6 ++-
fs/f2fs/segment.c | 17 +++++--
fs/f2fs/super.c | 59 ++++++++++++++++---------
fs/f2fs/sysfs.c | 16 +++++++
6 files changed, 99 insertions(+), 26 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs
index 91822ce258317..dca326e0ee3e1 100644
--- a/Documentation/ABI/testing/sysfs-fs-f2fs
+++ b/Documentation/ABI/testing/sysfs-fs-f2fs
@@ -243,3 +243,11 @@ Description:
- Del: echo '[h/c]!extension' > /sys/fs/f2fs/<disk>/extension_list
- [h] means add/del hot file extension
- [c] means add/del cold file extension
+
+What: /sys/fs/f2fs/<disk>/unusable
+Date April 2019
+Contact: "Daniel Rosenberg" <[email protected]>
+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.
diff --git a/Documentation/filesystems/f2fs.txt b/Documentation/filesystems/f2fs.txt
index 66aca042988ee..bebd1be3ba495 100644
--- a/Documentation/filesystems/f2fs.txt
+++ b/Documentation/filesystems/f2fs.txt
@@ -214,11 +214,22 @@ fsync_mode=%s Control the policy of fsync. Currently supports "posix",
non-atomic files likewise "nobarrier" mount option.
test_dummy_encryption Enable dummy encryption, which provides a fake fscrypt
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
disabled, any unmounting or unexpected shutdowns will cause
the filesystem contents to appear as they did when the
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
@@ -396,6 +407,12 @@ Files in /sys/fs/f2fs/<devname>
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
================================================================================
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index a39cc4ffeb4b1..11ef20fedfe72 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -136,6 +136,9 @@ struct f2fs_mount_info {
int alloc_mode; /* segment allocation policy */
int fsync_mode; /* fsync policy */
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
@@ -3085,7 +3088,8 @@ bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi);
void f2fs_clear_prefree_segments(struct f2fs_sb_info *sbi,
struct cp_control *cpc);
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);
int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra);
void f2fs_allocate_new_segments(struct f2fs_sb_info *sbi);
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index ec59cbd0e661d..33bf07222f99f 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -873,13 +873,14 @@ void f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi)
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 =
(overprovision_segments(sbi) - reserved_segments(sbi));
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 unusable;
struct seg_entry *se;
unsigned int segno;
@@ -893,7 +894,17 @@ int f2fs_disable_cp_again(struct f2fs_sb_info *sbi)
}
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;
if (is_sbi_flag_set(sbi, SBI_CP_DISABLED_QUICK) &&
dirty_segments(sbi) > ovp_hole_segs)
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 359fd68509d16..7d64e97611141 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -136,7 +136,10 @@ enum {
Opt_alloc,
Opt_fsync,
Opt_test_dummy_encryption,
- Opt_checkpoint,
+ Opt_checkpoint_disable,
+ Opt_checkpoint_disable_cap,
+ Opt_checkpoint_disable_cap_perc,
+ Opt_checkpoint_enable,
Opt_err,
};
@@ -195,7 +198,10 @@ 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_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},
};
@@ -772,22 +778,30 @@ 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 {
- kvfree(name);
+ case Opt_checkpoint_disable_cap_perc:
+ if (args->from && match_int(args, &arg))
return -EINVAL;
- }
- kvfree(name);
+ if (arg < 0 || arg > 100)
+ 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;
default:
f2fs_msg(sb, KERN_ERR,
@@ -1410,8 +1424,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
seq_printf(seq, ",alloc_mode=%s", "reuse");
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)
seq_printf(seq, ",fsync_mode=%s", "posix");
else if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_STRICT)
@@ -1440,6 +1454,7 @@ static void default_options(struct f2fs_sb_info *sbi)
set_opt(sbi, EXTENT_CACHE);
set_opt(sbi, NOHEAP);
clear_opt(sbi, DISABLE_CHECKPOINT);
+ F2FS_OPTION(sbi).unusable_cap = 0;
sbi->sb->s_flags |= SB_LAZYTIME;
set_opt(sbi, FLUSH_MERGE);
set_opt(sbi, DISCARD);
@@ -1468,6 +1483,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
struct cp_control cpc;
int err = 0;
int ret;
+ block_t unusable;
if (s_flags & SB_RDONLY) {
f2fs_msg(sbi->sb, KERN_ERR,
@@ -1495,7 +1511,8 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
goto restore_flag;
}
- if (f2fs_disable_cp_again(sbi)) {
+ unusable = f2fs_get_unusable_blocks(sbi);
+ if (f2fs_disable_cp_again(sbi, unusable)) {
err = -EAGAIN;
goto restore_flag;
}
@@ -1508,7 +1525,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
goto out_unlock;
spin_lock(&sbi->stat_lock);
- sbi->unusable_block_count = 0;
+ sbi->unusable_block_count = unusable;
spin_unlock(&sbi->stat_lock);
out_unlock:
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
index 729f46a3c9ee0..fa184880cff34 100644
--- a/fs/f2fs/sysfs.c
+++ b/fs/f2fs/sysfs.c
@@ -68,6 +68,20 @@ static ssize_t dirty_segments_show(struct f2fs_attr *a,
(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,
struct f2fs_sb_info *sbi, char *buf)
{
@@ -440,6 +454,7 @@ F2FS_GENERAL_RO_ATTR(dirty_segments);
F2FS_GENERAL_RO_ATTR(lifetime_write_kbytes);
F2FS_GENERAL_RO_ATTR(features);
F2FS_GENERAL_RO_ATTR(current_reserved_blocks);
+F2FS_GENERAL_RO_ATTR(unusable);
#ifdef CONFIG_FS_ENCRYPTION
F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO);
@@ -495,6 +510,7 @@ static struct attribute *f2fs_attrs[] = {
ATTR_LIST(inject_type),
#endif
ATTR_LIST(dirty_segments),
+ ATTR_LIST(unusable),
ATTR_LIST(lifetime_write_kbytes),
ATTR_LIST(features),
ATTR_LIST(reserved_blocks),
--
2.22.0.rc1.257.g3120a18244-goog
On 2019/5/30 8:49, Daniel Rosenberg wrote:
> Fixes possible underflows when dealing with unusable blocks.
>
> Signed-off-by: Daniel Rosenberg <[email protected]>
> ---
> fs/f2fs/f2fs.h | 15 ++++++++++-----
> 1 file changed, 10 insertions(+), 5 deletions(-)
>
> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> index 9b3d9977cd1ef..a39cc4ffeb4b1 100644
> --- a/fs/f2fs/f2fs.h
> +++ b/fs/f2fs/f2fs.h
> @@ -1769,8 +1769,12 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi,
>
> if (!__allow_reserved_blocks(sbi, inode, true))
> avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks;
> - if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
> - avail_user_block_count -= sbi->unusable_block_count;
> + if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
> + if (avail_user_block_count > sbi->unusable_block_count)
> + avail_user_block_count = 0;
avail_user_block_count -= sbi->unusable_block_count;
> + else
> + avail_user_block_count -= sbi->unusable_block_count;
avail_user_block_count = 0;
Thanks,
> + }
> if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) {
> diff = sbi->total_valid_block_count - avail_user_block_count;
> if (diff > *count)
> @@ -1970,7 +1974,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
> struct inode *inode, bool is_inode)
> {
> block_t valid_block_count;
> - unsigned int valid_node_count;
> + unsigned int valid_node_count, user_block_count;
> int err;
>
> if (is_inode) {
> @@ -1997,10 +2001,11 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
>
> if (!__allow_reserved_blocks(sbi, inode, false))
> valid_block_count += F2FS_OPTION(sbi).root_reserved_blocks;
> + user_block_count = sbi->user_block_count;
> if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
> - valid_block_count += sbi->unusable_block_count;
> + user_block_count -= sbi->unusable_block_count;
>
> - if (unlikely(valid_block_count > sbi->user_block_count)) {
> + if (unlikely(valid_block_count > user_block_count)) {
> spin_unlock(&sbi->stat_lock);
> goto enospc;
> }
>
On 2019-5-30 8:49, Daniel Rosenberg via Linux-f2fs-devel wrote:
> 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: Daniel Rosenberg <[email protected]>
Reviewed-by: Chao Yu <[email protected]>
Thanks,
On 2019/5/30 8:49, Daniel Rosenberg wrote:
> The existing threshold for allowable holes at checkpoint=disable time is
> too high. The OVP space contains reserved segments, which are always in
> the form of free segments. These must be subtracted from the OVP value.
>
> The current threshold is meant to be the maximum value of holes of a
> single type we can have and still guarantee that we can fill the disk
> without failing to find space for a block of a given type.
>
> If the disk is full, ignoring current reserved, which only helps us,
> the amount of unused blocks is equal to the OVP area. Of that, there
> are reserved segments, which must be free segments, and the rest of the
> ovp area, which can come from either free segments or holes. The maximum
> possible amount of holes is OVP-reserved.
>
> Now, consider the disk when mounting with checkpoint=disable.
> We must be able to fill all available free space with either data or
> node blocks. When we start with checkpoint=disable, holes are locked to
> their current type. Say we have H of one type of hole, and H+X of the
> other. We can fill H of that space with arbitrary typed blocks via SSR.
> For the remaining H+X blocks, we may not have any of a given block type
> left at all. For instance, if we were to fill the disk entirely with
> blocks of the type with fewer holes, the H+X blocks of the opposite type
> would not be used. If H+X > OVP-reserved, there would be more holes than
> could possibly exist, and we would have failed to find a suitable block
> earlier on, leading to a crash in update_sit_entry.
>
> If H+X <= OVP-reserved, then the holes end up effectively masked by the OVP
> region in this case.
>
> Signed-off-by: Daniel Rosenberg <[email protected]>
Reviewed-by: Chao Yu <[email protected]>
Thanks,
On 2019/5/30 8:49, Daniel Rosenberg wrote:
> On a remount, you can currently set root reserved if it was not
> previously set. This can cause an underflow if reserved has been set to
> a very high value, since then root reserved + current reserved could be
> greater than user_block_count. inc_valid_block_count later subtracts out
> these values from user_block_count, causing an underflow.
>
> Signed-off-by: Daniel Rosenberg <[email protected]>
Reviewed-by: Chao Yu <[email protected]>
Thanks,
On 06/03, Chao Yu wrote:
> On 2019/5/30 8:49, Daniel Rosenberg wrote:
> > Fixes possible underflows when dealing with unusable blocks.
> >
> > Signed-off-by: Daniel Rosenberg <[email protected]>
> > ---
> > fs/f2fs/f2fs.h | 15 ++++++++++-----
> > 1 file changed, 10 insertions(+), 5 deletions(-)
> >
> > diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
> > index 9b3d9977cd1ef..a39cc4ffeb4b1 100644
> > --- a/fs/f2fs/f2fs.h
> > +++ b/fs/f2fs/f2fs.h
> > @@ -1769,8 +1769,12 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi,
> >
> > if (!__allow_reserved_blocks(sbi, inode, true))
> > avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks;
> > - if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
> > - avail_user_block_count -= sbi->unusable_block_count;
> > + if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
> > + if (avail_user_block_count > sbi->unusable_block_count)
> > + avail_user_block_count = 0;
>
> avail_user_block_count -= sbi->unusable_block_count;
>
> > + else
> > + avail_user_block_count -= sbi->unusable_block_count;
>
> avail_user_block_count = 0;
>
I fixed this.
Thanks,
> Thanks,
>
> > + }
> > if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) {
> > diff = sbi->total_valid_block_count - avail_user_block_count;
> > if (diff > *count)
> > @@ -1970,7 +1974,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
> > struct inode *inode, bool is_inode)
> > {
> > block_t valid_block_count;
> > - unsigned int valid_node_count;
> > + unsigned int valid_node_count, user_block_count;
> > int err;
> >
> > if (is_inode) {
> > @@ -1997,10 +2001,11 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
> >
> > if (!__allow_reserved_blocks(sbi, inode, false))
> > valid_block_count += F2FS_OPTION(sbi).root_reserved_blocks;
> > + user_block_count = sbi->user_block_count;
> > if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
> > - valid_block_count += sbi->unusable_block_count;
> > + user_block_count -= sbi->unusable_block_count;
> >
> > - if (unlikely(valid_block_count > sbi->user_block_count)) {
> > + if (unlikely(valid_block_count > user_block_count)) {
> > spin_unlock(&sbi->stat_lock);
> > goto enospc;
> > }
> >
On 2019/6/4 4:26, Jaegeuk Kim wrote:
> On 06/03, Chao Yu wrote:
>> On 2019/5/30 8:49, Daniel Rosenberg wrote:
>>> Fixes possible underflows when dealing with unusable blocks.
>>>
>>> Signed-off-by: Daniel Rosenberg <[email protected]>
>>> ---
>>> fs/f2fs/f2fs.h | 15 ++++++++++-----
>>> 1 file changed, 10 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
>>> index 9b3d9977cd1ef..a39cc4ffeb4b1 100644
>>> --- a/fs/f2fs/f2fs.h
>>> +++ b/fs/f2fs/f2fs.h
>>> @@ -1769,8 +1769,12 @@ static inline int inc_valid_block_count(struct f2fs_sb_info *sbi,
>>>
>>> if (!__allow_reserved_blocks(sbi, inode, true))
>>> avail_user_block_count -= F2FS_OPTION(sbi).root_reserved_blocks;
>>> - if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
>>> - avail_user_block_count -= sbi->unusable_block_count;
>>> + if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
>>> + if (avail_user_block_count > sbi->unusable_block_count)
>>> + avail_user_block_count = 0;
>>
>> avail_user_block_count -= sbi->unusable_block_count;
>>
>>> + else
>>> + avail_user_block_count -= sbi->unusable_block_count;
>>
>> avail_user_block_count = 0;
>>
>
> I fixed this.
Okay, if there is no v4, please add
Reviewed-by: Chao Yu <[email protected]>
Thanks,
>
> Thanks,
>
>> Thanks,
>>
>>> + }
>>> if (unlikely(sbi->total_valid_block_count > avail_user_block_count)) {
>>> diff = sbi->total_valid_block_count - avail_user_block_count;
>>> if (diff > *count)
>>> @@ -1970,7 +1974,7 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
>>> struct inode *inode, bool is_inode)
>>> {
>>> block_t valid_block_count;
>>> - unsigned int valid_node_count;
>>> + unsigned int valid_node_count, user_block_count;
>>> int err;
>>>
>>> if (is_inode) {
>>> @@ -1997,10 +2001,11 @@ static inline int inc_valid_node_count(struct f2fs_sb_info *sbi,
>>>
>>> if (!__allow_reserved_blocks(sbi, inode, false))
>>> valid_block_count += F2FS_OPTION(sbi).root_reserved_blocks;
>>> + user_block_count = sbi->user_block_count;
>>> if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
>>> - valid_block_count += sbi->unusable_block_count;
>>> + user_block_count -= sbi->unusable_block_count;
>>>
>>> - if (unlikely(valid_block_count > sbi->user_block_count)) {
>>> + if (unlikely(valid_block_count > user_block_count)) {
>>> spin_unlock(&sbi->stat_lock);
>>> goto enospc;
>>> }
>>>
> .
>