179 lines
5.8 KiB
Diff
179 lines
5.8 KiB
Diff
From 6fe0d820b76da3a4f1f8d1fd605b2afc9edcb3f8 Mon Sep 17 00:00:00 2001
|
|
From: "Darrick J. Wong" <djwong@kernel.org>
|
|
Date: Sun, 3 Nov 2024 20:19:39 -0800
|
|
Subject: xfs: fix chown with rt quota
|
|
|
|
Make chown's quota adjustments work with realtime files. This is mostly
|
|
a matter of calling xfs_inode_count_blocks on a given file to figure out
|
|
the number of blocks allocated to the data device and to the realtime
|
|
device, and using those quantities to update the quota accounting when
|
|
the id changes. Delayed allocation reservations are moved from the old
|
|
dquot's incore reservation to the new dquot's incore reservation.
|
|
|
|
Note that there was a missing ILOCK bug in xfs_qm_dqusage_adjust that we
|
|
must fix before calling xfs_iread_extents. Prior to 2.6.37 the locking
|
|
was correct, but then someone removed the ILOCK as part of a cleanup.
|
|
Nobody noticed because nowhere in the git history have we ever supported
|
|
rt+quota so nobody can use this.
|
|
|
|
I'm leaving git breadcrumbs in case anyone is desperate enough to try to
|
|
backport the rtquota code to old kernels.
|
|
|
|
Not-Cc: <stable@vger.kernel.org> # v2.6.37
|
|
Fixes: 52fda114249578 ("xfs: simplify xfs_qm_dqusage_adjust")
|
|
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
|
|
Reviewed-by: Christoph Hellwig <hch@lst.de>
|
|
---
|
|
fs/xfs/xfs_qm.c | 44 +++++++++++++++++++++++++++-----------------
|
|
fs/xfs/xfs_trans.c | 31 +++++++++++++++++++++++++++++--
|
|
2 files changed, 56 insertions(+), 19 deletions(-)
|
|
|
|
--- a/fs/xfs/xfs_qm.c
|
|
+++ b/fs/xfs/xfs_qm.c
|
|
@@ -1153,8 +1153,8 @@ xfs_qm_dqusage_adjust(
|
|
void *data)
|
|
{
|
|
struct xfs_inode *ip;
|
|
- xfs_qcnt_t nblks;
|
|
- xfs_filblks_t rtblks = 0; /* total rt blks */
|
|
+ xfs_filblks_t nblks, rtblks;
|
|
+ unsigned int lock_mode;
|
|
int error;
|
|
|
|
ASSERT(XFS_IS_QUOTA_ON(mp));
|
|
@@ -1191,18 +1191,17 @@ xfs_qm_dqusage_adjust(
|
|
|
|
ASSERT(ip->i_delayed_blks == 0);
|
|
|
|
+ lock_mode = xfs_ilock_data_map_shared(ip);
|
|
if (XFS_IS_REALTIME_INODE(ip)) {
|
|
- struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);
|
|
-
|
|
error = xfs_iread_extents(tp, ip, XFS_DATA_FORK);
|
|
- if (error)
|
|
+ if (error) {
|
|
+ xfs_iunlock(ip, lock_mode);
|
|
goto error0;
|
|
-
|
|
- xfs_bmap_count_leaves(ifp, &rtblks);
|
|
+ }
|
|
}
|
|
-
|
|
- nblks = (xfs_qcnt_t)ip->i_nblocks - rtblks;
|
|
+ xfs_inode_count_blocks(tp, ip, &nblks, &rtblks);
|
|
xfs_iflags_clear(ip, XFS_IQUOTAUNCHECKED);
|
|
+ xfs_iunlock(ip, lock_mode);
|
|
|
|
/*
|
|
* Add the (disk blocks and inode) resources occupied by this
|
|
@@ -1858,9 +1857,8 @@ xfs_qm_vop_chown(
|
|
struct xfs_dquot *newdq)
|
|
{
|
|
struct xfs_dquot *prevdq;
|
|
- uint bfield = XFS_IS_REALTIME_INODE(ip) ?
|
|
- XFS_TRANS_DQ_RTBCOUNT : XFS_TRANS_DQ_BCOUNT;
|
|
-
|
|
+ xfs_filblks_t dblocks, rblocks;
|
|
+ bool isrt = XFS_IS_REALTIME_INODE(ip);
|
|
|
|
xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
|
|
ASSERT(XFS_IS_QUOTA_ON(ip->i_mount));
|
|
@@ -1870,11 +1868,17 @@ xfs_qm_vop_chown(
|
|
ASSERT(prevdq);
|
|
ASSERT(prevdq != newdq);
|
|
|
|
- xfs_trans_mod_ino_dquot(tp, ip, prevdq, bfield, -(ip->i_nblocks));
|
|
+ xfs_inode_count_blocks(tp, ip, &dblocks, &rblocks);
|
|
+
|
|
+ xfs_trans_mod_ino_dquot(tp, ip, prevdq, XFS_TRANS_DQ_BCOUNT,
|
|
+ -(xfs_qcnt_t)dblocks);
|
|
+ xfs_trans_mod_ino_dquot(tp, ip, prevdq, XFS_TRANS_DQ_RTBCOUNT,
|
|
+ -(xfs_qcnt_t)rblocks);
|
|
xfs_trans_mod_ino_dquot(tp, ip, prevdq, XFS_TRANS_DQ_ICOUNT, -1);
|
|
|
|
/* the sparkling new dquot */
|
|
- xfs_trans_mod_ino_dquot(tp, ip, newdq, bfield, ip->i_nblocks);
|
|
+ xfs_trans_mod_ino_dquot(tp, ip, newdq, XFS_TRANS_DQ_BCOUNT, dblocks);
|
|
+ xfs_trans_mod_ino_dquot(tp, ip, newdq, XFS_TRANS_DQ_RTBCOUNT, rblocks);
|
|
xfs_trans_mod_ino_dquot(tp, ip, newdq, XFS_TRANS_DQ_ICOUNT, 1);
|
|
|
|
/*
|
|
@@ -1884,7 +1888,8 @@ xfs_qm_vop_chown(
|
|
* (having already bumped up the real counter) so that we don't have
|
|
* any reservation to give back when we commit.
|
|
*/
|
|
- xfs_trans_mod_dquot(tp, newdq, XFS_TRANS_DQ_RES_BLKS,
|
|
+ xfs_trans_mod_dquot(tp, newdq,
|
|
+ isrt ? XFS_TRANS_DQ_RES_RTBLKS : XFS_TRANS_DQ_RES_BLKS,
|
|
-ip->i_delayed_blks);
|
|
|
|
/*
|
|
@@ -1896,8 +1901,13 @@ xfs_qm_vop_chown(
|
|
*/
|
|
tp->t_flags |= XFS_TRANS_DIRTY;
|
|
xfs_dqlock(prevdq);
|
|
- ASSERT(prevdq->q_blk.reserved >= ip->i_delayed_blks);
|
|
- prevdq->q_blk.reserved -= ip->i_delayed_blks;
|
|
+ if (isrt) {
|
|
+ ASSERT(prevdq->q_rtb.reserved >= ip->i_delayed_blks);
|
|
+ prevdq->q_rtb.reserved -= ip->i_delayed_blks;
|
|
+ } else {
|
|
+ ASSERT(prevdq->q_blk.reserved >= ip->i_delayed_blks);
|
|
+ prevdq->q_blk.reserved -= ip->i_delayed_blks;
|
|
+ }
|
|
xfs_dqunlock(prevdq);
|
|
|
|
/*
|
|
--- a/fs/xfs/xfs_trans.c
|
|
+++ b/fs/xfs/xfs_trans.c
|
|
@@ -1262,11 +1262,26 @@ retry:
|
|
gdqp = (new_gdqp != ip->i_gdquot) ? new_gdqp : NULL;
|
|
pdqp = (new_pdqp != ip->i_pdquot) ? new_pdqp : NULL;
|
|
if (udqp || gdqp || pdqp) {
|
|
+ xfs_filblks_t dblocks, rblocks;
|
|
unsigned int qflags = XFS_QMOPT_RES_REGBLKS;
|
|
+ bool isrt = XFS_IS_REALTIME_INODE(ip);
|
|
|
|
if (force)
|
|
qflags |= XFS_QMOPT_FORCE_RES;
|
|
|
|
+ if (isrt) {
|
|
+ error = xfs_iread_extents(tp, ip, XFS_DATA_FORK);
|
|
+ if (error)
|
|
+ goto out_cancel;
|
|
+ }
|
|
+
|
|
+ xfs_inode_count_blocks(tp, ip, &dblocks, &rblocks);
|
|
+
|
|
+ if (isrt)
|
|
+ rblocks += ip->i_delayed_blks;
|
|
+ else
|
|
+ dblocks += ip->i_delayed_blks;
|
|
+
|
|
/*
|
|
* Reserve enough quota to handle blocks on disk and reserved
|
|
* for a delayed allocation. We'll actually transfer the
|
|
@@ -1274,8 +1289,20 @@ retry:
|
|
* though that part is only semi-transactional.
|
|
*/
|
|
error = xfs_trans_reserve_quota_bydquots(tp, mp, udqp, gdqp,
|
|
- pdqp, ip->i_nblocks + ip->i_delayed_blks,
|
|
- 1, qflags);
|
|
+ pdqp, dblocks, 1, qflags);
|
|
+ if ((error == -EDQUOT || error == -ENOSPC) && !retried) {
|
|
+ xfs_trans_cancel(tp);
|
|
+ xfs_blockgc_free_dquots(mp, udqp, gdqp, pdqp, 0);
|
|
+ retried = true;
|
|
+ goto retry;
|
|
+ }
|
|
+ if (error)
|
|
+ goto out_cancel;
|
|
+
|
|
+ /* Do the same for realtime. */
|
|
+ qflags = XFS_QMOPT_RES_RTBLKS | (qflags & XFS_QMOPT_FORCE_RES);
|
|
+ error = xfs_trans_reserve_quota_bydquots(tp, mp, udqp, gdqp,
|
|
+ pdqp, rblocks, 0, qflags);
|
|
if ((error == -EDQUOT || error == -ENOSPC) && !retried) {
|
|
xfs_trans_cancel(tp);
|
|
xfs_blockgc_free_dquots(mp, udqp, gdqp, pdqp, 0);
|