mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-09 10:09:11 +00:00
btrfs: fix bytes_may_use underflow in prealloc error condtition
commit b778cf962d
upstream.
I hit the following warning while running my error injection stress
testing:
WARNING: CPU: 3 PID: 1453 at fs/btrfs/space-info.h:108 btrfs_free_reserved_data_space_noquota+0xfd/0x160 [btrfs]
RIP: 0010:btrfs_free_reserved_data_space_noquota+0xfd/0x160 [btrfs]
Call Trace:
btrfs_free_reserved_data_space+0x4f/0x70 [btrfs]
__btrfs_prealloc_file_range+0x378/0x470 [btrfs]
elfcorehdr_read+0x40/0x40
? elfcorehdr_read+0x40/0x40
? btrfs_commit_transaction+0xca/0xa50 [btrfs]
? dput+0xb4/0x2a0
? btrfs_log_dentry_safe+0x55/0x70 [btrfs]
? btrfs_sync_file+0x30e/0x420 [btrfs]
? do_fsync+0x38/0x70
? __x64_sys_fdatasync+0x13/0x20
? do_syscall_64+0x5b/0x1b0
? entry_SYSCALL_64_after_hwframe+0x44/0xa9
This happens if we fail to insert our reserved file extent. At this
point we've already converted our reservation from ->bytes_may_use to
->bytes_reserved. However once we break we will attempt to free
everything from [cur_offset, end] from ->bytes_may_use, but our extent
reservation will overlap part of this.
Fix this problem by adding ins.offset (our extent allocation size) to
cur_offset so we remove the actual remaining part from ->bytes_may_use.
I validated this fix using my inject-error.py script
python inject-error.py -o should_fail_bio -t cache_save_setup -t \
__btrfs_prealloc_file_range \
-t insert_reserved_file_extent.constprop.0 \
-r "-5" ./run-fsstress.sh
where run-fsstress.sh simply mounts and runs fsstress on a disk.
CC: stable@vger.kernel.org # 4.4+
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
e541982a6e
commit
0ba8e5f347
1 changed files with 13 additions and 3 deletions
|
@ -10348,6 +10348,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
|
|||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||
struct btrfs_key ins;
|
||||
u64 cur_offset = start;
|
||||
u64 clear_offset = start;
|
||||
u64 i_size;
|
||||
u64 cur_bytes;
|
||||
u64 last_alloc = (u64)-1;
|
||||
|
@ -10382,6 +10383,15 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
|
|||
btrfs_end_transaction(trans);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* We've reserved this space, and thus converted it from
|
||||
* ->bytes_may_use to ->bytes_reserved. Any error that happens
|
||||
* from here on out we will only need to clear our reservation
|
||||
* for the remaining unreserved area, so advance our
|
||||
* clear_offset by our extent size.
|
||||
*/
|
||||
clear_offset += ins.offset;
|
||||
btrfs_dec_block_group_reservations(fs_info, ins.objectid);
|
||||
|
||||
last_alloc = ins.offset;
|
||||
|
@ -10462,9 +10472,9 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
|
|||
if (own_trans)
|
||||
btrfs_end_transaction(trans);
|
||||
}
|
||||
if (cur_offset < end)
|
||||
btrfs_free_reserved_data_space(inode, NULL, cur_offset,
|
||||
end - cur_offset + 1);
|
||||
if (clear_offset < end)
|
||||
btrfs_free_reserved_data_space(inode, NULL, clear_offset,
|
||||
end - clear_offset + 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue