Skip to content

Commit ef7f383

Browse files
Kalpak Shahtytso
authored andcommitted
ext4: Add nanosecond timestamps
This patch adds nanosecond timestamps for ext4. This involves adding *time_extra fields to the ext4_inode to extend the timestamps to 64-bits. Creation time is also added by this patch. These extended fields will fit into an inode if the filesystem was formatted with large inodes (-I 256 or larger) and there are currently no EAs consuming all of the available space. For new inodes we always reserve enough space for the kernel's known extended fields, but for inodes created with an old kernel this might not have been the case. So this patch also adds the EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE feature flag(ro-compat so that older kernels can't create inodes with a smaller extra_isize). which indicates if the fields fitting inside s_min_extra_isize are available or not. If the expansion of inodes if unsuccessful then this feature will be disabled. This feature is only enabled if requested by the sysadmin. None of the extended inode fields is critical for correct filesystem operation. Signed-off-by: Andreas Dilger <adilger@clusterfs.com> Signed-off-by: Kalpak Shah <kalpak@clusterfs.com> Signed-off-by: Eric Sandeen <sandeen@redhat.com> Signed-off-by: Dave Kleikamp <shaggy@linux.vnet.ibm.com> Signed-off-by: Mingming Cao <cmm@us.ibm.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
1 parent 0f49d5d commit ef7f383

File tree

9 files changed

+147
-25
lines changed

9 files changed

+147
-25
lines changed

fs/ext4/ialloc.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,8 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode * dir, int mode)
563563
inode->i_ino = ino;
564564
/* This is the optimal IO size (for stat), not the fs block size */
565565
inode->i_blocks = 0;
566-
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
566+
inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime =
567+
ext4_current_time(inode);
567568

568569
memset(ei->i_data, 0, sizeof(ei->i_data));
569570
ei->i_dir_start_lookup = 0;
@@ -595,9 +596,8 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode * dir, int mode)
595596
spin_unlock(&sbi->s_next_gen_lock);
596597

597598
ei->i_state = EXT4_STATE_NEW;
598-
ei->i_extra_isize =
599-
(EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) ?
600-
sizeof(struct ext4_inode) - EXT4_GOOD_OLD_INODE_SIZE : 0;
599+
600+
ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize;
601601

602602
ret = inode;
603603
if(DQUOT_ALLOC_INODE(inode)) {

fs/ext4/inode.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,7 @@ static int ext4_splice_branch(handle_t *handle, struct inode *inode,
726726

727727
/* We are done with atomic stuff, now do the rest of housekeeping */
728728

729-
inode->i_ctime = CURRENT_TIME_SEC;
729+
inode->i_ctime = ext4_current_time(inode);
730730
ext4_mark_inode_dirty(handle, inode);
731731

732732
/* had we spliced it onto indirect block? */
@@ -2375,7 +2375,7 @@ void ext4_truncate(struct inode *inode)
23752375
ext4_discard_reservation(inode);
23762376

23772377
mutex_unlock(&ei->truncate_mutex);
2378-
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
2378+
inode->i_mtime = inode->i_ctime = ext4_current_time(inode);
23792379
ext4_mark_inode_dirty(handle, inode);
23802380

23812381
/*
@@ -2629,10 +2629,6 @@ void ext4_read_inode(struct inode * inode)
26292629
}
26302630
inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);
26312631
inode->i_size = le32_to_cpu(raw_inode->i_size);
2632-
inode->i_atime.tv_sec = (signed)le32_to_cpu(raw_inode->i_atime);
2633-
inode->i_ctime.tv_sec = (signed)le32_to_cpu(raw_inode->i_ctime);
2634-
inode->i_mtime.tv_sec = (signed)le32_to_cpu(raw_inode->i_mtime);
2635-
inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_mtime.tv_nsec = 0;
26362632

26372633
ei->i_state = 0;
26382634
ei->i_dir_start_lookup = 0;
@@ -2710,6 +2706,11 @@ void ext4_read_inode(struct inode * inode)
27102706
} else
27112707
ei->i_extra_isize = 0;
27122708

2709+
EXT4_INODE_GET_XTIME(i_ctime, inode, raw_inode);
2710+
EXT4_INODE_GET_XTIME(i_mtime, inode, raw_inode);
2711+
EXT4_INODE_GET_XTIME(i_atime, inode, raw_inode);
2712+
EXT4_EINODE_GET_XTIME(i_crtime, ei, raw_inode);
2713+
27132714
if (S_ISREG(inode->i_mode)) {
27142715
inode->i_op = &ext4_file_inode_operations;
27152716
inode->i_fop = &ext4_file_operations;
@@ -2791,9 +2792,12 @@ static int ext4_do_update_inode(handle_t *handle,
27912792
}
27922793
raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
27932794
raw_inode->i_size = cpu_to_le32(ei->i_disksize);
2794-
raw_inode->i_atime = cpu_to_le32(inode->i_atime.tv_sec);
2795-
raw_inode->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec);
2796-
raw_inode->i_mtime = cpu_to_le32(inode->i_mtime.tv_sec);
2795+
2796+
EXT4_INODE_SET_XTIME(i_ctime, inode, raw_inode);
2797+
EXT4_INODE_SET_XTIME(i_mtime, inode, raw_inode);
2798+
EXT4_INODE_SET_XTIME(i_atime, inode, raw_inode);
2799+
EXT4_EINODE_SET_XTIME(i_crtime, ei, raw_inode);
2800+
27972801
raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
27982802
raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);
27992803
raw_inode->i_flags = cpu_to_le32(ei->i_flags);

fs/ext4/ioctl.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ int ext4_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
9797
ei->i_flags = flags;
9898

9999
ext4_set_inode_flags(inode);
100-
inode->i_ctime = CURRENT_TIME_SEC;
100+
inode->i_ctime = ext4_current_time(inode);
101101

102102
err = ext4_mark_iloc_dirty(handle, inode, &iloc);
103103
flags_err:
@@ -134,7 +134,7 @@ int ext4_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
134134
return PTR_ERR(handle);
135135
err = ext4_reserve_inode_write(handle, inode, &iloc);
136136
if (err == 0) {
137-
inode->i_ctime = CURRENT_TIME_SEC;
137+
inode->i_ctime = ext4_current_time(inode);
138138
inode->i_generation = generation;
139139
err = ext4_mark_iloc_dirty(handle, inode, &iloc);
140140
}

fs/ext4/namei.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,7 +1295,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
12951295
* happen is that the times are slightly out of date
12961296
* and/or different from the directory change time.
12971297
*/
1298-
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
1298+
dir->i_mtime = dir->i_ctime = ext4_current_time(dir);
12991299
ext4_update_dx_flag(dir);
13001300
dir->i_version++;
13011301
ext4_mark_inode_dirty(handle, dir);
@@ -2056,7 +2056,7 @@ static int ext4_rmdir (struct inode * dir, struct dentry *dentry)
20562056
* recovery. */
20572057
inode->i_size = 0;
20582058
ext4_orphan_add(handle, inode);
2059-
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
2059+
inode->i_ctime = dir->i_ctime = dir->i_mtime = ext4_current_time(inode);
20602060
ext4_mark_inode_dirty(handle, inode);
20612061
drop_nlink(dir);
20622062
ext4_update_dx_flag(dir);
@@ -2106,13 +2106,13 @@ static int ext4_unlink(struct inode * dir, struct dentry *dentry)
21062106
retval = ext4_delete_entry(handle, dir, de, bh);
21072107
if (retval)
21082108
goto end_unlink;
2109-
dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
2109+
dir->i_ctime = dir->i_mtime = ext4_current_time(dir);
21102110
ext4_update_dx_flag(dir);
21112111
ext4_mark_inode_dirty(handle, dir);
21122112
drop_nlink(inode);
21132113
if (!inode->i_nlink)
21142114
ext4_orphan_add(handle, inode);
2115-
inode->i_ctime = dir->i_ctime;
2115+
inode->i_ctime = ext4_current_time(inode);
21162116
ext4_mark_inode_dirty(handle, inode);
21172117
retval = 0;
21182118

@@ -2203,7 +2203,7 @@ static int ext4_link (struct dentry * old_dentry,
22032203
if (IS_DIRSYNC(dir))
22042204
handle->h_sync = 1;
22052205

2206-
inode->i_ctime = CURRENT_TIME_SEC;
2206+
inode->i_ctime = ext4_current_time(inode);
22072207
inc_nlink(inode);
22082208
atomic_inc(&inode->i_count);
22092209

@@ -2305,7 +2305,7 @@ static int ext4_rename (struct inode * old_dir, struct dentry *old_dentry,
23052305
* Like most other Unix systems, set the ctime for inodes on a
23062306
* rename.
23072307
*/
2308-
old_inode->i_ctime = CURRENT_TIME_SEC;
2308+
old_inode->i_ctime = ext4_current_time(old_inode);
23092309
ext4_mark_inode_dirty(handle, old_inode);
23102310

23112311
/*
@@ -2338,9 +2338,9 @@ static int ext4_rename (struct inode * old_dir, struct dentry *old_dentry,
23382338

23392339
if (new_inode) {
23402340
drop_nlink(new_inode);
2341-
new_inode->i_ctime = CURRENT_TIME_SEC;
2341+
new_inode->i_ctime = ext4_current_time(new_inode);
23422342
}
2343-
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC;
2343+
old_dir->i_ctime = old_dir->i_mtime = ext4_current_time(old_dir);
23442344
ext4_update_dx_flag(old_dir);
23452345
if (dir_bh) {
23462346
BUFFER_TRACE(dir_bh, "get_write_access");

fs/ext4/super.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,6 +1651,8 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent)
16511651
sbi->s_inode_size);
16521652
goto failed_mount;
16531653
}
1654+
if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE)
1655+
sb->s_time_gran = 1 << (EXT4_EPOCH_BITS - 2);
16541656
}
16551657
sbi->s_frag_size = EXT4_MIN_FRAG_SIZE <<
16561658
le32_to_cpu(es->s_log_frag_size);
@@ -1874,6 +1876,32 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent)
18741876
}
18751877

18761878
ext4_setup_super (sb, es, sb->s_flags & MS_RDONLY);
1879+
1880+
/* determine the minimum size of new large inodes, if present */
1881+
if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE) {
1882+
sbi->s_want_extra_isize = sizeof(struct ext4_inode) -
1883+
EXT4_GOOD_OLD_INODE_SIZE;
1884+
if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
1885+
EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE)) {
1886+
if (sbi->s_want_extra_isize <
1887+
le16_to_cpu(es->s_want_extra_isize))
1888+
sbi->s_want_extra_isize =
1889+
le16_to_cpu(es->s_want_extra_isize);
1890+
if (sbi->s_want_extra_isize <
1891+
le16_to_cpu(es->s_min_extra_isize))
1892+
sbi->s_want_extra_isize =
1893+
le16_to_cpu(es->s_min_extra_isize);
1894+
}
1895+
}
1896+
/* Check if enough inode space is available */
1897+
if (EXT4_GOOD_OLD_INODE_SIZE + sbi->s_want_extra_isize >
1898+
sbi->s_inode_size) {
1899+
sbi->s_want_extra_isize = sizeof(struct ext4_inode) -
1900+
EXT4_GOOD_OLD_INODE_SIZE;
1901+
printk(KERN_INFO "EXT4-fs: required extra inode space not"
1902+
"available.\n");
1903+
}
1904+
18771905
/*
18781906
* akpm: core read_super() calls in here with the superblock locked.
18791907
* That deadlocks, because orphan cleanup needs to lock the superblock

fs/ext4/xattr.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,7 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
10131013
}
10141014
if (!error) {
10151015
ext4_xattr_update_super_block(handle, inode->i_sb);
1016-
inode->i_ctime = CURRENT_TIME_SEC;
1016+
inode->i_ctime = ext4_current_time(inode);
10171017
error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);
10181018
/*
10191019
* The bh is consumed by ext4_mark_iloc_dirty, even with

include/linux/ext4_fs.h

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ struct ext4_inode {
288288
__le16 i_uid; /* Low 16 bits of Owner Uid */
289289
__le32 i_size; /* Size in bytes */
290290
__le32 i_atime; /* Access time */
291-
__le32 i_ctime; /* Creation time */
291+
__le32 i_ctime; /* Inode Change time */
292292
__le32 i_mtime; /* Modification time */
293293
__le32 i_dtime; /* Deletion Time */
294294
__le16 i_gid; /* Low 16 bits of Group Id */
@@ -337,10 +337,85 @@ struct ext4_inode {
337337
} osd2; /* OS dependent 2 */
338338
__le16 i_extra_isize;
339339
__le16 i_pad1;
340+
__le32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */
341+
__le32 i_mtime_extra; /* extra Modification time(nsec << 2 | epoch) */
342+
__le32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */
343+
__le32 i_crtime; /* File Creation time */
344+
__le32 i_crtime_extra; /* extra FileCreationtime (nsec << 2 | epoch) */
340345
};
341346

342347
#define i_size_high i_dir_acl
343348

349+
#define EXT4_EPOCH_BITS 2
350+
#define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1)
351+
#define EXT4_NSEC_MASK (~0UL << EXT4_EPOCH_BITS)
352+
353+
/*
354+
* Extended fields will fit into an inode if the filesystem was formatted
355+
* with large inodes (-I 256 or larger) and there are not currently any EAs
356+
* consuming all of the available space. For new inodes we always reserve
357+
* enough space for the kernel's known extended fields, but for inodes
358+
* created with an old kernel this might not have been the case. None of
359+
* the extended inode fields is critical for correct filesystem operation.
360+
* This macro checks if a certain field fits in the inode. Note that
361+
* inode-size = GOOD_OLD_INODE_SIZE + i_extra_isize
362+
*/
363+
#define EXT4_FITS_IN_INODE(ext4_inode, einode, field) \
364+
((offsetof(typeof(*ext4_inode), field) + \
365+
sizeof((ext4_inode)->field)) \
366+
<= (EXT4_GOOD_OLD_INODE_SIZE + \
367+
(einode)->i_extra_isize)) \
368+
369+
static inline __le32 ext4_encode_extra_time(struct timespec *time)
370+
{
371+
return cpu_to_le32((sizeof(time->tv_sec) > 4 ?
372+
time->tv_sec >> 32 : 0) |
373+
((time->tv_nsec << 2) & EXT4_NSEC_MASK));
374+
}
375+
376+
static inline void ext4_decode_extra_time(struct timespec *time, __le32 extra)
377+
{
378+
if (sizeof(time->tv_sec) > 4)
379+
time->tv_sec |= (__u64)(le32_to_cpu(extra) & EXT4_EPOCH_MASK)
380+
<< 32;
381+
time->tv_nsec = (le32_to_cpu(extra) & EXT4_NSEC_MASK) >> 2;
382+
}
383+
384+
#define EXT4_INODE_SET_XTIME(xtime, inode, raw_inode) \
385+
do { \
386+
(raw_inode)->xtime = cpu_to_le32((inode)->xtime.tv_sec); \
387+
if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) \
388+
(raw_inode)->xtime ## _extra = \
389+
ext4_encode_extra_time(&(inode)->xtime); \
390+
} while (0)
391+
392+
#define EXT4_EINODE_SET_XTIME(xtime, einode, raw_inode) \
393+
do { \
394+
if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime)) \
395+
(raw_inode)->xtime = cpu_to_le32((einode)->xtime.tv_sec); \
396+
if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime ## _extra)) \
397+
(raw_inode)->xtime ## _extra = \
398+
ext4_encode_extra_time(&(einode)->xtime); \
399+
} while (0)
400+
401+
#define EXT4_INODE_GET_XTIME(xtime, inode, raw_inode) \
402+
do { \
403+
(inode)->xtime.tv_sec = (signed)le32_to_cpu((raw_inode)->xtime); \
404+
if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) \
405+
ext4_decode_extra_time(&(inode)->xtime, \
406+
raw_inode->xtime ## _extra); \
407+
} while (0)
408+
409+
#define EXT4_EINODE_GET_XTIME(xtime, einode, raw_inode) \
410+
do { \
411+
if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime)) \
412+
(einode)->xtime.tv_sec = \
413+
(signed)le32_to_cpu((raw_inode)->xtime); \
414+
if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime ## _extra)) \
415+
ext4_decode_extra_time(&(einode)->xtime, \
416+
raw_inode->xtime ## _extra); \
417+
} while (0)
418+
344419
#if defined(__KERNEL__) || defined(__linux__)
345420
#define i_reserved1 osd1.linux1.l_i_reserved1
346421
#define i_frag osd2.linux2.l_i_frag
@@ -539,6 +614,13 @@ static inline struct ext4_inode_info *EXT4_I(struct inode *inode)
539614
return container_of(inode, struct ext4_inode_info, vfs_inode);
540615
}
541616

617+
static inline struct timespec ext4_current_time(struct inode *inode)
618+
{
619+
return (inode->i_sb->s_time_gran < NSEC_PER_SEC) ?
620+
current_fs_time(inode->i_sb) : CURRENT_TIME_SEC;
621+
}
622+
623+
542624
static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
543625
{
544626
return ino == EXT4_ROOT_INO ||
@@ -609,6 +691,7 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
609691
#define EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
610692
#define EXT4_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
611693
#define EXT4_FEATURE_RO_COMPAT_BTREE_DIR 0x0004
694+
#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040
612695

613696
#define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x0001
614697
#define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002
@@ -626,6 +709,7 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
626709
EXT4_FEATURE_INCOMPAT_64BIT)
627710
#define EXT4_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \
628711
EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \
712+
EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | \
629713
EXT4_FEATURE_RO_COMPAT_BTREE_DIR)
630714

631715
/*

include/linux/ext4_fs_i.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ struct ext4_inode_info {
153153

154154
unsigned long i_ext_generation;
155155
struct ext4_ext_cache i_cached_extent;
156+
/*
157+
* File creation time. Its function is same as that of
158+
* struct timespec i_{a,c,m}time in the generic inode.
159+
*/
160+
struct timespec i_crtime;
156161
};
157162

158163
#endif /* _LINUX_EXT4_FS_I */

include/linux/ext4_fs_sb.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ struct ext4_sb_info {
8181
char *s_qf_names[MAXQUOTAS]; /* Names of quota files with journalled quota */
8282
int s_jquota_fmt; /* Format of quota to use */
8383
#endif
84+
unsigned int s_want_extra_isize; /* New inodes should reserve # bytes */
8485

8586
#ifdef EXTENTS_STATS
8687
/* ext4 extents stats */

0 commit comments

Comments
 (0)