fs/f2fs/gc.c | 2 +- fs/f2fs/segment.h | 99 ++++++++++++++++++++++------------------------- 2 files changed, 47 insertions(+), 54 deletions(-)
The check for enough sections in segment.h has the following issues:
1. has_not_enough_free_secs() should return "enough secs" when "free_secs
>= upper_secs", not just structly greater. Conversely, it should only
return "not enough secs" when "free_secs < lower_secs", not when they are
equal. This accounts for the possibility that blocks can fit within
curseg without requiring an additional free section.
2. __get_secs_required() currently separates the needed space to section
and block parts, checking them against free sections and curseg,
respectively. This does not consider the case where curseg cannot hold
the whole block part, but excess free sections beyond the section part
can accommodate some of the block part.
3. has_curseg_enough_space() only checks CURSEG_HOT_DATA for dentry
blocks, but when active_logs=6, they may be placed in WARM and COLD
sections. Also, the current logic does not consider that dentry and data
blocks can be put in the same section when active_logs=2 or 6.
This patch modifies the three functions to address the above issues:
1. Rename has_curseg_enough_space() to get_additional_blocks_required().
Calculate the minimum node, dentry, and data blocks curseg can
accommodate. Then subtract these from the total required blocks of
respective type to determine the worst-case number of blocks that must
be placed in free sections.
2. In __get_secs_required(), get the number of blocks needing new
sections from the new get_additional_blocks_required(). Return the upper
bound of necessary free sections for these blocks. For active_logs=2 or
6, dentry blocks are combined with data blocks.
3. In has_not_enough_free_secs(), get the required sections from
__get_secs_required(), and return “not enough secs” if “free_secs <
required_secs”.
Signed-off-by: Joanne Chang <joannechien@google.com>
---
fs/f2fs/gc.c | 2 +-
fs/f2fs/segment.h | 99 ++++++++++++++++++++++-------------------------
2 files changed, 47 insertions(+), 54 deletions(-)
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 384fa7e2085b..6afd57fa5387 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -2000,7 +2000,7 @@ int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control)
goto stop;
}
- __get_secs_required(sbi, NULL, &upper_secs, NULL);
+ upper_secs = __get_secs_required(sbi);
/*
* Write checkpoint to reclaim prefree segments.
diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h
index 07dcbcbeb7c6..20daaccb34a5 100644
--- a/fs/f2fs/segment.h
+++ b/fs/f2fs/segment.h
@@ -621,97 +621,90 @@ static inline unsigned int get_left_section_blocks(struct f2fs_sb_info *sbi,
return CAP_BLKS_PER_SEC(sbi) - get_ckpt_valid_blocks(sbi, segno, true);
}
-static inline bool has_curseg_enough_space(struct f2fs_sb_info *sbi,
- unsigned int node_blocks, unsigned int data_blocks,
- unsigned int dent_blocks)
+static inline void get_additional_blocks_required(struct f2fs_sb_info *sbi,
+ unsigned int *total_node_blocks, unsigned int *total_data_blocks,
+ unsigned int *total_dent_blocks, bool separate_dent)
{
- unsigned int segno, left_blocks, blocks;
+ unsigned int segno, left_blocks;
int i;
+ unsigned int min_free_node_blocks = CAP_BLKS_PER_SEC(sbi);
+ unsigned int min_free_dent_blocks = CAP_BLKS_PER_SEC(sbi);
+ unsigned int min_free_data_blocks = CAP_BLKS_PER_SEC(sbi);
/* check current data/node sections in the worst case. */
for (i = CURSEG_HOT_DATA; i < NR_PERSISTENT_LOG; i++) {
segno = CURSEG_I(sbi, i)->segno;
if (unlikely(segno == NULL_SEGNO))
- return false;
+ return;
left_blocks = get_left_section_blocks(sbi, i, segno);
- blocks = i <= CURSEG_COLD_DATA ? data_blocks : node_blocks;
- if (blocks > left_blocks)
- return false;
+ if (i > CURSEG_COLD_DATA)
+ min_free_node_blocks = min(min_free_node_blocks, left_blocks);
+ else if (i == CURSEG_HOT_DATA && separate_dent)
+ min_free_dent_blocks = left_blocks;
+ else
+ min_free_data_blocks = min(min_free_data_blocks, left_blocks);
}
- /* check current data section for dentry blocks. */
- segno = CURSEG_I(sbi, CURSEG_HOT_DATA)->segno;
-
- if (unlikely(segno == NULL_SEGNO))
- return false;
-
- left_blocks = get_left_section_blocks(sbi, CURSEG_HOT_DATA, segno);
-
- if (dent_blocks > left_blocks)
- return false;
- return true;
+ *total_node_blocks = (*total_node_blocks > min_free_node_blocks) ?
+ *total_node_blocks - min_free_node_blocks : 0;
+ *total_dent_blocks = (*total_dent_blocks > min_free_dent_blocks) ?
+ *total_dent_blocks - min_free_dent_blocks : 0;
+ *total_data_blocks = (*total_data_blocks > min_free_data_blocks) ?
+ *total_data_blocks - min_free_data_blocks : 0;
}
/*
- * calculate needed sections for dirty node/dentry and call
- * has_curseg_enough_space, please note that, it needs to account
- * dirty data as well in lfs mode when checkpoint is disabled.
+ * call get_additional_blocks_required to calculate dirty blocks
+ * needing to be placed in free sections, please note that, it
+ * needs to account dirty data as well in lfs mode when checkpoint
+ * is disabled.
*/
-static inline void __get_secs_required(struct f2fs_sb_info *sbi,
- unsigned int *lower_p, unsigned int *upper_p, bool *curseg_p)
+static inline int __get_secs_required(struct f2fs_sb_info *sbi)
{
unsigned int total_node_blocks = get_pages(sbi, F2FS_DIRTY_NODES) +
get_pages(sbi, F2FS_DIRTY_DENTS) +
get_pages(sbi, F2FS_DIRTY_IMETA);
unsigned int total_dent_blocks = get_pages(sbi, F2FS_DIRTY_DENTS);
unsigned int total_data_blocks = 0;
- unsigned int node_secs = total_node_blocks / CAP_BLKS_PER_SEC(sbi);
- unsigned int dent_secs = total_dent_blocks / CAP_BLKS_PER_SEC(sbi);
- unsigned int data_secs = 0;
- unsigned int node_blocks = total_node_blocks % CAP_BLKS_PER_SEC(sbi);
- unsigned int dent_blocks = total_dent_blocks % CAP_BLKS_PER_SEC(sbi);
- unsigned int data_blocks = 0;
+ bool separate_dent = true;
- if (f2fs_lfs_mode(sbi)) {
+ if (f2fs_lfs_mode(sbi))
total_data_blocks = get_pages(sbi, F2FS_DIRTY_DATA);
- data_secs = total_data_blocks / CAP_BLKS_PER_SEC(sbi);
- data_blocks = total_data_blocks % CAP_BLKS_PER_SEC(sbi);
+
+ /*
+ * When active_logs != 4, dentry blocks and data blocks can be
+ * mixed in the same logs, so check their space together.
+ */
+ if (F2FS_OPTION(sbi).active_logs != 4) {
+ total_data_blocks += total_dent_blocks;
+ total_dent_blocks = 0;
+ separate_dent = false;
}
- if (lower_p)
- *lower_p = node_secs + dent_secs + data_secs;
- if (upper_p)
- *upper_p = node_secs + dent_secs + data_secs +
- (node_blocks ? 1 : 0) + (dent_blocks ? 1 : 0) +
- (data_blocks ? 1 : 0);
- if (curseg_p)
- *curseg_p = has_curseg_enough_space(sbi,
- node_blocks, data_blocks, dent_blocks);
+ get_additional_blocks_required(sbi, &total_node_blocks, &total_dent_blocks,
+ &total_data_blocks, separate_dent);
+
+ return DIV_ROUND_UP(total_node_blocks, CAP_BLKS_PER_SEC(sbi)) +
+ DIV_ROUND_UP(total_dent_blocks, CAP_BLKS_PER_SEC(sbi)) +
+ DIV_ROUND_UP(total_data_blocks, CAP_BLKS_PER_SEC(sbi));
}
static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi,
int freed, int needed)
{
- unsigned int free_secs, lower_secs, upper_secs;
- bool curseg_space;
+ unsigned int free_secs, required_secs;
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING)))
return false;
- __get_secs_required(sbi, &lower_secs, &upper_secs, &curseg_space);
-
free_secs = free_sections(sbi) + freed;
- lower_secs += needed + reserved_sections(sbi);
- upper_secs += needed + reserved_sections(sbi);
+ required_secs = needed + reserved_sections(sbi) +
+ __get_secs_required(sbi);
- if (free_secs > upper_secs)
- return false;
- if (free_secs <= lower_secs)
- return true;
- return !curseg_space;
+ return free_secs < required_secs;
}
static inline bool has_enough_free_secs(struct f2fs_sb_info *sbi,
--
2.52.0.239.gd5f0c6e74e-goog
© 2016 - 2025 Red Hat, Inc.