[RFC] [PATCH] ext3 nanosecond timestamps

Andreas Dilger <[email protected]>
Newsgroups gmane.comp.file-systems.ext2.devel
Message-ID <[email protected]>
This patch (originally based on a patch by Alex Tomas, updated by
Andreas Gruenbacher, updated by Girish Shilamkar) adds nanosecond
timestamps to ext3.  In order to support this, the filesystem needs to
be formatted with large inodes to have space for the enhanced timestamps.

In addition to nanosecond timestamps (in earlier patches), this patch
uses the 2 remaining bits in the __u32 to extend the unix epoch on
64-bit platforms.  This is done in an INCOMPATIBLE way with earlier
patches, so if those patches are in use anywhere (e.g. SLES?) then it
would be important to know that.  The reason for the incompatible change
is that putting the epoch bits at the bottom gives us more flexibility
to shift the EXT3_EPOCH_BITS if we really need to do that in the future.
This is not a critical part of the change, and as yet this code is not
in use anywhere AFAIK so it can be modified.

The patch UNDERSTANDS a new RO_COMPAT_NS_TIMESTAMP flag, which indicates
that all inodes in the filesystem have space for the ns timestamps, but
it does not SET this flag itself.  That is left to e2fsck to ensure that
all existing inodes have space for the ns timestamps, for the extremely
rare case where both the in-inode EA and external-block EA space is totally
full, and some application like "make" is depending on the ns timestamps
to not go backward if an inode is flushed from cache.

New files ALWAYS support the ns timestamps, so the need to add support
to e2fsck for RO_COMPAT_NS_TIMESTAMP is left as an exercise for the reader.

There is also a reserved field to allow another "must have" field to be
added to the inode without the need for another RO_COMPAT flag.

Signed-off-by: Girish Shilamkar <[email protected]>
Signed-off-by: Andreas Dilger <[email protected]>

Index: linux-stage/fs/ext3/ialloc.c
===================================================================
--- linux-orig/fs/ext3/ialloc.c	2006-05-30 20:32:57.000000000 +0530
+++ linux-stage/fs/ext3/ialloc.c	2006-06-02 16:38:50.000000000 +0530
@@ -610,7 +610,8 @@
 	/* This is the optimal IO size (for stat), not the fs block size */
 	inode->i_blksize = PAGE_SIZE;
 	inode->i_blocks = 0;
-	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
+	inode->i_mtime = inode->i_atime = inode->i_ctime = 
+						ext3_current_time(inode);
 
 	memset(ei->i_data, 0, sizeof(ei->i_data));
 	ei->i_dir_start_lookup = 0;
Index: linux-stage/fs/ext3/inode.c
===================================================================
--- linux-orig/fs/ext3/inode.c	2006-05-30 20:32:57.000000000 +0530
+++ linux-stage/fs/ext3/inode.c	2006-06-06 19:42:37.000000000 +0530
@@ -616,7 +620,7 @@
 
 	/* We are done with atomic stuff, now do the rest of housekeeping */
 
-	inode->i_ctime = CURRENT_TIME_SEC;
+	inode->i_ctime = ext3_current_time(inode);
 	ext3_mark_inode_dirty(handle, inode);
 
 	/* had we spliced it onto indirect block? */
@@ -2232,7 +2236,7 @@
 	ext3_discard_reservation(inode);
 
 	up(&ei->truncate_sem);
-	inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
+	inode->i_mtime = inode->i_ctime = ext3_current_time(inode);
 	ext3_mark_inode_dirty(handle, inode);
 
 	/* In a multi-transaction truncate, we only make the final
@@ -2467,11 +2503,11 @@
 	}
 	inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);
 	inode->i_size = le32_to_cpu(raw_inode->i_size);
-	inode->i_atime.tv_sec = le32_to_cpu(raw_inode->i_atime);
-	inode->i_ctime.tv_sec = le32_to_cpu(raw_inode->i_ctime);
-	inode->i_mtime.tv_sec = le32_to_cpu(raw_inode->i_mtime);
-	inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_mtime.tv_nsec = 0;
 
+	EXT3_INODE_GET_XTIME(i_atime, i_atime_extra, inode, raw_inode);
+	EXT3_INODE_GET_XTIME(i_ctime, i_ctime_extra, inode, raw_inode);
+	EXT3_INODE_GET_XTIME(i_mtime, i_mtime_extra, inode, raw_inode);
+	
 	ei->i_state = 0;
 	ei->i_dir_start_lookup = 0;
 	ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);
@@ -2625,9 +2661,11 @@
 	}
 	raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
 	raw_inode->i_size = cpu_to_le32(ei->i_disksize);
-	raw_inode->i_atime = cpu_to_le32(inode->i_atime.tv_sec);
-	raw_inode->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec);
-	raw_inode->i_mtime = cpu_to_le32(inode->i_mtime.tv_sec);
+
+	EXT3_INODE_SET_XTIME(i_atime, i_atime_extra, inode, raw_inode);
+	EXT3_INODE_SET_XTIME(i_ctime, i_ctime_extra, inode, raw_inode);
+	EXT3_INODE_SET_XTIME(i_mtime, i_mtime_extra, inode, raw_inode);
+
 	raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
 	raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);
 	raw_inode->i_flags = cpu_to_le32(ei->i_flags);
Index: linux-stage/fs/ext3/ioctl.c
===================================================================
--- linux-orig/fs/ext3/ioctl.c	2006-05-30 20:32:56.000000000 +0530
+++ linux-stage/fs/ext3/ioctl.c	2006-06-01 12:29:24.000000000 +0530
@@ -112,7 +112,7 @@
 		ei->i_flags = flags;
 
 		ext3_set_inode_flags(inode);
-		inode->i_ctime = CURRENT_TIME_SEC;
+		inode->i_ctime = ext3_current_time(inode);
 
 		err = ext3_mark_iloc_dirty(handle, inode, &iloc);
 flags_err:
@@ -150,7 +150,7 @@
 			return PTR_ERR(handle);
 		err = ext3_reserve_inode_write(handle, inode, &iloc);
 		if (err == 0) {
-			inode->i_ctime = CURRENT_TIME_SEC;
+			inode->i_ctime = ext3_current_time(inode);
 			inode->i_generation = generation;
 			err = ext3_mark_iloc_dirty(handle, inode, &iloc);
 		}
Index: linux-stage/fs/ext3/namei.c
===================================================================
--- linux-orig/fs/ext3/namei.c	2006-05-30 20:32:57.000000000 +0530
+++ linux-stage/fs/ext3/namei.c	2006-06-01 12:29:24.000000000 +0530
@@ -1266,7 +1266,7 @@
 	 * happen is that the times are slightly out of date
 	 * and/or different from the directory change time.
 	 */
-	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
+	dir->i_mtime = dir->i_ctime = ext3_current_time(dir);
 	ext3_update_dx_flag(dir);
 	dir->i_version++;
 	ext3_mark_inode_dirty(handle, dir);
@@ -2059,7 +2059,7 @@
 	inode->i_version++;
 	inode->i_nlink = 0;
 	ext3_orphan_add(handle, inode);
-	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = ext3_current_time(inode);
 	ext3_mark_inode_dirty(handle, inode);
	dir->i_nlink--;
 	ext3_update_dx_flag(dir);
@@ -2109,7 +2109,7 @@
 	retval = ext3_delete_entry(handle, dir, de, bh);
 	if (retval)
 		goto end_unlink;
-	dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
+	dir->i_ctime = dir->i_mtime = ext3_current_time(dir);
 	ext3_update_dx_flag(dir);
 	ext3_mark_inode_dirty(handle, dir);
	inode->i_nlink--;
@@ -2216,7 +2216,7 @@
 	if (IS_DIRSYNC(dir))
 		handle->h_sync = 1;
 
-	inode->i_ctime = CURRENT_TIME_SEC;
+	inode->i_ctime = ext3_current_time(inode);
 	ext3_inc_count(handle, inode);
 	atomic_inc(&inode->i_count);
 
@@ -2318,7 +2318,7 @@
 	 * Like most other Unix systems, set the ctime for inodes on a
 	 * rename.
 	 */
-	old_inode->i_ctime = CURRENT_TIME_SEC;
+	old_inode->i_ctime = ext3_current_time(old_inode);
 	ext3_mark_inode_dirty(handle, old_inode);
 
 	/*
@@ -2351,9 +2351,9 @@
 
 	if (new_inode) {
 		new_inode->i_nlink--;
-		new_inode->i_ctime = CURRENT_TIME_SEC;
+		new_inode->i_ctime = ext3_current_time(new_inode);
 	}
-	old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC;
+	old_dir->i_ctime = old_dir->i_mtime = ext3_current_time(old_dir);
 	ext3_update_dx_flag(old_dir);
 	if (dir_bh) {
 		BUFFER_TRACE(dir_bh, "get_write_access");
Index: linux-stage/fs/ext3/super.c
===================================================================
--- linux-orig/fs/ext3/super.c	2006-05-30 20:32:57.000000000 +0530
+++ linux-stage/fs/ext3/super.c	2006-06-01 12:29:24.000000000 +0530
@@ -1469,6 +1469,8 @@
 				sbi->s_inode_size);
 			goto failed_mount;
 		}
+		if (sbi->s_inode_size > EXT3_GOOD_OLD_INODE_SIZE)
+			sb->s_time_gran = 1 << (EXT3_EPOCH_BITS - 2);
 	}
 	sbi->s_frag_size = EXT3_MIN_FRAG_SIZE <<
 				   le32_to_cpu(es->s_log_frag_size);
Index: linux-stage/fs/ext3/xattr.c
===================================================================
--- linux-orig/fs/ext3/xattr.c	2006-05-30 20:36:51.000000000 +0530
+++ linux-stage/fs/ext3/xattr.c	2006-06-01 12:29:24.000000000 +0530
@@ -1010,7 +1010,7 @@
 	}
 	if (!error) {
 		ext3_xattr_update_super_block(handle, inode->i_sb);
-		inode->i_ctime = CURRENT_TIME_SEC;
+		inode->i_ctime = ext3_current_time(inode);
 		error = ext3_mark_iloc_dirty(handle, inode, &is.iloc);
 		/*
 		 * The bh is consumed by ext3_mark_iloc_dirty, even with
Index: linux-stage/include/linux/ext3_fs.h
===================================================================
--- linux-orig/include/linux/ext3_fs.h	2006-05-30 20:32:57.000000000 +0530
+++ linux-stage/include/linux/ext3_fs.h	2006-06-01 20:55:48.000000000 +0530
@@ -307,9 +307,48 @@
 	} osd2;				/* OS dependent 2 */
 	__le16	i_extra_isize;
 	__le16	i_pad1;
+	__le32	i_ctime_extra;	/* extra Change time      (nsec << 2 | epoch) */
+	__le32	i_mtime_extra;	/* extra Modification time(nsec << 2 | epoch) */
+	__le32	i_atime_extra;	/* extra Access time      (nsec << 2 | epoch) */
+	__le32  i_extra_reserved1;
 };
 
 #define i_size_high	i_dir_acl
+
+#define EXT3_EPOCH_BITS 2
+#define EXT3_EPOCH_MASK ((1 << EXT3_EPOCH_BITS) - 1)
+#define EXT3_NSEC_MASK  (~0UL << EXT3_EPOCH_BITS)
+
+#define EXT3_INODE_SET_XTIME(xtime, extra_xtime, inode, raw_inode)	       \
+do {									       \
+	(raw_inode)->xtime = cpu_to_le32((inode)->xtime.tv_sec);	       \
+									       \
+	if (offsetof(typeof(*raw_inode), extra_xtime) -			       \
+	    offsetof(typeof(*raw_inode), i_extra_isize) +		       \
+	    sizeof((raw_inode)->extra_xtime) <=	(raw_inode)->i_extra_isize)    \
+		(raw_inode)->extra_xtime =				       \
+			cpu_to_le32((sizeof((inode)->xtime.tv_sec) > 4 ?       \
+				     ((__u64)(inode)->xtime.tv_sec >> 32) : 0)|\
+				    (((inode)->xtime.tv_nsec << 2) &	       \
+				     EXT3_NSEC_MASK));			       \
+} while (0)
+
+#define EXT3_INODE_GET_XTIME(xtime, extra_xtime, inode, raw_inode)	       \
+do {									       \
+	(inode)->xtime.tv_sec = le32_to_cpu((raw_inode)->xtime);	       \
+									       \
+	if (offsetof(typeof(*raw_inode), extra_xtime) -			       \
+	    offsetof(typeof(*raw_inode), i_extra_isize) +		       \
+	    sizeof((raw_inode)->extra_xtime) <= (raw_inode)->i_extra_isize) {  \
+		if (sizeof((inode)->xtime.tv_sec) > 4)			       \
+			(inode)->xtime.tv_sec |=			       \
+				(__u64)(le32_to_cpu((raw_inode)->extra_xtime) &\
+					EXT3_EPOCH_MASK) << 32;		       \
+		(inode)->xtime.tv_nsec =				       \
+				    (le32_to_cpu((raw_inode)->extra_xtime) &   \
+				     EXT3_NSEC_MASK) >> 2;		       \
+	}								       \
+} while (0)
 
 #if defined(__KERNEL__) || defined(__linux__)
 #define i_reserved1     osd1.linux1.l_i_reserved1
@@ -495,6 +498,13 @@
 {
 	return container_of(inode, struct ext3_inode_info, vfs_inode);
 }
+
+static inline struct timespec ext3_current_time(struct inode *inode)
+{
+	return (inode->i_sb->s_time_gran < 1000000000) ?
+		current_fs_time(inode->i_sb) : CURRENT_TIME_SEC;
+}
+
 #else
 /* Assume that user mode programs are passing in an ext3fs superblock, not
  * a kernel struct super_block.  This will allow us to call the feature-test
@@ -556,6 +556,7 @@
 #define EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER	0x0001
 #define EXT3_FEATURE_RO_COMPAT_LARGE_FILE	0x0002
 #define EXT3_FEATURE_RO_COMPAT_BTREE_DIR	0x0004
+#define EXT3_FEATURE_RO_COMPAT_NS_TIMESTAMP	0x0008
 
 #define EXT3_FEATURE_INCOMPAT_COMPRESSION	0x0001
 #define EXT3_FEATURE_INCOMPAT_FILETYPE		0x0002
@@ -569,6 +570,7 @@
 					 EXT3_FEATURE_INCOMPAT_META_BG)
 #define EXT3_FEATURE_RO_COMPAT_SUPP	(EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER| \
 					 EXT3_FEATURE_RO_COMPAT_LARGE_FILE| \
+					 EXT3_FEATURE_RO_COMPAT_NS_TIMESTAMP| \
 					 EXT3_FEATURE_RO_COMPAT_BTREE_DIR)
 
 /*
Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.


All the advantages of Linux Managed Hosting--Without the Cost and Risk!
Fully trained technicians. The highest number of Red Hat certifications in
the hosting industry. Fanatical Support. Click to learn more
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=107521&bid=248729&dat=121642
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.