#include "xfs_inode.h"
 #include "xfs_inode_item.h"
 #include "xfs_trace.h"
+#include "xfs_icache.h"
 
 /*
  * Deferred Operations in XFS
  * deferred ops state is transferred to the capture structure and the
  * transaction is then ready for the caller to commit it.  If there are no
  * intent items to capture, this function returns NULL.
+ *
+ * If capture_ip is not NULL, the capture structure will obtain an extra
+ * reference to the inode.
  */
 static struct xfs_defer_capture *
 xfs_defer_ops_capture(
-       struct xfs_trans                *tp)
+       struct xfs_trans                *tp,
+       struct xfs_inode                *capture_ip)
 {
        struct xfs_defer_capture        *dfc;
 
        /* Preserve the log reservation size. */
        dfc->dfc_logres = tp->t_log_res;
 
+       /*
+        * Grab an extra reference to this inode and attach it to the capture
+        * structure.
+        */
+       if (capture_ip) {
+               ihold(VFS_I(capture_ip));
+               dfc->dfc_capture_ip = capture_ip;
+       }
+
        return dfc;
 }
 
        struct xfs_defer_capture        *dfc)
 {
        xfs_defer_cancel_list(mp, &dfc->dfc_dfops);
+       if (dfc->dfc_capture_ip)
+               xfs_irele(dfc->dfc_capture_ip);
        kmem_free(dfc);
 }
 
 /*
  * Capture any deferred ops and commit the transaction.  This is the last step
- * needed to finish a log intent item that we recovered from the log.
+ * needed to finish a log intent item that we recovered from the log.  If any
+ * of the deferred ops operate on an inode, the caller must pass in that inode
+ * so that the reference can be transferred to the capture structure.  The
+ * caller must hold ILOCK_EXCL on the inode, and must unlock it before calling
+ * xfs_defer_ops_continue.
  */
 int
 xfs_defer_ops_capture_and_commit(
        struct xfs_trans                *tp,
+       struct xfs_inode                *capture_ip,
        struct list_head                *capture_list)
 {
        struct xfs_mount                *mp = tp->t_mountp;
        struct xfs_defer_capture        *dfc;
        int                             error;
 
+       ASSERT(!capture_ip || xfs_isilocked(capture_ip, XFS_ILOCK_EXCL));
+
        /* If we don't capture anything, commit transaction and exit. */
-       dfc = xfs_defer_ops_capture(tp);
+       dfc = xfs_defer_ops_capture(tp, capture_ip);
        if (!dfc)
                return xfs_trans_commit(tp);
 
 
 /*
  * Attach a chain of captured deferred ops to a new transaction and free the
- * capture structure.
+ * capture structure.  If an inode was captured, it will be passed back to the
+ * caller with ILOCK_EXCL held and joined to the transaction with lockflags==0.
+ * The caller now owns the inode reference.
  */
 void
 xfs_defer_ops_continue(
        struct xfs_defer_capture        *dfc,
-       struct xfs_trans                *tp)
+       struct xfs_trans                *tp,
+       struct xfs_inode                **captured_ipp)
 {
        ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
        ASSERT(!(tp->t_flags & XFS_TRANS_DIRTY));
 
+       /* Lock and join the captured inode to the new transaction. */
+       if (dfc->dfc_capture_ip) {
+               xfs_ilock(dfc->dfc_capture_ip, XFS_ILOCK_EXCL);
+               xfs_trans_ijoin(tp, dfc->dfc_capture_ip, 0);
+       }
+       *captured_ipp = dfc->dfc_capture_ip;
+
        /* Move captured dfops chain and state to the transaction. */
        list_splice_init(&dfc->dfc_dfops, &tp->t_dfops);
        tp->t_flags |= dfc->dfc_tpflags;
 
 
        /* Log reservation saved from the transaction. */
        unsigned int            dfc_logres;
+
+       /*
+        * An inode reference that must be maintained to complete the deferred
+        * work.
+        */
+       struct xfs_inode        *dfc_capture_ip;
 };
 
 /*
  * This doesn't normally happen except log recovery.
  */
 int xfs_defer_ops_capture_and_commit(struct xfs_trans *tp,
-               struct list_head *capture_list);
-void xfs_defer_ops_continue(struct xfs_defer_capture *d, struct xfs_trans *tp);
+               struct xfs_inode *capture_ip, struct list_head *capture_list);
+void xfs_defer_ops_continue(struct xfs_defer_capture *d, struct xfs_trans *tp,
+               struct xfs_inode **captured_ipp);
 void xfs_defer_ops_release(struct xfs_mount *mp, struct xfs_defer_capture *d);
 
 #endif /* __XFS_DEFER_H__ */