Commit 182940f4 authored by Boris Burkov's avatar Boris Burkov Committed by David Sterba

btrfs: qgroup: add new quota mode for simple quotas

Add a new quota mode called "simple quotas". It can be enabled by the
existing quota enable ioctl via a new command, and sets an incompat
bit, as the implementation of simple quotas will make backwards
incompatible changes to the disk format of the extent tree.
Signed-off-by: default avatarBoris Burkov <boris@bur.io>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 6b0cd63b
...@@ -959,8 +959,7 @@ int btrfs_add_delayed_tree_ref(struct btrfs_trans_handle *trans, ...@@ -959,8 +959,7 @@ int btrfs_add_delayed_tree_ref(struct btrfs_trans_handle *trans,
return -ENOMEM; return -ENOMEM;
} }
if (test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) && if (btrfs_qgroup_enabled(fs_info) && !generic_ref->skip_qgroup) {
!generic_ref->skip_qgroup) {
record = kzalloc(sizeof(*record), GFP_NOFS); record = kzalloc(sizeof(*record), GFP_NOFS);
if (!record) { if (!record) {
kmem_cache_free(btrfs_delayed_tree_ref_cachep, ref); kmem_cache_free(btrfs_delayed_tree_ref_cachep, ref);
...@@ -1063,8 +1062,7 @@ int btrfs_add_delayed_data_ref(struct btrfs_trans_handle *trans, ...@@ -1063,8 +1062,7 @@ int btrfs_add_delayed_data_ref(struct btrfs_trans_handle *trans,
return -ENOMEM; return -ENOMEM;
} }
if (test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) && if (btrfs_qgroup_enabled(fs_info) && !generic_ref->skip_qgroup) {
!generic_ref->skip_qgroup) {
record = kzalloc(sizeof(*record), GFP_NOFS); record = kzalloc(sizeof(*record), GFP_NOFS);
if (!record) { if (!record) {
kmem_cache_free(btrfs_delayed_data_ref_cachep, ref); kmem_cache_free(btrfs_delayed_data_ref_cachep, ref);
......
...@@ -220,7 +220,8 @@ enum { ...@@ -220,7 +220,8 @@ enum {
BTRFS_FEATURE_INCOMPAT_NO_HOLES | \ BTRFS_FEATURE_INCOMPAT_NO_HOLES | \
BTRFS_FEATURE_INCOMPAT_METADATA_UUID | \ BTRFS_FEATURE_INCOMPAT_METADATA_UUID | \
BTRFS_FEATURE_INCOMPAT_RAID1C34 | \ BTRFS_FEATURE_INCOMPAT_RAID1C34 | \
BTRFS_FEATURE_INCOMPAT_ZONED) BTRFS_FEATURE_INCOMPAT_ZONED | \
BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA)
#ifdef CONFIG_BTRFS_DEBUG #ifdef CONFIG_BTRFS_DEBUG
/* /*
......
...@@ -3697,7 +3697,8 @@ static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg) ...@@ -3697,7 +3697,8 @@ static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg)
switch (sa->cmd) { switch (sa->cmd) {
case BTRFS_QUOTA_CTL_ENABLE: case BTRFS_QUOTA_CTL_ENABLE:
ret = btrfs_quota_enable(fs_info); case BTRFS_QUOTA_CTL_ENABLE_SIMPLE_QUOTA:
ret = btrfs_quota_enable(fs_info, sa);
break; break;
case BTRFS_QUOTA_CTL_DISABLE: case BTRFS_QUOTA_CTL_DISABLE:
ret = btrfs_quota_disable(fs_info); ret = btrfs_quota_disable(fs_info);
......
...@@ -34,9 +34,21 @@ enum btrfs_qgroup_mode btrfs_qgroup_mode(struct btrfs_fs_info *fs_info) ...@@ -34,9 +34,21 @@ enum btrfs_qgroup_mode btrfs_qgroup_mode(struct btrfs_fs_info *fs_info)
{ {
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
return BTRFS_QGROUP_MODE_DISABLED; return BTRFS_QGROUP_MODE_DISABLED;
if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE)
return BTRFS_QGROUP_MODE_SIMPLE;
return BTRFS_QGROUP_MODE_FULL; return BTRFS_QGROUP_MODE_FULL;
} }
bool btrfs_qgroup_enabled(struct btrfs_fs_info *fs_info)
{
return btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_DISABLED;
}
bool btrfs_qgroup_full_accounting(struct btrfs_fs_info *fs_info)
{
return btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_FULL;
}
/* /*
* Helpers to access qgroup reservation * Helpers to access qgroup reservation
* *
...@@ -350,6 +362,8 @@ int btrfs_verify_qgroup_counts(struct btrfs_fs_info *fs_info, u64 qgroupid, ...@@ -350,6 +362,8 @@ int btrfs_verify_qgroup_counts(struct btrfs_fs_info *fs_info, u64 qgroupid,
static void qgroup_mark_inconsistent(struct btrfs_fs_info *fs_info) static void qgroup_mark_inconsistent(struct btrfs_fs_info *fs_info)
{ {
if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
return;
fs_info->qgroup_flags |= (BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT | fs_info->qgroup_flags |= (BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT |
BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN | BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN |
BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING); BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING);
...@@ -370,8 +384,9 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info) ...@@ -370,8 +384,9 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
int ret = 0; int ret = 0;
u64 flags = 0; u64 flags = 0;
u64 rescan_progress = 0; u64 rescan_progress = 0;
bool simple;
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED)
return 0; return 0;
fs_info->qgroup_ulist = ulist_alloc(GFP_KERNEL); fs_info->qgroup_ulist = ulist_alloc(GFP_KERNEL);
...@@ -421,14 +436,14 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info) ...@@ -421,14 +436,14 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
"old qgroup version, quota disabled"); "old qgroup version, quota disabled");
goto out; goto out;
} }
fs_info->qgroup_flags = btrfs_qgroup_status_flags(l, ptr);
simple = (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE);
if (btrfs_qgroup_status_generation(l, ptr) != if (btrfs_qgroup_status_generation(l, ptr) !=
fs_info->generation) { fs_info->generation && !simple) {
qgroup_mark_inconsistent(fs_info); qgroup_mark_inconsistent(fs_info);
btrfs_err(fs_info, btrfs_err(fs_info,
"qgroup generation mismatch, marked as inconsistent"); "qgroup generation mismatch, marked as inconsistent");
} }
fs_info->qgroup_flags = btrfs_qgroup_status_flags(l,
ptr);
rescan_progress = btrfs_qgroup_status_rescan(l, ptr); rescan_progress = btrfs_qgroup_status_rescan(l, ptr);
goto next1; goto next1;
} }
...@@ -571,7 +586,7 @@ bool btrfs_check_quota_leak(struct btrfs_fs_info *fs_info) ...@@ -571,7 +586,7 @@ bool btrfs_check_quota_leak(struct btrfs_fs_info *fs_info)
struct rb_node *node; struct rb_node *node;
bool ret = false; bool ret = false;
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED)
return ret; return ret;
/* /*
* Since we're unmounting, there is no race and no need to grab qgroup * Since we're unmounting, there is no race and no need to grab qgroup
...@@ -970,7 +985,8 @@ static int btrfs_clean_quota_tree(struct btrfs_trans_handle *trans, ...@@ -970,7 +985,8 @@ static int btrfs_clean_quota_tree(struct btrfs_trans_handle *trans,
return ret; return ret;
} }
int btrfs_quota_enable(struct btrfs_fs_info *fs_info) int btrfs_quota_enable(struct btrfs_fs_info *fs_info,
struct btrfs_ioctl_quota_ctl_args *quota_ctl_args)
{ {
struct btrfs_root *quota_root; struct btrfs_root *quota_root;
struct btrfs_root *tree_root = fs_info->tree_root; struct btrfs_root *tree_root = fs_info->tree_root;
...@@ -983,6 +999,7 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info) ...@@ -983,6 +999,7 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info)
struct btrfs_qgroup *prealloc = NULL; struct btrfs_qgroup *prealloc = NULL;
struct btrfs_trans_handle *trans = NULL; struct btrfs_trans_handle *trans = NULL;
struct ulist *ulist = NULL; struct ulist *ulist = NULL;
const bool simple = (quota_ctl_args->cmd == BTRFS_QUOTA_CTL_ENABLE_SIMPLE_QUOTA);
int ret = 0; int ret = 0;
int slot; int slot;
...@@ -1085,8 +1102,11 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info) ...@@ -1085,8 +1102,11 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info)
struct btrfs_qgroup_status_item); struct btrfs_qgroup_status_item);
btrfs_set_qgroup_status_generation(leaf, ptr, trans->transid); btrfs_set_qgroup_status_generation(leaf, ptr, trans->transid);
btrfs_set_qgroup_status_version(leaf, ptr, BTRFS_QGROUP_STATUS_VERSION); btrfs_set_qgroup_status_version(leaf, ptr, BTRFS_QGROUP_STATUS_VERSION);
fs_info->qgroup_flags = BTRFS_QGROUP_STATUS_FLAG_ON | fs_info->qgroup_flags = BTRFS_QGROUP_STATUS_FLAG_ON;
BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT; if (simple)
fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE;
else
fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
btrfs_set_qgroup_status_flags(leaf, ptr, fs_info->qgroup_flags & btrfs_set_qgroup_status_flags(leaf, ptr, fs_info->qgroup_flags &
BTRFS_QGROUP_STATUS_FLAGS_MASK); BTRFS_QGROUP_STATUS_FLAGS_MASK);
btrfs_set_qgroup_status_rescan(leaf, ptr, 0); btrfs_set_qgroup_status_rescan(leaf, ptr, 0);
...@@ -1214,8 +1234,14 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info) ...@@ -1214,8 +1234,14 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info)
spin_lock(&fs_info->qgroup_lock); spin_lock(&fs_info->qgroup_lock);
fs_info->quota_root = quota_root; fs_info->quota_root = quota_root;
set_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags); set_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
if (simple)
btrfs_set_fs_incompat(fs_info, SIMPLE_QUOTA);
spin_unlock(&fs_info->qgroup_lock); spin_unlock(&fs_info->qgroup_lock);
/* Skip rescan for simple qgroups. */
if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
goto out_free_path;
ret = qgroup_rescan_init(fs_info, 0, 1); ret = qgroup_rescan_init(fs_info, 0, 1);
if (!ret) { if (!ret) {
qgroup_rescan_zero_tracking(fs_info); qgroup_rescan_zero_tracking(fs_info);
...@@ -1330,6 +1356,7 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info) ...@@ -1330,6 +1356,7 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
quota_root = fs_info->quota_root; quota_root = fs_info->quota_root;
fs_info->quota_root = NULL; fs_info->quota_root = NULL;
fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_ON; fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_ON;
fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE;
fs_info->qgroup_drop_subtree_thres = BTRFS_MAX_LEVEL; fs_info->qgroup_drop_subtree_thres = BTRFS_MAX_LEVEL;
spin_unlock(&fs_info->qgroup_lock); spin_unlock(&fs_info->qgroup_lock);
...@@ -1810,6 +1837,9 @@ int btrfs_qgroup_trace_extent_nolock(struct btrfs_fs_info *fs_info, ...@@ -1810,6 +1837,9 @@ int btrfs_qgroup_trace_extent_nolock(struct btrfs_fs_info *fs_info,
struct btrfs_qgroup_extent_record *entry; struct btrfs_qgroup_extent_record *entry;
u64 bytenr = record->bytenr; u64 bytenr = record->bytenr;
if (!btrfs_qgroup_full_accounting(fs_info))
return 0;
lockdep_assert_held(&delayed_refs->lock); lockdep_assert_held(&delayed_refs->lock);
trace_btrfs_qgroup_trace_extent(fs_info, record); trace_btrfs_qgroup_trace_extent(fs_info, record);
...@@ -1863,6 +1893,8 @@ int btrfs_qgroup_trace_extent_post(struct btrfs_trans_handle *trans, ...@@ -1863,6 +1893,8 @@ int btrfs_qgroup_trace_extent_post(struct btrfs_trans_handle *trans,
struct btrfs_backref_walk_ctx ctx = { 0 }; struct btrfs_backref_walk_ctx ctx = { 0 };
int ret; int ret;
if (!btrfs_qgroup_full_accounting(trans->fs_info))
return 0;
/* /*
* We are always called in a context where we are already holding a * We are always called in a context where we are already holding a
* transaction handle. Often we are called when adding a data delayed * transaction handle. Often we are called when adding a data delayed
...@@ -1931,8 +1963,7 @@ int btrfs_qgroup_trace_extent(struct btrfs_trans_handle *trans, u64 bytenr, ...@@ -1931,8 +1963,7 @@ int btrfs_qgroup_trace_extent(struct btrfs_trans_handle *trans, u64 bytenr,
struct btrfs_delayed_ref_root *delayed_refs; struct btrfs_delayed_ref_root *delayed_refs;
int ret; int ret;
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) if (!btrfs_qgroup_full_accounting(fs_info) || bytenr == 0 || num_bytes == 0)
|| bytenr == 0 || num_bytes == 0)
return 0; return 0;
record = kzalloc(sizeof(*record), GFP_NOFS); record = kzalloc(sizeof(*record), GFP_NOFS);
if (!record) if (!record)
...@@ -1970,7 +2001,7 @@ int btrfs_qgroup_trace_leaf_items(struct btrfs_trans_handle *trans, ...@@ -1970,7 +2001,7 @@ int btrfs_qgroup_trace_leaf_items(struct btrfs_trans_handle *trans,
u64 bytenr, num_bytes; u64 bytenr, num_bytes;
/* We can be called directly from walk_up_proc() */ /* We can be called directly from walk_up_proc() */
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) if (!btrfs_qgroup_full_accounting(fs_info))
return 0; return 0;
for (i = 0; i < nr; i++) { for (i = 0; i < nr; i++) {
...@@ -2346,7 +2377,7 @@ static int qgroup_trace_subtree_swap(struct btrfs_trans_handle *trans, ...@@ -2346,7 +2377,7 @@ static int qgroup_trace_subtree_swap(struct btrfs_trans_handle *trans,
int level; int level;
int ret; int ret;
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) if (!btrfs_qgroup_full_accounting(fs_info))
return 0; return 0;
/* Wrong parameter order */ /* Wrong parameter order */
...@@ -2413,7 +2444,7 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans, ...@@ -2413,7 +2444,7 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans,
BUG_ON(root_level < 0 || root_level >= BTRFS_MAX_LEVEL); BUG_ON(root_level < 0 || root_level >= BTRFS_MAX_LEVEL);
BUG_ON(root_eb == NULL); BUG_ON(root_eb == NULL);
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) if (!btrfs_qgroup_full_accounting(fs_info))
return 0; return 0;
spin_lock(&fs_info->qgroup_lock); spin_lock(&fs_info->qgroup_lock);
...@@ -2747,7 +2778,7 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr, ...@@ -2747,7 +2778,7 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
* If quotas get disabled meanwhile, the resources need to be freed and * If quotas get disabled meanwhile, the resources need to be freed and
* we can't just exit here. * we can't just exit here.
*/ */
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) || if (!btrfs_qgroup_full_accounting(fs_info) ||
fs_info->qgroup_flags & BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING) fs_info->qgroup_flags & BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING)
goto out_free; goto out_free;
...@@ -2816,6 +2847,9 @@ int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans) ...@@ -2816,6 +2847,9 @@ int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans)
u64 qgroup_to_skip; u64 qgroup_to_skip;
int ret = 0; int ret = 0;
if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
return 0;
delayed_refs = &trans->transaction->delayed_refs; delayed_refs = &trans->transaction->delayed_refs;
qgroup_to_skip = delayed_refs->qgroup_to_skip; qgroup_to_skip = delayed_refs->qgroup_to_skip;
while ((node = rb_first(&delayed_refs->dirty_extent_root))) { while ((node = rb_first(&delayed_refs->dirty_extent_root))) {
...@@ -2931,7 +2965,7 @@ int btrfs_run_qgroups(struct btrfs_trans_handle *trans) ...@@ -2931,7 +2965,7 @@ int btrfs_run_qgroups(struct btrfs_trans_handle *trans)
qgroup_mark_inconsistent(fs_info); qgroup_mark_inconsistent(fs_info);
spin_lock(&fs_info->qgroup_lock); spin_lock(&fs_info->qgroup_lock);
} }
if (test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) if (btrfs_qgroup_enabled(fs_info))
fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_ON; fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_ON;
else else
fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_ON; fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_ON;
...@@ -2990,7 +3024,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid, ...@@ -2990,7 +3024,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
if (!committing) if (!committing)
mutex_lock(&fs_info->qgroup_ioctl_lock); mutex_lock(&fs_info->qgroup_ioctl_lock);
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) if (!btrfs_qgroup_enabled(fs_info))
goto out; goto out;
quota_root = fs_info->quota_root; quota_root = fs_info->quota_root;
...@@ -3076,7 +3110,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid, ...@@ -3076,7 +3110,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
qgroup_dirty(fs_info, dstgroup); qgroup_dirty(fs_info, dstgroup);
} }
if (srcid) { if (srcid && btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_FULL) {
srcgroup = find_qgroup_rb(fs_info, srcid); srcgroup = find_qgroup_rb(fs_info, srcid);
if (!srcgroup) if (!srcgroup)
goto unlock; goto unlock;
...@@ -3339,6 +3373,9 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans, ...@@ -3339,6 +3373,9 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans,
int slot; int slot;
int ret; int ret;
if (!btrfs_qgroup_full_accounting(fs_info))
return 1;
mutex_lock(&fs_info->qgroup_rescan_lock); mutex_lock(&fs_info->qgroup_rescan_lock);
extent_root = btrfs_extent_root(fs_info, extent_root = btrfs_extent_root(fs_info,
fs_info->qgroup_rescan_progress.objectid); fs_info->qgroup_rescan_progress.objectid);
...@@ -3419,10 +3456,15 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans, ...@@ -3419,10 +3456,15 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans,
static bool rescan_should_stop(struct btrfs_fs_info *fs_info) static bool rescan_should_stop(struct btrfs_fs_info *fs_info)
{ {
return btrfs_fs_closing(fs_info) || if (btrfs_fs_closing(fs_info))
test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state) || return true;
!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) || if (test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state))
fs_info->qgroup_flags & BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN; return true;
if (!btrfs_qgroup_enabled(fs_info))
return true;
if (fs_info->qgroup_flags & BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN)
return true;
return false;
} }
static void btrfs_qgroup_rescan_worker(struct btrfs_work *work) static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
...@@ -3436,6 +3478,9 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work) ...@@ -3436,6 +3478,9 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
bool stopped = false; bool stopped = false;
bool did_leaf_rescans = false; bool did_leaf_rescans = false;
if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
return;
path = btrfs_alloc_path(); path = btrfs_alloc_path();
if (!path) if (!path)
goto out; goto out;
...@@ -3539,6 +3584,11 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid, ...@@ -3539,6 +3584,11 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
{ {
int ret = 0; int ret = 0;
if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE) {
btrfs_warn(fs_info, "qgroup rescan init failed, running in simple mode");
return -EINVAL;
}
if (!init_flags) { if (!init_flags) {
/* we're resuming qgroup rescan at mount time */ /* we're resuming qgroup rescan at mount time */
if (!(fs_info->qgroup_flags & if (!(fs_info->qgroup_flags &
...@@ -3569,7 +3619,7 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid, ...@@ -3569,7 +3619,7 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
btrfs_warn(fs_info, btrfs_warn(fs_info,
"qgroup rescan init failed, qgroup is not enabled"); "qgroup rescan init failed, qgroup is not enabled");
ret = -EINVAL; ret = -EINVAL;
} else if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) { } else if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED) {
/* Quota disable is in progress */ /* Quota disable is in progress */
ret = -EBUSY; ret = -EBUSY;
} }
...@@ -3828,7 +3878,7 @@ static int qgroup_reserve_data(struct btrfs_inode *inode, ...@@ -3828,7 +3878,7 @@ static int qgroup_reserve_data(struct btrfs_inode *inode,
u64 to_reserve; u64 to_reserve;
int ret; int ret;
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &root->fs_info->flags) || if (btrfs_qgroup_mode(root->fs_info) == BTRFS_QGROUP_MODE_DISABLED ||
!is_fstree(root->root_key.objectid) || len == 0) !is_fstree(root->root_key.objectid) || len == 0)
return 0; return 0;
...@@ -3960,7 +4010,7 @@ static int __btrfs_qgroup_release_data(struct btrfs_inode *inode, ...@@ -3960,7 +4010,7 @@ static int __btrfs_qgroup_release_data(struct btrfs_inode *inode,
int trace_op = QGROUP_RELEASE; int trace_op = QGROUP_RELEASE;
int ret; int ret;
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &inode->root->fs_info->flags)) if (btrfs_qgroup_mode(inode->root->fs_info) == BTRFS_QGROUP_MODE_DISABLED)
return 0; return 0;
/* In release case, we shouldn't have @reserved */ /* In release case, we shouldn't have @reserved */
...@@ -4071,7 +4121,7 @@ int btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes, ...@@ -4071,7 +4121,7 @@ int btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes,
struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_fs_info *fs_info = root->fs_info;
int ret; int ret;
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) || if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED ||
!is_fstree(root->root_key.objectid) || num_bytes == 0) !is_fstree(root->root_key.objectid) || num_bytes == 0)
return 0; return 0;
...@@ -4116,7 +4166,7 @@ void btrfs_qgroup_free_meta_all_pertrans(struct btrfs_root *root) ...@@ -4116,7 +4166,7 @@ void btrfs_qgroup_free_meta_all_pertrans(struct btrfs_root *root)
{ {
struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_fs_info *fs_info = root->fs_info;
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) || if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED ||
!is_fstree(root->root_key.objectid)) !is_fstree(root->root_key.objectid))
return; return;
...@@ -4132,7 +4182,7 @@ void __btrfs_qgroup_free_meta(struct btrfs_root *root, int num_bytes, ...@@ -4132,7 +4182,7 @@ void __btrfs_qgroup_free_meta(struct btrfs_root *root, int num_bytes,
{ {
struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_fs_info *fs_info = root->fs_info;
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) || if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED ||
!is_fstree(root->root_key.objectid)) !is_fstree(root->root_key.objectid))
return; return;
...@@ -4191,7 +4241,7 @@ void btrfs_qgroup_convert_reserved_meta(struct btrfs_root *root, int num_bytes) ...@@ -4191,7 +4241,7 @@ void btrfs_qgroup_convert_reserved_meta(struct btrfs_root *root, int num_bytes)
{ {
struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_fs_info *fs_info = root->fs_info;
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags) || if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED ||
!is_fstree(root->root_key.objectid)) !is_fstree(root->root_key.objectid))
return; return;
/* Same as btrfs_qgroup_free_meta_prealloc() */ /* Same as btrfs_qgroup_free_meta_prealloc() */
...@@ -4299,7 +4349,7 @@ int btrfs_qgroup_add_swapped_blocks(struct btrfs_trans_handle *trans, ...@@ -4299,7 +4349,7 @@ int btrfs_qgroup_add_swapped_blocks(struct btrfs_trans_handle *trans,
int level = btrfs_header_level(subvol_parent) - 1; int level = btrfs_header_level(subvol_parent) - 1;
int ret = 0; int ret = 0;
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) if (!btrfs_qgroup_full_accounting(fs_info))
return 0; return 0;
if (btrfs_node_ptr_generation(subvol_parent, subvol_slot) > if (btrfs_node_ptr_generation(subvol_parent, subvol_slot) >
...@@ -4409,7 +4459,7 @@ int btrfs_qgroup_trace_subtree_after_cow(struct btrfs_trans_handle *trans, ...@@ -4409,7 +4459,7 @@ int btrfs_qgroup_trace_subtree_after_cow(struct btrfs_trans_handle *trans,
int ret = 0; int ret = 0;
int i; int i;
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) if (!btrfs_qgroup_full_accounting(fs_info))
return 0; return 0;
if (!is_fstree(root->root_key.objectid) || !root->reloc_root) if (!is_fstree(root->root_key.objectid) || !root->reloc_root)
return 0; return 0;
......
...@@ -101,8 +101,15 @@ ...@@ -101,8 +101,15 @@
* subtree rescan for them. * subtree rescan for them.
*/ */
#define BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN (1UL << 3) /*
#define BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING (1UL << 4) * These flags share the flags field of the btrfs_qgroup_status_item with the
* persisted flags defined in btrfs_tree.h.
*
* To minimize the chance of collision with new persisted status flags, these
* count backwards from the MSB.
*/
#define BTRFS_QGROUP_RUNTIME_FLAG_CANCEL_RESCAN (1ULL << 63)
#define BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING (1ULL << 62)
/* /*
* Record a dirty extent, and info qgroup to update quota on it * Record a dirty extent, and info qgroup to update quota on it
...@@ -276,13 +283,17 @@ enum { ...@@ -276,13 +283,17 @@ enum {
ENUM_BIT(QGROUP_FREE), ENUM_BIT(QGROUP_FREE),
}; };
int btrfs_quota_enable(struct btrfs_fs_info *fs_info);
enum btrfs_qgroup_mode { enum btrfs_qgroup_mode {
BTRFS_QGROUP_MODE_DISABLED, BTRFS_QGROUP_MODE_DISABLED,
BTRFS_QGROUP_MODE_FULL, BTRFS_QGROUP_MODE_FULL,
BTRFS_QGROUP_MODE_SIMPLE
}; };
enum btrfs_qgroup_mode btrfs_qgroup_mode(struct btrfs_fs_info *fs_info); enum btrfs_qgroup_mode btrfs_qgroup_mode(struct btrfs_fs_info *fs_info);
bool btrfs_qgroup_enabled(struct btrfs_fs_info *fs_info);
bool btrfs_qgroup_full_accounting(struct btrfs_fs_info *fs_info);
int btrfs_quota_enable(struct btrfs_fs_info *fs_info,
struct btrfs_ioctl_quota_ctl_args *quota_ctl_args);
int btrfs_quota_disable(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); int btrfs_qgroup_rescan(struct btrfs_fs_info *fs_info);
void btrfs_qgroup_rescan_resume(struct btrfs_fs_info *fs_info); void btrfs_qgroup_rescan_resume(struct btrfs_fs_info *fs_info);
......
...@@ -510,7 +510,7 @@ int btrfs_subvolume_reserve_metadata(struct btrfs_root *root, ...@@ -510,7 +510,7 @@ int btrfs_subvolume_reserve_metadata(struct btrfs_root *root,
struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_block_rsv *global_rsv = &fs_info->global_block_rsv; struct btrfs_block_rsv *global_rsv = &fs_info->global_block_rsv;
if (test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) { if (btrfs_qgroup_enabled(fs_info)) {
/* One for parent inode, two for dir entries */ /* One for parent inode, two for dir entries */
qgroup_num_bytes = 3 * fs_info->nodesize; qgroup_num_bytes = 3 * fs_info->nodesize;
ret = btrfs_qgroup_reserve_meta_prealloc(root, ret = btrfs_qgroup_reserve_meta_prealloc(root,
......
...@@ -1620,11 +1620,10 @@ static int qgroup_account_snapshot(struct btrfs_trans_handle *trans, ...@@ -1620,11 +1620,10 @@ static int qgroup_account_snapshot(struct btrfs_trans_handle *trans,
int ret; int ret;
/* /*
* Save some performance in the case that qgroups are not * Save some performance in the case that full qgroups are not enabled.
* enabled. If this check races with the ioctl, rescan will * If this check races with the ioctl, rescan will kick in anyway.
* kick in anyway.
*/ */
if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags)) if (!btrfs_qgroup_full_accounting(fs_info))
return 0; return 0;
/* /*
......
...@@ -334,6 +334,7 @@ struct btrfs_ioctl_fs_info_args { ...@@ -334,6 +334,7 @@ struct btrfs_ioctl_fs_info_args {
#define BTRFS_FEATURE_INCOMPAT_ZONED (1ULL << 12) #define BTRFS_FEATURE_INCOMPAT_ZONED (1ULL << 12)
#define BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 (1ULL << 13) #define BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 (1ULL << 13)
#define BTRFS_FEATURE_INCOMPAT_RAID_STRIPE_TREE (1ULL << 14) #define BTRFS_FEATURE_INCOMPAT_RAID_STRIPE_TREE (1ULL << 14)
#define BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA (1ULL << 16)
struct btrfs_ioctl_feature_flags { struct btrfs_ioctl_feature_flags {
__u64 compat_flags; __u64 compat_flags;
...@@ -754,6 +755,7 @@ struct btrfs_ioctl_get_dev_stats { ...@@ -754,6 +755,7 @@ struct btrfs_ioctl_get_dev_stats {
#define BTRFS_QUOTA_CTL_ENABLE 1 #define BTRFS_QUOTA_CTL_ENABLE 1
#define BTRFS_QUOTA_CTL_DISABLE 2 #define BTRFS_QUOTA_CTL_DISABLE 2
#define BTRFS_QUOTA_CTL_RESCAN__NOTUSED 3 #define BTRFS_QUOTA_CTL_RESCAN__NOTUSED 3
#define BTRFS_QUOTA_CTL_ENABLE_SIMPLE_QUOTA 4
struct btrfs_ioctl_quota_ctl_args { struct btrfs_ioctl_quota_ctl_args {
__u64 cmd; __u64 cmd;
__u64 status; __u64 status;
......
...@@ -1233,9 +1233,17 @@ static inline __u16 btrfs_qgroup_level(__u64 qgroupid) ...@@ -1233,9 +1233,17 @@ static inline __u16 btrfs_qgroup_level(__u64 qgroupid)
*/ */
#define BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT (1ULL << 2) #define BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT (1ULL << 2)
/*
* Whether or not this filesystem is using simple quotas. Not exactly the
* incompat bit, because we support using simple quotas, disabling it, then
* going back to full qgroup quotas.
*/
#define BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE (1ULL << 3)
#define BTRFS_QGROUP_STATUS_FLAGS_MASK (BTRFS_QGROUP_STATUS_FLAG_ON | \ #define BTRFS_QGROUP_STATUS_FLAGS_MASK (BTRFS_QGROUP_STATUS_FLAG_ON | \
BTRFS_QGROUP_STATUS_FLAG_RESCAN | \ BTRFS_QGROUP_STATUS_FLAG_RESCAN | \
BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT) BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT | \
BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE)
#define BTRFS_QGROUP_STATUS_VERSION 1 #define BTRFS_QGROUP_STATUS_VERSION 1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment