[XFS] Improve buffered read throughput by removing unnecessary timer calls
that showed in ´kernel profiles.

SGI-PV: 925163
SGI-Modid: xfs-linux:xfs-kern:23861a

Signed-off-by: Nathan Scott <nathans@sgi.com>
diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c
index 77708a8..68c83ec 100644
--- a/fs/xfs/linux-2.6/xfs_iops.c
+++ b/fs/xfs/linux-2.6/xfs_iops.c
@@ -69,6 +69,137 @@
 #include <linux/xattr.h>
 #include <linux/namei.h>
 
+/*
+ * Change the requested timestamp in the given inode.
+ * We don't lock across timestamp updates, and we don't log them but
+ * we do record the fact that there is dirty information in core.
+ *
+ * NOTE -- callers MUST combine XFS_ICHGTIME_MOD or XFS_ICHGTIME_CHG
+ *		with XFS_ICHGTIME_ACC to be sure that access time
+ *		update will take.  Calling first with XFS_ICHGTIME_ACC
+ *		and then XFS_ICHGTIME_MOD may fail to modify the access
+ *		timestamp if the filesystem is mounted noacctm.
+ */
+void
+xfs_ichgtime(
+	xfs_inode_t	*ip,
+	int		flags)
+{
+	struct inode	*inode = LINVFS_GET_IP(XFS_ITOV(ip));
+	timespec_t	tv;
+
+	/*
+	 * We're not supposed to change timestamps in readonly-mounted
+	 * filesystems.  Throw it away if anyone asks us.
+	 */
+	if (unlikely(IS_RDONLY(inode)))
+		return;
+
+	/*
+	 * Don't update access timestamps on reads if mounted "noatime".
+	 * Throw it away if anyone asks us.
+	 */
+	if (unlikely(
+	    (ip->i_mount->m_flags & XFS_MOUNT_NOATIME || IS_NOATIME(inode)) &&
+	    (flags & (XFS_ICHGTIME_ACC|XFS_ICHGTIME_MOD|XFS_ICHGTIME_CHG)) ==
+			XFS_ICHGTIME_ACC))
+		return;
+
+	nanotime(&tv);
+	if (flags & XFS_ICHGTIME_MOD) {
+		inode->i_mtime = tv;
+		ip->i_d.di_mtime.t_sec = (__int32_t)tv.tv_sec;
+		ip->i_d.di_mtime.t_nsec = (__int32_t)tv.tv_nsec;
+	}
+	if (flags & XFS_ICHGTIME_ACC) {
+		inode->i_atime = tv;
+		ip->i_d.di_atime.t_sec = (__int32_t)tv.tv_sec;
+		ip->i_d.di_atime.t_nsec = (__int32_t)tv.tv_nsec;
+	}
+	if (flags & XFS_ICHGTIME_CHG) {
+		inode->i_ctime = tv;
+		ip->i_d.di_ctime.t_sec = (__int32_t)tv.tv_sec;
+		ip->i_d.di_ctime.t_nsec = (__int32_t)tv.tv_nsec;
+	}
+
+	/*
+	 * We update the i_update_core field _after_ changing
+	 * the timestamps in order to coordinate properly with
+	 * xfs_iflush() so that we don't lose timestamp updates.
+	 * This keeps us from having to hold the inode lock
+	 * while doing this.  We use the SYNCHRONIZE macro to
+	 * ensure that the compiler does not reorder the update
+	 * of i_update_core above the timestamp updates above.
+	 */
+	SYNCHRONIZE();
+	ip->i_update_core = 1;
+	if (!(inode->i_state & I_LOCK))
+		mark_inode_dirty_sync(inode);
+}
+
+/*
+ * Variant on the above which avoids querying the system clock
+ * in situations where we know the Linux inode timestamps have
+ * just been updated (and so we can update our inode cheaply).
+ * We also skip the readonly and noatime checks here, they are
+ * also catered for already.
+ */
+void
+xfs_ichgtime_fast(
+	xfs_inode_t	*ip,
+	struct inode	*inode,
+	int		flags)
+{
+	timespec_t	*tvp;
+
+	/*
+	 * We're not supposed to change timestamps in readonly-mounted
+	 * filesystems.  Throw it away if anyone asks us.
+	 */
+	if (unlikely(IS_RDONLY(inode)))
+		return;
+
+	/*
+	 * Don't update access timestamps on reads if mounted "noatime".
+	 * Throw it away if anyone asks us.
+	 */
+	if (unlikely(
+	    (ip->i_mount->m_flags & XFS_MOUNT_NOATIME || IS_NOATIME(inode)) &&
+	    ((flags & (XFS_ICHGTIME_ACC|XFS_ICHGTIME_MOD|XFS_ICHGTIME_CHG)) ==
+			XFS_ICHGTIME_ACC)))
+		return;
+
+	if (flags & XFS_ICHGTIME_MOD) {
+		tvp = &inode->i_mtime;
+		ip->i_d.di_mtime.t_sec = (__int32_t)tvp->tv_sec;
+		ip->i_d.di_mtime.t_nsec = (__int32_t)tvp->tv_nsec;
+	}
+	if (flags & XFS_ICHGTIME_ACC) {
+		tvp = &inode->i_atime;
+		ip->i_d.di_atime.t_sec = (__int32_t)tvp->tv_sec;
+		ip->i_d.di_atime.t_nsec = (__int32_t)tvp->tv_nsec;
+	}
+	if (flags & XFS_ICHGTIME_CHG) {
+		tvp = &inode->i_ctime;
+		ip->i_d.di_ctime.t_sec = (__int32_t)tvp->tv_sec;
+		ip->i_d.di_ctime.t_nsec = (__int32_t)tvp->tv_nsec;
+	}
+
+	/*
+	 * We update the i_update_core field _after_ changing
+	 * the timestamps in order to coordinate properly with
+	 * xfs_iflush() so that we don't lose timestamp updates.
+	 * This keeps us from having to hold the inode lock
+	 * while doing this.  We use the SYNCHRONIZE macro to
+	 * ensure that the compiler does not reorder the update
+	 * of i_update_core above the timestamp updates above.
+	 */
+	SYNCHRONIZE();
+	ip->i_update_core = 1;
+	if (!(inode->i_state & I_LOCK))
+		mark_inode_dirty_sync(inode);
+}
+
 
 /*
  * Pull the link count and size up from the xfs inode to the linux inode
diff --git a/fs/xfs/linux-2.6/xfs_iops.h b/fs/xfs/linux-2.6/xfs_iops.h
index 6a69a62..415de44 100644
--- a/fs/xfs/linux-2.6/xfs_iops.h
+++ b/fs/xfs/linux-2.6/xfs_iops.h
@@ -48,4 +48,8 @@
 extern int xfs_ioctl(struct bhv_desc *, struct inode *, struct file *,
                         int, unsigned int, void __user *);
 
+struct xfs_inode;
+extern void xfs_ichgtime(struct xfs_inode *, int);
+extern void xfs_ichgtime_fast(struct xfs_inode *, struct inode *, int);
+
 #endif /* __XFS_IOPS_H__ */
diff --git a/fs/xfs/linux-2.6/xfs_lrw.c b/fs/xfs/linux-2.6/xfs_lrw.c
index 3b5fabe..c04f0c0 100644
--- a/fs/xfs/linux-2.6/xfs_lrw.c
+++ b/fs/xfs/linux-2.6/xfs_lrw.c
@@ -302,7 +302,7 @@
 	xfs_iunlock(ip, XFS_IOLOCK_SHARED);
 
 	if (likely(!(ioflags & IO_INVIS)))
-		xfs_ichgtime(ip, XFS_ICHGTIME_ACC);
+		xfs_ichgtime_fast(ip, inode, XFS_ICHGTIME_ACC);
 
 unlock_isem:
 	if (unlikely(ioflags & IO_ISDIRECT))
@@ -367,7 +367,7 @@
 		XFS_STATS_ADD(xs_read_bytes, ret);
 
 	if (likely(!(ioflags & IO_INVIS)))
-		xfs_ichgtime(ip, XFS_ICHGTIME_ACC);
+		xfs_ichgtime_fast(ip, LINVFS_GET_IP(vp), XFS_ICHGTIME_ACC);
 
 	return ret;
 }
@@ -732,15 +732,10 @@
 		}
 	}
 
-	/*
-	 * On Linux, generic_file_write updates the times even if
-	 * no data is copied in so long as the write had a size.
-	 *
-	 * We must update xfs' times since revalidate will overcopy xfs.
-	 */
-	if (!(ioflags & IO_INVIS)) {
-		xfs_ichgtime(xip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+	if (likely(!(ioflags & IO_INVIS))) {
 		inode_update_time(inode, 1);
+		xfs_ichgtime_fast(xip, inode,
+				  XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
 	}
 
 	/*
diff --git a/fs/xfs/linux-2.6/xfs_vnode.h b/fs/xfs/linux-2.6/xfs_vnode.h
index 76a8ddd..ef1c65f 100644
--- a/fs/xfs/linux-2.6/xfs_vnode.h
+++ b/fs/xfs/linux-2.6/xfs_vnode.h
@@ -567,13 +567,6 @@
 }
 
 /*
- * Update modify/access/change times on the vnode
- */
-#define VN_MTIMESET(vp, tvp)	(LINVFS_GET_IP(vp)->i_mtime = *(tvp))
-#define VN_ATIMESET(vp, tvp)	(LINVFS_GET_IP(vp)->i_atime = *(tvp))
-#define VN_CTIMESET(vp, tvp)	(LINVFS_GET_IP(vp)->i_ctime = *(tvp))
-
-/*
  * Dealing with bad inodes
  */
 static inline void vn_mark_bad(struct vnode *vp)
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 5bf7b70..a648cc8 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -3688,73 +3688,6 @@
 	return( 0 );
 }
 
-/*
- * Change the requested timestamp in the given inode.
- * We don't lock across timestamp updates, and we don't log them but
- * we do record the fact that there is dirty information in core.
- *
- * NOTE -- callers MUST combine XFS_ICHGTIME_MOD or XFS_ICHGTIME_CHG
- *		with XFS_ICHGTIME_ACC to be sure that access time
- *		update will take.  Calling first with XFS_ICHGTIME_ACC
- *		and then XFS_ICHGTIME_MOD may fail to modify the access
- *		timestamp if the filesystem is mounted noacctm.
- */
-void
-xfs_ichgtime(xfs_inode_t *ip,
-	     int flags)
-{
-	timespec_t	tv;
-	vnode_t		*vp = XFS_ITOV(ip);
-	struct inode	*inode = LINVFS_GET_IP(vp);
-
-	/*
-	 * We're not supposed to change timestamps in readonly-mounted
-	 * filesystems.  Throw it away if anyone asks us.
-	 */
-	if (unlikely(vp->v_vfsp->vfs_flag & VFS_RDONLY))
-		return;
-
-	/*
-	 * Don't update access timestamps on reads if mounted "noatime"
-	 * Throw it away if anyone asks us.
-	 */
-	if ((ip->i_mount->m_flags & XFS_MOUNT_NOATIME || IS_NOATIME(inode)) &&
-	    ((flags & (XFS_ICHGTIME_ACC|XFS_ICHGTIME_MOD|XFS_ICHGTIME_CHG))
-			== XFS_ICHGTIME_ACC))
-		return;
-
-	nanotime(&tv);
-	if (flags & XFS_ICHGTIME_MOD) {
-		VN_MTIMESET(vp, &tv);
-		ip->i_d.di_mtime.t_sec = (__int32_t)tv.tv_sec;
-		ip->i_d.di_mtime.t_nsec = (__int32_t)tv.tv_nsec;
-	}
-	if (flags & XFS_ICHGTIME_ACC) {
-		VN_ATIMESET(vp, &tv);
-		ip->i_d.di_atime.t_sec = (__int32_t)tv.tv_sec;
-		ip->i_d.di_atime.t_nsec = (__int32_t)tv.tv_nsec;
-	}
-	if (flags & XFS_ICHGTIME_CHG) {
-		VN_CTIMESET(vp, &tv);
-		ip->i_d.di_ctime.t_sec = (__int32_t)tv.tv_sec;
-		ip->i_d.di_ctime.t_nsec = (__int32_t)tv.tv_nsec;
-	}
-
-	/*
-	 * We update the i_update_core field _after_ changing
-	 * the timestamps in order to coordinate properly with
-	 * xfs_iflush() so that we don't lose timestamp updates.
-	 * This keeps us from having to hold the inode lock
-	 * while doing this.  We use the SYNCHRONIZE macro to
-	 * ensure that the compiler does not reorder the update
-	 * of i_update_core above the timestamp updates above.
-	 */
-	SYNCHRONIZE();
-	ip->i_update_core = 1;
-	if (!(inode->i_state & I_LOCK))
-		mark_inode_dirty_sync(inode);
-}
-
 #ifdef XFS_ILOCK_TRACE
 ktrace_t	*xfs_ilock_trace_buf;