xfs: factor xfs_iflush_done
xfs_iflush_done() does 3 distinct operations to the inodes attached to the buffer. Separate these operations out into functions so that it is easier to modify these operations independently in future. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
This commit is contained in:
parent
5717ea4d52
commit
a69a1dc284
@ -641,101 +641,63 @@ xfs_inode_item_destroy(
|
||||
|
||||
|
||||
/*
|
||||
* This is the inode flushing I/O completion routine. It is called
|
||||
* from interrupt level when the buffer containing the inode is
|
||||
* flushed to disk. It is responsible for removing the inode item
|
||||
* from the AIL if it has not been re-logged, and unlocking the inode's
|
||||
* flush lock.
|
||||
*
|
||||
* To reduce AIL lock traffic as much as possible, we scan the buffer log item
|
||||
* list for other inodes that will run this function. We remove them from the
|
||||
* buffer list so we can process all the inode IO completions in one AIL lock
|
||||
* traversal.
|
||||
*
|
||||
* Note: Now that we attach the log item to the buffer when we first log the
|
||||
* inode in memory, we can have unflushed inodes on the buffer list here. These
|
||||
* inodes will have a zero ili_last_fields, so skip over them here.
|
||||
* We only want to pull the item from the AIL if it is actually there
|
||||
* and its location in the log has not changed since we started the
|
||||
* flush. Thus, we only bother if the inode's lsn has not changed.
|
||||
*/
|
||||
void
|
||||
xfs_iflush_done(
|
||||
struct xfs_buf *bp)
|
||||
static void
|
||||
xfs_iflush_ail_updates(
|
||||
struct xfs_ail *ailp,
|
||||
struct list_head *list)
|
||||
{
|
||||
struct xfs_log_item *lip;
|
||||
xfs_lsn_t tail_lsn = 0;
|
||||
|
||||
/* this is an opencoded batch version of xfs_trans_ail_delete */
|
||||
spin_lock(&ailp->ail_lock);
|
||||
list_for_each_entry(lip, list, li_bio_list) {
|
||||
xfs_lsn_t lsn;
|
||||
|
||||
clear_bit(XFS_LI_FAILED, &lip->li_flags);
|
||||
if (INODE_ITEM(lip)->ili_flush_lsn != lip->li_lsn)
|
||||
continue;
|
||||
|
||||
lsn = xfs_ail_delete_one(ailp, lip);
|
||||
if (!tail_lsn && lsn)
|
||||
tail_lsn = lsn;
|
||||
}
|
||||
xfs_ail_update_finish(ailp, tail_lsn);
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk the list of inodes that have completed their IOs. If they are clean
|
||||
* remove them from the list and dissociate them from the buffer. Buffers that
|
||||
* are still dirty remain linked to the buffer and on the list. Caller must
|
||||
* handle them appropriately.
|
||||
*/
|
||||
static void
|
||||
xfs_iflush_finish(
|
||||
struct xfs_buf *bp,
|
||||
struct list_head *list)
|
||||
{
|
||||
struct xfs_inode_log_item *iip;
|
||||
struct xfs_log_item *lip, *n;
|
||||
struct xfs_ail *ailp = bp->b_mount->m_ail;
|
||||
int need_ail = 0;
|
||||
LIST_HEAD(tmp);
|
||||
|
||||
/*
|
||||
* Pull the attached inodes from the buffer one at a time and take the
|
||||
* appropriate action on them.
|
||||
*/
|
||||
list_for_each_entry_safe(lip, n, &bp->b_li_list, li_bio_list) {
|
||||
iip = INODE_ITEM(lip);
|
||||
|
||||
if (xfs_iflags_test(iip->ili_inode, XFS_ISTALE)) {
|
||||
xfs_iflush_abort(iip->ili_inode);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!iip->ili_last_fields)
|
||||
continue;
|
||||
|
||||
list_move_tail(&lip->li_bio_list, &tmp);
|
||||
|
||||
/* Do an unlocked check for needing the AIL lock. */
|
||||
if (iip->ili_flush_lsn == lip->li_lsn ||
|
||||
test_bit(XFS_LI_FAILED, &lip->li_flags))
|
||||
need_ail++;
|
||||
}
|
||||
|
||||
/*
|
||||
* We only want to pull the item from the AIL if it is actually there
|
||||
* and its location in the log has not changed since we started the
|
||||
* flush. Thus, we only bother if the inode's lsn has not changed.
|
||||
*/
|
||||
if (need_ail) {
|
||||
xfs_lsn_t tail_lsn = 0;
|
||||
|
||||
/* this is an opencoded batch version of xfs_trans_ail_delete */
|
||||
spin_lock(&ailp->ail_lock);
|
||||
list_for_each_entry(lip, &tmp, li_bio_list) {
|
||||
clear_bit(XFS_LI_FAILED, &lip->li_flags);
|
||||
if (lip->li_lsn == INODE_ITEM(lip)->ili_flush_lsn) {
|
||||
xfs_lsn_t lsn = xfs_ail_delete_one(ailp, lip);
|
||||
if (!tail_lsn && lsn)
|
||||
tail_lsn = lsn;
|
||||
}
|
||||
}
|
||||
xfs_ail_update_finish(ailp, tail_lsn);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up and unlock the flush lock now we are done. We can clear the
|
||||
* ili_last_fields bits now that we know that the data corresponding to
|
||||
* them is safely on disk.
|
||||
*/
|
||||
list_for_each_entry_safe(lip, n, &tmp, li_bio_list) {
|
||||
list_for_each_entry_safe(lip, n, list, li_bio_list) {
|
||||
struct xfs_inode_log_item *iip = INODE_ITEM(lip);
|
||||
bool drop_buffer = false;
|
||||
|
||||
list_del_init(&lip->li_bio_list);
|
||||
iip = INODE_ITEM(lip);
|
||||
|
||||
spin_lock(&iip->ili_lock);
|
||||
|
||||
/*
|
||||
* Remove the reference to the cluster buffer if the inode is
|
||||
* clean in memory. Drop the buffer reference once we've dropped
|
||||
* the locks we hold. If the inode is dirty in memory, we need
|
||||
* to put the inode item back on the buffer list for another
|
||||
* pass through the flush machinery.
|
||||
* clean in memory and drop the buffer reference once we've
|
||||
* dropped the locks we hold.
|
||||
*/
|
||||
ASSERT(iip->ili_item.li_buf == bp);
|
||||
if (!iip->ili_fields) {
|
||||
iip->ili_item.li_buf = NULL;
|
||||
list_del_init(&lip->li_bio_list);
|
||||
drop_buffer = true;
|
||||
} else {
|
||||
list_add(&lip->li_bio_list, &bp->b_li_list);
|
||||
}
|
||||
iip->ili_last_fields = 0;
|
||||
iip->ili_flush_lsn = 0;
|
||||
@ -746,6 +708,51 @@ xfs_iflush_done(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Inode buffer IO completion routine. It is responsible for removing inodes
|
||||
* attached to the buffer from the AIL if they have not been re-logged, as well
|
||||
* as completing the flush and unlocking the inode.
|
||||
*/
|
||||
void
|
||||
xfs_iflush_done(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
struct xfs_log_item *lip, *n;
|
||||
LIST_HEAD(flushed_inodes);
|
||||
LIST_HEAD(ail_updates);
|
||||
|
||||
/*
|
||||
* Pull the attached inodes from the buffer one at a time and take the
|
||||
* appropriate action on them.
|
||||
*/
|
||||
list_for_each_entry_safe(lip, n, &bp->b_li_list, li_bio_list) {
|
||||
struct xfs_inode_log_item *iip = INODE_ITEM(lip);
|
||||
|
||||
if (xfs_iflags_test(iip->ili_inode, XFS_ISTALE)) {
|
||||
xfs_iflush_abort(iip->ili_inode);
|
||||
continue;
|
||||
}
|
||||
if (!iip->ili_last_fields)
|
||||
continue;
|
||||
|
||||
/* Do an unlocked check for needing the AIL lock. */
|
||||
if (iip->ili_flush_lsn == lip->li_lsn ||
|
||||
test_bit(XFS_LI_FAILED, &lip->li_flags))
|
||||
list_move_tail(&lip->li_bio_list, &ail_updates);
|
||||
else
|
||||
list_move_tail(&lip->li_bio_list, &flushed_inodes);
|
||||
}
|
||||
|
||||
if (!list_empty(&ail_updates)) {
|
||||
xfs_iflush_ail_updates(bp->b_mount->m_ail, &ail_updates);
|
||||
list_splice_tail(&ail_updates, &flushed_inodes);
|
||||
}
|
||||
|
||||
xfs_iflush_finish(bp, &flushed_inodes);
|
||||
if (!list_empty(&flushed_inodes))
|
||||
list_splice_tail(&flushed_inodes, &bp->b_li_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the inode flushing abort routine. It is called from xfs_iflush when
|
||||
* the filesystem is shutting down to clean up the inode state. It is
|
||||
|
Loading…
Reference in New Issue
Block a user