2024-04-07 13:02:07

by Chao Yu

[permalink] [raw]
Subject: [PATCH 1/2] f2fs: use per-log target_bitmap to improve lookup performace of ssr allocation

After commit 899fee36fac0 ("f2fs: fix to avoid data corruption by
forbidding SSR overwrite"), valid block bitmap of current openned
segment is fixed, let's introduce a per-log bitmap instead of temp
bitmap to avoid unnecessary calculation overhead whenever allocating
free slot w/ SSR allocator.

Signed-off-by: Chao Yu <[email protected]>
---
fs/f2fs/segment.c | 30 ++++++++++++++++++++++--------
fs/f2fs/segment.h | 1 +
2 files changed, 23 insertions(+), 8 deletions(-)

diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 4a3cf2888faf..ecb9ee80d5e0 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -2840,31 +2840,39 @@ static int new_curseg(struct f2fs_sb_info *sbi, int type, bool new_sec)
return 0;
}

-static int __next_free_blkoff(struct f2fs_sb_info *sbi,
- int segno, block_t start)
+static void __get_segment_bitmap(struct f2fs_sb_info *sbi,
+ unsigned long *target_map,
+ int segno)
{
struct seg_entry *se = get_seg_entry(sbi, segno);
int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long);
- unsigned long *target_map = SIT_I(sbi)->tmp_map;
unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map;
unsigned long *cur_map = (unsigned long *)se->cur_valid_map;
int i;

for (i = 0; i < entries; i++)
target_map[i] = ckpt_map[i] | cur_map[i];
+}
+
+static int __next_free_blkoff(struct f2fs_sb_info *sbi, unsigned long *bitmap,
+ int segno, block_t start)
+{
+ __get_segment_bitmap(sbi, bitmap, segno);

- return __find_rev_next_zero_bit(target_map, BLKS_PER_SEG(sbi), start);
+ return __find_rev_next_zero_bit(bitmap, BLKS_PER_SEG(sbi), start);
}

static int f2fs_find_next_ssr_block(struct f2fs_sb_info *sbi,
- struct curseg_info *seg)
+ struct curseg_info *seg)
{
- return __next_free_blkoff(sbi, seg->segno, seg->next_blkoff + 1);
+ return __find_rev_next_zero_bit(seg->target_map,
+ BLKS_PER_SEG(sbi), seg->next_blkoff + 1);
}

bool f2fs_segment_has_free_slot(struct f2fs_sb_info *sbi, int segno)
{
- return __next_free_blkoff(sbi, segno, 0) < BLKS_PER_SEG(sbi);
+ return __next_free_blkoff(sbi, SIT_I(sbi)->tmp_map, segno, 0) <
+ BLKS_PER_SEG(sbi);
}

/*
@@ -2890,7 +2898,8 @@ static int change_curseg(struct f2fs_sb_info *sbi, int type)

reset_curseg(sbi, type, 1);
curseg->alloc_type = SSR;
- curseg->next_blkoff = __next_free_blkoff(sbi, curseg->segno, 0);
+ curseg->next_blkoff = __next_free_blkoff(sbi, curseg->target_map,
+ curseg->segno, 0);

sum_page = f2fs_get_sum_page(sbi, new_segno);
if (IS_ERR(sum_page)) {
@@ -4652,6 +4661,10 @@ static int build_curseg(struct f2fs_sb_info *sbi)
sizeof(struct f2fs_journal), GFP_KERNEL);
if (!array[i].journal)
return -ENOMEM;
+ array[i].target_map = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE,
+ GFP_KERNEL);
+ if (!array[i].target_map)
+ return -ENOMEM;
if (i < NR_PERSISTENT_LOG)
array[i].seg_type = CURSEG_HOT_DATA + i;
else if (i == CURSEG_COLD_DATA_PINNED)
@@ -5470,6 +5483,7 @@ static void destroy_curseg(struct f2fs_sb_info *sbi)
for (i = 0; i < NR_CURSEG_TYPE; i++) {
kfree(array[i].sum_blk);
kfree(array[i].journal);
+ kfree(array[i].target_map);

#ifdef CONFIG_BLK_DEV_ZONED
if (f2fs_sb_has_blkzoned(sbi)) {
diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h
index 855978ca869f..08d667e6a36f 100644
--- a/fs/f2fs/segment.h
+++ b/fs/f2fs/segment.h
@@ -293,6 +293,7 @@ struct curseg_info {
struct f2fs_summary_block *sum_blk; /* cached summary block */
struct rw_semaphore journal_rwsem; /* protect journal area */
struct f2fs_journal *journal; /* cached journal info */
+ unsigned long *target_map; /* bitmap for SSR allocator */
unsigned char alloc_type; /* current allocation type */
unsigned short seg_type; /* segment type like CURSEG_XXX_TYPE */
unsigned int segno; /* current segment number */
--
2.40.1



2024-04-07 13:02:17

by Chao Yu

[permalink] [raw]
Subject: [PATCH 2/2] f2fs: introduce written_map to indicate written datas

Currently, __exchange_data_block() will check checkpointed state of data,
if it is not checkpointed, it will try to exchange blkaddrs directly in
dnode.

However, after commit 899fee36fac0 ("f2fs: fix to avoid data corruption
by forbidding SSR overwrite"), in order to disallow SSR allocator to
reuse all written data/node type blocks, all written blocks were set as
checkpointed.

In order to reenable metadata exchange functionality, let's introduce
written_map to indicate all written blocks including checkpointed one,
or newly written and invalidated one, and use it for SSR allocation,
and then ckpt_valid_bitmap can indicate real checkpointed status, and
we can use it correctly in __exchange_data_block().

[testcase]
xfs_io -f /mnt/f2fs/src -c "pwrite 0 2m"
xfs_io -f /mnt/f2fs/dst -c "pwrite 0 2m"
xfs_io /mnt/f2fs/src -c "fiemap -v"
xfs_io /mnt/f2fs/dst -c "fiemap -v"
f2fs_io move_range /mnt/f2fs/src /mnt/f2fs/dst 0 0 2097152
xfs_io /mnt/f2fs/src -c "fiemap -v"
xfs_io /mnt/f2fs/dst -c "fiemap -v"

[before]
/mnt/f2fs/src:
EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS
0: [0..4095]: 8445952..8450047 4096 0x1001
/mnt/f2fs/dst:
EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS
0: [0..4095]: 143360..147455 4096 0x1001

/mnt/f2fs/src:
/mnt/f2fs/dst:
EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS
0: [0..4095]: 4284416..4288511 4096 0x1001

[after]
/mnt/f2fs/src:
EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS
0: [0..4095]: 147456..151551 4096 0x1001
/mnt/f2fs/dst:
EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS
0: [0..4095]: 151552..155647 4096 0x1001

/mnt/f2fs/src:
/mnt/f2fs/dst:
EXT: FILE-OFFSET BLOCK-RANGE TOTAL FLAGS
0: [0..4095]: 147456..151551 4096 0x1001

Signed-off-by: Chao Yu <[email protected]>
---
fs/f2fs/segment.c | 24 ++++++++++++------------
fs/f2fs/segment.h | 6 ++++++
2 files changed, 18 insertions(+), 12 deletions(-)

diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index ecb9ee80d5e0..ec3288381397 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -2456,13 +2456,12 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
sbi->discard_blks--;

/*
- * SSR should never reuse block which is checkpointed
- * or newly invalidated.
+ * if CP disabling is enable, it allows SSR to reuse newly
+ * invalidated block, otherwise forbidding it to pretect fsyned
+ * datas.
*/
- if (!is_sbi_flag_set(sbi, SBI_CP_DISABLED)) {
- if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map))
- se->ckpt_valid_blocks++;
- }
+ if (!is_sbi_flag_set(sbi, SBI_CP_DISABLED))
+ f2fs_set_bit(offset, se->written_map);
} else {
exist = f2fs_test_and_clear_bit(offset, se->cur_valid_map);
#ifdef CONFIG_F2FS_CHECK_FS
@@ -2498,8 +2497,6 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
f2fs_test_and_clear_bit(offset, se->discard_map))
sbi->discard_blks++;
}
- if (!f2fs_test_bit(offset, se->ckpt_valid_map))
- se->ckpt_valid_blocks += del;

__mark_sit_entry_dirty(sbi, segno);

@@ -2847,11 +2844,11 @@ static void __get_segment_bitmap(struct f2fs_sb_info *sbi,
struct seg_entry *se = get_seg_entry(sbi, segno);
int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long);
unsigned long *ckpt_map = (unsigned long *)se->ckpt_valid_map;
- unsigned long *cur_map = (unsigned long *)se->cur_valid_map;
+ unsigned long *written_map = (unsigned long *)se->written_map;
int i;

for (i = 0; i < entries; i++)
- target_map[i] = ckpt_map[i] | cur_map[i];
+ target_map[i] = ckpt_map[i] | written_map[i];
}

static int __next_free_blkoff(struct f2fs_sb_info *sbi, unsigned long *bitmap,
@@ -4529,9 +4526,9 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
return -ENOMEM;

#ifdef CONFIG_F2FS_CHECK_FS
- bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (3 + discard_map);
+ bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (4 + discard_map);
#else
- bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (2 + discard_map);
+ bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (3 + discard_map);
#endif
sit_i->bitmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL);
if (!sit_i->bitmap)
@@ -4546,6 +4543,9 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
sit_i->sentries[start].ckpt_valid_map = bitmap;
bitmap += SIT_VBLOCK_MAP_SIZE;

+ sit_i->sentries[start].written_map = bitmap;
+ bitmap += SIT_VBLOCK_MAP_SIZE;
+
#ifdef CONFIG_F2FS_CHECK_FS
sit_i->sentries[start].cur_valid_map_mir = bitmap;
bitmap += SIT_VBLOCK_MAP_SIZE;
diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h
index 08d667e6a36f..eda04d9ed5a5 100644
--- a/fs/f2fs/segment.h
+++ b/fs/f2fs/segment.h
@@ -205,6 +205,10 @@ struct seg_entry {
* checkpoint pack. This information is used by the SSR mode.
*/
unsigned char *ckpt_valid_map; /* validity bitmap of blocks last cp */
+ unsigned char *written_map; /*
+ * blocks were written, including newly
+ * invalidated data
+ */
unsigned char *discard_map;
unsigned long long mtime; /* modification time of the segment */
};
@@ -370,6 +374,7 @@ static inline void seg_info_from_raw_sit(struct seg_entry *se,
se->ckpt_valid_blocks = GET_SIT_VBLOCKS(rs);
memcpy(se->cur_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE);
memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE);
+ memcpy(se->written_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE);
#ifdef CONFIG_F2FS_CHECK_FS
memcpy(se->cur_valid_map_mir, rs->valid_map, SIT_VBLOCK_MAP_SIZE);
#endif
@@ -412,6 +417,7 @@ static inline void seg_info_to_raw_sit(struct seg_entry *se,
__seg_info_to_raw_sit(se, rs);

memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE);
+ memcpy(se->written_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE);
se->ckpt_valid_blocks = se->valid_blocks;
}

--
2.40.1