diff options
-rw-r--r-- | fs/xfs/libxfs/xfs_defer.c | 45 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_defer.h | 22 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_log_recover.h | 11 | ||||
-rw-r--r-- | fs/xfs/xfs_bmap_item.c | 8 | ||||
-rw-r--r-- | fs/xfs/xfs_icache.c | 41 | ||||
-rw-r--r-- | fs/xfs/xfs_log_recover.c | 35 | ||||
-rw-r--r-- | fs/xfs/xfs_trans.h | 6 |
7 files changed, 146 insertions, 22 deletions
diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c index c53443252389..5a9436d83833 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 @@ -541,6 +542,18 @@ xfs_defer_move( stp->t_flags &= ~XFS_TRANS_LOWMODE; } +/* Unlock the inodes attached to this dfops capture device. */ +void +xfs_defer_capture_iunlock( + struct xfs_defer_capture *dfc) +{ + unsigned int i; + + for (i = 0; i < XFS_DEFER_OPS_NR_INODES && dfc->dfc_inodes[i]; i++) + xfs_iunlock(dfc->dfc_inodes[i], XFS_ILOCK_EXCL); + dfc->dfc_ilocked = false; +} + /* * Prepare a chain of fresh deferred ops work items to be completed later. Log * recovery requires the ability to put off until later the actual finishing @@ -553,13 +566,23 @@ 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. If the transaction commit + * succeeds, the caller must call xfs_defer_capture_iunlock to unlock the + * inodes before moving on to more recovery work. */ struct xfs_defer_capture * xfs_defer_capture( - struct xfs_trans *tp) + struct xfs_trans *tp, + struct xfs_inode *ip1, + struct xfs_inode *ip2) { struct xfs_defer_capture *dfc; + /* Do not pass in an ipp2 without an ipp1. */ + ASSERT(ip1 || !ip2); + if (list_empty(&tp->t_dfops)) return NULL; @@ -582,6 +605,16 @@ xfs_defer_capture( dfc->dfc_tres.tr_logcount = tp->t_log_count; dfc->dfc_tres.tr_logflags = XFS_TRANS_PERM_LOG_RES; + /* + * Transfer responsibility for unlocking and releasing the inodes to + * the capture structure. + */ + dfc->dfc_ilocked = true; + if (ip1) + dfc->dfc_inodes[0] = ip1; + if (ip2 && ip2 != ip1) + dfc->dfc_inodes[1] = ip2; + return dfc; } @@ -594,6 +627,13 @@ xfs_defer_continue( ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES); ASSERT(!(tp->t_flags & XFS_TRANS_DIRTY)); + /* Lock and join the inodes to the new transaction. */ + xfs_defer_continue_inodes(dfc, tp); + if (dfc->dfc_inodes[0]) + xfs_trans_ijoin(tp, dfc->dfc_inodes[0], 0); + if (dfc->dfc_inodes[1]) + xfs_trans_ijoin(tp, dfc->dfc_inodes[1], 0); + /* Move captured dfops chain and state to the transaction. */ list_splice_init(&dfc->dfc_dfops, &tp->t_dfops); tp->t_flags |= dfc->dfc_tpflags; @@ -609,5 +649,8 @@ xfs_defer_capture_free( struct xfs_defer_capture *dfc) { xfs_defer_cancel_list(mp, &dfc->dfc_dfops); + if (dfc->dfc_ilocked) + xfs_defer_capture_iunlock(dfc); + xfs_defer_capture_irele(dfc); kmem_free(dfc); } diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h index 063276c063b6..2a4c63573e3d 100644 --- a/fs/xfs/libxfs/xfs_defer.h +++ b/fs/xfs/libxfs/xfs_defer.h @@ -11,6 +11,12 @@ struct xfs_defer_op_type; struct xfs_defer_capture; /* + * Deferred operation item relogging limits. + */ +#define XFS_DEFER_OPS_NR_INODES 2 /* join up to two inodes */ +#define XFS_DEFER_OPS_NR_BUFS 2 /* join up to two buffers */ + +/* * Header for deferred operation list. */ enum xfs_defer_ops_type { @@ -78,15 +84,29 @@ struct xfs_defer_capture { unsigned int dfc_tpflags; unsigned int dfc_blkres; struct xfs_trans_res dfc_tres; + + /* + * Inodes to hold when we want to finish the deferred work items. + * Always set the first element before setting the second. + */ + bool dfc_ilocked; + struct xfs_inode *dfc_inodes[XFS_DEFER_OPS_NR_INODES]; }; /* * Functions to capture a chain of deferred operations and continue them later. * This doesn't normally happen except log recovery. */ -struct xfs_defer_capture *xfs_defer_capture(struct xfs_trans *tp); +struct xfs_defer_capture *xfs_defer_capture(struct xfs_trans *tp, + struct xfs_inode *ip1, struct xfs_inode *ip2); void xfs_defer_continue(struct xfs_defer_capture *dfc, struct xfs_trans *tp); +void xfs_defer_capture_iunlock(struct xfs_defer_capture *dfc); void xfs_defer_capture_free(struct xfs_mount *mp, struct xfs_defer_capture *dfc); +/* These functions must be provided outside of libxfs. */ +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/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h index 4f752096f7c7..8cf5e23c0aef 100644 --- a/fs/xfs/libxfs/xfs_log_recover.h +++ b/fs/xfs/libxfs/xfs_log_recover.h @@ -125,7 +125,14 @@ void xlog_recover_iodone(struct xfs_buf *bp); void xlog_recover_release_intent(struct xlog *log, unsigned short intent_type, uint64_t intent_id); -int xlog_recover_trans_commit(struct xfs_trans *tp, - struct list_head *capture_list); +int xlog_recover_trans_commit_inodes(struct xfs_trans *tp, + struct list_head *capture_list, struct xfs_inode *ip1, + struct xfs_inode *ip2); + +static inline int +xlog_recover_trans_commit(struct xfs_trans *tp, struct list_head *capture_list) +{ + return xlog_recover_trans_commit_inodes(tp, capture_list, NULL, NULL); +} #endif /* __XFS_LOG_RECOVER_H__ */ diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c index b6d3a5766148..fa52bfd66ce1 100644 --- a/fs/xfs/xfs_bmap_item.c +++ b/fs/xfs/xfs_bmap_item.c @@ -513,15 +513,11 @@ xfs_bui_item_recover( xfs_bmap_unmap_extent(tp, ip, &irec); } - /* Commit transaction, which frees tp. */ - error = xlog_recover_trans_commit(tp, capture_list); - if (error) - goto err_unlock; - return 0; + /* Commit transaction, which frees the transaction and the inode. */ + return xlog_recover_trans_commit_inodes(tp, capture_list, ip, NULL); err_cancel: xfs_trans_cancel(tp); -err_unlock: xfs_iunlock(ip, XFS_ILOCK_EXCL); err_rele: xfs_irele(ip); diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 101028ebb571..5b1202cac4ea 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" @@ -1692,3 +1693,43 @@ xfs_start_block_reaping( xfs_queue_eofblocks(mp); xfs_queue_cowblocks(mp); } + +/* + * Prepare the inodes to participate in further log intent item recovery. + * For now, that means attaching dquots and locking them, since libxfs doesn't + * know how to do that. + */ +void +xfs_defer_continue_inodes( + struct xfs_defer_capture *dfc, + struct xfs_trans *tp) +{ + int i; + int error; + + for (i = 0; i < XFS_DEFER_OPS_NR_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; + } + + if (dfc->dfc_inodes[1]) + xfs_lock_two_inodes(dfc->dfc_inodes[0], XFS_ILOCK_EXCL, + dfc->dfc_inodes[1], XFS_ILOCK_EXCL); + else if (dfc->dfc_inodes[0]) + xfs_ilock(dfc->dfc_inodes[0], XFS_ILOCK_EXCL); + dfc->dfc_ilocked = true; +} + +/* Release all the inodes 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_OPS_NR_INODES && dfc->dfc_inodes[i]; i++) { + xfs_irele(dfc->dfc_inodes[i]); + dfc->dfc_inodes[i] = NULL; + } +} diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 5e588ba2717b..77e7cea177a4 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1791,23 +1791,43 @@ xlog_recover_release_intent( spin_unlock(&ailp->ail_lock); } +static inline void +xlog_recover_irele( + struct xfs_inode *ip) +{ + xfs_iunlock(ip, XFS_ILOCK_EXCL); + xfs_irele(ip); +} + /* * 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, and will - * take care of releasing all the relevant resources. + * take care of releasing all the relevant resources. If we captured deferred + * ops, the inodes are attached to it and must not be touched. If not, we have + * to unlock and free them ourselves. */ int -xlog_recover_trans_commit( +xlog_recover_trans_commit_inodes( struct xfs_trans *tp, - struct list_head *capture_list) + struct list_head *capture_list, + struct xfs_inode *ip1, + struct xfs_inode *ip2) { struct xfs_mount *mp = tp->t_mountp; - struct xfs_defer_capture *dfc = xfs_defer_capture(tp); + struct xfs_defer_capture *dfc = xfs_defer_capture(tp, ip1, ip2); int error; /* If we don't capture anything, commit tp and exit. */ - if (!dfc) - return xfs_trans_commit(tp); + if (!dfc) { + error = xfs_trans_commit(tp); + + /* We still own the inodes, so unlock and release them. */ + if (ip2 && ip2 != ip1) + xlog_recover_irele(ip2); + if (ip1) + xlog_recover_irele(ip1); + return error; + } /* * Commit the transaction. If that fails, clean up the defer ops and @@ -1819,6 +1839,9 @@ xlog_recover_trans_commit( return error; } + /* Unlock the captured inodes so that we can move on with recovery. */ + xfs_defer_capture_iunlock(dfc); + list_add_tail(&dfc->dfc_list, capture_list); return 0; } diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index b68272666ce1..e27df685d3cd 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -97,12 +97,6 @@ void xfs_log_item_init(struct xfs_mount *mp, struct xfs_log_item *item, #define XFS_ITEM_FLUSHING 3 /* - * Deferred operation item relogging limits. - */ -#define XFS_DEFER_OPS_NR_INODES 2 /* join up to two inodes */ -#define XFS_DEFER_OPS_NR_BUFS 2 /* join up to two buffers */ - -/* * This is the structure maintained for every active transaction. */ typedef struct xfs_trans { |