• Filipe Manana's avatar
    btrfs: prevent transaction block reserve underflow when starting transaction · a7ddeeb0
    Filipe Manana authored
    When starting a transaction, with a non-zero number of items, we reserve
    metadata space for that number of items and for delayed refs by doing a
    call to btrfs_block_rsv_add(), with the transaction block reserve passed
    as the block reserve argument. This reserves metadata space and adds it
    to the transaction block reserve. Later we migrate the space we reserved
    for delayed references from the transaction block reserve into the delayed
    refs block reserve, by calling btrfs_migrate_to_delayed_refs_rsv().
    
    btrfs_migrate_to_delayed_refs_rsv() decrements the number of bytes to
    migrate from the source block reserve, and this however may result in an
    underflow in case the space added to the transaction block reserve ended
    up being used by another task that has not reserved enough space for its
    own use - examples are tasks doing reflinks or hole punching because they
    end up calling btrfs_replace_file_extents() -> btrfs_drop_extents() and
    may need to modify/COW a variable number of leaves/paths, so they keep
    trying to use space from the transaction block reserve when they need to
    COW an extent buffer, and may end up trying to use more space then they
    have reserved (1 unit/path only for removing file extent items).
    
    This can be avoided by simply reserving space first without adding it to
    the transaction block reserve, then add the space for delayed refs to the
    delayed refs block reserve and finally add the remaining reserved space
    to the transaction block reserve. This also makes the code a bit shorter
    and simpler. So just do that.
    Reviewed-by: default avatarJosef Bacik <josef@toxicpanda.com>
    Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
    Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
    a7ddeeb0
transaction.c 78.8 KB