]> git.hungrycats.org Git - linux/commitdiff
btrfs: allow balance usage filter to select full block groups
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Mon, 16 Mar 2020 03:55:28 +0000 (23:55 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Fri, 18 Sep 2026 21:36:33 +0000 (17:36 -0400)
There's a gap in what we can specify with the current balance usage
filter.  This command doesn't balance all data block groups:

        1.  btrfs balance start -dusage=0..100 /fs

and neither does this one:

        2.  btrfs balance start -dusage=100 /fs

and since commit a105bb88f46b6 in 2013, we can't work around with:

        3.  btrfs balance start -dusage=101 /fs/that/is/raid1

and there's a related problem when resuming paused balances, e.g. during
a mount.

Commands #1 and #2 fail as documented in the btrfs-balance man page:

       usage=<percent>, usage=<range>
           Balances only block groups with usage under the given
           percentage.

Since completely full block groups are not "under" 100%, the filter
expression "usage=100" covers any block group with at least 1 unused byte,
but not completely full block groups.

Command #3 fails because of commit a105bb88f46b6 "Btrfs: fix a regression
in balance usage filter" which removed the ability to specify percentage
values over 100; however, we don't want to revert that because we do
want to guard against integer overflows and other surprises that arise
from truly bad user inputs.

There are a few ways to fix this.  This patch implements one that does not
alter any existing behavior:  we allow 'usage=101' with the meaning "100%
+ 1 byte".  This leaves "usage=100" referring to any block group that is
not completely full, in case anyone relies on that filter behavior, while
allowing a method for accepting completely full blocks in the filter.
It also allows 'usage=101' to work around the balance recovery issue
below.

Starting with commit 596410151e "Btrfs: recover balance on mount",
btrfs will insert a 'usage=90' filter when resuming a balance if no
'usage' filter was used on the original balance.  If the original balance
does not use 'convert' or 'usage', then 'usage=90' will be inserted.
This can be surprising when pausing balances to let snapshot deletes run,
as in this log:

        BTRFS info (device dm-0): balance: start -dlimit=999
        BTRFS info (device dm-0): relocating block group 1776771137536 flags data
        BTRFS info (device dm-0): found 18 extents, stage: move data extents
        BTRFS info (device dm-0): found 18 extents, stage: update data pointers
        BTRFS info (device dm-0): balance: paused
        BTRFS debug (device dm-0): cleaner removing 2649
        BTRFS info (device dm-0): balance: resume -dusage=90,limit=998
        BTRFS info (device dm-0): balance: ended with status: 0

We expected to balance 999 block groups, instead we got one, because the
resumed balance used a very different filter than the original.  If we set
'usage=100', we avoid this behavior on balance resume, but we also fail
to balance all but one of the full block groups.  To work around this,
we need a way to set the BTRFS_BALANCE_ARGS_USAGE flag without losing
the ability to select full block groups.

Signed-off-by: Zygo Blaxell <ce3g8jdj@umail.furryterror.org>
(cherry picked from commit f8392690b5fb1010484f7fa33b36e37c4a894de2)

fs/btrfs/volumes.c

index 37b54ce2d5366e1e8258ffcf6a2c174cec56a7c1..6a59b9a2cd479c7199e41afdb66fda6eab370b93 100644 (file)
@@ -4237,7 +4237,7 @@ static int chunk_usage_range_filter(struct btrfs_fs_info *fs_info, u64 chunk_off
        if (bargs->usage_max == 0)
                user_thresh_max = 1;
        else if (bargs->usage_max > 100)
-               user_thresh_max = cache->length;
+               user_thresh_max = cache->length + 1;
        else
                user_thresh_max = mult_perc(cache->length, bargs->usage_max);
 
@@ -4266,7 +4266,7 @@ static int chunk_usage_filter(struct btrfs_fs_info *fs_info, u64 chunk_offset,
        if (bargs->usage_min == 0)
                user_thresh = 1;
        else if (bargs->usage > 100)
-               user_thresh = cache->length;
+               user_thresh = cache->length + 1;
        else
                user_thresh = mult_perc(cache->length, bargs->usage);