Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

f2fs: add ckpt_valid_blocks to the section entry

when performing buffered writes in a large section,
overhead is incurred due to the iteration through
ckpt_valid_blocks within the section.
when SEGS_PER_SEC is 128, this overhead accounts for 20% within
the f2fs_write_single_data_page routine.
as the size of the section increases, the overhead also grows.
to handle this problem ckpt_valid_blocks is
added within the section entries.

Test
insmod null_blk.ko nr_devices=1 completion_nsec=1 submit_queues=8
hw_queue_depth=64 max_sectors=512 bs=4096 memory_backed=1
make_f2fs /dev/block/nullb0
make_f2fs -s 128 /dev/block/nullb0
fio --bs=512k --size=1536M --rw=write --name=1
--filename=/mnt/test_dir/seq_write
--ioengine=io_uring --iodepth=64 --end_fsync=1

before
SEGS_PER_SEC 1
2556MiB/s
SEGS_PER_SEC 128
2145MiB/s

after
SEGS_PER_SEC 1
2556MiB/s
SEGS_PER_SEC 128
2556MiB/s

Signed-off-by: yohan.joung <yohan.joung@sk.com>
Reviewed-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>

authored by

yohan.joung and committed by
Jaegeuk Kim
deecd282 249ad438

+88 -21
+38 -7
fs/f2fs/segment.c
··· 2447 2447 * that the consecutive input blocks belong to the same segment. 2448 2448 */ 2449 2449 static int update_sit_entry_for_release(struct f2fs_sb_info *sbi, struct seg_entry *se, 2450 - block_t blkaddr, unsigned int offset, int del) 2450 + unsigned int segno, block_t blkaddr, unsigned int offset, int del) 2451 2451 { 2452 2452 bool exist; 2453 2453 #ifdef CONFIG_F2FS_CHECK_FS ··· 2492 2492 f2fs_test_and_clear_bit(offset + i, se->discard_map)) 2493 2493 sbi->discard_blks++; 2494 2494 2495 - if (!f2fs_test_bit(offset + i, se->ckpt_valid_map)) 2495 + if (!f2fs_test_bit(offset + i, se->ckpt_valid_map)) { 2496 2496 se->ckpt_valid_blocks -= 1; 2497 + if (__is_large_section(sbi)) 2498 + get_sec_entry(sbi, segno)->ckpt_valid_blocks -= 1; 2499 + } 2497 2500 } 2501 + 2502 + if (__is_large_section(sbi)) 2503 + sanity_check_valid_blocks(sbi, segno); 2498 2504 2499 2505 return del; 2500 2506 } 2501 2507 2502 2508 static int update_sit_entry_for_alloc(struct f2fs_sb_info *sbi, struct seg_entry *se, 2503 - block_t blkaddr, unsigned int offset, int del) 2509 + unsigned int segno, block_t blkaddr, unsigned int offset, int del) 2504 2510 { 2505 2511 bool exist; 2506 2512 #ifdef CONFIG_F2FS_CHECK_FS ··· 2539 2533 * or newly invalidated. 2540 2534 */ 2541 2535 if (!is_sbi_flag_set(sbi, SBI_CP_DISABLED)) { 2542 - if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map)) 2536 + if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map)) { 2543 2537 se->ckpt_valid_blocks++; 2538 + if (__is_large_section(sbi)) 2539 + get_sec_entry(sbi, segno)->ckpt_valid_blocks++; 2540 + } 2544 2541 } 2545 2542 2546 - if (!f2fs_test_bit(offset, se->ckpt_valid_map)) 2543 + if (!f2fs_test_bit(offset, se->ckpt_valid_map)) { 2547 2544 se->ckpt_valid_blocks += del; 2545 + if (__is_large_section(sbi)) 2546 + get_sec_entry(sbi, segno)->ckpt_valid_blocks += del; 2547 + } 2548 + 2549 + if (__is_large_section(sbi)) 2550 + sanity_check_valid_blocks(sbi, segno); 2548 2551 2549 2552 return del; 2550 2553 } ··· 2584 2569 2585 2570 /* Update valid block bitmap */ 2586 2571 if (del > 0) { 2587 - del = update_sit_entry_for_alloc(sbi, se, blkaddr, offset, del); 2572 + del = update_sit_entry_for_alloc(sbi, se, segno, blkaddr, offset, del); 2588 2573 } else { 2589 - del = update_sit_entry_for_release(sbi, se, blkaddr, offset, del); 2574 + del = update_sit_entry_for_release(sbi, se, segno, blkaddr, offset, del); 2590 2575 } 2591 2576 2592 2577 __mark_sit_entry_dirty(sbi, segno); ··· 4723 4708 &raw_sit->entries[sit_offset]); 4724 4709 } 4725 4710 4711 + /* update ckpt_valid_block */ 4712 + if (__is_large_section(sbi)) { 4713 + set_ckpt_valid_blocks(sbi, segno); 4714 + sanity_check_valid_blocks(sbi, segno); 4715 + } 4716 + 4726 4717 __clear_bit(segno, bitmap); 4727 4718 sit_i->dirty_sentries--; 4728 4719 ses->entry_cnt--; ··· 5049 5028 } 5050 5029 } 5051 5030 up_read(&curseg->journal_rwsem); 5031 + 5032 + /* update ckpt_valid_block */ 5033 + if (__is_large_section(sbi)) { 5034 + unsigned int segno; 5035 + 5036 + for (segno = 0; segno < MAIN_SEGS(sbi); segno += SEGS_PER_SEC(sbi)) { 5037 + set_ckpt_valid_blocks(sbi, segno); 5038 + sanity_check_valid_blocks(sbi, segno); 5039 + } 5040 + } 5052 5041 5053 5042 if (err) 5054 5043 return err;
+50 -14
fs/f2fs/segment.h
··· 211 211 212 212 struct sec_entry { 213 213 unsigned int valid_blocks; /* # of valid blocks in a section */ 214 + unsigned int ckpt_valid_blocks; /* # of valid blocks last cp in a section */ 214 215 }; 215 216 216 217 #define MAX_SKIP_GC_COUNT 16 ··· 348 347 static inline unsigned int get_ckpt_valid_blocks(struct f2fs_sb_info *sbi, 349 348 unsigned int segno, bool use_section) 350 349 { 351 - if (use_section && __is_large_section(sbi)) { 352 - unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); 353 - unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno); 354 - unsigned int blocks = 0; 355 - int i; 356 - 357 - for (i = 0; i < SEGS_PER_SEC(sbi); i++, start_segno++) { 358 - struct seg_entry *se = get_seg_entry(sbi, start_segno); 359 - 360 - blocks += se->ckpt_valid_blocks; 361 - } 362 - return blocks; 363 - } 364 - return get_seg_entry(sbi, segno)->ckpt_valid_blocks; 350 + if (use_section && __is_large_section(sbi)) 351 + return get_sec_entry(sbi, segno)->ckpt_valid_blocks; 352 + else 353 + return get_seg_entry(sbi, segno)->ckpt_valid_blocks; 365 354 } 366 355 356 + static inline void set_ckpt_valid_blocks(struct f2fs_sb_info *sbi, 357 + unsigned int segno) 358 + { 359 + unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); 360 + unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno); 361 + unsigned int blocks = 0; 362 + int i; 363 + 364 + for (i = 0; i < SEGS_PER_SEC(sbi); i++, start_segno++) { 365 + struct seg_entry *se = get_seg_entry(sbi, start_segno); 366 + 367 + blocks += se->ckpt_valid_blocks; 368 + } 369 + get_sec_entry(sbi, segno)->ckpt_valid_blocks = blocks; 370 + } 371 + 372 + #ifdef CONFIG_F2FS_CHECK_FS 373 + static inline void sanity_check_valid_blocks(struct f2fs_sb_info *sbi, 374 + unsigned int segno) 375 + { 376 + unsigned int secno = GET_SEC_FROM_SEG(sbi, segno); 377 + unsigned int start_segno = GET_SEG_FROM_SEC(sbi, secno); 378 + unsigned int blocks = 0; 379 + int i; 380 + 381 + for (i = 0; i < SEGS_PER_SEC(sbi); i++, start_segno++) { 382 + struct seg_entry *se = get_seg_entry(sbi, start_segno); 383 + 384 + blocks += se->ckpt_valid_blocks; 385 + } 386 + 387 + if (blocks != get_sec_entry(sbi, segno)->ckpt_valid_blocks) { 388 + f2fs_err(sbi, 389 + "Inconsistent ckpt valid blocks: " 390 + "seg entry(%d) vs sec entry(%d) at secno %d", 391 + blocks, get_sec_entry(sbi, segno)->ckpt_valid_blocks, secno); 392 + f2fs_bug_on(sbi, 1); 393 + } 394 + } 395 + #else 396 + static inline void sanity_check_valid_blocks(struct f2fs_sb_info *sbi, 397 + unsigned int segno) 398 + { 399 + } 400 + #endif 367 401 static inline void seg_info_from_raw_sit(struct seg_entry *se, 368 402 struct f2fs_sit_entry *rs) 369 403 {