btrfs-progs: mkfs: add support for squota

Add the ability to enable simple quotas from mkfs with '-O squota'

There is some complication around handling enable_gen while still
counting the root node of an fs. To handle this, employ a hack of doing
a no-op write on the root node to bump its generation up above that of
the qgroup enable generation, which results in counting it properly.

Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Boris Burkov <boris@bur.io>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Boris Burkov 2023-09-27 10:46:46 -07:00 committed by David Sterba
parent 5bd97022f3
commit 14ac1a6051
3 changed files with 86 additions and 8 deletions

View File

@ -1696,6 +1696,8 @@ static int repair_qgroup_status(struct btrfs_fs_info *info, bool silent)
struct btrfs_path path; struct btrfs_path path;
struct btrfs_key key; struct btrfs_key key;
struct btrfs_qgroup_status_item *status_item; struct btrfs_qgroup_status_item *status_item;
bool simple = btrfs_fs_incompat(info, SIMPLE_QUOTA);
u64 flags = BTRFS_QGROUP_STATUS_FLAG_ON;
if (!silent) if (!silent)
printf("Repair qgroup status item\n"); printf("Repair qgroup status item\n");
@ -1718,8 +1720,9 @@ static int repair_qgroup_status(struct btrfs_fs_info *info, bool silent)
status_item = btrfs_item_ptr(path.nodes[0], path.slots[0], status_item = btrfs_item_ptr(path.nodes[0], path.slots[0],
struct btrfs_qgroup_status_item); struct btrfs_qgroup_status_item);
btrfs_set_qgroup_status_flags(path.nodes[0], status_item, if (simple)
BTRFS_QGROUP_STATUS_FLAG_ON); flags |= BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE;
btrfs_set_qgroup_status_flags(path.nodes[0], status_item, flags);
btrfs_set_qgroup_status_rescan(path.nodes[0], status_item, 0); btrfs_set_qgroup_status_rescan(path.nodes[0], status_item, 0);
btrfs_set_qgroup_status_generation(path.nodes[0], status_item, btrfs_set_qgroup_status_generation(path.nodes[0], status_item,
trans->transid); trans->transid);

View File

@ -109,6 +109,15 @@ static const struct btrfs_feature mkfs_features[] = {
VERSION_NULL(default), VERSION_NULL(default),
.desc = "quota support (qgroups)" .desc = "quota support (qgroups)"
}, },
{
.name = "squota",
.incompat_flag = BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA,
.sysfs_name = "squota",
VERSION_TO_STRING2(compat, 6,7),
VERSION_NULL(safe),
VERSION_NULL(default),
.desc = "squota support (simple accounting qgroups)"
},
{ {
.name = "extref", .name = "extref",
.incompat_flag = BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF, .incompat_flag = BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF,

View File

@ -59,6 +59,8 @@
#include "mkfs/common.h" #include "mkfs/common.h"
#include "mkfs/rootdir.h" #include "mkfs/rootdir.h"
#include "libbtrfs/ctree.h"
struct mkfs_allocation { struct mkfs_allocation {
u64 data; u64 data;
u64 metadata; u64 metadata;
@ -882,6 +884,51 @@ static int insert_qgroup_items(struct btrfs_trans_handle *trans,
return ret; return ret;
} }
/*
* Workaround for squota so the enable_gen can be properly used.
*/
static int touch_root_subvol(struct btrfs_fs_info *fs_info)
{
struct btrfs_trans_handle *trans;
struct btrfs_key key = {
.objectid = BTRFS_FIRST_FREE_OBJECTID,
.type = BTRFS_INODE_ITEM_KEY,
.offset = 0,
};
struct extent_buffer *leaf;
int slot;
struct btrfs_path path;
int ret;
trans = btrfs_start_transaction(fs_info->fs_root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
errno = -ret;
error_msg(ERROR_MSG_START_TRANS, "%m");
return ret;
}
btrfs_init_path(&path);
ret = btrfs_search_slot(trans, fs_info->fs_root, &key, &path, 0, 1);
if (ret)
goto fail;
leaf = path.nodes[0];
slot = path.slots[0];
btrfs_item_key_to_cpu(leaf, &key, slot);
btrfs_mark_buffer_dirty(leaf);
ret = btrfs_commit_transaction(trans, fs_info->fs_root);
if (ret < 0) {
errno = -ret;
error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
return ret;
}
btrfs_release_path(&path);
return 0;
fail:
btrfs_abort_transaction(trans, ret);
btrfs_release_path(&path);
return ret;
}
static int setup_quota_root(struct btrfs_fs_info *fs_info) static int setup_quota_root(struct btrfs_fs_info *fs_info)
{ {
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
@ -890,8 +937,11 @@ static int setup_quota_root(struct btrfs_fs_info *fs_info)
struct btrfs_path path; struct btrfs_path path;
struct btrfs_key key; struct btrfs_key key;
int qgroup_repaired = 0; int qgroup_repaired = 0;
bool simple = btrfs_fs_incompat(fs_info, SIMPLE_QUOTA);
int flags;
int ret; int ret;
/* One to modify tree root, one for quota root */ /* One to modify tree root, one for quota root */
trans = btrfs_start_transaction(fs_info->tree_root, 2); trans = btrfs_start_transaction(fs_info->tree_root, 2);
if (IS_ERR(trans)) { if (IS_ERR(trans)) {
@ -921,13 +971,19 @@ static int setup_quota_root(struct btrfs_fs_info *fs_info)
qsi = btrfs_item_ptr(path.nodes[0], path.slots[0], qsi = btrfs_item_ptr(path.nodes[0], path.slots[0],
struct btrfs_qgroup_status_item); struct btrfs_qgroup_status_item);
btrfs_set_qgroup_status_generation(path.nodes[0], qsi, 0); btrfs_set_qgroup_status_generation(path.nodes[0], qsi, trans->transid);
btrfs_set_qgroup_status_rescan(path.nodes[0], qsi, 0); btrfs_set_qgroup_status_rescan(path.nodes[0], qsi, 0);
flags = BTRFS_QGROUP_STATUS_FLAG_ON;
if (simple) {
btrfs_set_qgroup_status_enable_gen(path.nodes[0], qsi, trans->transid);
flags |= BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE;
}
else {
flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
}
/* Mark current status info inconsistent, and fix it later */ btrfs_set_qgroup_status_version(path.nodes[0], qsi, 1);
btrfs_set_qgroup_status_flags(path.nodes[0], qsi, btrfs_set_qgroup_status_flags(path.nodes[0], qsi, flags);
BTRFS_QGROUP_STATUS_FLAG_ON |
BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT);
btrfs_release_path(&path); btrfs_release_path(&path);
/* Currently mkfs will only create one subvolume */ /* Currently mkfs will only create one subvolume */
@ -944,6 +1000,15 @@ static int setup_quota_root(struct btrfs_fs_info *fs_info)
return ret; return ret;
} }
/* Hack to count the default subvol metadata by dirtying it */
if (simple) {
ret = touch_root_subvol(fs_info);
if (ret) {
error("failed to touch root dir for simple quota accounting %d (%m)", ret);
goto fail;
}
}
/* /*
* Qgroup is setup but with wrong info, use qgroup-verify * Qgroup is setup but with wrong info, use qgroup-verify
* infrastructure to repair them. (Just acts as offline rescan) * infrastructure to repair them. (Just acts as offline rescan)
@ -1813,7 +1878,8 @@ raid_groups:
} }
} }
if (features.runtime_flags & BTRFS_FEATURE_RUNTIME_QUOTA) { if (features.runtime_flags & BTRFS_FEATURE_RUNTIME_QUOTA ||
features.incompat_flags & BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA) {
ret = setup_quota_root(fs_info); ret = setup_quota_root(fs_info);
if (ret < 0) { if (ret < 0) {
error("failed to initialize quota: %d (%m)", ret); error("failed to initialize quota: %d (%m)", ret);