xfs: use perag through unlink processing
authorDave Chinner <dchinner@redhat.com>
Wed, 2 Jun 2021 00:48:51 +0000 (10:48 +1000)
committerDave Chinner <david@fromorbit.com>
Wed, 2 Jun 2021 00:48:51 +0000 (10:48 +1000)
Unlinked lists are held in the perag, and freeing of inodes needs to
be passed a perag, too, so look up the perag early in the unlink
processing and use it throughout.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Brian Foster <bfoster@redhat.com>
fs/xfs/libxfs/xfs_ialloc.c
fs/xfs/libxfs/xfs_ialloc.h
fs/xfs/xfs_inode.c

index 63a1b6d..2ed6de6 100644 (file)
@@ -2134,35 +2134,33 @@ error:
  */
 int
 xfs_difree(
-       struct xfs_trans        *tp,            /* transaction pointer */
-       xfs_ino_t               inode,          /* inode to be freed */
-       struct xfs_icluster     *xic)   /* cluster info if deleted */
+       struct xfs_trans        *tp,
+       struct xfs_perag        *pag,
+       xfs_ino_t               inode,
+       struct xfs_icluster     *xic)
 {
        /* REFERENCED */
        xfs_agblock_t           agbno;  /* block number containing inode */
        struct xfs_buf          *agbp;  /* buffer for allocation group header */
        xfs_agino_t             agino;  /* allocation group inode number */
-       xfs_agnumber_t          agno;   /* allocation group number */
        int                     error;  /* error return value */
        struct xfs_mount        *mp = tp->t_mountp;
        struct xfs_inobt_rec_incore rec;/* btree record */
-       struct xfs_perag        *pag;
 
        /*
         * Break up inode number into its components.
         */
-       agno = XFS_INO_TO_AGNO(mp, inode);
-       if (agno >= mp->m_sb.sb_agcount) {
-               xfs_warn(mp, "%s: agno >= mp->m_sb.sb_agcount (%d >= %d).",
-                       __func__, agno, mp->m_sb.sb_agcount);
+       if (pag->pag_agno != XFS_INO_TO_AGNO(mp, inode)) {
+               xfs_warn(mp, "%s: agno != pag->pag_agno (%d != %d).",
+                       __func__, XFS_INO_TO_AGNO(mp, inode), pag->pag_agno);
                ASSERT(0);
                return -EINVAL;
        }
        agino = XFS_INO_TO_AGINO(mp, inode);
-       if (inode != XFS_AGINO_TO_INO(mp, agno, agino))  {
+       if (inode != XFS_AGINO_TO_INO(mp, pag->pag_agno, agino))  {
                xfs_warn(mp, "%s: inode != XFS_AGINO_TO_INO() (%llu != %llu).",
                        __func__, (unsigned long long)inode,
-                       (unsigned long long)XFS_AGINO_TO_INO(mp, agno, agino));
+                       (unsigned long long)XFS_AGINO_TO_INO(mp, pag->pag_agno, agino));
                ASSERT(0);
                return -EINVAL;
        }
@@ -2176,7 +2174,7 @@ xfs_difree(
        /*
         * Get the allocation group header.
         */
-       error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);
+       error = xfs_ialloc_read_agi(mp, tp, pag->pag_agno, &agbp);
        if (error) {
                xfs_warn(mp, "%s: xfs_ialloc_read_agi() returned error %d.",
                        __func__, error);
@@ -2186,7 +2184,6 @@ xfs_difree(
        /*
         * Fix up the inode allocation btree.
         */
-       pag = agbp->b_pag;
        error = xfs_difree_inobt(mp, tp, agbp, pag, agino, xic, &rec);
        if (error)
                goto error0;
index 886f674..9df7c80 100644 (file)
@@ -39,17 +39,8 @@ xfs_make_iptr(struct xfs_mount *mp, struct xfs_buf *b, int o)
 int xfs_dialloc(struct xfs_trans **tpp, xfs_ino_t parent, umode_t mode,
                xfs_ino_t *new_ino);
 
-/*
- * Free disk inode.  Carefully avoids touching the incore inode, all
- * manipulations incore are the caller's responsibility.
- * The on-disk inode is not changed by this operation, only the
- * btree (free inode mask) is changed.
- */
-int                                    /* error */
-xfs_difree(
-       struct xfs_trans *tp,           /* transaction pointer */
-       xfs_ino_t       inode,          /* inode to be freed */
-       struct xfs_icluster *ifree);    /* cluster info if deleted */
+int xfs_difree(struct xfs_trans *tp, struct xfs_perag *pag,
+               xfs_ino_t ino, struct xfs_icluster *ifree);
 
 /*
  * Return the location of the inode in imap, for mapping it into a buffer.
index 8d204d5..336c350 100644 (file)
@@ -45,7 +45,8 @@ kmem_zone_t *xfs_inode_zone;
 #define        XFS_ITRUNC_MAX_EXTENTS  2
 
 STATIC int xfs_iunlink(struct xfs_trans *, struct xfs_inode *);
-STATIC int xfs_iunlink_remove(struct xfs_trans *, struct xfs_inode *);
+STATIC int xfs_iunlink_remove(struct xfs_trans *tp, struct xfs_perag *pag,
+       struct xfs_inode *);
 
 /*
  * helper function to extract extent size hint from inode
@@ -1241,7 +1242,11 @@ xfs_link(
         * Handle initial link state of O_TMPFILE inode
         */
        if (VFS_I(sip)->i_nlink == 0) {
-               error = xfs_iunlink_remove(tp, sip);
+               struct xfs_perag        *pag;
+
+               pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, sip->i_ino));
+               error = xfs_iunlink_remove(tp, pag, sip);
+               xfs_perag_put(pag);
                if (error)
                        goto error_return;
        }
@@ -1934,7 +1939,7 @@ xfs_iunlink_destroy(
 STATIC int
 xfs_iunlink_update_bucket(
        struct xfs_trans        *tp,
-       xfs_agnumber_t          agno,
+       struct xfs_perag        *pag,
        struct xfs_buf          *agibp,
        unsigned int            bucket_index,
        xfs_agino_t             new_agino)
@@ -1943,10 +1948,10 @@ xfs_iunlink_update_bucket(
        xfs_agino_t             old_value;
        int                     offset;
 
-       ASSERT(xfs_verify_agino_or_null(tp->t_mountp, agno, new_agino));
+       ASSERT(xfs_verify_agino_or_null(tp->t_mountp, pag->pag_agno, new_agino));
 
        old_value = be32_to_cpu(agi->agi_unlinked[bucket_index]);
-       trace_xfs_iunlink_update_bucket(tp->t_mountp, agno, bucket_index,
+       trace_xfs_iunlink_update_bucket(tp->t_mountp, pag->pag_agno, bucket_index,
                        old_value, new_agino);
 
        /*
@@ -1970,7 +1975,7 @@ xfs_iunlink_update_bucket(
 STATIC void
 xfs_iunlink_update_dinode(
        struct xfs_trans        *tp,
-       xfs_agnumber_t          agno,
+       struct xfs_perag        *pag,
        xfs_agino_t             agino,
        struct xfs_buf          *ibp,
        struct xfs_dinode       *dip,
@@ -1980,9 +1985,9 @@ xfs_iunlink_update_dinode(
        struct xfs_mount        *mp = tp->t_mountp;
        int                     offset;
 
-       ASSERT(xfs_verify_agino_or_null(mp, agno, next_agino));
+       ASSERT(xfs_verify_agino_or_null(mp, pag->pag_agno, next_agino));
 
-       trace_xfs_iunlink_update_dinode(mp, agno, agino,
+       trace_xfs_iunlink_update_dinode(mp, pag->pag_agno, agino,
                        be32_to_cpu(dip->di_next_unlinked), next_agino);
 
        dip->di_next_unlinked = cpu_to_be32(next_agino);
@@ -2000,7 +2005,7 @@ STATIC int
 xfs_iunlink_update_inode(
        struct xfs_trans        *tp,
        struct xfs_inode        *ip,
-       xfs_agnumber_t          agno,
+       struct xfs_perag        *pag,
        xfs_agino_t             next_agino,
        xfs_agino_t             *old_next_agino)
 {
@@ -2010,7 +2015,7 @@ xfs_iunlink_update_inode(
        xfs_agino_t             old_value;
        int                     error;
 
-       ASSERT(xfs_verify_agino_or_null(mp, agno, next_agino));
+       ASSERT(xfs_verify_agino_or_null(mp, pag->pag_agno, next_agino));
 
        error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &ibp);
        if (error)
@@ -2019,7 +2024,7 @@ xfs_iunlink_update_inode(
 
        /* Make sure the old pointer isn't garbage. */
        old_value = be32_to_cpu(dip->di_next_unlinked);
-       if (!xfs_verify_agino_or_null(mp, agno, old_value)) {
+       if (!xfs_verify_agino_or_null(mp, pag->pag_agno, old_value)) {
                xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, dip,
                                sizeof(*dip), __this_address);
                error = -EFSCORRUPTED;
@@ -2042,7 +2047,7 @@ xfs_iunlink_update_inode(
        }
 
        /* Ok, update the new pointer. */
-       xfs_iunlink_update_dinode(tp, agno, XFS_INO_TO_AGINO(mp, ip->i_ino),
+       xfs_iunlink_update_dinode(tp, pag, XFS_INO_TO_AGINO(mp, ip->i_ino),
                        ibp, dip, &ip->i_imap, next_agino);
        return 0;
 out:
@@ -2063,10 +2068,10 @@ xfs_iunlink(
        struct xfs_inode        *ip)
 {
        struct xfs_mount        *mp = tp->t_mountp;
+       struct xfs_perag        *pag;
        struct xfs_agi          *agi;
        struct xfs_buf          *agibp;
        xfs_agino_t             next_agino;
-       xfs_agnumber_t          agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
        xfs_agino_t             agino = XFS_INO_TO_AGINO(mp, ip->i_ino);
        short                   bucket_index = agino % XFS_AGI_UNLINKED_BUCKETS;
        int                     error;
@@ -2075,10 +2080,12 @@ xfs_iunlink(
        ASSERT(VFS_I(ip)->i_mode != 0);
        trace_xfs_iunlink(ip);
 
+       pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino));
+
        /* Get the agi buffer first.  It ensures lock ordering on the list. */
-       error = xfs_read_agi(mp, tp, agno, &agibp);
+       error = xfs_read_agi(mp, tp, pag->pag_agno, &agibp);
        if (error)
-               return error;
+               goto out;
        agi = agibp->b_addr;
 
        /*
@@ -2088,9 +2095,10 @@ xfs_iunlink(
         */
        next_agino = be32_to_cpu(agi->agi_unlinked[bucket_index]);
        if (next_agino == agino ||
-           !xfs_verify_agino_or_null(mp, agno, next_agino)) {
+           !xfs_verify_agino_or_null(mp, pag->pag_agno, next_agino)) {
                xfs_buf_mark_corrupt(agibp);
-               return -EFSCORRUPTED;
+               error = -EFSCORRUPTED;
+               goto out;
        }
 
        if (next_agino != NULLAGINO) {
@@ -2100,23 +2108,26 @@ xfs_iunlink(
                 * There is already another inode in the bucket, so point this
                 * inode to the current head of the list.
                 */
-               error = xfs_iunlink_update_inode(tp, ip, agno, next_agino,
+               error = xfs_iunlink_update_inode(tp, ip, pag, next_agino,
                                &old_agino);
                if (error)
-                       return error;
+                       goto out;
                ASSERT(old_agino == NULLAGINO);
 
                /*
                 * agino has been unlinked, add a backref from the next inode
                 * back to agino.
                 */
-               error = xfs_iunlink_add_backref(agibp->b_pag, agino, next_agino);
+               error = xfs_iunlink_add_backref(pag, agino, next_agino);
                if (error)
-                       return error;
+                       goto out;
        }
 
        /* Point the head of the list to point to this inode. */
-       return xfs_iunlink_update_bucket(tp, agno, agibp, bucket_index, agino);
+       error = xfs_iunlink_update_bucket(tp, pag, agibp, bucket_index, agino);
+out:
+       xfs_perag_put(pag);
+       return error;
 }
 
 /* Return the imap, dinode pointer, and buffer for an inode. */
@@ -2164,14 +2175,13 @@ xfs_iunlink_map_ino(
 STATIC int
 xfs_iunlink_map_prev(
        struct xfs_trans        *tp,
-       xfs_agnumber_t          agno,
+       struct xfs_perag        *pag,
        xfs_agino_t             head_agino,
        xfs_agino_t             target_agino,
        xfs_agino_t             *agino,
        struct xfs_imap         *imap,
        struct xfs_dinode       **dipp,
-       struct xfs_buf          **bpp,
-       struct xfs_perag        *pag)
+       struct xfs_buf          **bpp)
 {
        struct xfs_mount        *mp = tp->t_mountp;
        xfs_agino_t             next_agino;
@@ -2183,7 +2193,8 @@ xfs_iunlink_map_prev(
        /* See if our backref cache can find it faster. */
        *agino = xfs_iunlink_lookup_backref(pag, target_agino);
        if (*agino != NULLAGINO) {
-               error = xfs_iunlink_map_ino(tp, agno, *agino, imap, dipp, bpp);
+               error = xfs_iunlink_map_ino(tp, pag->pag_agno, *agino, imap,
+                               dipp, bpp);
                if (error)
                        return error;
 
@@ -2199,7 +2210,7 @@ xfs_iunlink_map_prev(
                WARN_ON_ONCE(1);
        }
 
-       trace_xfs_iunlink_map_prev_fallback(mp, agno);
+       trace_xfs_iunlink_map_prev_fallback(mp, pag->pag_agno);
 
        /* Otherwise, walk the entire bucket until we find it. */
        next_agino = head_agino;
@@ -2210,8 +2221,8 @@ xfs_iunlink_map_prev(
                        xfs_trans_brelse(tp, *bpp);
 
                *agino = next_agino;
-               error = xfs_iunlink_map_ino(tp, agno, next_agino, imap, dipp,
-                               bpp);
+               error = xfs_iunlink_map_ino(tp, pag->pag_agno, next_agino, imap,
+                               dipp, bpp);
                if (error)
                        return error;
 
@@ -2220,7 +2231,7 @@ xfs_iunlink_map_prev(
                 * Make sure this pointer is valid and isn't an obvious
                 * infinite loop.
                 */
-               if (!xfs_verify_agino(mp, agno, unlinked_agino) ||
+               if (!xfs_verify_agino(mp, pag->pag_agno, unlinked_agino) ||
                    next_agino == unlinked_agino) {
                        XFS_CORRUPTION_ERROR(__func__,
                                        XFS_ERRLEVEL_LOW, mp,
@@ -2240,6 +2251,7 @@ xfs_iunlink_map_prev(
 STATIC int
 xfs_iunlink_remove(
        struct xfs_trans        *tp,
+       struct xfs_perag        *pag,
        struct xfs_inode        *ip)
 {
        struct xfs_mount        *mp = tp->t_mountp;
@@ -2247,7 +2259,6 @@ xfs_iunlink_remove(
        struct xfs_buf          *agibp;
        struct xfs_buf          *last_ibp;
        struct xfs_dinode       *last_dip = NULL;
-       xfs_agnumber_t          agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
        xfs_agino_t             agino = XFS_INO_TO_AGINO(mp, ip->i_ino);
        xfs_agino_t             next_agino;
        xfs_agino_t             head_agino;
@@ -2257,7 +2268,7 @@ xfs_iunlink_remove(
        trace_xfs_iunlink_remove(ip);
 
        /* Get the agi buffer first.  It ensures lock ordering on the list. */
-       error = xfs_read_agi(mp, tp, agno, &agibp);
+       error = xfs_read_agi(mp, tp, pag->pag_agno, &agibp);
        if (error)
                return error;
        agi = agibp->b_addr;
@@ -2267,7 +2278,7 @@ xfs_iunlink_remove(
         * go on.  Make sure the head pointer isn't garbage.
         */
        head_agino = be32_to_cpu(agi->agi_unlinked[bucket_index]);
-       if (!xfs_verify_agino(mp, agno, head_agino)) {
+       if (!xfs_verify_agino(mp, pag->pag_agno, head_agino)) {
                XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
                                agi, sizeof(*agi));
                return -EFSCORRUPTED;
@@ -2278,7 +2289,7 @@ xfs_iunlink_remove(
         * the old pointer value so that we can update whatever was previous
         * to us in the list to point to whatever was next in the list.
         */
-       error = xfs_iunlink_update_inode(tp, ip, agno, NULLAGINO, &next_agino);
+       error = xfs_iunlink_update_inode(tp, ip, pag, NULLAGINO, &next_agino);
        if (error)
                return error;
 
@@ -2290,8 +2301,7 @@ xfs_iunlink_remove(
         * this inode's backref to point from the next inode.
         */
        if (next_agino != NULLAGINO) {
-               error = xfs_iunlink_change_backref(agibp->b_pag, next_agino,
-                               NULLAGINO);
+               error = xfs_iunlink_change_backref(pag, next_agino, NULLAGINO);
                if (error)
                        return error;
        }
@@ -2301,14 +2311,13 @@ xfs_iunlink_remove(
                xfs_agino_t     prev_agino;
 
                /* We need to search the list for the inode being freed. */
-               error = xfs_iunlink_map_prev(tp, agno, head_agino, agino,
-                               &prev_agino, &imap, &last_dip, &last_ibp,
-                               agibp->b_pag);
+               error = xfs_iunlink_map_prev(tp, pag, head_agino, agino,
+                               &prev_agino, &imap, &last_dip, &last_ibp);
                if (error)
                        return error;
 
                /* Point the previous inode on the list to the next inode. */
-               xfs_iunlink_update_dinode(tp, agno, prev_agino, last_ibp,
+               xfs_iunlink_update_dinode(tp, pag, prev_agino, last_ibp,
                                last_dip, &imap, next_agino);
 
                /*
@@ -2324,7 +2333,7 @@ xfs_iunlink_remove(
        }
 
        /* Point the head of the list to the next unlinked inode. */
-       return xfs_iunlink_update_bucket(tp, agno, agibp, bucket_index,
+       return xfs_iunlink_update_bucket(tp, pag, agibp, bucket_index,
                        next_agino);
 }
 
@@ -2335,12 +2344,11 @@ xfs_iunlink_remove(
  */
 static void
 xfs_ifree_mark_inode_stale(
-       struct xfs_buf          *bp,
+       struct xfs_perag        *pag,
        struct xfs_inode        *free_ip,
        xfs_ino_t               inum)
 {
-       struct xfs_mount        *mp = bp->b_mount;
-       struct xfs_perag        *pag = bp->b_pag;
+       struct xfs_mount        *mp = pag->pag_mount;
        struct xfs_inode_log_item *iip;
        struct xfs_inode        *ip;
 
@@ -2430,10 +2438,11 @@ out_iflags_unlock:
  * inodes that are in memory - they all must be marked stale and attached to
  * the cluster buffer.
  */
-STATIC int
+static int
 xfs_ifree_cluster(
-       struct xfs_inode        *free_ip,
        struct xfs_trans        *tp,
+       struct xfs_perag        *pag,
+       struct xfs_inode        *free_ip,
        struct xfs_icluster     *xic)
 {
        struct xfs_mount        *mp = free_ip->i_mount;
@@ -2495,7 +2504,7 @@ xfs_ifree_cluster(
                 * already marked XFS_ISTALE.
                 */
                for (i = 0; i < igeo->inodes_per_cluster; i++)
-                       xfs_ifree_mark_inode_stale(bp, free_ip, inum + i);
+                       xfs_ifree_mark_inode_stale(pag, free_ip, inum + i);
 
                xfs_trans_stale_inode_buf(tp, bp);
                xfs_trans_binval(tp, bp);
@@ -2518,9 +2527,11 @@ xfs_ifree(
        struct xfs_trans        *tp,
        struct xfs_inode        *ip)
 {
-       int                     error;
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_perag        *pag;
        struct xfs_icluster     xic = { 0 };
        struct xfs_inode_log_item *iip = ip->i_itemp;
+       int                     error;
 
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
        ASSERT(VFS_I(ip)->i_nlink == 0);
@@ -2528,16 +2539,18 @@ xfs_ifree(
        ASSERT(ip->i_disk_size == 0 || !S_ISREG(VFS_I(ip)->i_mode));
        ASSERT(ip->i_nblocks == 0);
 
+       pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino));
+
        /*
         * Pull the on-disk inode from the AGI unlinked list.
         */
-       error = xfs_iunlink_remove(tp, ip);
+       error = xfs_iunlink_remove(tp, pag, ip);
        if (error)
-               return error;
+               goto out;
 
-       error = xfs_difree(tp, ip->i_ino, &xic);
+       error = xfs_difree(tp, pag, ip->i_ino, &xic);
        if (error)
-               return error;
+               goto out;
 
        /*
         * Free any local-format data sitting around before we reset the
@@ -2552,7 +2565,7 @@ xfs_ifree(
 
        VFS_I(ip)->i_mode = 0;          /* mark incore inode as free */
        ip->i_diflags = 0;
-       ip->i_diflags2 = ip->i_mount->m_ino_geo.new_diflags2;
+       ip->i_diflags2 = mp->m_ino_geo.new_diflags2;
        ip->i_forkoff = 0;              /* mark the attr fork not in use */
        ip->i_df.if_format = XFS_DINODE_FMT_EXTENTS;
        if (xfs_iflags_test(ip, XFS_IPRESERVE_DM_FIELDS))
@@ -2571,8 +2584,9 @@ xfs_ifree(
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
 
        if (xic.deleted)
-               error = xfs_ifree_cluster(ip, tp, &xic);
-
+               error = xfs_ifree_cluster(tp, pag, ip, &xic);
+out:
+       xfs_perag_put(pag);
        return error;
 }
 
@@ -3176,8 +3190,13 @@ xfs_rename(
         * in future.
         */
        if (wip) {
+               struct xfs_perag        *pag;
+
                ASSERT(VFS_I(wip)->i_nlink == 0);
-               error = xfs_iunlink_remove(tp, wip);
+
+               pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, wip->i_ino));
+               error = xfs_iunlink_remove(tp, pag, wip);
+               xfs_perag_put(pag);
                if (error)
                        goto out_trans_cancel;