diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 802918507cc0..9dcd16dec754 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -5105,9 +5105,7 @@ static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg) struct inode *inode = file_inode(file); struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb); struct btrfs_ioctl_quota_ctl_args *sa; - struct btrfs_trans_handle *trans = NULL; int ret; - int err; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -5123,28 +5121,19 @@ static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg) } down_write(&fs_info->subvol_sem); - trans = btrfs_start_transaction(fs_info->tree_root, 2); - if (IS_ERR(trans)) { - ret = PTR_ERR(trans); - goto out; - } switch (sa->cmd) { case BTRFS_QUOTA_CTL_ENABLE: - ret = btrfs_quota_enable(trans, fs_info); + ret = btrfs_quota_enable(fs_info); break; case BTRFS_QUOTA_CTL_DISABLE: - ret = btrfs_quota_disable(trans, fs_info); + ret = btrfs_quota_disable(fs_info); break; default: ret = -EINVAL; break; } - err = btrfs_commit_transaction(trans); - if (err && !ret) - ret = err; -out: kfree(sa); up_write(&fs_info->subvol_sem); drop_write: diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index c25dc47210a3..97a59a444500 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -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; diff --git a/fs/btrfs/qgroup.h b/fs/btrfs/qgroup.h index d60dd06445ce..bec7c9b17a8e 100644 --- a/fs/btrfs/qgroup.h +++ b/fs/btrfs/qgroup.h @@ -141,10 +141,8 @@ struct btrfs_qgroup { #define QGROUP_RELEASE (1<<1) #define QGROUP_FREE (1<<2) -int btrfs_quota_enable(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info); -int btrfs_quota_disable(struct btrfs_trans_handle *trans, - struct btrfs_fs_info *fs_info); +int btrfs_quota_enable(struct btrfs_fs_info *fs_info); +int btrfs_quota_disable(struct btrfs_fs_info *fs_info); int btrfs_qgroup_rescan(struct btrfs_fs_info *fs_info); void btrfs_qgroup_rescan_resume(struct btrfs_fs_info *fs_info); int btrfs_qgroup_wait_for_completion(struct btrfs_fs_info *fs_info,