diff options
-rw-r--r-- | fs/proc/array.c | 6 | ||||
-rw-r--r-- | fs/proc/stat.c | 3 | ||||
-rw-r--r-- | include/linux/time_namespace.h | 28 | ||||
-rw-r--r-- | kernel/nsproxy.c | 7 | ||||
-rw-r--r-- | kernel/time/namespace.c | 6 | ||||
-rw-r--r-- | tools/testing/selftests/timens/procfs.c | 58 |
6 files changed, 92 insertions, 16 deletions
diff --git a/fs/proc/array.c b/fs/proc/array.c index 65ec2029fa80..277f654f289e 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -56,6 +56,7 @@ #include <linux/types.h> #include <linux/errno.h> #include <linux/time.h> +#include <linux/time_namespace.h> #include <linux/kernel.h> #include <linux/kernel_stat.h> #include <linux/tty.h> @@ -533,8 +534,9 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, priority = task_prio(task); nice = task_nice(task); - /* convert nsec -> ticks */ - start_time = nsec_to_clock_t(task->start_boottime); + /* apply timens offset for boottime and convert nsec -> ticks */ + start_time = + nsec_to_clock_t(timens_add_boottime_ns(task->start_boottime)); seq_put_decimal_ull(m, "", pid_nr_ns(pid, ns)); seq_puts(m, " ("); diff --git a/fs/proc/stat.c b/fs/proc/stat.c index 4695b6de3151..f25e8531fd27 100644 --- a/fs/proc/stat.c +++ b/fs/proc/stat.c @@ -10,6 +10,7 @@ #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/time.h> +#include <linux/time_namespace.h> #include <linux/irqnr.h> #include <linux/sched/cputime.h> #include <linux/tick.h> @@ -118,6 +119,8 @@ static int show_stat(struct seq_file *p, void *v) irq = softirq = steal = 0; guest = guest_nice = 0; getboottime64(&boottime); + /* shift boot timestamp according to the timens offset */ + timens_sub_boottime(&boottime); for_each_possible_cpu(i) { struct kernel_cpustat kcpustat; diff --git a/include/linux/time_namespace.h b/include/linux/time_namespace.h index 5b6031385db0..30312166e70a 100644 --- a/include/linux/time_namespace.h +++ b/include/linux/time_namespace.h @@ -45,7 +45,7 @@ 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); +void 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) @@ -77,6 +77,20 @@ static inline void timens_add_boottime(struct timespec64 *ts) *ts = timespec64_add(*ts, ns_offsets->boottime); } +static inline u64 timens_add_boottime_ns(u64 nsec) +{ + struct timens_offsets *ns_offsets = ¤t->nsproxy->time_ns->offsets; + + return nsec + timespec64_to_ns(&ns_offsets->boottime); +} + +static inline void timens_sub_boottime(struct timespec64 *ts) +{ + struct timens_offsets *ns_offsets = ¤t->nsproxy->time_ns->offsets; + + *ts = timespec64_sub(*ts, ns_offsets->boottime); +} + ktime_t do_timens_ktime_to_host(clockid_t clockid, ktime_t tim, struct timens_offsets *offsets); @@ -122,14 +136,22 @@ struct time_namespace *copy_time_ns(unsigned long flags, return old_ns; } -static inline int timens_on_fork(struct nsproxy *nsproxy, +static inline void timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk) { - return 0; + return; } static inline void timens_add_monotonic(struct timespec64 *ts) { } static inline void timens_add_boottime(struct timespec64 *ts) { } + +static inline u64 timens_add_boottime_ns(u64 nsec) +{ + return nsec; +} + +static inline void timens_sub_boottime(struct timespec64 *ts) { } + static inline ktime_t timens_ktime_to_host(clockid_t clockid, ktime_t tim) { return tim; diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index 12dd41b39a7f..e2e6c5dc433f 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -153,7 +153,6 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) struct nsproxy *old_ns = tsk->nsproxy; struct user_namespace *user_ns = task_cred_xxx(tsk, user_ns); struct nsproxy *new_ns; - int ret; if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNET | @@ -180,11 +179,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) if (IS_ERR(new_ns)) return PTR_ERR(new_ns); - ret = timens_on_fork(new_ns, tsk); - if (ret) { - free_nsproxy(new_ns); - return ret; - } + timens_on_fork(new_ns, tsk); tsk->nsproxy = new_ns; return 0; diff --git a/kernel/time/namespace.c b/kernel/time/namespace.c index afc65e6be33e..e0f9509b17c3 100644 --- a/kernel/time/namespace.c +++ b/kernel/time/namespace.c @@ -308,22 +308,20 @@ static int timens_install(struct nsset *nsset, struct ns_common *new) return 0; } -int timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk) +void timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk) { struct ns_common *nsc = &nsproxy->time_ns_for_children->ns; struct time_namespace *ns = to_time_ns(nsc); /* create_new_namespaces() already incremented the ref counter */ if (nsproxy->time_ns == nsproxy->time_ns_for_children) - return 0; + return; get_time_ns(ns); put_time_ns(nsproxy->time_ns); nsproxy->time_ns = ns; timens_commit(tsk, ns); - - return 0; } static struct user_namespace *timens_owner(struct ns_common *ns) diff --git a/tools/testing/selftests/timens/procfs.c b/tools/testing/selftests/timens/procfs.c index 7f14f0fdac84..f2519154208a 100644 --- a/tools/testing/selftests/timens/procfs.c +++ b/tools/testing/selftests/timens/procfs.c @@ -93,6 +93,33 @@ static int read_proc_uptime(struct timespec *uptime) return 0; } +static int read_proc_stat_btime(unsigned long long *boottime_sec) +{ + FILE *proc; + char line_buf[2048]; + + proc = fopen("/proc/stat", "r"); + if (proc == NULL) { + pr_perror("Unable to open /proc/stat"); + return -1; + } + + while (fgets(line_buf, 2048, proc)) { + if (sscanf(line_buf, "btime %llu", boottime_sec) != 1) + continue; + fclose(proc); + return 0; + } + if (errno) { + pr_perror("fscanf"); + fclose(proc); + return -errno; + } + pr_err("failed to parse /proc/stat"); + fclose(proc); + return -1; +} + static int check_uptime(void) { struct timespec uptime_new, uptime_old; @@ -123,18 +150,47 @@ static int check_uptime(void) return 0; } +static int check_stat_btime(void) +{ + unsigned long long btime_new, btime_old; + unsigned long long btime_expected; + + if (switch_ns(parent_ns)) + return pr_err("switch_ns(%d)", parent_ns); + + if (read_proc_stat_btime(&btime_old)) + return 1; + + if (switch_ns(child_ns)) + return pr_err("switch_ns(%d)", child_ns); + + if (read_proc_stat_btime(&btime_new)) + return 1; + + btime_expected = btime_old - TEN_DAYS_IN_SEC; + if (btime_new != btime_expected) { + pr_fail("btime in /proc/stat: old %llu, new %llu [%llu]", + btime_old, btime_new, btime_expected); + return 1; + } + + ksft_test_result_pass("Passed for /proc/stat btime\n"); + return 0; +} + int main(int argc, char *argv[]) { int ret = 0; nscheck(); - ksft_set_plan(1); + ksft_set_plan(2); if (init_namespaces()) return 1; ret |= check_uptime(); + ret |= check_stat_btime(); if (ret) ksft_exit_fail(); |