]> www.infradead.org Git - users/hch/xfs.git/commitdiff
xfs: fix chown with rt quota
authorDarrick J. Wong <djwong@kernel.org>
Mon, 23 Sep 2024 20:20:37 +0000 (13:20 -0700)
committerChristoph Hellwig <hch@lst.de>
Wed, 9 Oct 2024 13:55:43 +0000 (15:55 +0200)
Make chown's quota adjustments work with realtime files.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
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);