{
struct xfs_inode *ip = iip->ili_inode;
+ ASSERT(iip->ili_item.li_buf == NULL);
+
ip->i_itemp = NULL;
kmem_cache_free(xfs_ili_zone, iip);
}
inode_item_done(
struct xfs_inode_log_item *iip)
{
- xfs_dinode_t *dip;
- xfs_inode_t *ip;
- xfs_mount_t *mp;
xfs_buf_t *bp;
int error;
- ip = iip->ili_inode;
- mp = iip->ili_item.li_mountp;
- ASSERT(ip != NULL);
+ ASSERT(iip->ili_inode != NULL);
if (!(iip->ili_fields & XFS_ILOG_ALL))
- goto free;
+ goto free_item;
- /*
- * Get the buffer containing the on-disk inode.
- */
- error = xfs_imap_to_bp(mp, NULL, &ip->i_imap, &dip, &bp, 0);
- if (error) {
- fprintf(stderr, _("%s: warning - imap_to_bp failed (%d)\n"),
- progname, error);
- goto free;
- }
+ bp = iip->ili_item.li_buf;
+ iip->ili_item.li_buf = NULL;
/*
* Flush the inode and disassociate it from the transaction regardless
* of whether the flush succeed or not. If we fail the flush, make sure
* we still release the buffer reference we currently hold.
*/
- error = libxfs_iflush_int(ip, bp);
+ error = libxfs_iflush_int(iip->ili_inode, bp);
bp->b_transp = NULL; /* remove xact ptr */
if (error) {
fprintf(stderr, _("%s: warning - iflush_int failed (%d)\n"),
progname, error);
- libxfs_buf_relse(bp);
goto free;
}
libxfs_buf_mark_dirty(bp);
- libxfs_buf_relse(bp);
free:
+ libxfs_buf_relse(bp);
+free_item:
xfs_inode_item_put(iip);
}
#include "xfs_shared.h"
#include "xfs_format.h"
#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
#include "xfs_inode.h"
#include "xfs_trans.h"
}
/*
- * This is called to mark the fields indicated in fieldmask as needing
- * to be logged when the transaction is committed. The inode must
- * already be associated with the given transaction.
+ * This is called to mark the fields indicated in fieldmask as needing to be
+ * logged when the transaction is committed. The inode must already be
+ * associated with the given transaction.
*
- * The values for fieldmask are defined in xfs_inode_item.h. We always
- * log all of the core inode if any of it has changed, and we always log
- * all of the inline data/extents/b-tree root if any of them has changed.
+ * The values for fieldmask are defined in xfs_inode_item.h. We always log all
+ * of the core inode if any of it has changed, and we always log all of the
+ * inline data/extents/b-tree root if any of them has changed.
+ *
+ * Grab and pin the cluster buffer associated with this inode to avoid RMW
+ * cycles at inode writeback time. Avoid the need to add error handling to every
+ * xfs_trans_log_inode() call by shutting down on read error. This will cause
+ * transactions to fail and everything to error out, just like if we return a
+ * read error in a dirty transaction and cancel it.
*/
void
xfs_trans_log_inode(
spin_lock(&iip->ili_lock);
iip->ili_fsync_fields |= flags;
+ if (!iip->ili_item.li_buf) {
+ struct xfs_buf *bp;
+ int error;
+
+ /*
+ * We hold the ILOCK here, so this inode is not going to be
+ * flushed while we are here. Further, because there is no
+ * buffer attached to the item, we know that there is no IO in
+ * progress, so nothing will clear the ili_fields while we read
+ * in the buffer. Hence we can safely drop the spin lock and
+ * read the buffer knowing that the state will not change from
+ * here.
+ */
+ spin_unlock(&iip->ili_lock);
+ error = xfs_imap_to_bp(ip->i_mount, tp, &ip->i_imap, NULL,
+ &bp, 0);
+ if (error) {
+ xfs_force_shutdown(ip->i_mount, SHUTDOWN_META_IO_ERROR);
+ return;
+ }
+
+ /*
+ * 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.
+ */
+ xfs_buf_hold(bp);
+ xfs_trans_brelse(tp, bp);
+
+ spin_lock(&iip->ili_lock);
+ iip->ili_item.li_buf = bp;
+ }
+
/*
* Always OR in the bits from the ili_last_fields field. This is to
* coordinate with the xfs_iflush() and xfs_iflush_done() routines in