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:
Dave Chinner 2020-06-29 14:49:20 -07:00 коммит произвёл Darrick J. Wong
Родитель 5717ea4d52
Коммит a69a1dc284
1 изменённых файлов: 87 добавлений и 80 удалений

Просмотреть файл

@ -640,102 +640,64 @@ 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.
*/
void
xfs_iflush_done(
struct xfs_buf *bp)
{
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) {
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, &tmp, li_bio_list) {
list_for_each_entry(lip, list, li_bio_list) {
xfs_lsn_t lsn;
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 (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);
}
/*
* 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.
* 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.
*/
list_for_each_entry_safe(lip, n, &tmp, li_bio_list) {
bool drop_buffer = false;
static void
xfs_iflush_finish(
struct xfs_buf *bp,
struct list_head *list)
{
struct xfs_log_item *lip, *n;
list_del_init(&lip->li_bio_list);
iip = INODE_ITEM(lip);
list_for_each_entry_safe(lip, n, list, li_bio_list) {
struct xfs_inode_log_item *iip = INODE_ITEM(lip);
bool drop_buffer = false;
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