#include "xfs_error.h"
 #include "xfs_icache.h"
 #include "xfs_health.h"
+#include "xfs_trans.h"
 
 /*
  * Bulk Stat
                .formatter      = formatter,
                .breq           = breq,
        };
+       struct xfs_trans        *tp;
        int                     error;
 
        if (breq->mnt_userns != &init_user_ns) {
        if (!bc.buf)
                return -ENOMEM;
 
-       error = xfs_bulkstat_one_int(breq->mp, breq->mnt_userns, NULL,
-                                    breq->startino, &bc);
+       /*
+        * Grab an empty transaction so that we can use its recursive buffer
+        * locking abilities to detect cycles in the inobt without deadlocking.
+        */
+       error = xfs_trans_alloc_empty(breq->mp, &tp);
+       if (error)
+               goto out;
 
+       error = xfs_bulkstat_one_int(breq->mp, breq->mnt_userns, tp,
+                       breq->startino, &bc);
+       xfs_trans_cancel(tp);
+out:
        kmem_free(bc.buf);
 
        /*
                .formatter      = formatter,
                .breq           = breq,
        };
+       struct xfs_trans        *tp;
        int                     error;
 
        if (breq->mnt_userns != &init_user_ns) {
        if (!bc.buf)
                return -ENOMEM;
 
-       error = xfs_iwalk(breq->mp, NULL, breq->startino, breq->flags,
-                       xfs_bulkstat_iwalk, breq->icount, &bc);
+       /*
+        * Grab an empty transaction so that we can use its recursive buffer
+        * locking abilities to detect cycles in the inobt without deadlocking.
+        */
+       error = xfs_trans_alloc_empty(breq->mp, &tp);
+       if (error)
+               goto out;
 
+       error = xfs_iwalk(breq->mp, tp, breq->startino, breq->flags,
+                       xfs_bulkstat_iwalk, breq->icount, &bc);
+       xfs_trans_cancel(tp);
+out:
        kmem_free(bc.buf);
 
        /*
                .formatter      = formatter,
                .breq           = breq,
        };
+       struct xfs_trans        *tp;
        int                     error = 0;
 
        if (xfs_bulkstat_already_done(breq->mp, breq->startino))
                return 0;
 
-       error = xfs_inobt_walk(breq->mp, NULL, breq->startino, breq->flags,
+       /*
+        * Grab an empty transaction so that we can use its recursive buffer
+        * locking abilities to detect cycles in the inobt without deadlocking.
+        */
+       error = xfs_trans_alloc_empty(breq->mp, &tp);
+       if (error)
+               goto out;
+
+       error = xfs_inobt_walk(breq->mp, tp, breq->startino, breq->flags,
                        xfs_inumbers_walk, breq->icount, &ic);
+       xfs_trans_cancel(tp);
+out:
 
        /*
         * We found some inode groups, so clear the error status and return
 
 
        /* Skip empty inobt records? */
        unsigned int                    skip_empty:1;
+
+       /* Drop the (hopefully empty) transaction when calling iwalk_fn. */
+       unsigned int                    drop_trans:1;
 };
 
 /*
        int                             *has_more)
 {
        struct xfs_mount                *mp = iwag->mp;
-       struct xfs_trans                *tp = iwag->tp;
        struct xfs_inobt_rec_incore     *irec;
        xfs_agino_t                     next_agino;
        int                             error;
        ASSERT(iwag->nr_recs > 0);
 
        /* Delete cursor but remember the last record we cached... */
-       xfs_iwalk_del_inobt(tp, curpp, agi_bpp, 0);
+       xfs_iwalk_del_inobt(iwag->tp, curpp, agi_bpp, 0);
        irec = &iwag->recs[iwag->nr_recs - 1];
        ASSERT(next_agino >= irec->ir_startino + XFS_INODES_PER_CHUNK);
 
+       if (iwag->drop_trans) {
+               xfs_trans_cancel(iwag->tp);
+               iwag->tp = NULL;
+       }
+
        error = xfs_iwalk_ag_recs(iwag);
        if (error)
                return error;
        if (!has_more)
                return 0;
 
+       if (iwag->drop_trans) {
+               error = xfs_trans_alloc_empty(mp, &iwag->tp);
+               if (error)
+                       return error;
+       }
+
        /* ...and recreate the cursor just past where we left off. */
-       error = xfs_inobt_cur(mp, tp, iwag->pag, XFS_BTNUM_INO, curpp, agi_bpp);
+       error = xfs_inobt_cur(mp, iwag->tp, iwag->pag, XFS_BTNUM_INO, curpp,
+                       agi_bpp);
        if (error)
                return error;
 
        struct xfs_iwalk_ag             *iwag)
 {
        struct xfs_mount                *mp = iwag->mp;
-       struct xfs_trans                *tp = iwag->tp;
        struct xfs_perag                *pag = iwag->pag;
        struct xfs_buf                  *agi_bp = NULL;
        struct xfs_btree_cur            *cur = NULL;
        error = xfs_iwalk_run_callbacks(iwag, &cur, &agi_bp, &has_more);
 
 out:
-       xfs_iwalk_del_inobt(tp, &cur, &agi_bp, error);
+       xfs_iwalk_del_inobt(iwag->tp, &cur, &agi_bp, error);
        return error;
 }
 
        error = xfs_iwalk_alloc(iwag);
        if (error)
                goto out;
+       /*
+        * Grab an empty transaction so that we can use its recursive buffer
+        * locking abilities to detect cycles in the inobt without deadlocking.
+        */
+       error = xfs_trans_alloc_empty(mp, &iwag->tp);
+       if (error)
+               goto out;
+       iwag->drop_trans = 1;
 
        error = xfs_iwalk_ag(iwag);
+       if (iwag->tp)
+               xfs_trans_cancel(iwag->tp);
        xfs_iwalk_free(iwag);
 out:
        xfs_perag_put(iwag->pag);