|
@@ -875,8 +875,7 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-int btrfs_quota_enable(struct btrfs_trans_handle *trans,
|
|
|
- struct btrfs_fs_info *fs_info)
|
|
|
+int btrfs_quota_enable(struct btrfs_fs_info *fs_info)
|
|
|
{
|
|
|
struct btrfs_root *quota_root;
|
|
|
struct btrfs_root *tree_root = fs_info->tree_root;
|
|
@@ -886,6 +885,7 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,
|
|
|
struct btrfs_key key;
|
|
|
struct btrfs_key found_key;
|
|
|
struct btrfs_qgroup *qgroup = NULL;
|
|
|
+ struct btrfs_trans_handle *trans = NULL;
|
|
|
int ret = 0;
|
|
|
int slot;
|
|
|
|
|
@@ -893,9 +893,25 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,
|
|
|
if (fs_info->quota_root)
|
|
|
goto out;
|
|
|
|
|
|
+ /*
|
|
|
+ * 1 for quota root item
|
|
|
+ * 1 for BTRFS_QGROUP_STATUS item
|
|
|
+ *
|
|
|
+ * Yet we also need 2*n items for a QGROUP_INFO/QGROUP_LIMIT items
|
|
|
+ * per subvolume. However those are not currently reserved since it
|
|
|
+ * would be a lot of overkill.
|
|
|
+ */
|
|
|
+ trans = btrfs_start_transaction(tree_root, 2);
|
|
|
+ if (IS_ERR(trans)) {
|
|
|
+ ret = PTR_ERR(trans);
|
|
|
+ trans = NULL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
fs_info->qgroup_ulist = ulist_alloc(GFP_KERNEL);
|
|
|
if (!fs_info->qgroup_ulist) {
|
|
|
ret = -ENOMEM;
|
|
|
+ btrfs_abort_transaction(trans, ret);
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
@@ -906,12 +922,14 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,
|
|
|
BTRFS_QUOTA_TREE_OBJECTID);
|
|
|
if (IS_ERR(quota_root)) {
|
|
|
ret = PTR_ERR(quota_root);
|
|
|
+ btrfs_abort_transaction(trans, ret);
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
|
path = btrfs_alloc_path();
|
|
|
if (!path) {
|
|
|
ret = -ENOMEM;
|
|
|
+ btrfs_abort_transaction(trans, ret);
|
|
|
goto out_free_root;
|
|
|
}
|
|
|
|
|
@@ -921,8 +939,10 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,
|
|
|
|
|
|
ret = btrfs_insert_empty_item(trans, quota_root, path, &key,
|
|
|
sizeof(*ptr));
|
|
|
- if (ret)
|
|
|
+ if (ret) {
|
|
|
+ btrfs_abort_transaction(trans, ret);
|
|
|
goto out_free_path;
|
|
|
+ }
|
|
|
|
|
|
leaf = path->nodes[0];
|
|
|
ptr = btrfs_item_ptr(leaf, path->slots[0],
|
|
@@ -944,9 +964,10 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,
|
|
|
ret = btrfs_search_slot_for_read(tree_root, &key, path, 1, 0);
|
|
|
if (ret > 0)
|
|
|
goto out_add_root;
|
|
|
- if (ret < 0)
|
|
|
+ if (ret < 0) {
|
|
|
+ btrfs_abort_transaction(trans, ret);
|
|
|
goto out_free_path;
|
|
|
-
|
|
|
+ }
|
|
|
|
|
|
while (1) {
|
|
|
slot = path->slots[0];
|
|
@@ -956,18 +977,23 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,
|
|
|
if (found_key.type == BTRFS_ROOT_REF_KEY) {
|
|
|
ret = add_qgroup_item(trans, quota_root,
|
|
|
found_key.offset);
|
|
|
- if (ret)
|
|
|
+ if (ret) {
|
|
|
+ btrfs_abort_transaction(trans, ret);
|
|
|
goto out_free_path;
|
|
|
+ }
|
|
|
|
|
|
qgroup = add_qgroup_rb(fs_info, found_key.offset);
|
|
|
if (IS_ERR(qgroup)) {
|
|
|
ret = PTR_ERR(qgroup);
|
|
|
+ btrfs_abort_transaction(trans, ret);
|
|
|
goto out_free_path;
|
|
|
}
|
|
|
}
|
|
|
ret = btrfs_next_item(tree_root, path);
|
|
|
- if (ret < 0)
|
|
|
+ if (ret < 0) {
|
|
|
+ btrfs_abort_transaction(trans, ret);
|
|
|
goto out_free_path;
|
|
|
+ }
|
|
|
if (ret)
|
|
|
break;
|
|
|
}
|
|
@@ -975,18 +1001,28 @@ int btrfs_quota_enable(struct btrfs_trans_handle *trans,
|
|
|
out_add_root:
|
|
|
btrfs_release_path(path);
|
|
|
ret = add_qgroup_item(trans, quota_root, BTRFS_FS_TREE_OBJECTID);
|
|
|
- if (ret)
|
|
|
+ if (ret) {
|
|
|
+ btrfs_abort_transaction(trans, ret);
|
|
|
goto out_free_path;
|
|
|
+ }
|
|
|
|
|
|
qgroup = add_qgroup_rb(fs_info, BTRFS_FS_TREE_OBJECTID);
|
|
|
if (IS_ERR(qgroup)) {
|
|
|
ret = PTR_ERR(qgroup);
|
|
|
+ btrfs_abort_transaction(trans, ret);
|
|
|
goto out_free_path;
|
|
|
}
|
|
|
spin_lock(&fs_info->qgroup_lock);
|
|
|
fs_info->quota_root = quota_root;
|
|
|
set_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
|
|
|
spin_unlock(&fs_info->qgroup_lock);
|
|
|
+
|
|
|
+ ret = btrfs_commit_transaction(trans);
|
|
|
+ if (ret) {
|
|
|
+ trans = NULL;
|
|
|
+ goto out_free_path;
|
|
|
+ }
|
|
|
+
|
|
|
ret = qgroup_rescan_init(fs_info, 0, 1);
|
|
|
if (!ret) {
|
|
|
qgroup_rescan_zero_tracking(fs_info);
|
|
@@ -1006,20 +1042,35 @@ out:
|
|
|
if (ret) {
|
|
|
ulist_free(fs_info->qgroup_ulist);
|
|
|
fs_info->qgroup_ulist = NULL;
|
|
|
+ if (trans)
|
|
|
+ btrfs_end_transaction(trans);
|
|
|
}
|
|
|
mutex_unlock(&fs_info->qgroup_ioctl_lock);
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-int btrfs_quota_disable(struct btrfs_trans_handle *trans,
|
|
|
- struct btrfs_fs_info *fs_info)
|
|
|
+int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
|
|
|
{
|
|
|
struct btrfs_root *quota_root;
|
|
|
+ struct btrfs_trans_handle *trans = NULL;
|
|
|
int ret = 0;
|
|
|
|
|
|
mutex_lock(&fs_info->qgroup_ioctl_lock);
|
|
|
if (!fs_info->quota_root)
|
|
|
goto out;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 1 For the root item
|
|
|
+ *
|
|
|
+ * We should also reserve enough items for the quota tree deletion in
|
|
|
+ * btrfs_clean_quota_tree but this is not done.
|
|
|
+ */
|
|
|
+ trans = btrfs_start_transaction(fs_info->tree_root, 1);
|
|
|
+ if (IS_ERR(trans)) {
|
|
|
+ ret = PTR_ERR(trans);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
clear_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
|
|
|
btrfs_qgroup_wait_for_completion(fs_info, false);
|
|
|
spin_lock(&fs_info->qgroup_lock);
|
|
@@ -1031,12 +1082,16 @@ int btrfs_quota_disable(struct btrfs_trans_handle *trans,
|
|
|
btrfs_free_qgroup_config(fs_info);
|
|
|
|
|
|
ret = btrfs_clean_quota_tree(trans, quota_root);
|
|
|
- if (ret)
|
|
|
- goto out;
|
|
|
+ if (ret) {
|
|
|
+ btrfs_abort_transaction(trans, ret);
|
|
|
+ goto end_trans;
|
|
|
+ }
|
|
|
|
|
|
ret = btrfs_del_root(trans, fs_info, "a_root->root_key);
|
|
|
- if (ret)
|
|
|
- goto out;
|
|
|
+ if (ret) {
|
|
|
+ btrfs_abort_transaction(trans, ret);
|
|
|
+ goto end_trans;
|
|
|
+ }
|
|
|
|
|
|
list_del("a_root->dirty_list);
|
|
|
|
|
@@ -1048,6 +1103,9 @@ int btrfs_quota_disable(struct btrfs_trans_handle *trans,
|
|
|
free_extent_buffer(quota_root->node);
|
|
|
free_extent_buffer(quota_root->commit_root);
|
|
|
kfree(quota_root);
|
|
|
+
|
|
|
+end_trans:
|
|
|
+ ret = btrfs_end_transaction(trans);
|
|
|
out:
|
|
|
mutex_unlock(&fs_info->qgroup_ioctl_lock);
|
|
|
return ret;
|