Skip to content

Commit a7ddeeb

Browse files
fdmananakdave
authored andcommitted
btrfs: prevent transaction block reserve underflow when starting transaction
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: Josef Bacik <[email protected]> Signed-off-by: Filipe Manana <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 2ed45c0 commit a7ddeeb

File tree

3 files changed

+4
-12
lines changed

3 files changed

+4
-12
lines changed

fs/btrfs/delayed-ref.c

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,24 +103,17 @@ void btrfs_update_delayed_refs_rsv(struct btrfs_trans_handle *trans)
103103
* Transfer bytes to our delayed refs rsv.
104104
*
105105
* @fs_info: the filesystem
106-
* @src: source block rsv to transfer from
107106
* @num_bytes: number of bytes to transfer
108107
*
109-
* This transfers up to the num_bytes amount from the src rsv to the
108+
* This transfers up to the num_bytes amount, previously reserved, to the
110109
* delayed_refs_rsv. Any extra bytes are returned to the space info.
111110
*/
112111
void btrfs_migrate_to_delayed_refs_rsv(struct btrfs_fs_info *fs_info,
113-
struct btrfs_block_rsv *src,
114112
u64 num_bytes)
115113
{
116114
struct btrfs_block_rsv *delayed_refs_rsv = &fs_info->delayed_refs_rsv;
117115
u64 to_free = 0;
118116

119-
spin_lock(&src->lock);
120-
src->reserved -= num_bytes;
121-
src->size -= num_bytes;
122-
spin_unlock(&src->lock);
123-
124117
spin_lock(&delayed_refs_rsv->lock);
125118
if (delayed_refs_rsv->size > delayed_refs_rsv->reserved) {
126119
u64 delta = delayed_refs_rsv->size -

fs/btrfs/delayed-ref.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,6 @@ void btrfs_update_delayed_refs_rsv(struct btrfs_trans_handle *trans);
407407
int btrfs_delayed_refs_rsv_refill(struct btrfs_fs_info *fs_info,
408408
enum btrfs_reserve_flush_enum flush);
409409
void btrfs_migrate_to_delayed_refs_rsv(struct btrfs_fs_info *fs_info,
410-
struct btrfs_block_rsv *src,
411410
u64 num_bytes);
412411
bool btrfs_check_space_for_delayed_refs(struct btrfs_fs_info *fs_info);
413412

fs/btrfs/transaction.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -631,14 +631,14 @@ start_transaction(struct btrfs_root *root, unsigned int num_items,
631631
reloc_reserved = true;
632632
}
633633

634-
ret = btrfs_block_rsv_add(fs_info, rsv, num_bytes, flush);
634+
ret = btrfs_reserve_metadata_bytes(fs_info, rsv, num_bytes, flush);
635635
if (ret)
636636
goto reserve_fail;
637637
if (delayed_refs_bytes) {
638-
btrfs_migrate_to_delayed_refs_rsv(fs_info, rsv,
639-
delayed_refs_bytes);
638+
btrfs_migrate_to_delayed_refs_rsv(fs_info, delayed_refs_bytes);
640639
num_bytes -= delayed_refs_bytes;
641640
}
641+
btrfs_block_rsv_add_bytes(rsv, num_bytes, true);
642642

643643
if (rsv->space_info->force_alloc)
644644
do_chunk_alloc = true;

0 commit comments

Comments
 (0)