Re: [PATCH] btrfs: fix u32 to s64 type conversion in dirty_metadata_bytes accounting

David Sterba <[email protected]>
Newsgroups org.kernel.vger.linux-btrfs
Message-ID <[email protected]>
On Mon, Jun 29, 2026 at 01:08:05PM +0100, Filipe Manana wrote:
> On Mon, Jun 29, 2026 at 8:09 AM Dave Chen <[email protected]> wrote:
> >
> > The percpu_counter dirty_metadata_bytes is updated by negating eb->len
> > and passing it to percpu_counter_add_batch(), whose amount parameter is
> > s64.  Since commit 84cda1a6087d ("btrfs: cache folio size and shift in
> > extent_buffer"), eb->len is u32.  The u32 result of -eb->len, when
> > widened to the s64 parameter, becomes a large positive value instead of
> > the intended negative value.  For eb->len == 16384 the counter adds
> > +4294950912 instead of subtracting 16384.
> >
> > The counter therefore grows on every metadata writeback instead of
> > shrinking by the extent buffer size, permanently exceeding
> > BTRFS_DIRTY_METADATA_THRESH and causing __btrfs_btree_balance_dirty()
> > to trigger balance_dirty_pages_ratelimited() unconditionally, adding
> > unnecessary writeback pressure.
> >
> > Cast eb->len to s64 before negation at both call sites so the
> > subtraction is performed in signed 64-bit arithmetic.
> >
> > Signed-off-by: Dave Chen <[email protected]>
> 
> Curious, do you have any performance data to give an idea of the impact?
> 
> I'm asking not only for clarity but also to determine if adding a
> Fixes tag for stable backports makes sense.
> The commit that caused this issue is 84cda1a6087d ("btrfs: cache folio
> size and shift in extent_buffer"), which changed struct
> extent_buffer::len from unsigned long (64-bit on 64 bits platforms) to
> u32.

Oh my that's very well hidden signedness bug. I'd rather not rely on the
correct implicit conversions and add a helper like
percpu_counter_sub_batch() that takes the positive value to subtract and
correctly casts it to s64 inside.

We have more places with negative unsigned value passed to
percpu_counter_add_batch(), e.g. in btrfs_remove_ordered_extent().  The
entry->num_bytes is u64 so it's correct.

In the rest of kernel the negative values are not used frequently, in
xfs, networking, bpf.

This fix can go in as-is as it's minimal, the API update can follow so
we can be sure gems like

lib/flex_proportions.c:fprop_reflect_period_percpu()

		percpu_counter_add_batch(&pl->events,
                       -val + (val >> (period-pl->period)), PROP_BATCH);

can be made more understandable.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.