]> www.infradead.org Git - users/hch/xfs.git/commitdiff
xfs: fix chown with rt quota
authorDarrick J. Wong <djwong@kernel.org>
Tue, 15 Oct 2024 19:39:31 +0000 (12:39 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 5 Nov 2024 21:36:18 +0000 (13:36 -0800)
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
fs/xfs/xfs_trans.c

index d9d09195eabb0d302214c51d03e69234ffa616c9..1c7d861dfbecebef59f7a81856e51f1f8ab403ec 100644 (file)
@@ -1351,8 +1351,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));
@@ -1393,18 +1393,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
@@ -2043,9 +2042,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));
@@ -2056,11 +2054,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);
 
        /*
@@ -2070,7 +2074,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);
 
        /*
@@ -2082,8 +2087,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);
 
        /*
index 4db022c189e1340ace4dbd848aa94d45725e94e1..30fbed27cf05ccc535c0050bb4c4faded0887357 100644 (file)
@@ -1288,11 +1288,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
@@ -1300,8 +1315,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);