diff options
-rw-r--r-- | fs/xfs/libxfs/xfs_defer.c | 39 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_defer.h | 12 | ||||
-rw-r--r-- | fs/xfs/xfs_bmap_item.c | 3 | ||||
-rw-r--r-- | fs/xfs/xfs_icache.c | 47 |
4 files changed, 101 insertions, 0 deletions
diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c index 043f516ff940..4637a5fc5178 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,41 @@ 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; + + for (i = 0; i < XFS_DEFER_CAPTURE_INODES; i++) { + if (dfc->dfc_inodes[i] == ip) + return 0; + if (dfc->dfc_inodes[i] == NULL) + break; + } + + ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); + + 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; + 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 fea610f5d3e1..7306fa5bd16d 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 52c273b6d403..72d4f6d30ba5 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 5daef654956c..e02877f0f5e1 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" @@ -1844,3 +1845,49 @@ xfs_start_block_reaping( xfs_queue_eofblocks(mp); xfs_queue_cowblocks(mp); } + +/* 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; +} |