Commit adb0af40 authored by Boris Burkov's avatar Boris Burkov Committed by David Sterba
Browse files

btrfs: relax squota parent qgroup deletion rule



Currently, with squotas, we do not allow removing a parent qgroup with
no members if it still has usage accounted to it. This makes it really
difficult to recover from accounting bugs, as we have no good way of
getting back to 0 usage.

Instead, allow deletion (it's safe at 0 members..) while still warning
about the inconsistency by adding a squota parent check.

Reviewed-by: default avatarQu Wenruo <wqu@suse.com>
Signed-off-by: default avatarBoris Burkov <boris@bur.io>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 9c46bcda
Loading
Loading
Loading
Loading
+35 −15
Original line number Diff line number Diff line
@@ -1718,6 +1718,36 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid)
	return ret;
}

static bool can_delete_parent_qgroup(struct btrfs_qgroup *qgroup)

{
	ASSERT(btrfs_qgroup_level(qgroup->qgroupid));
	return list_empty(&qgroup->members);
}

/*
 * Return true if we can delete the squota qgroup and false otherwise.
 *
 * Rules for whether we can delete:
 *
 * A subvolume qgroup can be removed iff the subvolume is fully deleted, which
 * is iff there is 0 usage in the qgroup.
 *
 * A higher level qgroup can be removed iff it has no members.
 * Note: We audit its usage to warn on inconsitencies without blocking deletion.
 */
static bool can_delete_squota_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup)
{
	ASSERT(btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE);

	if (btrfs_qgroup_level(qgroup->qgroupid) > 0) {
		squota_check_parent_usage(fs_info, qgroup);
		return can_delete_parent_qgroup(qgroup);
	}

	return !(qgroup->rfer || qgroup->excl || qgroup->rfer_cmpr || qgroup->excl_cmpr);
}

/*
 * Return 0 if we can not delete the qgroup (not empty or has children etc).
 * Return >0 if we can delete the qgroup.
@@ -1728,23 +1758,13 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup
	struct btrfs_key key;
	BTRFS_PATH_AUTO_FREE(path);

	/*
	 * Squota would never be inconsistent, but there can still be case
	 * where a dropped subvolume still has qgroup numbers, and squota
	 * relies on such qgroup for future accounting.
	 *
	 * So for squota, do not allow dropping any non-zero qgroup.
	 */
	if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE &&
	    (qgroup->rfer || qgroup->excl || qgroup->excl_cmpr || qgroup->rfer_cmpr))
		return 0;
	/* Since squotas cannot be inconsistent, they have special rules for deletion. */
	if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
		return can_delete_squota_qgroup(fs_info, qgroup);

	/* For higher level qgroup, we can only delete it if it has no child. */
	if (btrfs_qgroup_level(qgroup->qgroupid)) {
		if (!list_empty(&qgroup->members))
			return 0;
		return 1;
	}
	if (btrfs_qgroup_level(qgroup->qgroupid))
		return can_delete_parent_qgroup(qgroup);

	/*
	 * For level-0 qgroups, we can only delete it if it has no subvolume