• Qu Wenruo's avatar
    btrfs: fix qgroup data rsv leak caused by falloc failure · a3ee79bd
    Qu Wenruo authored
    [BUG]
    When running fsstress with only falloc workload, and a very low qgroup
    limit set, we can get qgroup data rsv leak at unmount time.
    
     BTRFS warning (device dm-0): qgroup 0/5 has unreleased space, type 0 rsv 20480
     BTRFS error (device dm-0): qgroup reserved space leaked
    
    The minimal reproducer looks like:
    
      #!/bin/bash
      dev=/dev/test/test
      mnt="/mnt/btrfs"
      fsstress=~/xfstests-dev/ltp/fsstress
      runtime=8
    
      workload()
      {
              umount $dev &> /dev/null
              umount $mnt &> /dev/null
              mkfs.btrfs -f $dev > /dev/null
              mount $dev $mnt
    
              btrfs quota en $mnt
              btrfs quota rescan -w $mnt
              btrfs qgroup limit 16m 0/5 $mnt
    
              $fsstress -w -z -f creat=10 -f fallocate=10 -p 2 -n 100 \
      		-d $mnt -v > /tmp/fsstress
    
              umount $mnt
              if dmesg | grep leak ; then
    		echo "!!! FAILED !!!"
      		exit 1
              fi
      }
    
      for (( i=0; i < $runtime; i++)); do
              echo "=== $i/$runtime==="
              workload
      done
    
    Normally it would fail before round 4.
    
    [CAUSE]
    In function insert_prealloc_file_extent(), we first call
    btrfs_qgroup_release_data() to know how many bytes are reserved for
    qgroup data rsv.
    
    Then use that @qgroup_released number to continue our work.
    
    But after we call btrfs_qgroup_release_data(), we should either queue
    @qgroup_released to delayed ref or free them manually in error path.
    
    Unfortunately, we lack the error handling to free the released bytes,
    leaking qgroup data rsv.
    
    All the error handling function outside won't help at all, as we have
    released the range, meaning in inode io tree, the EXTENT_QGROUP_RESERVED
    bit is already cleared, thus all btrfs_qgroup_free_data() call won't
    free any data rsv.
    
    [FIX]
    Add free_qgroup tag to manually free the released qgroup data rsv.
    Reported-by: default avatarNikolay Borisov <nborisov@suse.com>
    Reported-by: default avatarDavid Sterba <dsterba@suse.cz>
    Fixes: 9729f10a ("btrfs: inode: move qgroup reserved space release to the callers of insert_reserved_file_extent()")
    CC: stable@vger.kernel.org # 5.10+
    Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
    Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
    a3ee79bd
inode.c 296 KB