This is the basic framework for the 64 bit time migration for filesystems. There might be some changes or additions required as I start adapting to filesystems.
This gives the basic high level concept so that we can start discussing.
Actual changes to vfs and other file systems will be in a separate series.
Deepa Dinamani (4): fs: vfs: add accessors for inode times fs: Add new data type for inode times fs: Add support for 64 bit time fs: macros and functions support 64 bit time
include/linux/fs.h | 42 ++++++++++++++++++++++++++++++++------- include/linux/stat.h | 12 +++++++++--- include/linux/time.h | 2 ++ include/linux/time64.h | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ kernel/time/time.c | 28 ++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 10 deletions(-)
This is in preparation for the y2038 changes for the vfs. vfs inode timestamps overflow after 2038.
This abstracts the timestamp representation so that any logic to convert between the struct inode timestamps and other interfaces can be placed here.
The plan is to globally change all references to these types through macros only. So when the actually internal representation changes, it will be transparent everywhere outside of the macros.
The internal representation changes will support larger timestamp range.
Signed-off-by: Deepa Dinamani deepa.kernel@gmail.com --- include/linux/fs.h | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/include/linux/fs.h b/include/linux/fs.h index 14ffad4..ab8799c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -679,6 +679,11 @@ struct inode { void *i_private; /* fs or device private pointer */ };
+#define FS_INODE_SET_XTIME(xtime, inode, ts) \ + ((inode)->i_##xtime = (ts)) +#define FS_INODE_GET_XTIME(xtime, inode) \ + ((inode)->i_##xtime) + static inline int inode_unhashed(struct inode *inode) { return hlist_unhashed(&inode->i_hash);
The current representation of inode times in struct inode: struct timespec is not y2038 safe.
The 64 bit counterpart of struct timespec: struct timespec64 suffers from the shortcoming that the data type sizes are different on 32 bit and 64 bit systems.
Introduce a new struct inode_time to overcome the above limitations.
Also add time conversion api's between struct timespec64 and struct inode_time. This is required as the 64-bit time functions typically return struct timespec64 types.
Signed-off-by: Deepa Dinamani deepa.kernel@gmail.com --- include/linux/time64.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+)
diff --git a/include/linux/time64.h b/include/linux/time64.h index 367d5af..9523704 100644 --- a/include/linux/time64.h +++ b/include/linux/time64.h @@ -26,6 +26,20 @@ struct itimerspec64 {
#endif
+/* + * Internal kernel representation of inode fields. + * This structure is not exposed to userspace. + * Use struct timespec64 representation for all userspace. + */ +#ifdef CONFIG_FS_USES_64BIT_TIME +struct inode_time { + time64_t tv_sec; + s32 tv_nsec; +} __aligned(4) __packed; +#else +#define inode_time timespec +#endif + /* Parameters used to convert the timespec values: */ #define MSEC_PER_SEC 1000L #define USEC_PER_MSEC 1000L @@ -42,6 +56,28 @@ struct itimerspec64 {
#if __BITS_PER_LONG == 64
+static inline struct inode_time +timespec64_to_inode_time(const struct timespec64 ts64) +{ + struct inode_time ret; + + ret.tv_sec = ts64.tv_sec; + ret.tv_nsec = ts64.tv_nsec; + + return ret; +} + +static inline struct timespec64 +inode_time_to_timespec64(const struct inode_time itime) +{ + struct timespec64 ret; + + ret.tv_sec = itime.tv_sec; + ret.tv_nsec = itime.tv_nsec; + + return ret; +} + static inline struct timespec timespec64_to_timespec(const struct timespec64 ts64) { return ts64; @@ -76,6 +112,18 @@ static inline struct itimerspec64 itimerspec_to_itimerspec64(struct itimerspec *
#else
+static inline struct inode_time +timespec64_to_inode_time(const struct timespec64 ts64) +{ + return ts64; +} + +static inline struct timespec64 +inode_time_to_timespec64(const struct inode_time itime) +{ + return itime; +} + static inline struct timespec timespec64_to_timespec(const struct timespec64 ts64) { struct timespec ret;
The current representation of inode times in struct inode, struct iattr, struct kstat: struct timespec are not y2038 safe.
Add provision to convert them to use 64 bit times in the future.
struct inode uses struct inode time to maintain same size for times across 32 bit and 64 bit architectures. structs iattr and kstat have no such requirement since these data types are only used to pass values in and out of vfs layer.
In addition, inode_time is defined as packed and aligned to a 4 byte boundary to make the structure use 12 bytes each instead of 16 bytes. This will help save RAM space as inode structure is cached in memory. The other structures are transient and such a change would not be beneficial.
Signed-off-by: Deepa Dinamani deepa.kernel@gmail.com --- include/linux/fs.h | 33 ++++++++++++++++++++++++++------- include/linux/stat.h | 12 +++++++++--- 2 files changed, 35 insertions(+), 10 deletions(-)
diff --git a/include/linux/fs.h b/include/linux/fs.h index ab8799c..a7d4657 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -245,13 +245,19 @@ typedef void (dax_iodone_t)(struct buffer_head *bh_map, int uptodate); */ struct iattr { unsigned int ia_valid; - umode_t ia_mode; - kuid_t ia_uid; - kgid_t ia_gid; - loff_t ia_size; - struct timespec ia_atime; - struct timespec ia_mtime; - struct timespec ia_ctime; + umode_t ia_mode; + kuid_t ia_uid; + kgid_t ia_gid; + loff_t ia_size; +#ifdef CONFIG_FS_USES_64BIT_TIME + struct timespec64 ia_atime; + struct timespec64 ia_mtime; + struct timespec64 ia_ctime; +#else + struct timespec ia_atime; + struct timespec ia_mtime; + struct timespec ia_ctime; +#endif
/* * Not an attribute, but an auxiliary info for filesystems wanting to @@ -616,9 +622,15 @@ struct inode { }; dev_t i_rdev; loff_t i_size; +#ifdef CONFIG_FS_USES_64BIT_TIME + struct inode_time i_atime; + struct inode_time i_mtime; + struct inode_time i_ctime; +#else struct timespec i_atime; struct timespec i_mtime; struct timespec i_ctime; +#endif spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ unsigned short i_bytes; unsigned int i_blkbits; @@ -679,10 +691,17 @@ struct inode { void *i_private; /* fs or device private pointer */ };
+#ifdef CONFIG_64BIT_INODE_TIME +#define FS_INODE_SET_XTIME(xtime, inode, ts64) \ + ((inode)->i_##xtime = timespec64_to_inode_time(ts64)) +#define FS_INODE_GET_XTIME(xtime, inode) \ + (inode_time_to_timespec64((inode)->i_##xtime)) +#else #define FS_INODE_SET_XTIME(xtime, inode, ts) \ ((inode)->i_##xtime = (ts)) #define FS_INODE_GET_XTIME(xtime, inode) \ ((inode)->i_##xtime) +#endif
static inline int inode_unhashed(struct inode *inode) { diff --git a/include/linux/stat.h b/include/linux/stat.h index 075cb0c..e3443a9 100644 --- a/include/linux/stat.h +++ b/include/linux/stat.h @@ -27,9 +27,15 @@ struct kstat { kgid_t gid; dev_t rdev; loff_t size; - struct timespec atime; - struct timespec mtime; - struct timespec ctime; +#ifdef CONFIG_FS_USES_64BIT_TIME + struct timespec64 atime; + struct timespec64 mtime; + struct timespec64 ctime; +#else + struct timespec atime; + struct timespec mtime; + struct timespec ctime; +#endif unsigned long blksize; unsigned long long blocks; };
The current file system macros and time functions are not y2038 safe because of the use of struct timespec.
Change these to use timespec64.
The change will be globally enabled along with all the other 64-bit time file system changes.
Signed-off-by: Deepa Dinamani deepa.kernel@gmail.com --- include/linux/fs.h | 4 ++++ include/linux/time.h | 2 ++ include/linux/time64.h | 5 +++++ kernel/time/time.c | 28 ++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+)
diff --git a/include/linux/fs.h b/include/linux/fs.h index a7d4657..f614de6 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1399,7 +1399,11 @@ struct super_block { struct list_head s_inodes; /* all inodes */ };
+#ifdef CONFIG_FS_USES_64BIT_TIME +extern struct timespec64 current_fs_time(struct super_block *sb); +#else extern struct timespec current_fs_time(struct super_block *sb); +#endif
/* * Snapshotting support. diff --git a/include/linux/time.h b/include/linux/time.h index beebe3a..40fe4af 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -125,8 +125,10 @@ static inline bool timeval_valid(const struct timeval *tv)
extern struct timespec timespec_trunc(struct timespec t, unsigned gran);
+#ifndef CONFIG_FS_USES_64BIT_TIME #define CURRENT_TIME (current_kernel_time()) #define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 }) +#endif
/* Some architectures do not supply their own clocksource. * This is mainly the case in architectures that get their diff --git a/include/linux/time64.h b/include/linux/time64.h index 9523704..0e69446 100644 --- a/include/linux/time64.h +++ b/include/linux/time64.h @@ -40,6 +40,11 @@ struct inode_time { #define inode_time timespec #endif
+#ifdef CONFIG_64BIT_INODE_TIME +#define CURRENT_TIME (current_kernel_time64()) +#define CURRENT_TIME_SEC ((struct timespec64) { ktime_get_seconds(), 0 }) +#endif + /* Parameters used to convert the timespec values: */ #define MSEC_PER_SEC 1000L #define USEC_PER_MSEC 1000L diff --git a/kernel/time/time.c b/kernel/time/time.c index 86751c6..92b6db1 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -237,11 +237,19 @@ SYSCALL_DEFINE1(adjtimex, struct timex __user *, txc_p) * Return the current time truncated to the time granularity supported by * the fs. */ +#ifdef CONFIG_FS_USES_64BIT_TIME +struct timespec64 current_fs_time(struct super_block *sb) +{ + struct timespec64 now = current_kernel_time64(); + return timespec64_trunc(now, sb->s_time_gran); +} +#else struct timespec current_fs_time(struct super_block *sb) { struct timespec now = current_kernel_time(); return timespec_trunc(now, sb->s_time_gran); } +#endif EXPORT_SYMBOL(current_fs_time);
/* @@ -294,6 +302,25 @@ EXPORT_SYMBOL(jiffies_to_usecs); * Truncate a timespec to a granularity. Always rounds down. gran must * not be 0 nor greater than a second (NSEC_PER_SEC, or 10^9 ns). */ +#ifdef CONFIG_FS_USES_64BIT_TIME +/* TODO: Change name to timespec64_trunc after the global switch to + * CONFIG_FS_USES_64BIT_TIME. + */ +struct timespec64 timespec_trunc(struct timespec64 t, unsigned gran) +{ + /* Avoid division in the common cases 1 ns and 1 s. */ + if (gran == 1) { + /* nothing */ + } else if (gran == NSEC_PER_SEC) { + t.tv_nsec = 0; + } else if (gran > 1 && gran < NSEC_PER_SEC) { + t.tv_nsec -= t.tv_nsec % gran; + } else { + WARN(1, "illegal file time granularity: %u", gran); + } + return t; +} +#else struct timespec timespec_trunc(struct timespec t, unsigned gran) { /* Avoid division in the common cases 1 ns and 1 s. */ @@ -308,6 +335,7 @@ struct timespec timespec_trunc(struct timespec t, unsigned gran) } return t; } +#endif EXPORT_SYMBOL(timespec_trunc);
/*