• Qu Wenruo's avatar
    btrfs: avoid possible signal interruption of btrfs_drop_snapshot() on relocation tree · f3e3d9cc
    Qu Wenruo authored
    [BUG]
    There is a bug report about bad signal timing could lead to read-only
    fs during balance:
    
      BTRFS info (device xvdb): balance: start -d -m -s
      BTRFS info (device xvdb): relocating block group 73001861120 flags metadata
      BTRFS info (device xvdb): found 12236 extents, stage: move data extents
      BTRFS info (device xvdb): relocating block group 71928119296 flags data
      BTRFS info (device xvdb): found 3 extents, stage: move data extents
      BTRFS info (device xvdb): found 3 extents, stage: update data pointers
      BTRFS info (device xvdb): relocating block group 60922265600 flags metadata
      BTRFS: error (device xvdb) in btrfs_drop_snapshot:5505: errno=-4 unknown
      BTRFS info (device xvdb): forced readonly
      BTRFS info (device xvdb): balance: ended with status: -4
    
    [CAUSE]
    The direct cause is the -EINTR from the following call chain when a
    fatal signal is pending:
    
     relocate_block_group()
     |- clean_dirty_subvols()
        |- btrfs_drop_snapshot()
           |- btrfs_start_transaction()
              |- btrfs_delayed_refs_rsv_refill()
                 |- btrfs_reserve_metadata_bytes()
                    |- __reserve_metadata_bytes()
                       |- wait_reserve_ticket()
                          |- prepare_to_wait_event();
                          |- ticket->error = -EINTR;
    
    Normally this behavior is fine for most btrfs_start_transaction()
    callers, as they need to catch any other error, same for the signal, and
    exit ASAP.
    
    However for balance, especially for the clean_dirty_subvols() case, we're
    already doing cleanup works, getting -EINTR from btrfs_drop_snapshot()
    could cause a lot of unexpected problems.
    
    From the mentioned forced read-only report, to later balance error due
    to half dropped reloc trees.
    
    [FIX]
    Fix this problem by using btrfs_join_transaction() if
    btrfs_drop_snapshot() is called from relocation context.
    
    Since btrfs_join_transaction() won't get interrupted by signal, we can
    continue the cleanup.
    
    CC: stable@vger.kernel.org # 5.4+
    Reviewed-by: default avatarJosef Bacik <josef@toxicpanda.com>
    Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
    Reviewed-by: David Sterba <dsterba@suse.com>3
    Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
    f3e3d9cc
extent-tree.c 156 KB