Re: [PATCH] fuse: disable default bdi strictlimiting

Joanne Koong <[email protected]> Thu, 16 Jul 2026 15:31:00 -0700
Newsgroups dev.linux.lists.fuse-devel,org.kernel.vger.linux-fsdevel
Message-ID <CAJnrk1aTx44=jy0ODHY2sHh16bKsYkwBKj_O-MsnZYjXGW2cCA@mail.gmail.com>
On Thu, Jul 16, 2026 at 8:12=E2=80=AFAM Shakeel Butt <[email protected]=
v> wrote:
>
> Hi Jan & Joanne,
>
> On Wed, Jul 15, 2026 at 06:28:16PM -0700, Joanne Koong wrote:
> > On Wed, Jul 15, 2026 at 3:32=E2=80=AFAM Jan Kara <[email protected]> wrote:
> > >
> > > [resending once more, this time with correct Shakeel's address]
> > >
> > > On Tue 14-07-26 18:21:57, Joanne Koong wrote:
> > > > On Tue, Jul 14, 2026 at 10:10=E2=80=AFAM Jan Kara <[email protected]> wr=
ote:
> > > > > On Mon 13-07-26 18:13:29, Joanne Koong wrote:
> > > > > > On Sat, May 30, 2026 at 4:04=E2=80=AFAM Jan Kara <[email protected]>=
 wrote:
> > > > > > > On Thu 28-05-26 15:11:18, Joanne Koong wrote:
> > > > > > > > On Thu, May 28, 2026 at 5:34=E2=80=AFAM Jan Kara <jack@suse=
.cz> wrote:
> > > > > > > > > > I think this is also going to be a problem for cgroups =
with large
> > > > > > > > > > folios since they also, as I understand it, are constra=
ined with a
> > > > > > > > > > limited / tight dirty budget. I ran some initial benchm=
arks with
> > > > > > > > > > cgroup memory constraints on NVMe and saw similar insta=
bility (a
> > > > > > > > > > single writer in a 8 GB cgroup had max write latencies =
of 6 seconds vs
> > > > > > > > > > 15 ms without the cgroup, with the balance_dirty_pages(=
) throttling
> > > > > > > > > > oscillating rather than settling near the set point).
> > > > > > > > >
> > > > > > > > > Ok, so this is more folios (819) than my 512 gut feeling =
estimate :) What
> > > > > > > > > was the write throughput of the NVMe drive? The high driv=
e throughput also
> > > > > > > > > requires more dirty data to keep the drive saturated so t=
hat writeback
> > > > > > > > > throughput doesn't oscilate too much.
> > > > > > > >
> > > > > > > > The write throughput of the NVMe drive I was using was arou=
nd ~1.1
> > > > > > > > Gb/s (measured by running direct I/O). I think with the 1.6=
GB dirty
> > > > > > > > budget, the math for that ends up being that the device dra=
ins it in
> > > > > > > > ~1.5 secs. The performance I was seeing with the initial cg=
roup
> > > > > > > > benchmarks was with 4k pages (no large folios enabled) on b=
trfs.
> > > > > > >
> > > > > > > OK, you might want to experiment with some other filesystem (=
I suggest xfs
> > > > > > > or ext4) as well. Btrfs writeback behavior is a bit special w=
ith its data
> > > > > > > checksum computations etc. and thus latency of starting write=
back. It could
> > > > > > > contribute to the erratic behavior with the relatively low di=
rty limits.
> > > > > >
> > > > > > I reran the benchmarks on xfs and ext4 and saw similiar results=
. The
> > > > > > max write latency under the memcg were
> > > > > > xfs        5.1s
> > > > > > ext4      5.2 s
> > > > > > btrfs      6.2 s
> > > > > >
> > > > > > compared to the non-memcg case (0.4 to 2.0 ms). This didn't aff=
ect
> > > > > > throughput though, just the tail latency. When I ran xfs with l=
arge
> > > > > > folios disabled, I still saw ~4.1s.
> > > > >
> > > > > OK, interesting. Thanks for running these tests! Also it is good =
to know
> > > > > this is not really related to large folios, that somewhat simplif=
ies
> > > > > matters.
> > > > >
> > > > > > From the balance_dirty_pages() ftrace tracepoints, the dirty_ra=
telimit
> > > > > > value is consistently stable / accurate but it looks like what'=
s
> > > > > > happening is that during freerun, the writer essentially dirtie=
s at
> > > > > > memcpy speed until the freerun ceiling, and then the soft/propo=
rtional
> > > > > > throttling kicks in but doesn't kick in fast or hard enough, wh=
ich
> > > > > > allows the number of dirty pages to exceed the hard limit by ~2=
x, and
> > > > > > then at that point the writer is forced into the loop where it =
sleeps
> > > > > > max_pause (200ms) each iteration until writeback has drained th=
e
> > > > > > number of dirty pages back under the limit.
> > > > >
> > > > > OK, above you mentioned that the dirty limit for the memcgs is se=
t at
> > > > > 1.6GB. Does that mean that dirty throttling allows memcg to dirty=
 up to
> > > > > ~3.2GB of pages? I wouldn't have expected that...
> > > >
> > > > On a couple of the runs, I saw it get as high as up to 5GB.
> > > >
> > > > I think this is because the balance_dirty_pages() code uses the mem=
cg
> > > > stats (NR_FILE_DIRTY) but these stats are only periodically
> > > > flushed/refreshed when reading it, so the balance dirty code is
> > > > seeing/using lagging/non-uptodate values.
> > > >
> > > > I'm seeing this in
> > > >   balance_dirty_pages()
> > > >       balance_domain_limits(mdtc,...)
> > > >           domain_dirty_avail()
> > > >                 mem_cgroup_wb_stats()
> > > >                        mem_cgroup_flush_stats_ratelimited()
> > > >
> > > > where mem_cgroup_flush_stats_ratelimted() has this logic:
> > > >
> > > > void mem_cgroup_flush_stats_ratelimited(struct mem_cgroup *memcg)
> > > > {
> > > >         /* Only flush if the periodic flusher is one full cycle lat=
e */
> > > >         if (time_after64(jiffies_64, READ_ONCE(flush_last_time) + 2=
*FLUSH_TIME))
> > > >                 mem_cgroup_flush_stats(memcg);
> > > > }
> > > >
> > > > where FLUSH_TIME is defined as 2UL * HZ, which afaict means the
> > > > flushing can be around 2 seconds stale (accounting for the periodic
> > > > flusher in flush_memcg_stats_dwork() that flushes every FLUSH_TIME
> > > > interval).
> > > >
> > > > From what I see, the freerun and hard limit checks use this stale
> > > > under-reported value (in domain_dirty_freerun() and
> > > > wb_position_ratio()), which means the writer gets to keep dirtying =
and
> > > > blow past the limit for up to 2 extra seconds before the throttling
> > > > limits it.
> > >
> > > I see. That would indeed explain why we can overrun the dirty limit s=
o
> > > much.
> > >
> > > > Maybe one idea is to in the memcg throttle path do the flush based =
on
> > > > how many pages are getting dirtied instead of on how much time has
> > > > elapsed? It looks like flushing grabs the rstat lock though, so may=
be
> > > > only doing it in the throttled case where it's past freerun would b=
e
> > > > best. It looks like the logic in the regular non-ratelimited
> > > > mem_cgroup_flush_stats() call does something similar. I can run som=
e
> > > > experiments this week with this if you think it could be promising.
> > >
> > > Checking some git history, this actually seems to be a relatively rec=
ent
> > > change from Shakeel (added to CC) - d9b3ce8769e3 ("mm: writeback: rat=
elimit
> > > stat flush from mem_cgroup_wb_stats"). So you can check whether rever=
ting
> > > that change makes dirty throttling not overshoot so much.
> > >
> >
> > Reverting d9b3ce8769e3 fixes the issue. On xfs, I'm now seeing the
> > number of dirty pages stays bounded by the limit instead of going 2x+
> > over, and the max latency is now around ~0.33s instead of ~4s.
> >
> > > Shakeel, your change to use mem_cgroup_flush_stats_ratelimited() in
> > > mem_cgroup_wb_stats() results in applications overshooting memcg dirt=
y
> > > limits by several gigabytes which for small memcgs (with 8GB memory i=
n this
> > > case, which means a dirty limit of ~1.5GB) is a considerable issue. I=
 guess
> > > the ratelimiting should take into account the size of memcg (or ideal=
ly the
> > > dirty limit in the case of mem_cgroup_wb_stats()) as well. Using
> > > somewhat outdated numbers for dirty throttling is fine but when we ca=
n
> > > overshoot the dirty limit more than three times, it leads to rather l=
ong
> > > stalls in dirty throttling and similar issues...
>
> Yeah I agree this is an issue.
>
> >
> > It looks like commit d9b3ce8769e3 was landed in Feb 2024 when rstat
> > was still using the single global lock. I see commit 748922dcfabd
> > ("cgroup: use subsystem-specific rstat locks to avoid contention")
> > later in the tree, which was landed around May 2025 and reduces that
> > global lock contention. That might make the cost of the flush a lot
> > lower than what it was when the ratelimit was needed in d9b3ce8769e3,
> > so maybe reverting d9b3ce8769e3 wouldn't make much of a difference for
> > write-heavy workloads and would be the simplest fix?
>
> Unfortunetely global lock was just part of the cost and even per-subsyste=
m
> lock reduces contention across subsystems but flushing is still expensive=
 as the
> kernel may have to traverse the cgroup update tree for each cpu on the sy=
stem.

Ah I see, thanks for the context.

>
> CCing Yosry who is looking into similar issue but for zswap stats. Maybe =
a
> general solution for such specific stats is to move them from rstat to a
> separate mechanism where update can be a bit more expensive but the read =
side
> can be cheap.
>
> > If it does still
> > regress, I'd be happy to look into making the flush based on how many
> > pages have been dirtied and scaled to the dirty limit, if that sounds
> > like the best way forward.
>
> First let's see if the general solution is doable and simple otherwise we=
 can
> explore use-case specific solutions. In extreme case revert is an options=
 as
> well but I would prefer a more sophisticated ratelimiting (that Jan sugge=
sted)
> than a revert.
>

Sounds good. I'll keep an eye out for the work Yosry's doing on this
and look into hooking it up to writeback if/when his general solution
becomes available.

Thanks,
Joanne

> > I tried running the sysbench
> > oltp_write_only benchmark mentioned in d9b3ce8769e3 on my setup but
> > couldn't get the database to dirty pages fast enough to trigger the
> > balance dirty pages logic to see how it performs without the
> > ratelimiting. If you still have the script for how you ran the test
> > back in 2024, I'm happy to give it a go on my local machine.
>
> Unfortunately I don't have those scripts or setup.