summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/xfs/libxfs/xfs_defer.c40
-rw-r--r--fs/xfs/libxfs/xfs_defer.h12
-rw-r--r--fs/xfs/xfs_bmap_item.c3
-rw-r--r--fs/xfs/xfs_icache.c47
4 files changed, 102 insertions, 0 deletions
diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
index 043f516ff940..e23fc065d6e0 100644
--- a/fs/xfs/libxfs/xfs_defer.c
+++ b/fs/xfs/libxfs/xfs_defer.c
@@ -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
@@ -595,6 +596,8 @@ xfs_defer_continue(
ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
ASSERT(!(tp->t_flags & XFS_TRANS_DIRTY));
+ xfs_defer_continue_inodes(dfc, tp);
+
/* Move captured dfops chain and state to the transaction. */
list_splice_init(&dfc->dfc_dfops, &tp->t_dfops);
tp->t_flags |= dfc->dfc_tpflags;
@@ -610,5 +613,42 @@ xfs_defer_capture_free(
struct xfs_defer_capture *dfc)
{
xfs_defer_cancel_list(mp, &dfc->dfc_dfops);
+ xfs_defer_capture_irele(dfc);
kmem_free(dfc);
}
+
+/*
+ * Capture an inode along with the deferred ops freezer. Callers must hold
+ * ILOCK_EXCL, which will be dropped and reacquired when we're ready to
+ * continue replaying the deferred ops.
+ */
+int
+xfs_defer_capture_inode(
+ struct xfs_defer_capture *dfc,
+ struct xfs_inode *ip)
+{
+ unsigned int i;
+
+ ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
+
+ for (i = 0; i < XFS_DEFER_CAPTURE_INODES; i++) {
+ if (dfc->dfc_inodes[i] == ip)
+ goto out;
+ if (dfc->dfc_inodes[i] == NULL)
+ break;
+ }
+
+ if (i == XFS_DEFER_CAPTURE_INODES) {
+ ASSERT(0);
+ return -EFSCORRUPTED;
+ }
+
+ /*
+ * Attach this inode to the freezer and drop its ILOCK because we
+ * assume the caller will need to allocate a transaction.
+ */
+ dfc->dfc_inodes[i] = ip;
+out:
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ return 0;
+}
diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h
index a0ad379e38f8..532439dc5ec9 100644
--- a/fs/xfs/libxfs/xfs_defer.h
+++ b/fs/xfs/libxfs/xfs_defer.h
@@ -77,6 +77,11 @@ struct xfs_defer_capture {
struct list_head dfc_dfops;
unsigned int dfc_tpflags;
unsigned int dfc_blkres;
+
+ /* Inodes to hold when we want to finish the deferred work items. */
+ unsigned int dfc_ilockflags;
+#define XFS_DEFER_CAPTURE_INODES 2
+ struct xfs_inode *dfc_inodes[XFS_DEFER_CAPTURE_INODES];
};
/*
@@ -87,5 +92,12 @@ int xfs_defer_capture(struct xfs_trans *tp, struct xfs_defer_capture **dfcp);
void xfs_defer_continue(struct xfs_defer_capture *dfc, struct xfs_trans *tp);
void xfs_defer_capture_free(struct xfs_mount *mp,
struct xfs_defer_capture *dfc);
+int xfs_defer_capture_inode(struct xfs_defer_capture *dfc,
+ struct xfs_inode *ip);
+
+/* These functions must be provided by the xfs implementation. */
+void xfs_defer_continue_inodes(struct xfs_defer_capture *dfc,
+ struct xfs_trans *tp);
+void xfs_defer_capture_irele(struct xfs_defer_capture *dfc);
#endif /* __XFS_DEFER_H__ */
diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c
index d1b5554ac457..b901d953cfb8 100644
--- a/fs/xfs/xfs_bmap_item.c
+++ b/fs/xfs/xfs_bmap_item.c
@@ -510,6 +510,9 @@ xfs_bui_item_recover(
/* Commit transaction, which frees tp. */
error = xlog_recover_trans_commit(tp, dfcp);
+ if (error || count == 0)
+ goto err_unlock;
+ error = xfs_defer_capture_inode(*dfcp, ip);
if (error)
goto err_unlock;
return 0;
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index 78cda96b621f..d87c974e9b6f 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -12,6 +12,7 @@
#include "xfs_sb.h"
#include "xfs_mount.h"
#include "xfs_inode.h"
+#include "xfs_defer.h"
#include "xfs_trans.h"
#include "xfs_trans_priv.h"
#include "xfs_inode_item.h"
@@ -2430,3 +2431,49 @@ xfs_inactive_force_poll(
touch_softlockup_watchdog();
}
}
+
+/* Re-acquire the inode locks and attach them to the transaction. */
+void
+xfs_defer_continue_inodes(
+ struct xfs_defer_capture *dfc,
+ struct xfs_trans *tp)
+{
+ int i;
+ int error;
+
+ for (i = 0; i < XFS_DEFER_CAPTURE_INODES && dfc->dfc_inodes[i]; i++) {
+ error = xfs_qm_dqattach(dfc->dfc_inodes[i]);
+ if (error)
+ tp->t_mountp->m_qflags &= ~XFS_ALL_QUOTA_CHKD;
+ }
+
+ dfc->dfc_ilockflags = XFS_ILOCK_EXCL;
+ if (dfc->dfc_inodes[1]) {
+ xfs_lock_two_inodes(dfc->dfc_inodes[0], dfc->dfc_ilockflags,
+ dfc->dfc_inodes[1], dfc->dfc_ilockflags);
+ xfs_trans_ijoin(tp, dfc->dfc_inodes[0], 0);
+ xfs_trans_ijoin(tp, dfc->dfc_inodes[1], 0);
+ } else if (dfc->dfc_inodes[0]) {
+ xfs_ilock(dfc->dfc_inodes[0], dfc->dfc_ilockflags);
+ xfs_trans_ijoin(tp, dfc->dfc_inodes[0], 0);
+ }
+}
+
+/* Release all the inode resources attached to this dfops capture device. */
+void
+xfs_defer_capture_irele(
+ struct xfs_defer_capture *dfc)
+{
+ unsigned int i;
+
+ for (i = 0; i < XFS_DEFER_CAPTURE_INODES; i++) {
+ if (!dfc->dfc_inodes[i])
+ break;
+
+ if (dfc->dfc_ilockflags)
+ xfs_iunlock(dfc->dfc_inodes[i], dfc->dfc_ilockflags);
+ xfs_irele(dfc->dfc_inodes[i]);
+ dfc->dfc_inodes[i] = NULL;
+ }
+ dfc->dfc_ilockflags = 0;
+}