diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-27 16:47:05 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-27 16:47:05 -0800 |
commit | e279160f491392f1345f6eb4b0eeec5a6a2ecdd7 (patch) | |
tree | 018410c41f919774f70e6350fc251556bf789944 /include | |
parent | 534b0a8b677443c0aa8c4c71ff7887f08a2b9b41 (diff) | |
parent | fd928f3e32ba09381b287f8b732418434d932855 (diff) |
Merge tag 'timers-core-2020-01-27' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull timer updates from Thomas Gleixner:
"The timekeeping and timers departement provides:
- Time namespace support:
If a container migrates from one host to another then it expects
that clocks based on MONOTONIC and BOOTTIME are not subject to
disruption. Due to different boot time and non-suspended runtime
these clocks can differ significantly on two hosts, in the worst
case time goes backwards which is a violation of the POSIX
requirements.
The time namespace addresses this problem. It allows to set offsets
for clock MONOTONIC and BOOTTIME once after creation and before
tasks are associated with the namespace. These offsets are taken
into account by timers and timekeeping including the VDSO.
Offsets for wall clock based clocks (REALTIME/TAI) are not provided
by this mechanism. While in theory possible, the overhead and code
complexity would be immense and not justified by the esoteric
potential use cases which were discussed at Plumbers '18.
The overhead for tasks in the root namespace (ie where host time
offsets = 0) is in the noise and great effort was made to ensure
that especially in the VDSO. If time namespace is disabled in the
kernel configuration the code is compiled out.
Kudos to Andrei Vagin and Dmitry Sofanov who implemented this
feature and kept on for more than a year addressing review
comments, finding better solutions. A pleasant experience.
- Overhaul of the alarmtimer device dependency handling to ensure
that the init/suspend/resume ordering is correct.
- A new clocksource/event driver for Microchip PIT64
- Suspend/resume support for the Hyper-V clocksource
- The usual pile of fixes, updates and improvements mostly in the
driver code"
* tag 'timers-core-2020-01-27' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (71 commits)
alarmtimer: Make alarmtimer_get_rtcdev() a stub when CONFIG_RTC_CLASS=n
alarmtimer: Use wakeup source from alarmtimer platform device
alarmtimer: Make alarmtimer platform device child of RTC device
alarmtimer: Update alarmtimer_get_rtcdev() docs to reflect reality
hrtimer: Add missing sparse annotation for __run_timer()
lib/vdso: Only read hrtimer_res when needed in __cvdso_clock_getres()
MIPS: vdso: Define BUILD_VDSO32 when building a 32bit kernel
clocksource/drivers/hyper-v: Set TSC clocksource as default w/ InvariantTSC
clocksource/drivers/hyper-v: Untangle stimers and timesync from clocksources
clocksource/drivers/timer-microchip-pit64b: Fix sparse warning
clocksource/drivers/exynos_mct: Rename Exynos to lowercase
clocksource/drivers/timer-ti-dm: Fix uninitialized pointer access
clocksource/drivers/timer-ti-dm: Switch to platform_get_irq
clocksource/drivers/timer-ti-dm: Convert to devm_platform_ioremap_resource
clocksource/drivers/em_sti: Fix variable declaration in em_sti_probe
clocksource/drivers/em_sti: Convert to devm_platform_ioremap_resource
clocksource/drivers/bcm2835_timer: Fix memory leak of timer
clocksource/drivers/cadence-ttc: Use ttc driver as platform driver
clocksource/drivers/timer-microchip-pit64b: Add Microchip PIT64B support
clocksource/drivers/hyper-v: Reserve PAGE_SIZE space for tsc page
...
Diffstat (limited to 'include')
-rw-r--r-- | include/clocksource/hyperv_timer.h | 2 | ||||
-rw-r--r-- | include/linux/alarmtimer.h | 4 | ||||
-rw-r--r-- | include/linux/hrtimer.h | 3 | ||||
-rw-r--r-- | include/linux/nsproxy.h | 2 | ||||
-rw-r--r-- | include/linux/proc_ns.h | 3 | ||||
-rw-r--r-- | include/linux/time.h | 6 | ||||
-rw-r--r-- | include/linux/time_namespace.h | 133 | ||||
-rw-r--r-- | include/linux/user_namespace.h | 1 | ||||
-rw-r--r-- | include/uapi/linux/sched.h | 6 | ||||
-rw-r--r-- | include/vdso/datapage.h | 19 | ||||
-rw-r--r-- | include/vdso/helpers.h | 2 |
11 files changed, 176 insertions, 5 deletions
diff --git a/include/clocksource/hyperv_timer.h b/include/clocksource/hyperv_timer.h index 553e539469f0..34eef083c988 100644 --- a/include/clocksource/hyperv_timer.h +++ b/include/clocksource/hyperv_timer.h @@ -30,7 +30,7 @@ extern void hv_stimer_global_cleanup(void); extern void hv_stimer0_isr(void); #ifdef CONFIG_HYPERV_TIMER -extern struct clocksource *hyperv_cs; +extern u64 (*hv_read_reference_counter)(void); extern void hv_init_clocksource(void); extern struct ms_hyperv_tsc_page *hv_get_tsc_page(void); diff --git a/include/linux/alarmtimer.h b/include/linux/alarmtimer.h index 74748e306f4b..05e758b8b894 100644 --- a/include/linux/alarmtimer.h +++ b/include/linux/alarmtimer.h @@ -60,7 +60,11 @@ u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval); u64 alarm_forward_now(struct alarm *alarm, ktime_t interval); ktime_t alarm_expires_remaining(const struct alarm *alarm); +#ifdef CONFIG_RTC_CLASS /* Provide way to access the rtc device being used by alarmtimers */ struct rtc_device *alarmtimer_get_rtcdev(void); +#else +static inline struct rtc_device *alarmtimer_get_rtcdev(void) { return NULL; } +#endif #endif diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 1f98b52118f0..15c8ac313678 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -508,8 +508,7 @@ static inline u64 hrtimer_forward_now(struct hrtimer *timer, /* Precise sleep: */ extern int nanosleep_copyout(struct restart_block *, struct timespec64 *); -extern long hrtimer_nanosleep(const struct timespec64 *rqtp, - const enum hrtimer_mode mode, +extern long hrtimer_nanosleep(ktime_t rqtp, const enum hrtimer_mode mode, const clockid_t clockid); extern int schedule_hrtimeout_range(ktime_t *expires, u64 delta, diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h index 2ae1b1a4d84d..074f395b9ad2 100644 --- a/include/linux/nsproxy.h +++ b/include/linux/nsproxy.h @@ -35,6 +35,8 @@ struct nsproxy { struct mnt_namespace *mnt_ns; struct pid_namespace *pid_ns_for_children; struct net *net_ns; + struct time_namespace *time_ns; + struct time_namespace *time_ns_for_children; struct cgroup_namespace *cgroup_ns; }; extern struct nsproxy init_nsproxy; diff --git a/include/linux/proc_ns.h b/include/linux/proc_ns.h index d31cb6215905..d312e6281e69 100644 --- a/include/linux/proc_ns.h +++ b/include/linux/proc_ns.h @@ -32,6 +32,8 @@ extern const struct proc_ns_operations pidns_for_children_operations; extern const struct proc_ns_operations userns_operations; extern const struct proc_ns_operations mntns_operations; extern const struct proc_ns_operations cgroupns_operations; +extern const struct proc_ns_operations timens_operations; +extern const struct proc_ns_operations timens_for_children_operations; /* * We always define these enumerators @@ -43,6 +45,7 @@ enum { PROC_USER_INIT_INO = 0xEFFFFFFDU, PROC_PID_INIT_INO = 0xEFFFFFFCU, PROC_CGROUP_INIT_INO = 0xEFFFFFFBU, + PROC_TIME_INIT_INO = 0xEFFFFFFAU, }; #ifdef CONFIG_PROC_FS diff --git a/include/linux/time.h b/include/linux/time.h index 8e10b9dbd8c2..8ef5e5cc9f57 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -110,4 +110,10 @@ static inline bool itimerspec64_valid(const struct itimerspec64 *its) * Equivalent to !(time_before32(@t, @l) || time_after32(@t, @h)). */ #define time_between32(t, l, h) ((u32)(h) - (u32)(l) >= (u32)(t) - (u32)(l)) + +struct timens_offset { + s64 sec; + u64 nsec; +}; + #endif diff --git a/include/linux/time_namespace.h b/include/linux/time_namespace.h new file mode 100644 index 000000000000..824d54e057eb --- /dev/null +++ b/include/linux/time_namespace.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_TIMENS_H +#define _LINUX_TIMENS_H + + +#include <linux/sched.h> +#include <linux/kref.h> +#include <linux/nsproxy.h> +#include <linux/ns_common.h> +#include <linux/err.h> + +struct user_namespace; +extern struct user_namespace init_user_ns; + +struct timens_offsets { + struct timespec64 monotonic; + struct timespec64 boottime; +}; + +struct time_namespace { + struct kref kref; + struct user_namespace *user_ns; + struct ucounts *ucounts; + struct ns_common ns; + struct timens_offsets offsets; + struct page *vvar_page; + /* If set prevents changing offsets after any task joined namespace. */ + bool frozen_offsets; +} __randomize_layout; + +extern struct time_namespace init_time_ns; + +#ifdef CONFIG_TIME_NS +extern int vdso_join_timens(struct task_struct *task, + struct time_namespace *ns); + +static inline struct time_namespace *get_time_ns(struct time_namespace *ns) +{ + kref_get(&ns->kref); + return ns; +} + +struct time_namespace *copy_time_ns(unsigned long flags, + struct user_namespace *user_ns, + struct time_namespace *old_ns); +void free_time_ns(struct kref *kref); +int timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk); +struct vdso_data *arch_get_vdso_data(void *vvar_page); + +static inline void put_time_ns(struct time_namespace *ns) +{ + kref_put(&ns->kref, free_time_ns); +} + +void proc_timens_show_offsets(struct task_struct *p, struct seq_file *m); + +struct proc_timens_offset { + int clockid; + struct timespec64 val; +}; + +int proc_timens_set_offset(struct file *file, struct task_struct *p, + struct proc_timens_offset *offsets, int n); + +static inline void timens_add_monotonic(struct timespec64 *ts) +{ + struct timens_offsets *ns_offsets = ¤t->nsproxy->time_ns->offsets; + + *ts = timespec64_add(*ts, ns_offsets->monotonic); +} + +static inline void timens_add_boottime(struct timespec64 *ts) +{ + struct timens_offsets *ns_offsets = ¤t->nsproxy->time_ns->offsets; + + *ts = timespec64_add(*ts, ns_offsets->boottime); +} + +ktime_t do_timens_ktime_to_host(clockid_t clockid, ktime_t tim, + struct timens_offsets *offsets); + +static inline ktime_t timens_ktime_to_host(clockid_t clockid, ktime_t tim) +{ + struct time_namespace *ns = current->nsproxy->time_ns; + + if (likely(ns == &init_time_ns)) + return tim; + + return do_timens_ktime_to_host(clockid, tim, &ns->offsets); +} + +#else +static inline int vdso_join_timens(struct task_struct *task, + struct time_namespace *ns) +{ + return 0; +} + +static inline struct time_namespace *get_time_ns(struct time_namespace *ns) +{ + return NULL; +} + +static inline void put_time_ns(struct time_namespace *ns) +{ +} + +static inline +struct time_namespace *copy_time_ns(unsigned long flags, + struct user_namespace *user_ns, + struct time_namespace *old_ns) +{ + if (flags & CLONE_NEWTIME) + return ERR_PTR(-EINVAL); + + return old_ns; +} + +static inline int timens_on_fork(struct nsproxy *nsproxy, + struct task_struct *tsk) +{ + return 0; +} + +static inline void timens_add_monotonic(struct timespec64 *ts) { } +static inline void timens_add_boottime(struct timespec64 *ts) { } +static inline ktime_t timens_ktime_to_host(clockid_t clockid, ktime_t tim) +{ + return tim; +} +#endif + +#endif /* _LINUX_TIMENS_H */ diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index fb9f4f799554..6ef1c7109fc4 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -45,6 +45,7 @@ enum ucount_type { UCOUNT_NET_NAMESPACES, UCOUNT_MNT_NAMESPACES, UCOUNT_CGROUP_NAMESPACES, + UCOUNT_TIME_NAMESPACES, #ifdef CONFIG_INOTIFY_USER UCOUNT_INOTIFY_INSTANCES, UCOUNT_INOTIFY_WATCHES, diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h index 4a0217832464..2e3bc22c6f20 100644 --- a/include/uapi/linux/sched.h +++ b/include/uapi/linux/sched.h @@ -36,6 +36,12 @@ /* Flags for the clone3() syscall. */ #define CLONE_CLEAR_SIGHAND 0x100000000ULL /* Clear any signal handler and reset to SIG_DFL. */ +/* + * cloning flags intersect with CSIGNAL so can be used with unshare and clone3 + * syscalls only: + */ +#define CLONE_NEWTIME 0x00000080 /* New time namespace */ + #ifndef __ASSEMBLY__ /** * struct clone_args - arguments for the clone3 syscall diff --git a/include/vdso/datapage.h b/include/vdso/datapage.h index 2e302c0f41f7..c5f347cc5e55 100644 --- a/include/vdso/datapage.h +++ b/include/vdso/datapage.h @@ -21,6 +21,8 @@ #define CS_RAW 1 #define CS_BASES (CS_RAW + 1) +#define VCLOCK_TIMENS UINT_MAX + /** * struct vdso_timestamp - basetime per clock_id * @sec: seconds @@ -48,6 +50,7 @@ struct vdso_timestamp { * @mult: clocksource multiplier * @shift: clocksource shift * @basetime[clock_id]: basetime per clock_id + * @offset[clock_id]: time namespace offset per clock_id * @tz_minuteswest: minutes west of Greenwich * @tz_dsttime: type of DST correction * @hrtimer_res: hrtimer resolution @@ -55,6 +58,17 @@ struct vdso_timestamp { * * vdso_data will be accessed by 64 bit and compat code at the same time * so we should be careful before modifying this structure. + * + * @basetime is used to store the base time for the system wide time getter + * VVAR page. + * + * @offset is used by the special time namespace VVAR pages which are + * installed instead of the real VVAR page. These namespace pages must set + * @seq to 1 and @clock_mode to VLOCK_TIMENS to force the code into the + * time namespace slow path. The namespace aware functions retrieve the + * real system wide VVAR page, read host time and add the per clock offset. + * For clocks which are not affected by time namespace adjustment the + * offset must be zero. */ struct vdso_data { u32 seq; @@ -65,7 +79,10 @@ struct vdso_data { u32 mult; u32 shift; - struct vdso_timestamp basetime[VDSO_BASES]; + union { + struct vdso_timestamp basetime[VDSO_BASES]; + struct timens_offset offset[VDSO_BASES]; + }; s32 tz_minuteswest; s32 tz_dsttime; diff --git a/include/vdso/helpers.h b/include/vdso/helpers.h index 01641dbb68ef..9a2af9fca45e 100644 --- a/include/vdso/helpers.h +++ b/include/vdso/helpers.h @@ -10,7 +10,7 @@ static __always_inline u32 vdso_read_begin(const struct vdso_data *vd) { u32 seq; - while ((seq = READ_ONCE(vd->seq)) & 1) + while (unlikely((seq = READ_ONCE(vd->seq)) & 1)) cpu_relax(); smp_rmb(); |