All of lore.kernel.org
 help / color / mirror / Atom feed
From: Anand Jain <anajain.sg@gmail.com>
To: Boris Burkov <boris@bur.io>,
	linux-btrfs@vger.kernel.org, kernel-team@fb.com,
	fstests@vger.kernel.org
Subject: Re: [PATCH 2/2] btrfs: test qgroup deletion races with squota writes
Date: Mon, 18 May 2026 17:18:18 +0800	[thread overview]
Message-ID: <e29e3b26-bd22-41d7-92f0-8d89bf73f5ea@gmail.com> (raw)
In-Reply-To: <2c8668284ded13c7cc63ece213d9e1edc3e6ea85.1778632843.git.boris@bur.io>



It appears that transaction commit could race with the testcase.


--------
SECTION       -- gen
FSTYP         -- btrfs
PLATFORM      -- Linux/x86_64 vm1 7.1.0-rc3-gf5d9f327e4b2 #14 SMP
PREEMPT_DYNAMIC Sun May 17 21:25:06 +08 2026
MKFS_OPTIONS  -- -f /dev/vde
MOUNT_OPTIONS -- -o context=system_u:object_r:root_t:s0 /dev/vde
/mnt/scratch

btrfs/348    10s ... - output mismatch (see
/Volumes/work/ws/fstests/results//gen/btrfs/348.out.bad)
    --- tests/btrfs/348.out	2026-05-18 14:04:31.427635000 +0800
    +++ /Volumes/work/ws/fstests/results//gen/btrfs/348.out.bad
2026-05-18 17:13:33.000000000 +0800
    @@ -5,6 +5,4 @@
     ERROR: unable to destroy quota group: Device or resource busy
     ERROR: unable to create quota group: Invalid argument
     same txn destroy race
    -ERROR: unable to destroy quota group: Device or resource busy
     pre-existing data destroy
    -ERROR: unable to destroy quota group: Device or resource busy
    ...
    (Run 'diff -u /Volumes/work/ws/fstests/tests/btrfs/348.out
/Volumes/work/ws/fstests/results//gen/btrfs/348.out.bad'  to see the
entire di)

HINT: You _MAY_ be missing kernel fix:
      XXXXXXXXXXXX btrfs: check for subvolume before deleting squota qgroup

HINT: You _MAY_ be missing kernel fix:
      0c309d66dacd btrfs: forbid creating subvol qgroups

Ran: btrfs/348
Failures: btrfs/348
Failed 1 of 1 tests


Thanks, Anand







On 13/5/26 08:43, Boris Burkov wrote:
> When using simple quotas, an extent's EXTENT_OWNER_REF can long outlive
> the subvolume that created it, since the extent can pick up additional
> references that keep it alive after the owning subvolume is deleted.
> 
> Several lifecycle bugs around the owning qgroup arise from this:
> 
>   1. Freeing an extent whose owner qgroup is gone: must not cause an
>      underflow nor a transaction abort.
> 
>   2. Creating an extent in the same transaction that the owner subvolume
>      is deleted: the qgroup may already be gone by the time the squota
>      delta is recorded, leaving behind an EXTENT_OWNER_REF that points
>      at a qgroup that never accumulated a usage delta.  Manually
>      re-creating that qgroup and then freeing the extent would underflow
>      it.
> 
>   3. Destroying a live subvolume's qgroup while delayed refs for newly
>      allocated tree blocks have not yet flushed: rfer/excl read as 0,
>      so the destroy looked safe, but the alloc delta was silently lost.
> 
>   4. Destroying the qgroup of a live subvolume whose data predates
>      'btrfs quota enable --simple': pre-existing extents are not
>      accounted (generation < qgroup_enable_gen), so rfer/excl stay 0
>      permanently and a usage-only check happily destroys a qgroup
>      belonging to a still-mounted, still-writeable subvolume.
> 
> Add four cases covering these scenarios.  On a fixed kernel all four
> 'qgroup destroy' (and the re-create in case 2) operations are refused
> because the subvol check rejects the request; on an unfixed kernel
> they would succeed and leave the filesystem with broken accounting or
> trigger a transaction abort.
> 
> Signed-off-by: Boris Burkov <boris@bur.io>
> ---
>  tests/btrfs/348     | 136 ++++++++++++++++++++++++++++++++++++++++++++
>  tests/btrfs/348.out |  10 ++++
>  2 files changed, 146 insertions(+)
>  create mode 100755 tests/btrfs/348
>  create mode 100644 tests/btrfs/348.out
> 
> diff --git a/tests/btrfs/348 b/tests/btrfs/348
> new file mode 100755
> index 00000000..ab816301
> --- /dev/null
> +++ b/tests/btrfs/348
> @@ -0,0 +1,136 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (c) 2024 Meta Platforms, Inc.  All Rights Reserved.
> +#
> +# FS QA Test 348
> +#
> +# Test various race conditions between qgroup deletion and squota writes
> +#
> +. ./common/preamble
> +_begin_fstest auto qgroup subvol clone
> +
> +# Import common functions.
> +. ./common/reflink
> +
> +# real QA test starts here
> +
> +_require_scratch_reflink
> +_require_cp_reflink
> +_require_scratch_enable_simple_quota
> +
> +_fixed_by_kernel_commit XXXXXXXXXXXX \
> +	"btrfs: check for subvolume before deleting squota qgroup"
> +_fixed_by_kernel_commit 0c309d66dacd \
> +	"btrfs: forbid creating subvol qgroups"
> +
> +subv1=$SCRATCH_MNT/subv1
> +subv2=$SCRATCH_MNT/subv2
> +
> +prepare()
> +{
> +	_scratch_mkfs >> $seqres.full
> +	_scratch_mount
> +	$BTRFS_UTIL_PROG quota enable --simple $SCRATCH_MNT
> +	$BTRFS_UTIL_PROG subvolume create $subv1 >> $seqres.full
> +	$BTRFS_UTIL_PROG subvolume create $subv2 >> $seqres.full
> +	$XFS_IO_PROG -fc "pwrite -q 0 128K" $subv1/f
> +	_cp_reflink $subv1/f $subv2/f
> +}
> +
> +# An extent can long outlive its owner. Test this by deleting the owning
> +# subvolume, committing the transaction, then deleting the reflinked copy.
> +free_from_deleted_owner()
> +{
> +	echo "free from deleted owner"
> +	prepare
> +	local subvid1=$(_btrfs_get_subvolid $SCRATCH_MNT subv1)
> +
> +	$BTRFS_UTIL_PROG filesystem sync $SCRATCH_MNT
> +	$BTRFS_UTIL_PROG subvolume delete $subv1 >> $seqres.full
> +	$BTRFS_UTIL_PROG qgroup destroy 0/$subvid1 $SCRATCH_MNT >> $seqres.full
> +	$BTRFS_UTIL_PROG filesystem sync $SCRATCH_MNT
> +	rm $subv2/f
> +	_scratch_unmount
> +}
> +
> +# A race where we delete the owner in the same transaction as writing the
> +# extent leads to incrementing the squota usage of the missing qgroup.
> +# This leaves behind an owner ref with an owner id that cannot exist, so
> +# freeing the extent now frees from that qgroup, but there has never
> +# been a corresponding usage to free.
> +add_to_deleted_owner()
> +{
> +	echo "add to deleted owner"
> +	prepare
> +	local subvid1=$(_btrfs_get_subvolid $SCRATCH_MNT subv1)
> +
> +	$BTRFS_UTIL_PROG filesystem sync $SCRATCH_MNT
> +	$BTRFS_UTIL_PROG subvolume delete $subv1 >> $seqres.full
> +	$BTRFS_UTIL_PROG qgroup destroy 0/$subvid1 $SCRATCH_MNT >> $seqres.full
> +	$BTRFS_UTIL_PROG filesystem sync $SCRATCH_MNT
> +	$BTRFS_UTIL_PROG qgroup create 0/$subvid1 $SCRATCH_MNT >> $seqres.full
> +	rm $subv2/f
> +	_scratch_unmount
> +}
> +
> +# Create a subvol, destroy its qgroup before delayed refs update rfer/excl.
> +# On an unfixed kernel the qgroup destroy succeeds (rfer == 0), silently
> +# losing the alloc delta. On a fixed kernel the destroy is refused because
> +# the ROOT_ITEM still exists.
> +same_txn_destroy_race()
> +{
> +	echo "same txn destroy race"
> +	prepare
> +
> +	# Commit so the next subvol create starts a fresh transaction.
> +	$BTRFS_UTIL_PROG filesystem sync $SCRATCH_MNT
> +
> +	# New subvol -- tree block alloc delayed ref is pending, rfer/excl=0.
> +	local subv3=$SCRATCH_MNT/subv3
> +	$BTRFS_UTIL_PROG subvolume create $subv3 >> $seqres.full
> +	local subvid3=$(_btrfs_get_subvolid $SCRATCH_MNT subv3)
> +
> +	# rfer/excl still 0 (delayed refs not flushed). Destroy should fail
> +	# because the ROOT_ITEM exists.
> +	$BTRFS_UTIL_PROG qgroup destroy 0/$subvid3 $SCRATCH_MNT 2>&1
> +
> +	_scratch_unmount
> +}
> +
> +# Enable squotas on a filesystem that already has a subvolume with data.
> +# Pre-existing extents have generation < qgroup_enable_gen, so the qgroup's
> +# rfer/excl stay 0 permanently. On an unfixed kernel can_delete_squota_qgroup
> +# only checks rfer/excl, so the qgroup can be destroyed for a live subvol.
> +# On a fixed kernel, the subvol check prevents deletion.
> +pre_existing_data_destroy()
> +{
> +	echo "pre-existing data destroy"
> +	_scratch_mkfs >> $seqres.full
> +	_scratch_mount
> +
> +	# Create subvol and write data BEFORE enabling squotas.
> +	$BTRFS_UTIL_PROG subvolume create $subv1 >> $seqres.full
> +	$XFS_IO_PROG -fc "pwrite -q 0 128K" $subv1/f
> +	sync
> +
> +	# Enable squotas. Pre-existing data is not accounted (gen < enable_gen).
> +	$BTRFS_UTIL_PROG quota enable --simple $SCRATCH_MNT
> +	$BTRFS_UTIL_PROG filesystem sync $SCRATCH_MNT
> +
> +	local subvid1=$(_btrfs_get_subvolid $SCRATCH_MNT subv1)
> +
> +	# Destroy the qgroup. rfer/excl = 0 because data predates squotas.
> +	# Should fail because the ROOT_ITEM still exists.
> +	$BTRFS_UTIL_PROG qgroup destroy 0/$subvid1 $SCRATCH_MNT 2>&1
> +
> +	_scratch_unmount
> +}
> +
> +free_from_deleted_owner
> +add_to_deleted_owner
> +same_txn_destroy_race
> +pre_existing_data_destroy
> +
> +# success, all done
> +status=0
> +exit
> diff --git a/tests/btrfs/348.out b/tests/btrfs/348.out
> new file mode 100644
> index 00000000..0a1cf11f
> --- /dev/null
> +++ b/tests/btrfs/348.out
> @@ -0,0 +1,10 @@
> +QA output created by 348
> +free from deleted owner
> +ERROR: unable to destroy quota group: Device or resource busy
> +add to deleted owner
> +ERROR: unable to destroy quota group: Device or resource busy
> +ERROR: unable to create quota group: Invalid argument
> +same txn destroy race
> +ERROR: unable to destroy quota group: Device or resource busy
> +pre-existing data destroy
> +ERROR: unable to destroy quota group: Device or resource busy


  reply	other threads:[~2026-05-18  9:18 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-13  0:43 [PATCH 0/2] squota delete fstests Boris Burkov
2026-05-13  0:43 ` [PATCH 1/2] btrfs: inline enable_quota helper in test 301 Boris Burkov
2026-05-14  3:34   ` Zorro Lang
2026-05-15 21:15     ` Boris Burkov
2026-05-16 13:55       ` Zorro Lang
2026-05-19  6:18   ` Anand Jain
2026-05-13  0:43 ` [PATCH 2/2] btrfs: test qgroup deletion races with squota writes Boris Burkov
2026-05-18  9:18   ` Anand Jain [this message]
2026-05-18 16:28     ` Boris Burkov
2026-05-19  6:15       ` Anand Jain

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=e29e3b26-bd22-41d7-92f0-8d89bf73f5ea@gmail.com \
    --to=anajain.sg@gmail.com \
    --cc=boris@bur.io \
    --cc=fstests@vger.kernel.org \
    --cc=kernel-team@fb.com \
    --cc=linux-btrfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.