@@ -16,6 +16,7 @@
#include "xfs_inode.h"
#include "xfs_inode_item.h"
#include "xfs_trace.h"
+#include "xfs_icache.h"
/*
* Deferred Operations in XFS
@@ -553,10 +554,14 @@ xfs_defer_move(
* 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 inodes are passed in and this function returns a capture structure, the
+ * inodes are now owned by the capture structure.
*/
static struct xfs_defer_capture *
xfs_defer_ops_capture(
- struct xfs_trans *tp)
+ struct xfs_trans *tp,
+ struct xfs_inode *ip)
{
struct xfs_defer_capture *dfc;
@@ -588,6 +593,12 @@ xfs_defer_ops_capture(
dfc->dfc_tres.tr_logcount = 1;
dfc->dfc_tres.tr_logflags = XFS_TRANS_PERM_LOG_RES;
+ /*
+ * Transfer responsibility for unlocking and releasing the inodes to
+ * the capture structure.
+ */
+ dfc->dfc_ip = ip;
+
return dfc;
}
@@ -598,29 +609,49 @@ xfs_defer_ops_release(
struct xfs_defer_capture *dfc)
{
xfs_defer_cancel_list(mp, &dfc->dfc_dfops);
+ if (dfc->dfc_ip)
+ xfs_irele(dfc->dfc_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 not touch the inode after
+ * this call returns.
*/
int
xfs_defer_ops_capture_and_commit(
struct xfs_trans *tp,
+ struct xfs_inode *ip,
struct list_head *capture_list)
{
struct xfs_mount *mp = tp->t_mountp;
struct xfs_defer_capture *dfc;
int error;
+ ASSERT(ip == NULL || xfs_isilocked(ip, XFS_ILOCK_EXCL));
+
/* If we don't capture anything, commit transaction and exit. */
- dfc = xfs_defer_ops_capture(tp);
- if (!dfc)
- return xfs_trans_commit(tp);
+ dfc = xfs_defer_ops_capture(tp, ip);
+ if (!dfc) {
+ error = xfs_trans_commit(tp);
+ if (ip) {
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ xfs_irele(ip);
+ }
+ return error;
+ }
- /* Commit the transaction and add the capture structure to the list. */
+ /*
+ * Commit the transaction and add the capture structure to the list.
+ * Once that's done, we can unlock the inode.
+ */
error = xfs_trans_commit(tp);
+ if (ip)
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
if (error) {
xfs_defer_ops_release(mp, dfc);
return error;
@@ -632,16 +663,24 @@ xfs_defer_ops_capture_and_commit(
/*
* Attach a chain of captured deferred ops to a new transaction and free the
- * capture structure.
+ * capture structure. A captured inode will be passed back to the caller.
*/
void
xfs_defer_ops_continue(
struct xfs_defer_capture *dfc,
- struct xfs_trans *tp)
+ struct xfs_trans *tp,
+ struct xfs_inode **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_ip) {
+ xfs_ilock(dfc->dfc_ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, dfc->dfc_ip, 0);
+ }
+ *ipp = dfc->dfc_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;
@@ -77,15 +77,22 @@ struct xfs_defer_capture {
unsigned int dfc_tpflags;
unsigned int dfc_blkres;
struct xfs_trans_res dfc_tres;
+
+ /*
+ * An inode reference that must be maintained to complete the deferred
+ * work.
+ */
+ struct xfs_inode *dfc_ip;
};
/*
* Functions to capture a chain of deferred operations and continue them later.
* This doesn't normally happen except log recovery.
*/
-int xfs_defer_ops_capture_and_commit(struct xfs_trans *tp,
+int xfs_defer_ops_capture_and_commit(struct xfs_trans *tp, struct xfs_inode *ip,
struct list_head *capture_list);
-void xfs_defer_ops_continue(struct xfs_defer_capture *d, struct xfs_trans *tp);
+void xfs_defer_ops_continue(struct xfs_defer_capture *d, struct xfs_trans *tp,
+ struct xfs_inode **ipp);
void xfs_defer_ops_release(struct xfs_mount *mp, struct xfs_defer_capture *d);
#endif /* __XFS_DEFER_H__ */
@@ -513,15 +513,11 @@ xfs_bui_item_recover(
xfs_bmap_unmap_extent(tp, ip, &irec);
}
- /* Commit transaction, which frees tp. */
- error = xfs_defer_ops_capture_and_commit(tp, capture_list);
- if (error)
- goto err_unlock;
- return 0;
+ /* Commit transaction, which frees the transaction and the inode. */
+ return xfs_defer_ops_capture_and_commit(tp, ip, capture_list);
err_cancel:
xfs_trans_cancel(tp);
-err_unlock:
xfs_iunlock(ip, XFS_ILOCK_EXCL);
err_rele:
xfs_irele(ip);
@@ -627,7 +627,7 @@ xfs_efi_item_recover(
}
- return xfs_defer_ops_capture_and_commit(tp, capture_list);
+ return xfs_defer_ops_capture_and_commit(tp, NULL, capture_list);
abort_error:
xfs_trans_cancel(tp);
@@ -2439,6 +2439,7 @@ xlog_finish_defer_ops(
{
struct xfs_defer_capture *dfc, *next;
struct xfs_trans *tp;
+ struct xfs_inode *ip;
int error = 0;
list_for_each_entry_safe(dfc, next, capture_list, dfc_list) {
@@ -2449,9 +2450,13 @@ xlog_finish_defer_ops(
/* Transfer all collected dfops to this transaction. */
list_del_init(&dfc->dfc_list);
- xfs_defer_ops_continue(dfc, tp);
+ xfs_defer_ops_continue(dfc, tp, &ip);
error = xfs_trans_commit(tp);
+ if (ip) {
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ xfs_irele(ip);
+ }
if (error)
return error;
}
@@ -544,7 +544,7 @@ xfs_cui_item_recover(
}
xfs_refcount_finish_one_cleanup(tp, rcur, error);
- return xfs_defer_ops_capture_and_commit(tp, capture_list);
+ return xfs_defer_ops_capture_and_commit(tp, NULL, capture_list);
abort_error:
xfs_refcount_finish_one_cleanup(tp, rcur, error);
@@ -567,7 +567,7 @@ xfs_rui_item_recover(
}
xfs_rmap_finish_one_cleanup(tp, rcur, error);
- return xfs_defer_ops_capture_and_commit(tp, capture_list);
+ return xfs_defer_ops_capture_and_commit(tp, NULL, capture_list);
abort_error:
xfs_rmap_finish_one_cleanup(tp, rcur, error);