Skip to content

Commit

Permalink
btrfs: qgroup: check generation when recording simple quota delta
Browse files Browse the repository at this point in the history
Simple quotas count extents only from the moment the feature is enabled.
Therefore, if we do something like:

1. create subvol S
2. write F in S
3. enable quotas
4. remove F
5. write G in S

then after 3. and 4. we would expect the simple quota usage of S to be 0
(putting aside some metadata extents that might be written) and after
5., it should be the size of G plus metadata. Therefore, we need to be
able to determine whether a particular quota delta we are processing
predates simple quota enablement.

To do this, store the transaction id when quotas were enabled. In
fs_info for immediate use and in the quota status item to make it
recoverable on mount. When we see a delta, check if the generation of
the extent item is less than that of quota enablement. If so, we should
ignore the delta from this extent.

Signed-off-by: Boris Burkov <[email protected]>
Signed-off-by: David Sterba <[email protected]>
  • Loading branch information
boryas authored and kdave committed Oct 12, 2023
1 parent 5343cd9 commit bd7c1ea
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 6 deletions.
2 changes: 2 additions & 0 deletions fs/btrfs/accessors.h
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,8 @@ BTRFS_SETGET_FUNCS(qgroup_status_flags, struct btrfs_qgroup_status_item,
flags, 64);
BTRFS_SETGET_FUNCS(qgroup_status_rescan, struct btrfs_qgroup_status_item,
rescan, 64);
BTRFS_SETGET_FUNCS(qgroup_status_enable_gen, struct btrfs_qgroup_status_item,
enable_gen, 64);

/* btrfs_qgroup_info_item */
BTRFS_SETGET_FUNCS(qgroup_info_generation, struct btrfs_qgroup_info_item,
Expand Down
4 changes: 4 additions & 0 deletions fs/btrfs/extent-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -1566,6 +1566,7 @@ static int run_delayed_data_ref(struct btrfs_trans_handle *trans,
.rsv_bytes = href->reserved_bytes,
.is_data = true,
.is_inc = true,
.generation = trans->transid,
};

if (extent_op)
Expand Down Expand Up @@ -1738,6 +1739,7 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans,
.rsv_bytes = 0,
.is_data = false,
.is_inc = true,
.generation = trans->transid,
};

BUG_ON(!extent_op || !extent_op->update_flags);
Expand Down Expand Up @@ -3287,6 +3289,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans,
.rsv_bytes = 0,
.is_data = is_data,
.is_inc = false,
.generation = btrfs_extent_generation(leaf, ei),
};

/* In this branch refs == 1 */
Expand Down Expand Up @@ -4924,6 +4927,7 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
struct btrfs_squota_delta delta = {
.root = root_objectid,
.num_bytes = ins->offset,
.generation = trans->transid,
.rsv_bytes = 0,
.is_data = true,
.is_inc = true,
Expand Down
1 change: 1 addition & 0 deletions fs/btrfs/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,7 @@ struct btrfs_fs_info {
/* Protected by qgroup_rescan_lock */
bool qgroup_rescan_running;
u8 qgroup_drop_subtree_thres;
u64 qgroup_enable_gen;

/*
* If this is not 0, then it indicates a serious filesystem error has
Expand Down
28 changes: 22 additions & 6 deletions fs/btrfs/qgroup.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,15 @@ static void qgroup_mark_inconsistent(struct btrfs_fs_info *fs_info)
BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING);
}

static void qgroup_read_enable_gen(struct btrfs_fs_info *fs_info,
struct extent_buffer *leaf, int slot,
struct btrfs_qgroup_status_item *ptr)
{
ASSERT(btrfs_fs_incompat(fs_info, SIMPLE_QUOTA));
ASSERT(btrfs_item_size(leaf, slot) >= sizeof(*ptr));
fs_info->qgroup_enable_gen = btrfs_qgroup_status_enable_gen(leaf, ptr);
}

/*
* The full config is read in one go, only called from open_ctree()
* It doesn't use any locking, as at this point we're still single-threaded
Expand All @@ -384,7 +393,6 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
int ret = 0;
u64 flags = 0;
u64 rescan_progress = 0;
bool simple;

if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_DISABLED)
return 0;
Expand Down Expand Up @@ -437,9 +445,9 @@ int btrfs_read_qgroup_config(struct btrfs_fs_info *fs_info)
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) !=
fs_info->generation && !simple) {
if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE) {
qgroup_read_enable_gen(fs_info, l, slot, ptr);
} else if (btrfs_qgroup_status_generation(l, ptr) != fs_info->generation) {
qgroup_mark_inconsistent(fs_info);
btrfs_err(fs_info,
"qgroup generation mismatch, marked as inconsistent");
Expand Down Expand Up @@ -1103,10 +1111,12 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info,
btrfs_set_qgroup_status_generation(leaf, ptr, trans->transid);
btrfs_set_qgroup_status_version(leaf, ptr, BTRFS_QGROUP_STATUS_VERSION);
fs_info->qgroup_flags = BTRFS_QGROUP_STATUS_FLAG_ON;
if (simple)
if (simple) {
fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE;
else
btrfs_set_qgroup_status_enable_gen(leaf, ptr, trans->transid);
} else {
fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
}
btrfs_set_qgroup_status_flags(leaf, ptr, fs_info->qgroup_flags &
BTRFS_QGROUP_STATUS_FLAGS_MASK);
btrfs_set_qgroup_status_rescan(leaf, ptr, 0);
Expand Down Expand Up @@ -1210,6 +1220,8 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info,
goto out_free_path;
}

fs_info->qgroup_enable_gen = trans->transid;

mutex_unlock(&fs_info->qgroup_ioctl_lock);
/*
* Commit the transaction while not holding qgroup_ioctl_lock, to avoid
Expand Down Expand Up @@ -4655,6 +4667,10 @@ int btrfs_record_squota_delta(struct btrfs_fs_info *fs_info,
if (!is_fstree(root))
return 0;

/* If the extent predates enabling quotas, don't count it. */
if (delta->generation < fs_info->qgroup_enable_gen)
return 0;

spin_lock(&fs_info->qgroup_lock);
qgroup = find_qgroup_rb(fs_info, root);
if (!qgroup) {
Expand Down
2 changes: 2 additions & 0 deletions fs/btrfs/qgroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ struct btrfs_squota_delta {
u64 num_bytes;
/* The number of bytes reserved for this extent. */
u64 rsv_bytes;
/* The generation the extent was created in. */
u64 generation;
/* Whether we are using or freeing the extent. */
bool is_inc;
/* Whether the extent is data or metadata. */
Expand Down
9 changes: 9 additions & 0 deletions include/uapi/linux/btrfs_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -1277,6 +1277,15 @@ struct btrfs_qgroup_status_item {
* of the scan. It contains a logical address
*/
__le64 rescan;

/*
* The generation when quotas were last enabled. Used by simple quotas to
* avoid decrementing when freeing an extent that was written before
* enable.
*
* Set only if flags contain BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE.
*/
__le64 enable_gen;
} __attribute__ ((__packed__));

struct btrfs_qgroup_info_item {
Expand Down

0 comments on commit bd7c1ea

Please sign in to comment.