mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-27 00:51:35 +00:00
btrfs: qgroup: Fix qgroup reserved space underflow by only freeing reserved ranges
[BUG] For the following case, btrfs can underflow qgroup reserved space at an error path: (Page size 4K, function name without "btrfs_" prefix) Task A | Task B ---------------------------------------------------------------------- Buffered_write [0, 2K) | |- check_data_free_space() | | |- qgroup_reserve_data() | | Range aligned to page | | range [0, 4K) <<< | | 4K bytes reserved <<< | |- copy pages to page cache | | Buffered_write [2K, 4K) | |- check_data_free_space() | | |- qgroup_reserved_data() | | Range alinged to page | | range [0, 4K) | | Already reserved by A <<< | | 0 bytes reserved <<< | |- delalloc_reserve_metadata() | | And it *FAILED* (Maybe EQUOTA) | |- free_reserved_data_space() |- qgroup_free_data() Range aligned to page range [0, 4K) Freeing 4K (Special thanks to Chandan for the detailed report and analyse) [CAUSE] Above Task B is freeing reserved data range [0, 4K) which is actually reserved by Task A. And at writeback time, page dirty by Task A will go through writeback routine, which will free 4K reserved data space at file extent insert time, causing the qgroup underflow. [FIX] For btrfs_qgroup_free_data(), add @reserved parameter to only free data ranges reserved by previous btrfs_qgroup_reserve_data(). So in above case, Task B will try to free 0 byte, so no underflow. Reported-by: Chandan Rajendra <chandan@linux.vnet.ibm.com> Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com> Reviewed-by: Chandan Rajendra <chandan@linux.vnet.ibm.com> Tested-by: Chandan Rajendra <chandan@linux.vnet.ibm.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
364ecf3651
commit
bc42bda223
8 changed files with 117 additions and 46 deletions
|
@ -4389,7 +4389,8 @@ void btrfs_free_reserved_data_space_noquota(struct inode *inode, u64 start,
|
|||
* This one will handle the per-inode data rsv map for accurate reserved
|
||||
* space framework.
|
||||
*/
|
||||
void btrfs_free_reserved_data_space(struct inode *inode, u64 start, u64 len)
|
||||
void btrfs_free_reserved_data_space(struct inode *inode,
|
||||
struct extent_changeset *reserved, u64 start, u64 len)
|
||||
{
|
||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||
|
||||
|
@ -4399,7 +4400,7 @@ void btrfs_free_reserved_data_space(struct inode *inode, u64 start, u64 len)
|
|||
start = round_down(start, root->fs_info->sectorsize);
|
||||
|
||||
btrfs_free_reserved_data_space_noquota(inode, start, len);
|
||||
btrfs_qgroup_free_data(inode, start, len);
|
||||
btrfs_qgroup_free_data(inode, reserved, start, len);
|
||||
}
|
||||
|
||||
static void force_metadata_allocation(struct btrfs_fs_info *info)
|
||||
|
@ -6204,7 +6205,7 @@ int btrfs_delalloc_reserve_space(struct inode *inode,
|
|||
return ret;
|
||||
ret = btrfs_delalloc_reserve_metadata(BTRFS_I(inode), len);
|
||||
if (ret < 0)
|
||||
btrfs_free_reserved_data_space(inode, start, len);
|
||||
btrfs_free_reserved_data_space(inode, *reserved, start, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -6223,10 +6224,11 @@ int btrfs_delalloc_reserve_space(struct inode *inode,
|
|||
* list if there are no delalloc bytes left.
|
||||
* Also it will handle the qgroup reserved space.
|
||||
*/
|
||||
void btrfs_delalloc_release_space(struct inode *inode, u64 start, u64 len)
|
||||
void btrfs_delalloc_release_space(struct inode *inode,
|
||||
struct extent_changeset *reserved, u64 start, u64 len)
|
||||
{
|
||||
btrfs_delalloc_release_metadata(BTRFS_I(inode), len);
|
||||
btrfs_free_reserved_data_space(inode, start, len);
|
||||
btrfs_free_reserved_data_space(inode, reserved, start, len);
|
||||
}
|
||||
|
||||
static int update_block_group(struct btrfs_trans_handle *trans,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue