Skip to content

Commit

Permalink
xfs: log timestamp updates
Browse files Browse the repository at this point in the history
Timestamps on regular files are the last metadata that XFS does not update
transactionally.  Now that we use the delaylog mode exclusively and made
the log scode scale extremly well there is no need to bypass that code for
timestamp updates.  Logging all updates allows to drop a lot of code, and
will allow for further performance improvements later on.

Note that this patch drops optimized handling of fdatasync - it will be
added back in a separate commit.

Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
  • Loading branch information
Christoph Hellwig authored and Ben Myers committed Mar 13, 2012
1 parent 281627d commit 8a9c998
Show file tree
Hide file tree
Showing 13 changed files with 65 additions and 321 deletions.
83 changes: 14 additions & 69 deletions fs/xfs/xfs_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,6 @@ xfs_file_fsync(
struct inode *inode = file->f_mapping->host;
struct xfs_inode *ip = XFS_I(inode);
struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp;
int error = 0;
int log_flushed = 0;
xfs_lsn_t lsn = 0;
Expand Down Expand Up @@ -194,75 +193,15 @@ xfs_file_fsync(
}

/*
* We always need to make sure that the required inode state is safe on
* disk. The inode might be clean but we still might need to force the
* log because of committed transactions that haven't hit the disk yet.
* Likewise, there could be unflushed non-transactional changes to the
* inode core that have to go to disk and this requires us to issue
* a synchronous transaction to capture these changes correctly.
*
* This code relies on the assumption that if the i_update_core field
* of the inode is clear and the inode is unpinned then it is clean
* and no action is required.
* All metadata updates are logged, which means that we just have
* to flush the log up to the latest LSN that touched the inode.
*/
xfs_ilock(ip, XFS_ILOCK_SHARED);

/*
* First check if the VFS inode is marked dirty. All the dirtying
* of non-transactional updates do not go through mark_inode_dirty*,
* which allows us to distinguish between pure timestamp updates
* and i_size updates which need to be caught for fdatasync.
* After that also check for the dirty state in the XFS inode, which
* might gets cleared when the inode gets written out via the AIL
* or xfs_iflush_cluster.
*/
if (((inode->i_state & I_DIRTY_DATASYNC) ||
((inode->i_state & I_DIRTY_SYNC) && !datasync)) &&
ip->i_update_core) {
/*
* Kick off a transaction to log the inode core to get the
* updates. The sync transaction will also force the log.
*/
xfs_iunlock(ip, XFS_ILOCK_SHARED);
tp = xfs_trans_alloc(mp, XFS_TRANS_FSYNC_TS);
error = xfs_trans_reserve(tp, 0,
XFS_FSYNC_TS_LOG_RES(mp), 0, 0, 0);
if (error) {
xfs_trans_cancel(tp, 0);
return -error;
}
xfs_ilock(ip, XFS_ILOCK_EXCL);

/*
* Note - it's possible that we might have pushed ourselves out
* of the way during trans_reserve which would flush the inode.
* But there's no guarantee that the inode buffer has actually
* gone out yet (it's delwri). Plus the buffer could be pinned
* anyway if it's part of an inode in another recent
* transaction. So we play it safe and fire off the
* transaction anyway.
*/
xfs_trans_ijoin(tp, ip, 0);
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
error = xfs_trans_commit(tp, 0);

if (xfs_ipincount(ip))
lsn = ip->i_itemp->ili_last_lsn;
xfs_iunlock(ip, XFS_ILOCK_EXCL);
} else {
/*
* Timestamps/size haven't changed since last inode flush or
* inode transaction commit. That means either nothing got
* written or a transaction committed which caught the updates.
* If the latter happened and the transaction hasn't hit the
* disk yet, the inode will be still be pinned. If it is,
* force the log.
*/
if (xfs_ipincount(ip))
lsn = ip->i_itemp->ili_last_lsn;
xfs_iunlock(ip, XFS_ILOCK_SHARED);
}
xfs_iunlock(ip, XFS_ILOCK_SHARED);

if (!error && lsn)
if (lsn)
error = _xfs_log_force_lsn(mp, lsn, XFS_LOG_SYNC, &log_flushed);

/*
Expand Down Expand Up @@ -659,9 +598,6 @@ xfs_file_aio_write_checks(
return error;
}

if (likely(!(file->f_mode & FMODE_NOCMTIME)))
file_update_time(file);

/*
* If the offset is beyond the size of the file, we need to zero any
* blocks that fall between the existing EOF and the start of this
Expand All @@ -684,6 +620,15 @@ xfs_file_aio_write_checks(
if (error)
return error;

/*
* Updating the timestamps will grab the ilock again from
* xfs_fs_dirty_inode, so we have to call it after dropping the
* lock above. Eventually we should look into a way to avoid
* the pointless lock roundtrip.
*/
if (likely(!(file->f_mode & FMODE_NOCMTIME)))
file_update_time(file);

/*
* If we're writing the file then make sure to clear the setuid and
* setgid bits if the process is not being run by root. This keeps
Expand Down
1 change: 0 additions & 1 deletion fs/xfs/xfs_iget.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ xfs_inode_alloc(
ip->i_afp = NULL;
memset(&ip->i_df, 0, sizeof(xfs_ifork_t));
ip->i_flags = 0;
ip->i_update_core = 0;
ip->i_delayed_blks = 0;
memset(&ip->i_d, 0, sizeof(xfs_icdinode_t));

Expand Down
25 changes: 1 addition & 24 deletions fs/xfs/xfs_inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1656,7 +1656,6 @@ xfs_ifree_cluster(
iip = ip->i_itemp;
if (!iip || xfs_inode_clean(ip)) {
ASSERT(ip != free_ip);
ip->i_update_core = 0;
xfs_ifunlock(ip);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
continue;
Expand Down Expand Up @@ -2451,7 +2450,6 @@ xfs_iflush(
* to disk, because the log record didn't make it to disk!
*/
if (XFS_FORCED_SHUTDOWN(mp)) {
ip->i_update_core = 0;
if (iip)
iip->ili_format.ilf_fields = 0;
xfs_ifunlock(ip);
Expand Down Expand Up @@ -2533,26 +2531,6 @@ xfs_iflush_int(
/* set *dip = inode's place in the buffer */
dip = (xfs_dinode_t *)xfs_buf_offset(bp, ip->i_imap.im_boffset);

/*
* Clear i_update_core before copying out the data.
* This is for coordination with our timestamp updates
* that don't hold the inode lock. They will always
* update the timestamps BEFORE setting i_update_core,
* so if we clear i_update_core after they set it we
* are guaranteed to see their updates to the timestamps.
* I believe that this depends on strongly ordered memory
* semantics, but we have that. We use the SYNCHRONIZE
* macro to make sure that the compiler does not reorder
* the i_update_core access below the data copy below.
*/
ip->i_update_core = 0;
SYNCHRONIZE();

/*
* Make sure to get the latest timestamps from the Linux inode.
*/
xfs_synchronize_times(ip);

if (XFS_TEST_ERROR(dip->di_magic != cpu_to_be16(XFS_DINODE_MAGIC),
mp, XFS_ERRTAG_IFLUSH_1, XFS_RANDOM_IFLUSH_1)) {
xfs_alert_tag(mp, XFS_PTAG_IFLUSH,
Expand Down Expand Up @@ -2711,8 +2689,7 @@ xfs_iflush_int(
} else {
/*
* We're flushing an inode which is not in the AIL and has
* not been logged but has i_update_core set. For this
* case we can use a B_DELWRI flush and immediately drop
* not been logged. For this case we can immediately drop
* the inode flush lock because we can avoid the whole
* AIL state thing. It's OK to drop the flush lock now,
* because we've already locked the buffer and to do anything
Expand Down
5 changes: 0 additions & 5 deletions fs/xfs/xfs_inode.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ typedef struct xfs_inode {
spinlock_t i_flags_lock; /* inode i_flags lock */
/* Miscellaneous state. */
unsigned long i_flags; /* see defined flags below */
unsigned char i_update_core; /* timestamps/size is dirty */
unsigned int i_delayed_blks; /* count of delay alloc blks */

xfs_icdinode_t i_d; /* most of ondisk inode */
Expand Down Expand Up @@ -534,10 +533,6 @@ void xfs_promote_inode(struct xfs_inode *);
void xfs_lock_inodes(xfs_inode_t **, int, uint);
void xfs_lock_two_inodes(xfs_inode_t *, xfs_inode_t *, uint);

void xfs_synchronize_times(xfs_inode_t *);
void xfs_mark_inode_dirty(xfs_inode_t *);
void xfs_mark_inode_dirty_sync(xfs_inode_t *);

#define IHOLD(ip) \
do { \
ASSERT(atomic_read(&VFS_I(ip)->i_count) > 0) ; \
Expand Down
36 changes: 0 additions & 36 deletions fs/xfs/xfs_inode_item.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,42 +254,6 @@ xfs_inode_item_format(
vecp++;
nvecs = 1;

/*
* Clear i_update_core if the timestamps (or any other
* non-transactional modification) need flushing/logging
* and we're about to log them with the rest of the core.
*
* This is the same logic as xfs_iflush() but this code can't
* run at the same time as xfs_iflush because we're in commit
* processing here and so we have the inode lock held in
* exclusive mode. Although it doesn't really matter
* for the timestamps if both routines were to grab the
* timestamps or not. That would be ok.
*
* We clear i_update_core before copying out the data.
* This is for coordination with our timestamp updates
* that don't hold the inode lock. They will always
* update the timestamps BEFORE setting i_update_core,
* so if we clear i_update_core after they set it we
* are guaranteed to see their updates to the timestamps
* either here. Likewise, if they set it after we clear it
* here, we'll see it either on the next commit of this
* inode or the next time the inode gets flushed via
* xfs_iflush(). This depends on strongly ordered memory
* semantics, but we have that. We use the SYNCHRONIZE
* macro to make sure that the compiler does not reorder
* the i_update_core access below the data copy below.
*/
if (ip->i_update_core) {
ip->i_update_core = 0;
SYNCHRONIZE();
}

/*
* Make sure to get the latest timestamps from the Linux inode.
*/
xfs_synchronize_times(ip);

vecp->i_addr = &ip->i_d;
vecp->i_len = sizeof(struct xfs_icdinode);
vecp->i_type = XLOG_REG_TYPE_ICORE;
Expand Down
5 changes: 2 additions & 3 deletions fs/xfs/xfs_inode_item.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,8 @@ typedef struct xfs_inode_log_item {

static inline int xfs_inode_clean(xfs_inode_t *ip)
{
return (!ip->i_itemp ||
!(ip->i_itemp->ili_format.ilf_fields & XFS_ILOG_ALL)) &&
!ip->i_update_core;
return !ip->i_itemp ||
!(ip->i_itemp->ili_format.ilf_fields & XFS_ILOG_ALL);
}

extern void xfs_inode_item_init(struct xfs_inode *, struct xfs_mount *);
Expand Down
58 changes: 0 additions & 58 deletions fs/xfs/xfs_iops.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,59 +50,6 @@
#include <linux/fiemap.h>
#include <linux/slab.h>

/*
* Bring the timestamps in the XFS inode uptodate.
*
* Used before writing the inode to disk.
*/
void
xfs_synchronize_times(
xfs_inode_t *ip)
{
struct inode *inode = VFS_I(ip);

ip->i_d.di_atime.t_sec = (__int32_t)inode->i_atime.tv_sec;
ip->i_d.di_atime.t_nsec = (__int32_t)inode->i_atime.tv_nsec;
ip->i_d.di_ctime.t_sec = (__int32_t)inode->i_ctime.tv_sec;
ip->i_d.di_ctime.t_nsec = (__int32_t)inode->i_ctime.tv_nsec;
ip->i_d.di_mtime.t_sec = (__int32_t)inode->i_mtime.tv_sec;
ip->i_d.di_mtime.t_nsec = (__int32_t)inode->i_mtime.tv_nsec;
}

/*
* If the linux inode is valid, mark it dirty, else mark the dirty state
* in the XFS inode to make sure we pick it up when reclaiming the inode.
*/
void
xfs_mark_inode_dirty_sync(
xfs_inode_t *ip)
{
struct inode *inode = VFS_I(ip);

if (!(inode->i_state & (I_WILL_FREE|I_FREEING)))
mark_inode_dirty_sync(inode);
else {
barrier();
ip->i_update_core = 1;
}
}

void
xfs_mark_inode_dirty(
xfs_inode_t *ip)
{
struct inode *inode = VFS_I(ip);

if (!(inode->i_state & (I_WILL_FREE|I_FREEING)))
mark_inode_dirty(inode);
else {
barrier();
ip->i_update_core = 1;
}

}


int xfs_initxattrs(struct inode *inode, const struct xattr *xattr_array,
void *fs_info)
{
Expand Down Expand Up @@ -678,19 +625,16 @@ xfs_setattr_nonsize(
inode->i_atime = iattr->ia_atime;
ip->i_d.di_atime.t_sec = iattr->ia_atime.tv_sec;
ip->i_d.di_atime.t_nsec = iattr->ia_atime.tv_nsec;
ip->i_update_core = 1;
}
if (mask & ATTR_CTIME) {
inode->i_ctime = iattr->ia_ctime;
ip->i_d.di_ctime.t_sec = iattr->ia_ctime.tv_sec;
ip->i_d.di_ctime.t_nsec = iattr->ia_ctime.tv_nsec;
ip->i_update_core = 1;
}
if (mask & ATTR_MTIME) {
inode->i_mtime = iattr->ia_mtime;
ip->i_d.di_mtime.t_sec = iattr->ia_mtime.tv_sec;
ip->i_d.di_mtime.t_nsec = iattr->ia_mtime.tv_nsec;
ip->i_update_core = 1;
}

xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
Expand Down Expand Up @@ -918,13 +862,11 @@ xfs_setattr_size(
inode->i_ctime = iattr->ia_ctime;
ip->i_d.di_ctime.t_sec = iattr->ia_ctime.tv_sec;
ip->i_d.di_ctime.t_nsec = iattr->ia_ctime.tv_nsec;
ip->i_update_core = 1;
}
if (mask & ATTR_MTIME) {
inode->i_mtime = iattr->ia_mtime;
ip->i_d.di_mtime.t_sec = iattr->ia_mtime.tv_sec;
ip->i_d.di_mtime.t_nsec = iattr->ia_mtime.tv_nsec;
ip->i_update_core = 1;
}

xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
Expand Down
21 changes: 6 additions & 15 deletions fs/xfs/xfs_itable.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ xfs_bulkstat_one_int(
{
struct xfs_icdinode *dic; /* dinode core info pointer */
struct xfs_inode *ip; /* incore inode pointer */
struct inode *inode;
struct xfs_bstat *buf; /* return buffer */
int error = 0; /* error value */

Expand All @@ -86,7 +85,6 @@ xfs_bulkstat_one_int(
ASSERT(ip->i_imap.im_blkno != 0);

dic = &ip->i_d;
inode = VFS_I(ip);

/* xfs_iget returns the following without needing
* further change.
Expand All @@ -99,19 +97,12 @@ xfs_bulkstat_one_int(
buf->bs_uid = dic->di_uid;
buf->bs_gid = dic->di_gid;
buf->bs_size = dic->di_size;

/*
* We need to read the timestamps from the Linux inode because
* the VFS keeps writing directly into the inode structure instead
* of telling us about the updates.
*/
buf->bs_atime.tv_sec = inode->i_atime.tv_sec;
buf->bs_atime.tv_nsec = inode->i_atime.tv_nsec;
buf->bs_mtime.tv_sec = inode->i_mtime.tv_sec;
buf->bs_mtime.tv_nsec = inode->i_mtime.tv_nsec;
buf->bs_ctime.tv_sec = inode->i_ctime.tv_sec;
buf->bs_ctime.tv_nsec = inode->i_ctime.tv_nsec;

buf->bs_atime.tv_sec = dic->di_atime.t_sec;
buf->bs_atime.tv_nsec = dic->di_atime.t_nsec;
buf->bs_mtime.tv_sec = dic->di_mtime.t_sec;
buf->bs_mtime.tv_nsec = dic->di_mtime.t_nsec;
buf->bs_ctime.tv_sec = dic->di_ctime.t_sec;
buf->bs_ctime.tv_nsec = dic->di_ctime.t_nsec;
buf->bs_xflags = xfs_ip2xflags(ip);
buf->bs_extsize = dic->di_extsize << mp->m_sb.sb_blocklog;
buf->bs_extents = dic->di_nextents;
Expand Down
Loading

0 comments on commit 8a9c998

Please sign in to comment.