for-7.1-rc4-tag
-----BEGIN PGP SIGNATURE-----
iQJPBAABCgA5FiEE8rQSAMVO+zA4DBdWxWXV+ddtWDsFAmoRu2cbFIAAAAAABAAO
bWFudTIsMi41KzEuMTIsMiwyAAoJEMVl1fnXbVg7jk0P/iokJwhO+YP7rdnDPAl6
wXYq/e4HxSl65//wwkR2Q7zc/GWOSdGZoP7fHCtXY52RERDKq2xrkQ+fBtnEMvS3
N6bwM0kVBoGPkXRHrf55SdfH7ITldeVAYIRIajO5bVT/j/F8l9s7ivXmI0Ep1xgZ
Eiv5MuOiW/kPLzYW1pHP+UniaUhIgQGTRcHs7NotAn55q2odnLdddRWx8NGT1kAe
Owydf6c0/B/+7NLhTLQl/w4WmeFL3OR0b0HuHiVYBNuQBkgCxwcUsERfPjnWpAbr
Pll32JKmJxH1Rthr8qA++Xv72D31VNAYVwxyieq/kPSFg6rwjcKw2lLdFKv99fCT
3OcDg0N9X20RK4ZcyMSwiCkS92DFStmVy4FtVIdNXbgpRcbw/jHNB6CnFq+RHOVU
wBNYdLte7zSmroDSQ/U2l/xY0n1KCf/KcYBxnkIkri6fdl/f2o8/monPfbsUvYiK
0qI3ODSomBpQRU0vYddJ28KfEx0iHqSQzmDyRFDlDuNb7M24d5W82jWLf60Nlk4h
ngehWVaVvLm8y4YiRteD10TGD7ClBE6ilu0t0dS2ys7o7stIAuXbjIP435tYz2T4
B0ddujn7S0mwNCoT+5yRfmxPQFJpyt93jU65VTJ95Mc7Pg43/D6b5ju7tvZlVdNw
NT4nY8sOiLy1KR72SvguXPSr
=hKb2
-----END PGP SIGNATURE-----
Merge tag 'for-7.1-rc4-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux
Pull btrfs fixes from David Sterba:
"A batch of fixes to simple quotas:
- add conditional rescheduling point not dependent on the lock during
inode iterations to avoid delays with PREEMPT_NONE enabled
- fix subvolume deletion so it does not break the squota invariants
- properly handle enabling squota, tracking extents in the initial
transaction
- catch and warn about underflows, clamp to zero to avoid further
problems
And one fix to inode size handling:
- fix handling of preallocated extents beyond i_size when not using
the no-holes feature"
* tag 'for-7.1-rc4-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
btrfs: swallow btrfs_record_squota_delta() ENOENT
btrfs: clamp to avoid squota underflow
btrfs: fix squota accounting during enable generation
btrfs: check for subvolume before deleting squota qgroup
btrfs: always drop root->inodes lock before cond_resched()
btrfs: mark file extent range dirty after converting prealloc extents
master
commit
400544639d
|
|
@ -1246,7 +1246,9 @@ static struct btrfs_inode *find_first_inode_to_shrink(struct btrfs_root *root,
|
||||||
write_unlock(&tree->lock);
|
write_unlock(&tree->lock);
|
||||||
next:
|
next:
|
||||||
from = btrfs_ino(inode) + 1;
|
from = btrfs_ino(inode) + 1;
|
||||||
cond_resched_lock(&root->inodes.xa_lock);
|
xa_unlock(&root->inodes);
|
||||||
|
cond_resched();
|
||||||
|
xa_lock(&root->inodes);
|
||||||
}
|
}
|
||||||
xa_unlock(&root->inodes);
|
xa_unlock(&root->inodes);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -633,7 +633,7 @@ again:
|
||||||
trans->transid);
|
trans->transid);
|
||||||
btrfs_set_file_extent_num_bytes(leaf, fi,
|
btrfs_set_file_extent_num_bytes(leaf, fi,
|
||||||
end - other_start);
|
end - other_start);
|
||||||
return 0;
|
goto mark_dirty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -661,7 +661,7 @@ again:
|
||||||
other_end - start);
|
other_end - start);
|
||||||
btrfs_set_file_extent_offset(leaf, fi,
|
btrfs_set_file_extent_offset(leaf, fi,
|
||||||
start - orig_offset);
|
start - orig_offset);
|
||||||
return 0;
|
goto mark_dirty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -788,7 +788,12 @@ again:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
mark_dirty:
|
||||||
|
ret = btrfs_inode_set_file_extent_range(inode, start, end - start);
|
||||||
|
if (ret)
|
||||||
|
btrfs_abort_transaction(trans, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,7 @@ enum {
|
||||||
BTRFS_FS_LOG_RECOVERING,
|
BTRFS_FS_LOG_RECOVERING,
|
||||||
BTRFS_FS_OPEN,
|
BTRFS_FS_OPEN,
|
||||||
BTRFS_FS_QUOTA_ENABLED,
|
BTRFS_FS_QUOTA_ENABLED,
|
||||||
|
BTRFS_FS_SQUOTA_ENABLING,
|
||||||
BTRFS_FS_UPDATE_UUID_TREE_GEN,
|
BTRFS_FS_UPDATE_UUID_TREE_GEN,
|
||||||
BTRFS_FS_CREATING_FREE_SPACE_TREE,
|
BTRFS_FS_CREATING_FREE_SPACE_TREE,
|
||||||
BTRFS_FS_BTREE_ERR,
|
BTRFS_FS_BTREE_ERR,
|
||||||
|
|
|
||||||
|
|
@ -10699,7 +10699,9 @@ struct btrfs_inode *btrfs_find_first_inode(struct btrfs_root *root, u64 min_ino)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
from = btrfs_ino(inode) + 1;
|
from = btrfs_ino(inode) + 1;
|
||||||
cond_resched_lock(&root->inodes.xa_lock);
|
xa_unlock(&root->inodes);
|
||||||
|
cond_resched();
|
||||||
|
xa_lock(&root->inodes);
|
||||||
}
|
}
|
||||||
xa_unlock(&root->inodes);
|
xa_unlock(&root->inodes);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1107,7 +1107,13 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info,
|
||||||
if (simple) {
|
if (simple) {
|
||||||
fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE;
|
fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE;
|
||||||
btrfs_set_fs_incompat(fs_info, SIMPLE_QUOTA);
|
btrfs_set_fs_incompat(fs_info, SIMPLE_QUOTA);
|
||||||
btrfs_set_qgroup_status_enable_gen(leaf, ptr, trans->transid);
|
/*
|
||||||
|
* Set the enable generation to the next transaction, as we cannot
|
||||||
|
* ensure that extents written during this transaction will see any
|
||||||
|
* state we have set here. So we should treat all extents of the
|
||||||
|
* transaction as coming in before squotas was enabled.
|
||||||
|
*/
|
||||||
|
btrfs_set_qgroup_status_enable_gen(leaf, ptr, trans->transid + 1);
|
||||||
} else {
|
} else {
|
||||||
fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
|
fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
|
||||||
}
|
}
|
||||||
|
|
@ -1210,7 +1216,15 @@ out_add_root:
|
||||||
goto out_free_path;
|
goto out_free_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs_info->qgroup_enable_gen = trans->transid;
|
/*
|
||||||
|
* Set fs_info->qgroup_enable_gen and BTRFS_FS_SQUOTA_ENABLING
|
||||||
|
* under the transaction handle. We want to ensure that all extents in
|
||||||
|
* the next transaction definitely see them.
|
||||||
|
*/
|
||||||
|
if (simple) {
|
||||||
|
fs_info->qgroup_enable_gen = trans->transid + 1;
|
||||||
|
set_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags);
|
||||||
|
}
|
||||||
|
|
||||||
mutex_unlock(&fs_info->qgroup_ioctl_lock);
|
mutex_unlock(&fs_info->qgroup_ioctl_lock);
|
||||||
/*
|
/*
|
||||||
|
|
@ -1224,9 +1238,15 @@ out_add_root:
|
||||||
*/
|
*/
|
||||||
ret = btrfs_commit_transaction(trans);
|
ret = btrfs_commit_transaction(trans);
|
||||||
trans = NULL;
|
trans = NULL;
|
||||||
|
|
||||||
mutex_lock(&fs_info->qgroup_ioctl_lock);
|
mutex_lock(&fs_info->qgroup_ioctl_lock);
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
if (simple) {
|
||||||
|
clear_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags);
|
||||||
|
fs_info->qgroup_enable_gen = 0;
|
||||||
|
}
|
||||||
goto out_free_path;
|
goto out_free_path;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set quota enabled flag after committing the transaction, to avoid
|
* Set quota enabled flag after committing the transaction, to avoid
|
||||||
|
|
@ -1236,6 +1256,8 @@ out_add_root:
|
||||||
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)
|
||||||
|
clear_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags);
|
||||||
spin_unlock(&fs_info->qgroup_lock);
|
spin_unlock(&fs_info->qgroup_lock);
|
||||||
|
|
||||||
/* Skip rescan for simple qgroups. */
|
/* Skip rescan for simple qgroups. */
|
||||||
|
|
@ -1715,32 +1737,24 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool can_delete_parent_qgroup(struct btrfs_qgroup *qgroup)
|
static bool can_delete_parent_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup)
|
||||||
|
|
||||||
{
|
{
|
||||||
ASSERT(btrfs_qgroup_level(qgroup->qgroupid));
|
ASSERT(btrfs_qgroup_level(qgroup->qgroupid));
|
||||||
|
if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
|
||||||
|
squota_check_parent_usage(fs_info, qgroup);
|
||||||
return list_empty(&qgroup->members);
|
return list_empty(&qgroup->members);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return true if we can delete the squota qgroup and false otherwise.
|
* Because a shared extent can outlive its owning subvolume, we cannot delete a
|
||||||
*
|
* subvol squota qgroup until all of the extents it owns are gone, even if the
|
||||||
* Rules for whether we can delete:
|
* subvolume itself has been deleted.
|
||||||
*
|
|
||||||
* 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)
|
static bool can_delete_squota_subvol_qgroup(struct btrfs_fs_info *fs_info,
|
||||||
|
struct btrfs_qgroup *qgroup)
|
||||||
{
|
{
|
||||||
ASSERT(btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE);
|
ASSERT(btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE);
|
||||||
|
ASSERT(btrfs_qgroup_level(qgroup->qgroupid) == 0);
|
||||||
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 !(qgroup->rfer || qgroup->excl || qgroup->rfer_cmpr || qgroup->excl_cmpr);
|
||||||
}
|
}
|
||||||
|
|
@ -1754,14 +1768,11 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup
|
||||||
{
|
{
|
||||||
struct btrfs_key key;
|
struct btrfs_key key;
|
||||||
BTRFS_PATH_AUTO_FREE(path);
|
BTRFS_PATH_AUTO_FREE(path);
|
||||||
|
int ret;
|
||||||
/* 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. */
|
/* For higher level qgroup, we can only delete it if it has no child. */
|
||||||
if (btrfs_qgroup_level(qgroup->qgroupid))
|
if (btrfs_qgroup_level(qgroup->qgroupid))
|
||||||
return can_delete_parent_qgroup(qgroup);
|
return can_delete_parent_qgroup(fs_info, qgroup);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For level-0 qgroups, we can only delete it if it has no subvolume
|
* For level-0 qgroups, we can only delete it if it has no subvolume
|
||||||
|
|
@ -1777,10 +1788,21 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The @ret from btrfs_find_root() exactly matches our definition for
|
* Any subvol qgroup, regardless of mode, cannot be deleted if the
|
||||||
* the return value, thus can be returned directly.
|
* subvol still exists.
|
||||||
*/
|
*/
|
||||||
return btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL);
|
ret = btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL);
|
||||||
|
/*
|
||||||
|
* btrfs_find_root returns <0 on error, 0 if found, and >0 if not,
|
||||||
|
* so the "found" and "error" cases match our desired return values.
|
||||||
|
*/
|
||||||
|
if (ret <= 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* Squotas require additional checks, even if the subvol is deleted. */
|
||||||
|
if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
|
||||||
|
return can_delete_squota_subvol_qgroup(fs_info, qgroup);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid)
|
int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid)
|
||||||
|
|
@ -4922,7 +4944,8 @@ int btrfs_record_squota_delta(struct btrfs_fs_info *fs_info,
|
||||||
u64 num_bytes = delta->num_bytes;
|
u64 num_bytes = delta->num_bytes;
|
||||||
const int sign = (delta->is_inc ? 1 : -1);
|
const int sign = (delta->is_inc ? 1 : -1);
|
||||||
|
|
||||||
if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE)
|
if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE &&
|
||||||
|
!test_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!btrfs_is_fstree(root))
|
if (!btrfs_is_fstree(root))
|
||||||
|
|
@ -4934,8 +4957,9 @@ int btrfs_record_squota_delta(struct btrfs_fs_info *fs_info,
|
||||||
|
|
||||||
spin_lock(&fs_info->qgroup_lock);
|
spin_lock(&fs_info->qgroup_lock);
|
||||||
qgroup = find_qgroup_rb(fs_info, root);
|
qgroup = find_qgroup_rb(fs_info, root);
|
||||||
if (!qgroup) {
|
if (WARN_ON_ONCE(!qgroup)) {
|
||||||
ret = -ENOENT;
|
btrfs_warn(fs_info, "squota failed to find qgroup for root %llu", root);
|
||||||
|
ret = 0;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4944,8 +4968,19 @@ int btrfs_record_squota_delta(struct btrfs_fs_info *fs_info,
|
||||||
list_for_each_entry(qg, &qgroup_list, iterator) {
|
list_for_each_entry(qg, &qgroup_list, iterator) {
|
||||||
struct btrfs_qgroup_list *glist;
|
struct btrfs_qgroup_list *glist;
|
||||||
|
|
||||||
qg->excl += num_bytes * sign;
|
ASSERT(qg->excl == qg->rfer);
|
||||||
qg->rfer += num_bytes * sign;
|
if (WARN_ON_ONCE(sign < 0 && qg->excl < num_bytes)) {
|
||||||
|
btrfs_warn(fs_info,
|
||||||
|
"squota underflow qg %hu/%llu excl %llu num_bytes %llu",
|
||||||
|
btrfs_qgroup_level(qg->qgroupid),
|
||||||
|
btrfs_qgroup_subvolid(qg->qgroupid),
|
||||||
|
qg->excl, num_bytes);
|
||||||
|
qg->excl = 0;
|
||||||
|
qg->rfer = 0;
|
||||||
|
} else {
|
||||||
|
qg->excl += num_bytes * sign;
|
||||||
|
qg->rfer += num_bytes * sign;
|
||||||
|
}
|
||||||
qgroup_dirty(fs_info, qg);
|
qgroup_dirty(fs_info, qg);
|
||||||
|
|
||||||
list_for_each_entry(glist, &qg->groups, next_group)
|
list_for_each_entry(glist, &qg->groups, next_group)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue