[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