|
@@ -2835,43 +2835,60 @@ btrfs_qgroup_rescan_resume(struct btrfs_fs_info *fs_info)
|
|
|
* Return <0 for error (including -EQUOT)
|
|
|
*
|
|
|
* NOTE: this function may sleep for memory allocation.
|
|
|
+ * if btrfs_qgroup_reserve_data() is called multiple times with
|
|
|
+ * same @reserved, caller must ensure when error happens it's OK
|
|
|
+ * to free *ALL* reserved space.
|
|
|
*/
|
|
|
-int btrfs_qgroup_reserve_data(struct inode *inode, u64 start, u64 len)
|
|
|
+int btrfs_qgroup_reserve_data(struct inode *inode,
|
|
|
+ struct extent_changeset **reserved_ret, u64 start,
|
|
|
+ u64 len)
|
|
|
{
|
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
- struct extent_changeset changeset;
|
|
|
struct ulist_node *unode;
|
|
|
struct ulist_iterator uiter;
|
|
|
+ struct extent_changeset *reserved;
|
|
|
+ u64 orig_reserved;
|
|
|
+ u64 to_reserve;
|
|
|
int ret;
|
|
|
|
|
|
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &root->fs_info->flags) ||
|
|
|
!is_fstree(root->objectid) || len == 0)
|
|
|
return 0;
|
|
|
|
|
|
- changeset.bytes_changed = 0;
|
|
|
- ulist_init(&changeset.range_changed);
|
|
|
+ /* @reserved parameter is mandatory for qgroup */
|
|
|
+ if (WARN_ON(!reserved_ret))
|
|
|
+ return -EINVAL;
|
|
|
+ if (!*reserved_ret) {
|
|
|
+ *reserved_ret = extent_changeset_alloc();
|
|
|
+ if (!*reserved_ret)
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ reserved = *reserved_ret;
|
|
|
+ /* Record already reserved space */
|
|
|
+ orig_reserved = reserved->bytes_changed;
|
|
|
ret = set_record_extent_bits(&BTRFS_I(inode)->io_tree, start,
|
|
|
- start + len -1, EXTENT_QGROUP_RESERVED, &changeset);
|
|
|
+ start + len -1, EXTENT_QGROUP_RESERVED, reserved);
|
|
|
+
|
|
|
+ /* Newly reserved space */
|
|
|
+ to_reserve = reserved->bytes_changed - orig_reserved;
|
|
|
trace_btrfs_qgroup_reserve_data(inode, start, len,
|
|
|
- changeset.bytes_changed,
|
|
|
- QGROUP_RESERVE);
|
|
|
+ to_reserve, QGROUP_RESERVE);
|
|
|
if (ret < 0)
|
|
|
goto cleanup;
|
|
|
- ret = qgroup_reserve(root, changeset.bytes_changed, true);
|
|
|
+ ret = qgroup_reserve(root, to_reserve, true);
|
|
|
if (ret < 0)
|
|
|
goto cleanup;
|
|
|
|
|
|
- ulist_release(&changeset.range_changed);
|
|
|
return ret;
|
|
|
|
|
|
cleanup:
|
|
|
- /* cleanup already reserved ranges */
|
|
|
+ /* cleanup *ALL* already reserved ranges */
|
|
|
ULIST_ITER_INIT(&uiter);
|
|
|
- while ((unode = ulist_next(&changeset.range_changed, &uiter)))
|
|
|
+ while ((unode = ulist_next(&reserved->range_changed, &uiter)))
|
|
|
clear_extent_bit(&BTRFS_I(inode)->io_tree, unode->val,
|
|
|
unode->aux, EXTENT_QGROUP_RESERVED, 0, 0, NULL,
|
|
|
GFP_NOFS);
|
|
|
- ulist_release(&changeset.range_changed);
|
|
|
+ extent_changeset_release(reserved);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -2882,8 +2899,7 @@ static int __btrfs_qgroup_release_data(struct inode *inode, u64 start, u64 len,
|
|
|
int trace_op = QGROUP_RELEASE;
|
|
|
int ret;
|
|
|
|
|
|
- changeset.bytes_changed = 0;
|
|
|
- ulist_init(&changeset.range_changed);
|
|
|
+ extent_changeset_init(&changeset);
|
|
|
ret = clear_record_extent_bits(&BTRFS_I(inode)->io_tree, start,
|
|
|
start + len -1, EXTENT_QGROUP_RESERVED, &changeset);
|
|
|
if (ret < 0)
|
|
@@ -2899,7 +2915,7 @@ static int __btrfs_qgroup_release_data(struct inode *inode, u64 start, u64 len,
|
|
|
changeset.bytes_changed);
|
|
|
ret = changeset.bytes_changed;
|
|
|
out:
|
|
|
- ulist_release(&changeset.range_changed);
|
|
|
+ extent_changeset_release(&changeset);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -2999,8 +3015,7 @@ void btrfs_qgroup_check_reserved_leak(struct inode *inode)
|
|
|
struct ulist_iterator iter;
|
|
|
int ret;
|
|
|
|
|
|
- changeset.bytes_changed = 0;
|
|
|
- ulist_init(&changeset.range_changed);
|
|
|
+ extent_changeset_init(&changeset);
|
|
|
ret = clear_record_extent_bits(&BTRFS_I(inode)->io_tree, 0, (u64)-1,
|
|
|
EXTENT_QGROUP_RESERVED, &changeset);
|
|
|
|
|
@@ -3017,5 +3032,5 @@ void btrfs_qgroup_check_reserved_leak(struct inode *inode)
|
|
|
changeset.bytes_changed);
|
|
|
|
|
|
}
|
|
|
- ulist_release(&changeset.range_changed);
|
|
|
+ extent_changeset_release(&changeset);
|
|
|
}
|