* [PATCH 1/2] f2fs: fix missing discard candidates in fstrim @ 2025-01-02 10:13 Chunhai Guo 2025-01-02 10:13 ` [PATCH 2/2] f2fs: fix missing small discard " Chunhai Guo 2025-01-03 3:26 ` [PATCH 1/2] f2fs: fix missing discard candidates " Chao Yu 0 siblings, 2 replies; 14+ messages in thread From: Chunhai Guo @ 2025-01-02 10:13 UTC (permalink / raw) To: chao, jaegeuk; +Cc: linux-f2fs-devel, linux-kernel, Chunhai Guo fstrim may miss candidates that need to be discarded in fstrim, as shown in the examples below. The root cause is that when cpc->reason is set with CP_DISCARD, add_discard_addrs() expects that ckpt_valid_map and cur_valid_map have been synced by seg_info_to_raw_sit() [1] and tries to find the candidates based on ckpt_valid_map and discard_map. However, seg_info_to_raw_sit() does not actually run before f2fs_exist_trim_candidates(), which results in failure. root# cp testfile /f2fs_mountpoint root# f2fs_io fiemap 0 1 /f2fs_mountpoint/testfile Fiemap: offset = 0 len = 1 logical addr. physical addr. length flags 0 0000000000000000 0000000406a00000 000000003d800000 00001000 root# rm /f2fs_mountpoint/testfile root# fstrim -v -o 0x406a00000 -l 1024M /f2fs_mountpoint -- no candidate is found /f2fs_mountpoint: 0 B (0 bytes) trimmed [1] Please refer to commit d7bc2484b8d4 ("f2fs: fix small discards not to issue redundantly") for the relationship between seg_info_to_raw_sit() and add_discard_addrs(). Fixes: 25290fa5591d ("f2fs: return fs_trim if there is no candidate") Signed-off-by: Chunhai Guo <guochunhai@vivo.com> --- fs/f2fs/segment.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index eade36c5ef13..8fe9f794b581 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2070,7 +2070,7 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi, } static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, - bool check_only) + bool synced, bool check_only) { int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start); @@ -2098,7 +2098,7 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ for (i = 0; i < entries; i++) - dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : + dmap[i] = synced ? ~ckpt_map[i] & ~discard_map[i] : (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; while (force || SM_I(sbi)->dcc_info->nr_discards <= @@ -3275,7 +3275,7 @@ bool f2fs_exist_trim_candidates(struct f2fs_sb_info *sbi, down_write(&SIT_I(sbi)->sentry_lock); for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) { - if (add_discard_addrs(sbi, cpc, true)) { + if (add_discard_addrs(sbi, cpc, false, true)) { has_candidate = true; break; } @@ -4611,7 +4611,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) /* add discard candidates */ if (!(cpc->reason & CP_DISCARD)) { cpc->trim_start = segno; - add_discard_addrs(sbi, cpc, false); + add_discard_addrs(sbi, cpc, false, false); } if (to_journal) { @@ -4653,7 +4653,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) __u64 trim_start = cpc->trim_start; for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) - add_discard_addrs(sbi, cpc, false); + add_discard_addrs(sbi, cpc, true, false); cpc->trim_start = trim_start; } -- 2.34.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 2/2] f2fs: fix missing small discard in fstrim 2025-01-02 10:13 [PATCH 1/2] f2fs: fix missing discard candidates in fstrim Chunhai Guo @ 2025-01-02 10:13 ` Chunhai Guo 2025-01-03 3:36 ` Chao Yu 2025-05-22 11:11 ` Chao Yu 2025-01-03 3:26 ` [PATCH 1/2] f2fs: fix missing discard candidates " Chao Yu 1 sibling, 2 replies; 14+ messages in thread From: Chunhai Guo @ 2025-01-02 10:13 UTC (permalink / raw) To: chao, jaegeuk; +Cc: linux-f2fs-devel, linux-kernel, Chunhai Guo If userspace issues an fstrim with a range that does not include all segments with small discards, these segments will be reused without being discarded. This patch fixes this issue. This patch is somewhat similar to commit 650d3c4e56e1 ("f2fs: fix a missing discard prefree segments"). Fixes: d7bc2484b8d4 ("f2fs: fix small discards not to issue redundantly") Signed-off-by: Chunhai Guo <guochunhai@vivo.com> --- fs/f2fs/segment.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 8fe9f794b581..af9a62591c49 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -4552,6 +4552,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) struct list_head *head = &SM_I(sbi)->sit_entry_set; bool to_journal = !is_sbi_flag_set(sbi, SBI_IS_RESIZEFS); struct seg_entry *se; + bool force = (cpc->reason & CP_DISCARD); + __u64 trim_start = cpc->trim_start; down_write(&sit_i->sentry_lock); @@ -4609,7 +4611,9 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) #endif /* add discard candidates */ - if (!(cpc->reason & CP_DISCARD)) { + if (!force || (force && + (segno < trim_start || + segno > cpc->trim_end))) { cpc->trim_start = segno; add_discard_addrs(sbi, cpc, false, false); } @@ -4649,8 +4653,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) f2fs_bug_on(sbi, !list_empty(head)); f2fs_bug_on(sbi, sit_i->dirty_sentries); out: - if (cpc->reason & CP_DISCARD) { - __u64 trim_start = cpc->trim_start; + if (force) { + cpc->trim_start = trim_start; for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) add_discard_addrs(sbi, cpc, true, false); -- 2.34.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 2/2] f2fs: fix missing small discard in fstrim 2025-01-02 10:13 ` [PATCH 2/2] f2fs: fix missing small discard " Chunhai Guo @ 2025-01-03 3:36 ` Chao Yu 2025-01-03 7:00 ` Chunhai Guo 2025-05-22 11:11 ` Chao Yu 1 sibling, 1 reply; 14+ messages in thread From: Chao Yu @ 2025-01-03 3:36 UTC (permalink / raw) To: Chunhai Guo, jaegeuk; +Cc: Chao Yu, linux-f2fs-devel, linux-kernel On 2025/1/2 18:13, Chunhai Guo wrote: > If userspace issues an fstrim with a range that does not include all > segments with small discards, these segments will be reused without being I didn't get it, if fstrim didn't cover those segments, why do we need to issue small discard for out-of-range segments? Thanks, > discarded. This patch fixes this issue. > This patch is somewhat similar to commit 650d3c4e56e1 ("f2fs: fix a missing > discard prefree segments"). > > Fixes: d7bc2484b8d4 ("f2fs: fix small discards not to issue redundantly") > Signed-off-by: Chunhai Guo <guochunhai@vivo.com> > --- > fs/f2fs/segment.c | 10 +++++++--- > 1 file changed, 7 insertions(+), 3 deletions(-) > > diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c > index 8fe9f794b581..af9a62591c49 100644 > --- a/fs/f2fs/segment.c > +++ b/fs/f2fs/segment.c > @@ -4552,6 +4552,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) > struct list_head *head = &SM_I(sbi)->sit_entry_set; > bool to_journal = !is_sbi_flag_set(sbi, SBI_IS_RESIZEFS); > struct seg_entry *se; > + bool force = (cpc->reason & CP_DISCARD); > + __u64 trim_start = cpc->trim_start; > > down_write(&sit_i->sentry_lock); > > @@ -4609,7 +4611,9 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) > #endif > > /* add discard candidates */ > - if (!(cpc->reason & CP_DISCARD)) { > + if (!force || (force && > + (segno < trim_start || > + segno > cpc->trim_end))) { > cpc->trim_start = segno; > add_discard_addrs(sbi, cpc, false, false); > } > @@ -4649,8 +4653,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) > f2fs_bug_on(sbi, !list_empty(head)); > f2fs_bug_on(sbi, sit_i->dirty_sentries); > out: > - if (cpc->reason & CP_DISCARD) { > - __u64 trim_start = cpc->trim_start; > + if (force) { > + cpc->trim_start = trim_start; > > for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) > add_discard_addrs(sbi, cpc, true, false); ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/2] f2fs: fix missing small discard in fstrim 2025-01-03 3:36 ` Chao Yu @ 2025-01-03 7:00 ` Chunhai Guo 2025-05-21 14:05 ` Chunhai Guo 2025-05-22 8:48 ` Chao Yu 0 siblings, 2 replies; 14+ messages in thread From: Chunhai Guo @ 2025-01-03 7:00 UTC (permalink / raw) To: Chao Yu, Chunhai Guo, jaegeuk@kernel.org Cc: linux-f2fs-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org 在 1/3/2025 11:36 AM, Chao Yu 写道: > On 2025/1/2 18:13, Chunhai Guo wrote: >> If userspace issues an fstrim with a range that does not include all >> segments with small discards, these segments will be reused without being > I didn't get it, if fstrim didn't cover those segments, why do we need to > issue small discard for out-of-range segments? Currently, all the dirty sentries in the dirty_sentries_bitmap are handled in the fstrim process regardless of whether they are within the fstrim range or not. Therefore, this patch is necessary to address the issue. f2fs_flush_sit_entries() list_for_each_entry_safe(ses, tmp, head, set_list) { for_each_set_bit_from(segno, bitmap, end) { ... __clear_bit(segno, bitmap); // segno is cleared regardless of whether or not it is within the fstrim range ... } } Thanks, > Thanks, > >> discarded. This patch fixes this issue. >> This patch is somewhat similar to commit 650d3c4e56e1 ("f2fs: fix a missing >> discard prefree segments"). >> >> Fixes: d7bc2484b8d4 ("f2fs: fix small discards not to issue redundantly") >> Signed-off-by: Chunhai Guo <guochunhai@vivo.com> >> --- >> fs/f2fs/segment.c | 10 +++++++--- >> 1 file changed, 7 insertions(+), 3 deletions(-) >> >> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c >> index 8fe9f794b581..af9a62591c49 100644 >> --- a/fs/f2fs/segment.c >> +++ b/fs/f2fs/segment.c >> @@ -4552,6 +4552,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >> struct list_head *head = &SM_I(sbi)->sit_entry_set; >> bool to_journal = !is_sbi_flag_set(sbi, SBI_IS_RESIZEFS); >> struct seg_entry *se; >> + bool force = (cpc->reason & CP_DISCARD); >> + __u64 trim_start = cpc->trim_start; >> >> down_write(&sit_i->sentry_lock); >> >> @@ -4609,7 +4611,9 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >> #endif >> >> /* add discard candidates */ >> - if (!(cpc->reason & CP_DISCARD)) { >> + if (!force || (force && >> + (segno < trim_start || >> + segno > cpc->trim_end))) { >> cpc->trim_start = segno; >> add_discard_addrs(sbi, cpc, false, false); >> } >> @@ -4649,8 +4653,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >> f2fs_bug_on(sbi, !list_empty(head)); >> f2fs_bug_on(sbi, sit_i->dirty_sentries); >> out: >> - if (cpc->reason & CP_DISCARD) { >> - __u64 trim_start = cpc->trim_start; >> + if (force) { >> + cpc->trim_start = trim_start; >> >> for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) >> add_discard_addrs(sbi, cpc, true, false); ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/2] f2fs: fix missing small discard in fstrim 2025-01-03 7:00 ` Chunhai Guo @ 2025-05-21 14:05 ` Chunhai Guo 2025-05-22 8:48 ` Chao Yu 1 sibling, 0 replies; 14+ messages in thread From: Chunhai Guo @ 2025-05-21 14:05 UTC (permalink / raw) To: Chunhai Guo, Chao Yu, jaegeuk@kernel.org Cc: linux-f2fs-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org 在 1/3/2025 3:00 PM, Chunhai Guo 写道: > 在 1/3/2025 11:36 AM, Chao Yu 写道: >> On 2025/1/2 18:13, Chunhai Guo wrote: >>> If userspace issues an fstrim with a range that does not include all >>> segments with small discards, these segments will be reused without being >> I didn't get it, if fstrim didn't cover those segments, why do we need to >> issue small discard for out-of-range segments? > Currently, all the dirty sentries in the dirty_sentries_bitmap are > handled in the fstrim process regardless of whether they are within the > fstrim range or not. Therefore, this patch is necessary to address the > issue. > > f2fs_flush_sit_entries() > list_for_each_entry_safe(ses, tmp, head, set_list) { > for_each_set_bit_from(segno, bitmap, end) { > ... > __clear_bit(segno, bitmap); // segno is cleared regardless > of whether or not it is within the fstrim range > ... > } > } Hi Chao, Could you please provide some advice on this issue when you have a moment? Or do you have any questions about my explanation above? Thanks, > > Thanks, > >> Thanks, >> >>> discarded. This patch fixes this issue. >>> This patch is somewhat similar to commit 650d3c4e56e1 ("f2fs: fix a missing >>> discard prefree segments"). >>> >>> Fixes: d7bc2484b8d4 ("f2fs: fix small discards not to issue redundantly") >>> Signed-off-by: Chunhai Guo <guochunhai@vivo.com> >>> --- >>> fs/f2fs/segment.c | 10 +++++++--- >>> 1 file changed, 7 insertions(+), 3 deletions(-) >>> >>> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c >>> index 8fe9f794b581..af9a62591c49 100644 >>> --- a/fs/f2fs/segment.c >>> +++ b/fs/f2fs/segment.c >>> @@ -4552,6 +4552,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >>> struct list_head *head = &SM_I(sbi)->sit_entry_set; >>> bool to_journal = !is_sbi_flag_set(sbi, SBI_IS_RESIZEFS); >>> struct seg_entry *se; >>> + bool force = (cpc->reason & CP_DISCARD); >>> + __u64 trim_start = cpc->trim_start; >>> >>> down_write(&sit_i->sentry_lock); >>> >>> @@ -4609,7 +4611,9 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >>> #endif >>> >>> /* add discard candidates */ >>> - if (!(cpc->reason & CP_DISCARD)) { >>> + if (!force || (force && >>> + (segno < trim_start || >>> + segno > cpc->trim_end))) { >>> cpc->trim_start = segno; >>> add_discard_addrs(sbi, cpc, false, false); >>> } >>> @@ -4649,8 +4653,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >>> f2fs_bug_on(sbi, !list_empty(head)); >>> f2fs_bug_on(sbi, sit_i->dirty_sentries); >>> out: >>> - if (cpc->reason & CP_DISCARD) { >>> - __u64 trim_start = cpc->trim_start; >>> + if (force) { >>> + cpc->trim_start = trim_start; >>> >>> for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) >>> add_discard_addrs(sbi, cpc, true, false); > ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/2] f2fs: fix missing small discard in fstrim 2025-01-03 7:00 ` Chunhai Guo 2025-05-21 14:05 ` Chunhai Guo @ 2025-05-22 8:48 ` Chao Yu 1 sibling, 0 replies; 14+ messages in thread From: Chao Yu @ 2025-05-22 8:48 UTC (permalink / raw) To: Chunhai Guo, jaegeuk@kernel.org Cc: chao, linux-f2fs-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org On 1/3/25 15:00, Chunhai Guo wrote: > 在 1/3/2025 11:36 AM, Chao Yu 写道: >> On 2025/1/2 18:13, Chunhai Guo wrote: >>> If userspace issues an fstrim with a range that does not include all >>> segments with small discards, these segments will be reused without being >> I didn't get it, if fstrim didn't cover those segments, why do we need to >> issue small discard for out-of-range segments? > Currently, all the dirty sentries in the dirty_sentries_bitmap are > handled in the fstrim process regardless of whether they are within the > fstrim range or not. Therefore, this patch is necessary to address the > issue. fstrim flow doesn't depend on dirty status of segments, right? It will add discard range w/ below code? if (cpc->reason & CP_DISCARD) { __u64 trim_start = cpc->trim_start; for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) add_discard_addrs(sbi, cpc, false); cpc->trim_start = trim_start; } Thanks, > > f2fs_flush_sit_entries() > list_for_each_entry_safe(ses, tmp, head, set_list) { > for_each_set_bit_from(segno, bitmap, end) { > ... > __clear_bit(segno, bitmap); // segno is cleared regardless > of whether or not it is within the fstrim range > ... > } > } > > > Thanks, > >> Thanks, >> >>> discarded. This patch fixes this issue. >>> This patch is somewhat similar to commit 650d3c4e56e1 ("f2fs: fix a missing >>> discard prefree segments"). >>> >>> Fixes: d7bc2484b8d4 ("f2fs: fix small discards not to issue redundantly") >>> Signed-off-by: Chunhai Guo <guochunhai@vivo.com> >>> --- >>> fs/f2fs/segment.c | 10 +++++++--- >>> 1 file changed, 7 insertions(+), 3 deletions(-) >>> >>> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c >>> index 8fe9f794b581..af9a62591c49 100644 >>> --- a/fs/f2fs/segment.c >>> +++ b/fs/f2fs/segment.c >>> @@ -4552,6 +4552,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >>> struct list_head *head = &SM_I(sbi)->sit_entry_set; >>> bool to_journal = !is_sbi_flag_set(sbi, SBI_IS_RESIZEFS); >>> struct seg_entry *se; >>> + bool force = (cpc->reason & CP_DISCARD); >>> + __u64 trim_start = cpc->trim_start; >>> >>> down_write(&sit_i->sentry_lock); >>> >>> @@ -4609,7 +4611,9 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >>> #endif >>> >>> /* add discard candidates */ >>> - if (!(cpc->reason & CP_DISCARD)) { >>> + if (!force || (force && >>> + (segno < trim_start || >>> + segno > cpc->trim_end))) { >>> cpc->trim_start = segno; >>> add_discard_addrs(sbi, cpc, false, false); >>> } >>> @@ -4649,8 +4653,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >>> f2fs_bug_on(sbi, !list_empty(head)); >>> f2fs_bug_on(sbi, sit_i->dirty_sentries); >>> out: >>> - if (cpc->reason & CP_DISCARD) { >>> - __u64 trim_start = cpc->trim_start; >>> + if (force) { >>> + cpc->trim_start = trim_start; >>> >>> for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) >>> add_discard_addrs(sbi, cpc, true, false); > > ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/2] f2fs: fix missing small discard in fstrim 2025-01-02 10:13 ` [PATCH 2/2] f2fs: fix missing small discard " Chunhai Guo 2025-01-03 3:36 ` Chao Yu @ 2025-05-22 11:11 ` Chao Yu 2025-05-23 8:15 ` Chunhai Guo 1 sibling, 1 reply; 14+ messages in thread From: Chao Yu @ 2025-05-22 11:11 UTC (permalink / raw) To: Chunhai Guo, jaegeuk; +Cc: chao, linux-f2fs-devel, linux-kernel On 1/2/25 18:13, Chunhai Guo wrote: > If userspace issues an fstrim with a range that does not include all > segments with small discards, these segments will be reused without being > discarded. This patch fixes this issue. > This patch is somewhat similar to commit 650d3c4e56e1 ("f2fs: fix a missing > discard prefree segments"). I guess it's better to update commit message as we discussed? > > Fixes: d7bc2484b8d4 ("f2fs: fix small discards not to issue redundantly") > Signed-off-by: Chunhai Guo <guochunhai@vivo.com> > --- > fs/f2fs/segment.c | 10 +++++++--- > 1 file changed, 7 insertions(+), 3 deletions(-) > > diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c > index 8fe9f794b581..af9a62591c49 100644 > --- a/fs/f2fs/segment.c > +++ b/fs/f2fs/segment.c > @@ -4552,6 +4552,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) > struct list_head *head = &SM_I(sbi)->sit_entry_set; > bool to_journal = !is_sbi_flag_set(sbi, SBI_IS_RESIZEFS); > struct seg_entry *se; > + bool force = (cpc->reason & CP_DISCARD); > + __u64 trim_start = cpc->trim_start; > > down_write(&sit_i->sentry_lock); > > @@ -4609,7 +4611,9 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) > #endif > > /* add discard candidates */ > - if (!(cpc->reason & CP_DISCARD)) { > + if (!force || (force && if (!force || (f2fs_realtime_discard_enable() && (segno < trim_start || segno > trim_end))) Thanks, > + (segno < trim_start || > + segno > cpc->trim_end))) { > cpc->trim_start = segno; > add_discard_addrs(sbi, cpc, false, false); > } > @@ -4649,8 +4653,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) > f2fs_bug_on(sbi, !list_empty(head)); > f2fs_bug_on(sbi, sit_i->dirty_sentries); > out: > - if (cpc->reason & CP_DISCARD) { > - __u64 trim_start = cpc->trim_start; > + if (force) { > + cpc->trim_start = trim_start; > > for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) > add_discard_addrs(sbi, cpc, true, false); ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/2] f2fs: fix missing small discard in fstrim 2025-05-22 11:11 ` Chao Yu @ 2025-05-23 8:15 ` Chunhai Guo 0 siblings, 0 replies; 14+ messages in thread From: Chunhai Guo @ 2025-05-23 8:15 UTC (permalink / raw) To: Chao Yu, Chunhai Guo, jaegeuk@kernel.org Cc: linux-f2fs-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org 在 5/22/2025 7:11 PM, Chao Yu 写道: > On 1/2/25 18:13, Chunhai Guo wrote: >> If userspace issues an fstrim with a range that does not include all >> segments with small discards, these segments will be reused without being >> discarded. This patch fixes this issue. >> This patch is somewhat similar to commit 650d3c4e56e1 ("f2fs: fix a missing >> discard prefree segments"). > I guess it's better to update commit message as we discussed? OK. I will update this message. > >> Fixes: d7bc2484b8d4 ("f2fs: fix small discards not to issue redundantly") >> Signed-off-by: Chunhai Guo <guochunhai@vivo.com> >> --- >> fs/f2fs/segment.c | 10 +++++++--- >> 1 file changed, 7 insertions(+), 3 deletions(-) >> >> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c >> index 8fe9f794b581..af9a62591c49 100644 >> --- a/fs/f2fs/segment.c >> +++ b/fs/f2fs/segment.c >> @@ -4552,6 +4552,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >> struct list_head *head = &SM_I(sbi)->sit_entry_set; >> bool to_journal = !is_sbi_flag_set(sbi, SBI_IS_RESIZEFS); >> struct seg_entry *se; >> + bool force = (cpc->reason & CP_DISCARD); >> + __u64 trim_start = cpc->trim_start; >> >> down_write(&sit_i->sentry_lock); >> >> @@ -4609,7 +4611,9 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >> #endif >> >> /* add discard candidates */ >> - if (!(cpc->reason & CP_DISCARD)) { >> + if (!force || (force && > if (!force || (f2fs_realtime_discard_enable() && > (segno < trim_start || segno > trim_end))) Thank you for pointing this out. It indeed lacks the handling of realtime discard. I will submit a V2 patch to address it. Thanks, > > Thanks, > >> + (segno < trim_start || >> + segno > cpc->trim_end))) { >> cpc->trim_start = segno; >> add_discard_addrs(sbi, cpc, false, false); >> } >> @@ -4649,8 +4653,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >> f2fs_bug_on(sbi, !list_empty(head)); >> f2fs_bug_on(sbi, sit_i->dirty_sentries); >> out: >> - if (cpc->reason & CP_DISCARD) { >> - __u64 trim_start = cpc->trim_start; >> + if (force) { >> + cpc->trim_start = trim_start; >> >> for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) >> add_discard_addrs(sbi, cpc, true, false); ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] f2fs: fix missing discard candidates in fstrim 2025-01-02 10:13 [PATCH 1/2] f2fs: fix missing discard candidates in fstrim Chunhai Guo 2025-01-02 10:13 ` [PATCH 2/2] f2fs: fix missing small discard " Chunhai Guo @ 2025-01-03 3:26 ` Chao Yu 2025-01-03 8:07 ` Chunhai Guo 1 sibling, 1 reply; 14+ messages in thread From: Chao Yu @ 2025-01-03 3:26 UTC (permalink / raw) To: Chunhai Guo, jaegeuk; +Cc: Chao Yu, linux-f2fs-devel, linux-kernel On 2025/1/2 18:13, Chunhai Guo wrote: > fstrim may miss candidates that need to be discarded in fstrim, as shown in > the examples below. > The root cause is that when cpc->reason is set with CP_DISCARD, > add_discard_addrs() expects that ckpt_valid_map and cur_valid_map have been > synced by seg_info_to_raw_sit() [1] and tries to find the candidates based > on ckpt_valid_map and discard_map. However, seg_info_to_raw_sit() does not > actually run before f2fs_exist_trim_candidates(), which results in failure. Chunhai, Can you please use nodiscard option due to fstrim stopped to return trimmed length after below commit: 5a6154920faf ("f2fs: don't issue discard commands in online discard is on") > > root# cp testfile /f2fs_mountpoint > > root# f2fs_io fiemap 0 1 /f2fs_mountpoint/testfile > Fiemap: offset = 0 len = 1 > logical addr. physical addr. length flags > 0 0000000000000000 0000000406a00000 000000003d800000 00001000 > > root# rm /f2fs_mountpoint/testfile > > root# fstrim -v -o 0x406a00000 -l 1024M /f2fs_mountpoint -- no candidate is found > /f2fs_mountpoint: 0 B (0 bytes) trimmed > > [1] Please refer to commit d7bc2484b8d4 ("f2fs: fix small discards not to > issue redundantly") for the relationship between seg_info_to_raw_sit() and > add_discard_addrs(). > > Fixes: 25290fa5591d ("f2fs: return fs_trim if there is no candidate") > Signed-off-by: Chunhai Guo <guochunhai@vivo.com> > --- > fs/f2fs/segment.c | 10 +++++----- > 1 file changed, 5 insertions(+), 5 deletions(-) > > diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c > index eade36c5ef13..8fe9f794b581 100644 > --- a/fs/f2fs/segment.c > +++ b/fs/f2fs/segment.c > @@ -2070,7 +2070,7 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi, > } > > static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, > - bool check_only) > + bool synced, bool check_only) > { > int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); > struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start); > @@ -2098,7 +2098,7 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, > > /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ > for (i = 0; i < entries; i++) > - dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : > + dmap[i] = synced ? ~ckpt_map[i] & ~discard_map[i] : I guess this condition "force ? ~ckpt_map[i] & ~discard_map[i]" didn't cover all below cases, thoughts? - ckpt_map[i] == 0 // write data, and then remove data before checkpoint - ckpt_map[i] != 0 // remove data existed in previous checkpoint Thanks, > (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; > > while (force || SM_I(sbi)->dcc_info->nr_discards <= > @@ -3275,7 +3275,7 @@ bool f2fs_exist_trim_candidates(struct f2fs_sb_info *sbi, > > down_write(&SIT_I(sbi)->sentry_lock); > for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) { > - if (add_discard_addrs(sbi, cpc, true)) { > + if (add_discard_addrs(sbi, cpc, false, true)) { > has_candidate = true; > break; > } > @@ -4611,7 +4611,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) > /* add discard candidates */ > if (!(cpc->reason & CP_DISCARD)) { > cpc->trim_start = segno; > - add_discard_addrs(sbi, cpc, false); > + add_discard_addrs(sbi, cpc, false, false); > } > > if (to_journal) { > @@ -4653,7 +4653,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) > __u64 trim_start = cpc->trim_start; > > for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) > - add_discard_addrs(sbi, cpc, false); > + add_discard_addrs(sbi, cpc, true, false); > > cpc->trim_start = trim_start; > } ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] f2fs: fix missing discard candidates in fstrim 2025-01-03 3:26 ` [PATCH 1/2] f2fs: fix missing discard candidates " Chao Yu @ 2025-01-03 8:07 ` Chunhai Guo 2025-01-08 12:46 ` Chao Yu 0 siblings, 1 reply; 14+ messages in thread From: Chunhai Guo @ 2025-01-03 8:07 UTC (permalink / raw) To: Chao Yu, Chunhai Guo, jaegeuk@kernel.org Cc: linux-f2fs-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org 在 1/3/2025 11:26 AM, Chao Yu 写道: > On 2025/1/2 18:13, Chunhai Guo wrote: >> fstrim may miss candidates that need to be discarded in fstrim, as shown in >> the examples below. >> The root cause is that when cpc->reason is set with CP_DISCARD, >> add_discard_addrs() expects that ckpt_valid_map and cur_valid_map have been >> synced by seg_info_to_raw_sit() [1] and tries to find the candidates based >> on ckpt_valid_map and discard_map. However, seg_info_to_raw_sit() does not >> actually run before f2fs_exist_trim_candidates(), which results in failure. > Chunhai, > > Can you please use nodiscard option due to fstrim stopped to return > trimmed length after below commit: > > 5a6154920faf ("f2fs: don't issue discard commands in online discard is on") Thank you for your explanation, but I guess this issue is not relevant to this commit, and I understand that '0 B (0 bytes) trimmed' is fine. The real problem is that there are actually many candidates that should be handled in fstrim, but it cannot find any of them. f2fs_trim_fs() f2fs_write_checkpoint() ... if (cpc->reason & CP_DISCARD) { if (!f2fs_exist_trim_candidates(sbi, cpc)) { unblock_operations(sbi); goto out; // Not candidate is found here and exit. } ... } >> root# cp testfile /f2fs_mountpoint >> >> root# f2fs_io fiemap 0 1 /f2fs_mountpoint/testfile >> Fiemap: offset = 0 len = 1 >> logical addr. physical addr. length flags >> 0 0000000000000000 0000000406a00000 000000003d800000 00001000 >> >> root# rm /f2fs_mountpoint/testfile >> >> root# fstrim -v -o 0x406a00000 -l 1024M /f2fs_mountpoint -- no candidate is found >> /f2fs_mountpoint: 0 B (0 bytes) trimmed >> >> [1] Please refer to commit d7bc2484b8d4 ("f2fs: fix small discards not to >> issue redundantly") for the relationship between seg_info_to_raw_sit() and >> add_discard_addrs(). >> >> Fixes: 25290fa5591d ("f2fs: return fs_trim if there is no candidate") >> Signed-off-by: Chunhai Guo <guochunhai@vivo.com> >> --- >> fs/f2fs/segment.c | 10 +++++----- >> 1 file changed, 5 insertions(+), 5 deletions(-) >> >> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c >> index eade36c5ef13..8fe9f794b581 100644 >> --- a/fs/f2fs/segment.c >> +++ b/fs/f2fs/segment.c >> @@ -2070,7 +2070,7 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi, >> } >> >> static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, >> - bool check_only) >> + bool synced, bool check_only) >> { >> int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); >> struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start); >> @@ -2098,7 +2098,7 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, >> >> /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ >> for (i = 0; i < entries; i++) >> - dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : >> + dmap[i] = synced ? ~ckpt_map[i] & ~discard_map[i] : > I guess this condition "force ? ~ckpt_map[i] & ~discard_map[i]" didn't cover > all below cases, thoughts? > - ckpt_map[i] == 0 // write data, and then remove data before checkpoint > - ckpt_map[i] != 0 // remove data existed in previous checkpoint From the handling of ckpt_valid_map in update_sit_entry(), I guess the condition can cover both cases. For example, when the checkpoint is enabled, all the set bits in the ckpt_valid_map remain set before the checkpoint (even when the blocks are deleted), which makes it find all the right bits in both cases. Thanks, > > Thanks, > >> (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; >> >> while (force || SM_I(sbi)->dcc_info->nr_discards <= >> @@ -3275,7 +3275,7 @@ bool f2fs_exist_trim_candidates(struct f2fs_sb_info *sbi, >> >> down_write(&SIT_I(sbi)->sentry_lock); >> for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) { >> - if (add_discard_addrs(sbi, cpc, true)) { >> + if (add_discard_addrs(sbi, cpc, false, true)) { >> has_candidate = true; >> break; >> } >> @@ -4611,7 +4611,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >> /* add discard candidates */ >> if (!(cpc->reason & CP_DISCARD)) { >> cpc->trim_start = segno; >> - add_discard_addrs(sbi, cpc, false); >> + add_discard_addrs(sbi, cpc, false, false); >> } >> >> if (to_journal) { >> @@ -4653,7 +4653,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >> __u64 trim_start = cpc->trim_start; >> >> for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) >> - add_discard_addrs(sbi, cpc, false); >> + add_discard_addrs(sbi, cpc, true, false); >> >> cpc->trim_start = trim_start; >> } ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] f2fs: fix missing discard candidates in fstrim 2025-01-03 8:07 ` Chunhai Guo @ 2025-01-08 12:46 ` Chao Yu 2025-01-09 10:03 ` Chunhai Guo 0 siblings, 1 reply; 14+ messages in thread From: Chao Yu @ 2025-01-08 12:46 UTC (permalink / raw) To: Chunhai Guo, jaegeuk@kernel.org Cc: chao, linux-f2fs-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org On 2025/1/3 16:07, Chunhai Guo wrote: > 在 1/3/2025 11:26 AM, Chao Yu 写道: >> On 2025/1/2 18:13, Chunhai Guo wrote: >>> fstrim may miss candidates that need to be discarded in fstrim, as shown in >>> the examples below. >>> The root cause is that when cpc->reason is set with CP_DISCARD, >>> add_discard_addrs() expects that ckpt_valid_map and cur_valid_map have been >>> synced by seg_info_to_raw_sit() [1] and tries to find the candidates based >>> on ckpt_valid_map and discard_map. However, seg_info_to_raw_sit() does not >>> actually run before f2fs_exist_trim_candidates(), which results in failure. >> Chunhai, >> >> Can you please use nodiscard option due to fstrim stopped to return >> trimmed length after below commit: >> >> 5a6154920faf ("f2fs: don't issue discard commands in online discard is on") > > Thank you for your explanation, but I guess this issue is not relevant > to this commit, and I understand that '0 B (0 bytes) trimmed' is fine. > > The real problem is that there are actually many candidates that should > be handled in fstrim, but it cannot find any of them. > > f2fs_trim_fs() > f2fs_write_checkpoint() > ... > if (cpc->reason & CP_DISCARD) { > if (!f2fs_exist_trim_candidates(sbi, cpc)) { > unblock_operations(sbi); > goto out; // Not candidate is found here and exit. > } > ... > } > >>> root# cp testfile /f2fs_mountpoint >>> >>> root# f2fs_io fiemap 0 1 /f2fs_mountpoint/testfile >>> Fiemap: offset = 0 len = 1 >>> logical addr. physical addr. length flags >>> 0 0000000000000000 0000000406a00000 000000003d800000 00001000 >>> >>> root# rm /f2fs_mountpoint/testfile >>> >>> root# fstrim -v -o 0x406a00000 -l 1024M /f2fs_mountpoint -- no candidate is found >>> /f2fs_mountpoint: 0 B (0 bytes) trimmed >>> >>> [1] Please refer to commit d7bc2484b8d4 ("f2fs: fix small discards not to >>> issue redundantly") for the relationship between seg_info_to_raw_sit() and >>> add_discard_addrs(). >>> >>> Fixes: 25290fa5591d ("f2fs: return fs_trim if there is no candidate") >>> Signed-off-by: Chunhai Guo <guochunhai@vivo.com> >>> --- >>> fs/f2fs/segment.c | 10 +++++----- >>> 1 file changed, 5 insertions(+), 5 deletions(-) >>> >>> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c >>> index eade36c5ef13..8fe9f794b581 100644 >>> --- a/fs/f2fs/segment.c >>> +++ b/fs/f2fs/segment.c >>> @@ -2070,7 +2070,7 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi, >>> } >>> >>> static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, >>> - bool check_only) >>> + bool synced, bool check_only) >>> { >>> int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); >>> struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start); >>> @@ -2098,7 +2098,7 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, >>> >>> /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ >>> for (i = 0; i < entries; i++) >>> - dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : >>> + dmap[i] = synced ? ~ckpt_map[i] & ~discard_map[i] : >> I guess this condition "force ? ~ckpt_map[i] & ~discard_map[i]" didn't cover >> all below cases, thoughts? >> - ckpt_map[i] == 0 // write data, and then remove data before checkpoint >> - ckpt_map[i] != 0 // remove data existed in previous checkpoint > > From the handling of ckpt_valid_map in update_sit_entry(), I guess the > condition can cover both cases. > For example, when the checkpoint is enabled, all the set bits in the > ckpt_valid_map remain set before the checkpoint (even when the blocks > are deleted), which makes it find all the right bits in both cases. My point is for fstrim case, we only need to check discard_map bitmap? once bit(s) in discard_map bitmap is zero, no matter the status of bit(s) in ckpt_map bitmap, we need to trigger a checkpoit for following discard submission? Thanks, > > Thanks, > >> >> Thanks, >> >>> (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; >>> >>> while (force || SM_I(sbi)->dcc_info->nr_discards <= >>> @@ -3275,7 +3275,7 @@ bool f2fs_exist_trim_candidates(struct f2fs_sb_info *sbi, >>> >>> down_write(&SIT_I(sbi)->sentry_lock); >>> for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) { >>> - if (add_discard_addrs(sbi, cpc, true)) { >>> + if (add_discard_addrs(sbi, cpc, false, true)) { >>> has_candidate = true; >>> break; >>> } >>> @@ -4611,7 +4611,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >>> /* add discard candidates */ >>> if (!(cpc->reason & CP_DISCARD)) { >>> cpc->trim_start = segno; >>> - add_discard_addrs(sbi, cpc, false); >>> + add_discard_addrs(sbi, cpc, false, false); >>> } >>> >>> if (to_journal) { >>> @@ -4653,7 +4653,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >>> __u64 trim_start = cpc->trim_start; >>> >>> for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) >>> - add_discard_addrs(sbi, cpc, false); >>> + add_discard_addrs(sbi, cpc, true, false); >>> >>> cpc->trim_start = trim_start; >>> } > > ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] f2fs: fix missing discard candidates in fstrim 2025-01-08 12:46 ` Chao Yu @ 2025-01-09 10:03 ` Chunhai Guo 2025-01-13 13:32 ` Chao Yu 0 siblings, 1 reply; 14+ messages in thread From: Chunhai Guo @ 2025-01-09 10:03 UTC (permalink / raw) To: Chao Yu, Chunhai Guo, jaegeuk@kernel.org Cc: linux-f2fs-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org 在 1/8/2025 8:46 PM, Chao Yu 写道: > On 2025/1/3 16:07, Chunhai Guo wrote: >> 在 1/3/2025 11:26 AM, Chao Yu 写道: >>> On 2025/1/2 18:13, Chunhai Guo wrote: >>>> fstrim may miss candidates that need to be discarded in fstrim, as shown in >>>> the examples below. >>>> The root cause is that when cpc->reason is set with CP_DISCARD, >>>> add_discard_addrs() expects that ckpt_valid_map and cur_valid_map have been >>>> synced by seg_info_to_raw_sit() [1] and tries to find the candidates based >>>> on ckpt_valid_map and discard_map. However, seg_info_to_raw_sit() does not >>>> actually run before f2fs_exist_trim_candidates(), which results in failure. >>> Chunhai, >>> >>> Can you please use nodiscard option due to fstrim stopped to return >>> trimmed length after below commit: >>> >>> 5a6154920faf ("f2fs: don't issue discard commands in online discard is on") >> Thank you for your explanation, but I guess this issue is not relevant >> to this commit, and I understand that '0 B (0 bytes) trimmed' is fine. >> >> The real problem is that there are actually many candidates that should >> be handled in fstrim, but it cannot find any of them. >> >> f2fs_trim_fs() >> f2fs_write_checkpoint() >> ... >> if (cpc->reason & CP_DISCARD) { >> if (!f2fs_exist_trim_candidates(sbi, cpc)) { >> unblock_operations(sbi); >> goto out; // Not candidate is found here and exit. >> } >> ... >> } >> >>>> root# cp testfile /f2fs_mountpoint >>>> >>>> root# f2fs_io fiemap 0 1 /f2fs_mountpoint/testfile >>>> Fiemap: offset = 0 len = 1 >>>> logical addr. physical addr. length flags >>>> 0 0000000000000000 0000000406a00000 000000003d800000 00001000 >>>> >>>> root# rm /f2fs_mountpoint/testfile >>>> >>>> root# fstrim -v -o 0x406a00000 -l 1024M /f2fs_mountpoint -- no candidate is found >>>> /f2fs_mountpoint: 0 B (0 bytes) trimmed >>>> >>>> [1] Please refer to commit d7bc2484b8d4 ("f2fs: fix small discards not to >>>> issue redundantly") for the relationship between seg_info_to_raw_sit() and >>>> add_discard_addrs(). >>>> >>>> Fixes: 25290fa5591d ("f2fs: return fs_trim if there is no candidate") >>>> Signed-off-by: Chunhai Guo <guochunhai@vivo.com> >>>> --- >>>> fs/f2fs/segment.c | 10 +++++----- >>>> 1 file changed, 5 insertions(+), 5 deletions(-) >>>> >>>> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c >>>> index eade36c5ef13..8fe9f794b581 100644 >>>> --- a/fs/f2fs/segment.c >>>> +++ b/fs/f2fs/segment.c >>>> @@ -2070,7 +2070,7 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi, >>>> } >>>> >>>> static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, >>>> - bool check_only) >>>> + bool synced, bool check_only) >>>> { >>>> int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); >>>> struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start); >>>> @@ -2098,7 +2098,7 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, >>>> >>>> /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ >>>> for (i = 0; i < entries; i++) >>>> - dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : >>>> + dmap[i] = synced ? ~ckpt_map[i] & ~discard_map[i] : >>> I guess this condition "force ? ~ckpt_map[i] & ~discard_map[i]" didn't cover >>> all below cases, thoughts? >>> - ckpt_map[i] == 0 // write data, and then remove data before checkpoint >>> - ckpt_map[i] != 0 // remove data existed in previous checkpoint >> From the handling of ckpt_valid_map in update_sit_entry(), I guess the >> condition can cover both cases. >> For example, when the checkpoint is enabled, all the set bits in the >> ckpt_valid_map remain set before the checkpoint (even when the blocks >> are deleted), which makes it find all the right bits in both cases. > My point is for fstrim case, we only need to check discard_map bitmap? > once bit(s) in discard_map bitmap is zero, no matter the status of > bit(s) in ckpt_map bitmap, we need to trigger a checkpoit for following > discard submission? Oh, yes. It is reasonable to check only the discard_map bitmap. What do you think about the code below? for (i = 0; i < entries; i++) { if (check_only) dmap[i] = ~discard_map[i]; else dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; } Thanks, > > Thanks, > >> Thanks, >> >>> Thanks, >>> >>>> (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; >>>> >>>> while (force || SM_I(sbi)->dcc_info->nr_discards <= >>>> @@ -3275,7 +3275,7 @@ bool f2fs_exist_trim_candidates(struct f2fs_sb_info *sbi, >>>> >>>> down_write(&SIT_I(sbi)->sentry_lock); >>>> for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) { >>>> - if (add_discard_addrs(sbi, cpc, true)) { >>>> + if (add_discard_addrs(sbi, cpc, false, true)) { >>>> has_candidate = true; >>>> break; >>>> } >>>> @@ -4611,7 +4611,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >>>> /* add discard candidates */ >>>> if (!(cpc->reason & CP_DISCARD)) { >>>> cpc->trim_start = segno; >>>> - add_discard_addrs(sbi, cpc, false); >>>> + add_discard_addrs(sbi, cpc, false, false); >>>> } >>>> >>>> if (to_journal) { >>>> @@ -4653,7 +4653,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >>>> __u64 trim_start = cpc->trim_start; >>>> >>>> for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) >>>> - add_discard_addrs(sbi, cpc, false); >>>> + add_discard_addrs(sbi, cpc, true, false); >>>> >>>> cpc->trim_start = trim_start; >>>> } >> ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] f2fs: fix missing discard candidates in fstrim 2025-01-09 10:03 ` Chunhai Guo @ 2025-01-13 13:32 ` Chao Yu 2025-01-19 13:34 ` Chunhai Guo 0 siblings, 1 reply; 14+ messages in thread From: Chao Yu @ 2025-01-13 13:32 UTC (permalink / raw) To: Chunhai Guo, jaegeuk@kernel.org Cc: chao, linux-f2fs-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org On 2025/1/9 18:03, Chunhai Guo wrote: > 在 1/8/2025 8:46 PM, Chao Yu 写道: >> On 2025/1/3 16:07, Chunhai Guo wrote: >>> 在 1/3/2025 11:26 AM, Chao Yu 写道: >>>> On 2025/1/2 18:13, Chunhai Guo wrote: >>>>> fstrim may miss candidates that need to be discarded in fstrim, as shown in >>>>> the examples below. >>>>> The root cause is that when cpc->reason is set with CP_DISCARD, >>>>> add_discard_addrs() expects that ckpt_valid_map and cur_valid_map have been >>>>> synced by seg_info_to_raw_sit() [1] and tries to find the candidates based >>>>> on ckpt_valid_map and discard_map. However, seg_info_to_raw_sit() does not >>>>> actually run before f2fs_exist_trim_candidates(), which results in failure. >>>> Chunhai, >>>> >>>> Can you please use nodiscard option due to fstrim stopped to return >>>> trimmed length after below commit: >>>> >>>> 5a6154920faf ("f2fs: don't issue discard commands in online discard is on") >>> Thank you for your explanation, but I guess this issue is not relevant >>> to this commit, and I understand that '0 B (0 bytes) trimmed' is fine. >>> >>> The real problem is that there are actually many candidates that should >>> be handled in fstrim, but it cannot find any of them. >>> >>> f2fs_trim_fs() >>> f2fs_write_checkpoint() >>> ... >>> if (cpc->reason & CP_DISCARD) { >>> if (!f2fs_exist_trim_candidates(sbi, cpc)) { >>> unblock_operations(sbi); >>> goto out; // Not candidate is found here and exit. >>> } >>> ... >>> } >>> >>>>> root# cp testfile /f2fs_mountpoint >>>>> >>>>> root# f2fs_io fiemap 0 1 /f2fs_mountpoint/testfile >>>>> Fiemap: offset = 0 len = 1 >>>>> logical addr. physical addr. length flags >>>>> 0 0000000000000000 0000000406a00000 000000003d800000 00001000 >>>>> >>>>> root# rm /f2fs_mountpoint/testfile >>>>> >>>>> root# fstrim -v -o 0x406a00000 -l 1024M /f2fs_mountpoint -- no candidate is found >>>>> /f2fs_mountpoint: 0 B (0 bytes) trimmed >>>>> >>>>> [1] Please refer to commit d7bc2484b8d4 ("f2fs: fix small discards not to >>>>> issue redundantly") for the relationship between seg_info_to_raw_sit() and >>>>> add_discard_addrs(). >>>>> >>>>> Fixes: 25290fa5591d ("f2fs: return fs_trim if there is no candidate") >>>>> Signed-off-by: Chunhai Guo <guochunhai@vivo.com> >>>>> --- >>>>> fs/f2fs/segment.c | 10 +++++----- >>>>> 1 file changed, 5 insertions(+), 5 deletions(-) >>>>> >>>>> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c >>>>> index eade36c5ef13..8fe9f794b581 100644 >>>>> --- a/fs/f2fs/segment.c >>>>> +++ b/fs/f2fs/segment.c >>>>> @@ -2070,7 +2070,7 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi, >>>>> } >>>>> >>>>> static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, >>>>> - bool check_only) >>>>> + bool synced, bool check_only) >>>>> { >>>>> int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); >>>>> struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start); >>>>> @@ -2098,7 +2098,7 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, >>>>> >>>>> /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ >>>>> for (i = 0; i < entries; i++) >>>>> - dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : >>>>> + dmap[i] = synced ? ~ckpt_map[i] & ~discard_map[i] : >>>> I guess this condition "force ? ~ckpt_map[i] & ~discard_map[i]" didn't cover >>>> all below cases, thoughts? >>>> - ckpt_map[i] == 0 // write data, and then remove data before checkpoint >>>> - ckpt_map[i] != 0 // remove data existed in previous checkpoint >>> From the handling of ckpt_valid_map in update_sit_entry(), I guess the >>> condition can cover both cases. >>> For example, when the checkpoint is enabled, all the set bits in the >>> ckpt_valid_map remain set before the checkpoint (even when the blocks >>> are deleted), which makes it find all the right bits in both cases. >> My point is for fstrim case, we only need to check discard_map bitmap? >> once bit(s) in discard_map bitmap is zero, no matter the status of >> bit(s) in ckpt_map bitmap, we need to trigger a checkpoit for following >> discard submission? > > > Oh, yes. It is reasonable to check only the discard_map bitmap. What do > you think about the code below? > > for (i = 0; i < entries; i++) { > if (check_only) > dmap[i] = ~discard_map[i]; > else > dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : > (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; How about this? dmap[i] = force ? ~discard_map[i] : (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; Thanks, > } > > Thanks, > >> >> Thanks, >> >>> Thanks, >>> >>>> Thanks, >>>> >>>>> (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; >>>>> >>>>> while (force || SM_I(sbi)->dcc_info->nr_discards <= >>>>> @@ -3275,7 +3275,7 @@ bool f2fs_exist_trim_candidates(struct f2fs_sb_info *sbi, >>>>> >>>>> down_write(&SIT_I(sbi)->sentry_lock); >>>>> for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) { >>>>> - if (add_discard_addrs(sbi, cpc, true)) { >>>>> + if (add_discard_addrs(sbi, cpc, false, true)) { >>>>> has_candidate = true; >>>>> break; >>>>> } >>>>> @@ -4611,7 +4611,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >>>>> /* add discard candidates */ >>>>> if (!(cpc->reason & CP_DISCARD)) { >>>>> cpc->trim_start = segno; >>>>> - add_discard_addrs(sbi, cpc, false); >>>>> + add_discard_addrs(sbi, cpc, false, false); >>>>> } >>>>> >>>>> if (to_journal) { >>>>> @@ -4653,7 +4653,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >>>>> __u64 trim_start = cpc->trim_start; >>>>> >>>>> for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) >>>>> - add_discard_addrs(sbi, cpc, false); >>>>> + add_discard_addrs(sbi, cpc, true, false); >>>>> >>>>> cpc->trim_start = trim_start; >>>>> } >>> > ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] f2fs: fix missing discard candidates in fstrim 2025-01-13 13:32 ` Chao Yu @ 2025-01-19 13:34 ` Chunhai Guo 0 siblings, 0 replies; 14+ messages in thread From: Chunhai Guo @ 2025-01-19 13:34 UTC (permalink / raw) To: Chao Yu, Chunhai Guo, jaegeuk@kernel.org Cc: linux-f2fs-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org 在 1/13/2025 9:32 PM, Chao Yu 写道: > On 2025/1/9 18:03, Chunhai Guo wrote: >> 在 1/8/2025 8:46 PM, Chao Yu 写道: >>> On 2025/1/3 16:07, Chunhai Guo wrote: >>>> 在 1/3/2025 11:26 AM, Chao Yu 写道: >>>>> On 2025/1/2 18:13, Chunhai Guo wrote: >>>>>> fstrim may miss candidates that need to be discarded in fstrim, as shown in >>>>>> the examples below. >>>>>> The root cause is that when cpc->reason is set with CP_DISCARD, >>>>>> add_discard_addrs() expects that ckpt_valid_map and cur_valid_map have been >>>>>> synced by seg_info_to_raw_sit() [1] and tries to find the candidates based >>>>>> on ckpt_valid_map and discard_map. However, seg_info_to_raw_sit() does not >>>>>> actually run before f2fs_exist_trim_candidates(), which results in failure. >>>>> Chunhai, >>>>> >>>>> Can you please use nodiscard option due to fstrim stopped to return >>>>> trimmed length after below commit: >>>>> >>>>> 5a6154920faf ("f2fs: don't issue discard commands in online discard is on") >>>> Thank you for your explanation, but I guess this issue is not relevant >>>> to this commit, and I understand that '0 B (0 bytes) trimmed' is fine. >>>> >>>> The real problem is that there are actually many candidates that should >>>> be handled in fstrim, but it cannot find any of them. >>>> >>>> f2fs_trim_fs() >>>> f2fs_write_checkpoint() >>>> ... >>>> if (cpc->reason & CP_DISCARD) { >>>> if (!f2fs_exist_trim_candidates(sbi, cpc)) { >>>> unblock_operations(sbi); >>>> goto out; // Not candidate is found here and exit. >>>> } >>>> ... >>>> } >>>> >>>>>> root# cp testfile /f2fs_mountpoint >>>>>> >>>>>> root# f2fs_io fiemap 0 1 /f2fs_mountpoint/testfile >>>>>> Fiemap: offset = 0 len = 1 >>>>>> logical addr. physical addr. length flags >>>>>> 0 0000000000000000 0000000406a00000 000000003d800000 00001000 >>>>>> >>>>>> root# rm /f2fs_mountpoint/testfile >>>>>> >>>>>> root# fstrim -v -o 0x406a00000 -l 1024M /f2fs_mountpoint -- no candidate is found >>>>>> /f2fs_mountpoint: 0 B (0 bytes) trimmed >>>>>> >>>>>> [1] Please refer to commit d7bc2484b8d4 ("f2fs: fix small discards not to >>>>>> issue redundantly") for the relationship between seg_info_to_raw_sit() and >>>>>> add_discard_addrs(). >>>>>> >>>>>> Fixes: 25290fa5591d ("f2fs: return fs_trim if there is no candidate") >>>>>> Signed-off-by: Chunhai Guo<guochunhai@vivo.com> >>>>>> --- >>>>>> fs/f2fs/segment.c | 10 +++++----- >>>>>> 1 file changed, 5 insertions(+), 5 deletions(-) >>>>>> >>>>>> diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c >>>>>> index eade36c5ef13..8fe9f794b581 100644 >>>>>> --- a/fs/f2fs/segment.c >>>>>> +++ b/fs/f2fs/segment.c >>>>>> @@ -2070,7 +2070,7 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi, >>>>>> } >>>>>> >>>>>> static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, >>>>>> - bool check_only) >>>>>> + bool synced, bool check_only) >>>>>> { >>>>>> int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long); >>>>>> struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start); >>>>>> @@ -2098,7 +2098,7 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc, >>>>>> >>>>>> /* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */ >>>>>> for (i = 0; i < entries; i++) >>>>>> - dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : >>>>>> + dmap[i] = synced ? ~ckpt_map[i] & ~discard_map[i] : >>>>> I guess this condition "force ? ~ckpt_map[i] & ~discard_map[i]" didn't cover >>>>> all below cases, thoughts? >>>>> - ckpt_map[i] == 0 // write data, and then remove data before checkpoint >>>>> - ckpt_map[i] != 0 // remove data existed in previous checkpoint >>>> From the handling of ckpt_valid_map in update_sit_entry(), I guess the >>>> condition can cover both cases. >>>> For example, when the checkpoint is enabled, all the set bits in the >>>> ckpt_valid_map remain set before the checkpoint (even when the blocks >>>> are deleted), which makes it find all the right bits in both cases. >>> My point is for fstrim case, we only need to check discard_map bitmap? >>> once bit(s) in discard_map bitmap is zero, no matter the status of >>> bit(s) in ckpt_map bitmap, we need to trigger a checkpoit for following >>> discard submission? >> Oh, yes. It is reasonable to check only the discard_map bitmap. What do >> you think about the code below? >> >> for (i = 0; i < entries; i++) { >> if (check_only) >> dmap[i] = ~discard_map[i]; >> else >> dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] : >> (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; > How about this? > > dmap[i] = force ? ~discard_map[i] : > (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; I think it is OK, too. I believe it is identical for "~discard_map[i]" and "~ckpt_map[i] & ~discard_map[i]" for fstrim here. Moreover, I think the code logic can be simplified for all cases by finding all the discard blocks based only on discard_map, as shown in the code below. This might result in more discard blocks being sent for the segment during the first checkpoint after mounting, which were originally expected to be sent only in fstrim. Regardless, these discard blocks should eventually be sent, and the simplified code makes sense in this context. dmap[i] = ~discard_map[i]; I will send you the v2 patch for review. Thanks, > Thanks, > > >> } >> >> Thanks, >> >>> Thanks, >>> >>>> Thanks, >>>> >>>>> Thanks, >>>>> >>>>>> (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; >>>>>> >>>>>> while (force || SM_I(sbi)->dcc_info->nr_discards <= >>>>>> @@ -3275,7 +3275,7 @@ bool f2fs_exist_trim_candidates(struct f2fs_sb_info *sbi, >>>>>> >>>>>> down_write(&SIT_I(sbi)->sentry_lock); >>>>>> for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) { >>>>>> - if (add_discard_addrs(sbi, cpc, true)) { >>>>>> + if (add_discard_addrs(sbi, cpc, false, true)) { >>>>>> has_candidate = true; >>>>>> break; >>>>>> } >>>>>> @@ -4611,7 +4611,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >>>>>> /* add discard candidates */ >>>>>> if (!(cpc->reason & CP_DISCARD)) { >>>>>> cpc->trim_start = segno; >>>>>> - add_discard_addrs(sbi, cpc, false); >>>>>> + add_discard_addrs(sbi, cpc, false, false); >>>>>> } >>>>>> >>>>>> if (to_journal) { >>>>>> @@ -4653,7 +4653,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) >>>>>> __u64 trim_start = cpc->trim_start; >>>>>> >>>>>> for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) >>>>>> - add_discard_addrs(sbi, cpc, false); >>>>>> + add_discard_addrs(sbi, cpc, true, false); >>>>>> >>>>>> cpc->trim_start = trim_start; >>>>>> } ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2025-05-23 8:15 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-01-02 10:13 [PATCH 1/2] f2fs: fix missing discard candidates in fstrim Chunhai Guo 2025-01-02 10:13 ` [PATCH 2/2] f2fs: fix missing small discard " Chunhai Guo 2025-01-03 3:36 ` Chao Yu 2025-01-03 7:00 ` Chunhai Guo 2025-05-21 14:05 ` Chunhai Guo 2025-05-22 8:48 ` Chao Yu 2025-05-22 11:11 ` Chao Yu 2025-05-23 8:15 ` Chunhai Guo 2025-01-03 3:26 ` [PATCH 1/2] f2fs: fix missing discard candidates " Chao Yu 2025-01-03 8:07 ` Chunhai Guo 2025-01-08 12:46 ` Chao Yu 2025-01-09 10:03 ` Chunhai Guo 2025-01-13 13:32 ` Chao Yu 2025-01-19 13:34 ` Chunhai Guo
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).