Commit 20cb38a7 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull btrfs fixes from David Sterba:
 "Several fixes to qgroups that have been recently identified by test
  generic/475:

   - fix prealloc reserve leak in subvolume operations

   - various other fixes in reservation setup, conversion or cleanup"

* tag 'for-6.9-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: always clear PERTRANS metadata during commit
  btrfs: make btrfs_clear_delalloc_extent() free delalloc reserve
  btrfs: qgroup: convert PREALLOC to PERTRANS after record_root_in_trans
  btrfs: record delayed inode root in transaction
  btrfs: qgroup: fix qgroup prealloc rsv leak in subvolume operations
  btrfs: qgroup: correctly model root qgroup rsv in convert
parents 4f0a8fe3 6e68de0b
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -1133,6 +1133,9 @@ __btrfs_commit_inode_delayed_items(struct btrfs_trans_handle *trans,
	if (ret)
		return ret;

	ret = btrfs_record_root_in_trans(trans, node->root);
	if (ret)
		return ret;
	ret = btrfs_update_delayed_inode(trans, node->root, path, node);
	return ret;
}
+13 −2
Original line number Diff line number Diff line
@@ -2533,7 +2533,7 @@ void btrfs_clear_delalloc_extent(struct btrfs_inode *inode,
		 */
		if (bits & EXTENT_CLEAR_META_RESV &&
		    root != fs_info->tree_root)
			btrfs_delalloc_release_metadata(inode, len, false);
			btrfs_delalloc_release_metadata(inode, len, true);

		/* For sanity tests. */
		if (btrfs_is_testing(fs_info))
@@ -4503,6 +4503,7 @@ int btrfs_delete_subvolume(struct btrfs_inode *dir, struct dentry *dentry)
	struct btrfs_trans_handle *trans;
	struct btrfs_block_rsv block_rsv;
	u64 root_flags;
	u64 qgroup_reserved = 0;
	int ret;

	down_write(&fs_info->subvol_sem);
@@ -4547,12 +4548,20 @@ int btrfs_delete_subvolume(struct btrfs_inode *dir, struct dentry *dentry)
	ret = btrfs_subvolume_reserve_metadata(root, &block_rsv, 5, true);
	if (ret)
		goto out_undead;
	qgroup_reserved = block_rsv.qgroup_rsv_reserved;

	trans = btrfs_start_transaction(root, 0);
	if (IS_ERR(trans)) {
		ret = PTR_ERR(trans);
		goto out_release;
	}
	ret = btrfs_record_root_in_trans(trans, root);
	if (ret) {
		btrfs_abort_transaction(trans, ret);
		goto out_end_trans;
	}
	btrfs_qgroup_convert_reserved_meta(root, qgroup_reserved);
	qgroup_reserved = 0;
	trans->block_rsv = &block_rsv;
	trans->bytes_reserved = block_rsv.size;

@@ -4611,7 +4620,9 @@ int btrfs_delete_subvolume(struct btrfs_inode *dir, struct dentry *dentry)
	ret = btrfs_end_transaction(trans);
	inode->i_flags |= S_DEAD;
out_release:
	btrfs_subvolume_release_metadata(root, &block_rsv);
	btrfs_block_rsv_release(fs_info, &block_rsv, (u64)-1, NULL);
	if (qgroup_reserved)
		btrfs_qgroup_free_meta_prealloc(root, qgroup_reserved);
out_undead:
	if (ret) {
		spin_lock(&dest->root_item_lock);
+28 −9
Original line number Diff line number Diff line
@@ -613,6 +613,7 @@ static noinline int create_subvol(struct mnt_idmap *idmap,
	int ret;
	dev_t anon_dev;
	u64 objectid;
	u64 qgroup_reserved = 0;

	root_item = kzalloc(sizeof(*root_item), GFP_KERNEL);
	if (!root_item)
@@ -650,13 +651,18 @@ static noinline int create_subvol(struct mnt_idmap *idmap,
					       trans_num_items, false);
	if (ret)
		goto out_new_inode_args;
	qgroup_reserved = block_rsv.qgroup_rsv_reserved;

	trans = btrfs_start_transaction(root, 0);
	if (IS_ERR(trans)) {
		ret = PTR_ERR(trans);
		btrfs_subvolume_release_metadata(root, &block_rsv);
		goto out_new_inode_args;
		goto out_release_rsv;
	}
	ret = btrfs_record_root_in_trans(trans, BTRFS_I(dir)->root);
	if (ret)
		goto out;
	btrfs_qgroup_convert_reserved_meta(root, qgroup_reserved);
	qgroup_reserved = 0;
	trans->block_rsv = &block_rsv;
	trans->bytes_reserved = block_rsv.size;
	/* Tree log can't currently deal with an inode which is a new root. */
@@ -767,9 +773,11 @@ static noinline int create_subvol(struct mnt_idmap *idmap,
out:
	trans->block_rsv = NULL;
	trans->bytes_reserved = 0;
	btrfs_subvolume_release_metadata(root, &block_rsv);

	btrfs_end_transaction(trans);
out_release_rsv:
	btrfs_block_rsv_release(fs_info, &block_rsv, (u64)-1, NULL);
	if (qgroup_reserved)
		btrfs_qgroup_free_meta_prealloc(root, qgroup_reserved);
out_new_inode_args:
	btrfs_new_inode_args_destroy(&new_inode_args);
out_inode:
@@ -791,6 +799,8 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
	struct btrfs_pending_snapshot *pending_snapshot;
	unsigned int trans_num_items;
	struct btrfs_trans_handle *trans;
	struct btrfs_block_rsv *block_rsv;
	u64 qgroup_reserved = 0;
	int ret;

	/* We do not support snapshotting right now. */
@@ -827,19 +837,19 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
		goto free_pending;
	}

	btrfs_init_block_rsv(&pending_snapshot->block_rsv,
			     BTRFS_BLOCK_RSV_TEMP);
	block_rsv = &pending_snapshot->block_rsv;
	btrfs_init_block_rsv(block_rsv, BTRFS_BLOCK_RSV_TEMP);
	/*
	 * 1 to add dir item
	 * 1 to add dir index
	 * 1 to update parent inode item
	 */
	trans_num_items = create_subvol_num_items(inherit) + 3;
	ret = btrfs_subvolume_reserve_metadata(BTRFS_I(dir)->root,
					       &pending_snapshot->block_rsv,
	ret = btrfs_subvolume_reserve_metadata(BTRFS_I(dir)->root, block_rsv,
					       trans_num_items, false);
	if (ret)
		goto free_pending;
	qgroup_reserved = block_rsv->qgroup_rsv_reserved;

	pending_snapshot->dentry = dentry;
	pending_snapshot->root = root;
@@ -852,6 +862,13 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
		ret = PTR_ERR(trans);
		goto fail;
	}
	ret = btrfs_record_root_in_trans(trans, BTRFS_I(dir)->root);
	if (ret) {
		btrfs_end_transaction(trans);
		goto fail;
	}
	btrfs_qgroup_convert_reserved_meta(root, qgroup_reserved);
	qgroup_reserved = 0;

	trans->pending_snapshot = pending_snapshot;

@@ -881,7 +898,9 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
	if (ret && pending_snapshot->snap)
		pending_snapshot->snap->anon_dev = 0;
	btrfs_put_root(pending_snapshot->snap);
	btrfs_subvolume_release_metadata(root, &pending_snapshot->block_rsv);
	btrfs_block_rsv_release(fs_info, block_rsv, (u64)-1, NULL);
	if (qgroup_reserved)
		btrfs_qgroup_free_meta_prealloc(root, qgroup_reserved);
free_pending:
	if (pending_snapshot->anon_dev)
		free_anon_bdev(pending_snapshot->anon_dev);
+2 −0
Original line number Diff line number Diff line
@@ -4495,6 +4495,8 @@ void btrfs_qgroup_convert_reserved_meta(struct btrfs_root *root, int num_bytes)
				      BTRFS_QGROUP_RSV_META_PREALLOC);
	trace_qgroup_meta_convert(root, num_bytes);
	qgroup_convert_meta(fs_info, root->root_key.objectid, num_bytes);
	if (!sb_rdonly(fs_info->sb))
		add_root_meta_rsv(root, num_bytes, BTRFS_QGROUP_RSV_META_PERTRANS);
}

/*
+0 −10
Original line number Diff line number Diff line
@@ -548,13 +548,3 @@ int btrfs_subvolume_reserve_metadata(struct btrfs_root *root,
	}
	return ret;
}

void btrfs_subvolume_release_metadata(struct btrfs_root *root,
				      struct btrfs_block_rsv *rsv)
{
	struct btrfs_fs_info *fs_info = root->fs_info;
	u64 qgroup_to_release;

	btrfs_block_rsv_release(fs_info, rsv, (u64)-1, &qgroup_to_release);
	btrfs_qgroup_convert_reserved_meta(root, qgroup_to_release);
}
Loading