summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/xfs/libxfs/xfs_trans_inode.c9
-rw-r--r--fs/xfs/xfs_buf_item.c1
-rw-r--r--fs/xfs/xfs_icache.c1
-rw-r--r--fs/xfs/xfs_inode.c24
-rw-r--r--fs/xfs/xfs_inode_item.c16
5 files changed, 27 insertions, 24 deletions
diff --git a/fs/xfs/libxfs/xfs_trans_inode.c b/fs/xfs/libxfs/xfs_trans_inode.c
index ad5974365c58..e15129647e00 100644
--- a/fs/xfs/libxfs/xfs_trans_inode.c
+++ b/fs/xfs/libxfs/xfs_trans_inode.c
@@ -163,13 +163,16 @@ xfs_trans_log_inode(
/*
* We need an explicit buffer reference for the log item but
* don't want the buffer to remain attached to the transaction.
- * Hold the buffer but release the transaction reference.
+ * Hold the buffer but release the transaction reference once
+ * we've attached the inode log item to the buffer log item
+ * list.
*/
xfs_buf_hold(bp);
- xfs_trans_brelse(tp, bp);
-
spin_lock(&iip->ili_lock);
iip->ili_item.li_buf = bp;
+ bp->b_flags |= _XBF_INODES;
+ list_add_tail(&iip->ili_item.li_bio_list, &bp->b_li_list);
+ xfs_trans_brelse(tp, bp);
}
/*
diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c
index ecb3362395af..e9428c30862a 100644
--- a/fs/xfs/xfs_buf_item.c
+++ b/fs/xfs/xfs_buf_item.c
@@ -465,6 +465,7 @@ xfs_buf_item_unpin(
if (bip->bli_flags & XFS_BLI_STALE_INODE) {
xfs_buf_item_done(bp);
xfs_iflush_done(bp);
+ ASSERT(list_empty(&bp->b_li_list));
} else {
xfs_trans_ail_delete(lip, SHUTDOWN_LOG_IO_ERROR);
xfs_buf_item_relse(bp);
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index dc90a81abb1a..58a750ce689c 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -115,6 +115,7 @@ __xfs_inode_free(
{
/* asserts to verify all state is correct here */
ASSERT(atomic_read(&ip->i_pincount) == 0);
+ ASSERT(!ip->i_itemp || list_empty(&ip->i_itemp->ili_item.li_bio_list));
XFS_STATS_DEC(ip->i_mount, vn_active);
call_rcu(&VFS_I(ip)->i_rcu, xfs_inode_free_callback);
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 1c3a8bed4875..c4586ac3656a 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2584,27 +2584,24 @@ retry:
ASSERT(iip->ili_last_fields);
goto out_iunlock;
}
- ASSERT(!iip || list_empty(&iip->ili_item.li_bio_list));
/*
- * Clean inodes can be released immediately. Everything else has to go
- * through xfs_iflush_abort() on journal commit as the flock
- * synchronises removal of the inode from the cluster buffer against
- * inode reclaim.
+ * Inodes not attached to the buffer can be released immediately.
+ * Everything else has to go through xfs_iflush_abort() on journal
+ * commit as the flock synchronises removal of the inode from the
+ * cluster buffer against inode reclaim.
*/
- if (xfs_inode_clean(ip)) {
+ if (!iip || list_empty(&iip->ili_item.li_bio_list)) {
xfs_ifunlock(ip);
goto out_iunlock;
}
/* we have a dirty inode in memory that has not yet been flushed. */
- ASSERT(iip->ili_fields);
spin_lock(&iip->ili_lock);
iip->ili_last_fields = iip->ili_fields;
iip->ili_fields = 0;
iip->ili_fsync_fields = 0;
spin_unlock(&iip->ili_lock);
- list_add_tail(&iip->ili_item.li_bio_list, &bp->b_li_list);
ASSERT(iip->ili_last_fields);
out_iunlock:
@@ -3818,19 +3815,8 @@ flush_out:
xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn,
&iip->ili_item.li_lsn);
- /*
- * Attach the inode item callback to the buffer whether the flush
- * succeeded or not. If not, the caller will shut down and fail I/O
- * completion on the buffer to remove the inode from the AIL and release
- * the flush lock.
- */
- bp->b_flags |= _XBF_INODES;
- list_add_tail(&iip->ili_item.li_bio_list, &bp->b_li_list);
-
/* generate the checksum. */
xfs_dinode_calc_crc(mp, dip);
-
- ASSERT(!list_empty(&bp->b_li_list));
return error;
}
diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c
index 64bdda72f7b2..697248b7eb2b 100644
--- a/fs/xfs/xfs_inode_item.c
+++ b/fs/xfs/xfs_inode_item.c
@@ -660,6 +660,10 @@ xfs_inode_item_destroy(
* 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(
@@ -677,12 +681,15 @@ xfs_iflush_done(
*/
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)) {
- list_del_init(&lip->li_bio_list);
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. */
@@ -728,12 +735,16 @@ xfs_iflush_done(
/*
* 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.
+ * 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.
*/
ASSERT(iip->ili_item.li_buf == bp);
if (!iip->ili_fields) {
iip->ili_item.li_buf = NULL;
drop_buffer = true;
+ } else {
+ list_add(&lip->li_bio_list, &bp->b_li_list);
}
iip->ili_last_fields = 0;
iip->ili_flush_lsn = 0;
@@ -777,6 +788,7 @@ xfs_iflush_abort(
iip->ili_flush_lsn = 0;
bp = iip->ili_item.li_buf;
iip->ili_item.li_buf = NULL;
+ list_del_init(&iip->ili_item.li_bio_list);
spin_unlock(&iip->ili_lock);
}
xfs_ifunlock(ip);