History

Merge tag 'time-namespace-v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux

Pull time namespace updates from Christian Brauner: "When time namespaces were introduced we missed to virtualize the 'btime' field in /proc/stat. This confuses tasks which are in another time namespace with a virtualized boottime which is common in some container workloads. This contains Michael's series to fix 'btime' which Thomas asked me to take through my tree. To fix 'btime' virtualization we simply subtract the offset of the time namespace's boottime from btime before printing the stats. Note that since start_boottime of processes are seconds since boottime and the boottime stamp is now shifted according to the time namespace's offset, the offset of the time namespace also needs to be applied before the process stats are given to userspace. This avoids that processes shown by tools such as 'ps' appear as time travelers in the corresponding time namespace. Selftests are included to verify that btime virtualization in /proc/stat works as expected" * tag 'time-namespace-v5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/brauner/linux: namespace: make timens_on_fork() return nothing selftests/timens: added selftest for /proc/stat btime fs/proc: apply the time namespace offset to /proc/stat btime timens: additional helper functions for boottime offset handling

Linus Torvalds committed 5 years ago

Commit 6d93a192 parents 0ca2ce8 5c626346 files · +92 −16Browse files

Unified · Split

6 files
  1. fs/proc/array.c +4 −2
  2. fs/proc/stat.c +3 −0
  3. include/linux/time_namespace.h +25 −3
  4. kernel/nsproxy.c +1 −6
  5. kernel/time/namespace.c +2 −4
  6. tools/testing/selftests/timens/procfs.c +57 −1
fs/proc/array.c+4−2
@@ -56,6 +56,7 @@ · 55 unmodified lines
56#include <linux/types.h>56#include <linux/types.h>
57#include <linux/errno.h>57#include <linux/errno.h>
58#include <linux/time.h>58#include <linux/time.h>
59#include <linux/time_namespace.h>
59#include <linux/kernel.h>60#include <linux/kernel.h>
60#include <linux/kernel_stat.h>61#include <linux/kernel_stat.h>
61#include <linux/tty.h>62#include <linux/tty.h>
@@ -533,8 +534,9 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, · 471 unmodified lines
533 priority = task_prio(task);534 priority = task_prio(task);
534 nice = task_nice(task);535 nice = task_nice(task);
535 536
536 /* convert nsec -> ticks */537 /* apply timens offset for boottime and convert nsec -> ticks */
537 start_time = nsec_to_clock_t(task->start_boottime);538 start_time =
539 nsec_to_clock_t(timens_add_boottime_ns(task->start_boottime));
538 540
539 seq_put_decimal_ull(m, "", pid_nr_ns(pid, ns));541 seq_put_decimal_ull(m, "", pid_nr_ns(pid, ns));
540 seq_puts(m, " (");542 seq_puts(m, " (");
fs/proc/stat.c+3−0
@@ -10,6 +10,7 @@ · 9 unmodified lines
10#include <linux/seq_file.h>10#include <linux/seq_file.h>
11#include <linux/slab.h>11#include <linux/slab.h>
12#include <linux/time.h>12#include <linux/time.h>
13#include <linux/time_namespace.h>
13#include <linux/irqnr.h>14#include <linux/irqnr.h>
14#include <linux/sched/cputime.h>15#include <linux/sched/cputime.h>
15#include <linux/tick.h>16#include <linux/tick.h>
@@ -118,6 +119,8 @@ static int show_stat(struct seq_file *p, void *v) · 102 unmodified lines
118 irq = softirq = steal = 0;119 irq = softirq = steal = 0;
119 guest = guest_nice = 0;120 guest = guest_nice = 0;
120 getboottime64(&boottime);121 getboottime64(&boottime);
122 /* shift boot timestamp according to the timens offset */
123 timens_sub_boottime(&boottime);
121 124
122 for_each_possible_cpu(i) {125 for_each_possible_cpu(i) {
123 struct kernel_cpustat kcpustat;126 struct kernel_cpustat kcpustat;
include/linux/time_namespace.h+25−3
@@ -45,7 +45,7 @@ struct time_namespace *copy_time_ns(unsigned long flags, · 44 unmodified lines
45 struct user_namespace *user_ns,45 struct user_namespace *user_ns,
46 struct time_namespace *old_ns);46 struct time_namespace *old_ns);
47void free_time_ns(struct kref *kref);47void free_time_ns(struct kref *kref);
48int timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk);48void timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk);
49struct vdso_data *arch_get_vdso_data(void *vvar_page);49struct vdso_data *arch_get_vdso_data(void *vvar_page);
50 50
51static inline void put_time_ns(struct time_namespace *ns)51static inline void put_time_ns(struct time_namespace *ns)
@@ -77,6 +77,20 @@ static inline void timens_add_boottime(struct timespec64 *ts) · 25 unmodified lines
77 *ts = timespec64_add(*ts, ns_offsets->boottime);77 *ts = timespec64_add(*ts, ns_offsets->boottime);
78}78}
79 79
80static inline u64 timens_add_boottime_ns(u64 nsec)
81{
82 struct timens_offsets *ns_offsets = &current->nsproxy->time_ns->offsets;
83
84 return nsec + timespec64_to_ns(&ns_offsets->boottime);
85}
86
87static inline void timens_sub_boottime(struct timespec64 *ts)
88{
89 struct timens_offsets *ns_offsets = &current->nsproxy->time_ns->offsets;
90
91 *ts = timespec64_sub(*ts, ns_offsets->boottime);
92}
93
80ktime_t do_timens_ktime_to_host(clockid_t clockid, ktime_t tim,94ktime_t do_timens_ktime_to_host(clockid_t clockid, ktime_t tim,
81 struct timens_offsets *offsets);95 struct timens_offsets *offsets);
82 96
@@ -122,14 +136,22 @@ struct time_namespace *copy_time_ns(unsigned long flags, · 39 unmodified lines
122 return old_ns;136 return old_ns;
123}137}
124 138
125static inline int timens_on_fork(struct nsproxy *nsproxy,139static inline void timens_on_fork(struct nsproxy *nsproxy,
126 struct task_struct *tsk)140 struct task_struct *tsk)
127{141{
128 return 0;142 return;
129}143}
130 144
131static inline void timens_add_monotonic(struct timespec64 *ts) { }145static inline void timens_add_monotonic(struct timespec64 *ts) { }
132static inline void timens_add_boottime(struct timespec64 *ts) { }146static inline void timens_add_boottime(struct timespec64 *ts) { }
147
148static inline u64 timens_add_boottime_ns(u64 nsec)
149{
150 return nsec;
151}
152
153static inline void timens_sub_boottime(struct timespec64 *ts) { }
154
133static inline ktime_t timens_ktime_to_host(clockid_t clockid, ktime_t tim)155static inline ktime_t timens_ktime_to_host(clockid_t clockid, ktime_t tim)
134{156{
135 return tim;157 return tim;
kernel/nsproxy.c+1−6
@@ -153,7 +153,6 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) · 152 unmodified lines
153 struct nsproxy *old_ns = tsk->nsproxy;153 struct nsproxy *old_ns = tsk->nsproxy;
154 struct user_namespace *user_ns = task_cred_xxx(tsk, user_ns);154 struct user_namespace *user_ns = task_cred_xxx(tsk, user_ns);
155 struct nsproxy *new_ns;155 struct nsproxy *new_ns;
156 int ret;
157 156
158 if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |157 if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
159 CLONE_NEWPID | CLONE_NEWNET |158 CLONE_NEWPID | CLONE_NEWNET |
@@ -180,11 +179,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) · 20 unmodified lines
180 if (IS_ERR(new_ns))179 if (IS_ERR(new_ns))
181 return PTR_ERR(new_ns);180 return PTR_ERR(new_ns);
182 181
183 ret = timens_on_fork(new_ns, tsk);182 timens_on_fork(new_ns, tsk);
184 if (ret) {
185 free_nsproxy(new_ns);
186 return ret;
187 }
188 183
189 tsk->nsproxy = new_ns;184 tsk->nsproxy = new_ns;
190 return 0;185 return 0;
kernel/time/namespace.c+2−4
@@ -308,22 +308,20 @@ static int timens_install(struct nsset *nsset, struct ns_common *new) · 307 unmodified lines
308 return 0;308 return 0;
309}309}
310 310
311int timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk)311void timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk)
312{312{
313 struct ns_common *nsc = &nsproxy->time_ns_for_children->ns;313 struct ns_common *nsc = &nsproxy->time_ns_for_children->ns;
314 struct time_namespace *ns = to_time_ns(nsc);314 struct time_namespace *ns = to_time_ns(nsc);
315 315
316 /* create_new_namespaces() already incremented the ref counter */316 /* create_new_namespaces() already incremented the ref counter */
317 if (nsproxy->time_ns == nsproxy->time_ns_for_children)317 if (nsproxy->time_ns == nsproxy->time_ns_for_children)
318 return 0;318 return;
319 319
320 get_time_ns(ns);320 get_time_ns(ns);
321 put_time_ns(nsproxy->time_ns);321 put_time_ns(nsproxy->time_ns);
322 nsproxy->time_ns = ns;322 nsproxy->time_ns = ns;
323 323
324 timens_commit(tsk, ns);324 timens_commit(tsk, ns);
325
326 return 0;
327}325}
328 326
329static struct user_namespace *timens_owner(struct ns_common *ns)327static struct user_namespace *timens_owner(struct ns_common *ns)
tools/testing/selftests/timens/procfs.c+57−1
@@ -93,6 +93,33 @@ static int read_proc_uptime(struct timespec *uptime) · 92 unmodified lines
93 return 0;93 return 0;
94}94}
95 95
96static int read_proc_stat_btime(unsigned long long *boottime_sec)
97{
98 FILE *proc;
99 char line_buf[2048];
100
101 proc = fopen("/proc/stat", "r");
102 if (proc == NULL) {
103 pr_perror("Unable to open /proc/stat");
104 return -1;
105 }
106
107 while (fgets(line_buf, 2048, proc)) {
108 if (sscanf(line_buf, "btime %llu", boottime_sec) != 1)
109 continue;
110 fclose(proc);
111 return 0;
112 }
113 if (errno) {
114 pr_perror("fscanf");
115 fclose(proc);
116 return -errno;
117 }
118 pr_err("failed to parse /proc/stat");
119 fclose(proc);
120 return -1;
121}
122
96static int check_uptime(void)123static int check_uptime(void)
97{124{
98 struct timespec uptime_new, uptime_old;125 struct timespec uptime_new, uptime_old;
@@ -123,18 +150,47 @@ static int check_uptime(void) · 24 unmodified lines
123 return 0;150 return 0;
124}151}
125 152
153static int check_stat_btime(void)
154{
155 unsigned long long btime_new, btime_old;
156 unsigned long long btime_expected;
157
158 if (switch_ns(parent_ns))
159 return pr_err("switch_ns(%d)", parent_ns);
160
161 if (read_proc_stat_btime(&btime_old))
162 return 1;
163
164 if (switch_ns(child_ns))
165 return pr_err("switch_ns(%d)", child_ns);
166
167 if (read_proc_stat_btime(&btime_new))
168 return 1;
169
170 btime_expected = btime_old - TEN_DAYS_IN_SEC;
171 if (btime_new != btime_expected) {
172 pr_fail("btime in /proc/stat: old %llu, new %llu [%llu]",
173 btime_old, btime_new, btime_expected);
174 return 1;
175 }
176
177 ksft_test_result_pass("Passed for /proc/stat btime\n");
178 return 0;
179}
180
126int main(int argc, char *argv[])181int main(int argc, char *argv[])
127{182{
128 int ret = 0;183 int ret = 0;
129 184
130 nscheck();185 nscheck();
131 186
132 ksft_set_plan(1);187 ksft_set_plan(2);
133 188
134 if (init_namespaces())189 if (init_namespaces())
135 return 1;190 return 1;
136 191
137 ret |= check_uptime();192 ret |= check_uptime();
193 ret |= check_stat_btime();
138 194
139 if (ret)195 if (ret)
140 ksft_exit_fail();196 ksft_exit_fail();

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t