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
5656 #include <linux/types.h>
5757 #include <linux/errno.h>
5858 #include <linux/time.h>
59+#include <linux/time_namespace.h>
5960 #include <linux/kernel.h>
6061 #include <linux/kernel_stat.h>
6162 #include <linux/tty.h>
@@ -533,8 +534,9 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, · 471 unmodified lines
533534 priority = task_prio(task);
534535 nice = task_nice(task);
535536
536− /* convert nsec -> ticks */
537− start_time = nsec_to_clock_t(task->start_boottime);
537+ /* apply timens offset for boottime and convert nsec -> ticks */
538+ start_time =
539+ nsec_to_clock_t(timens_add_boottime_ns(task->start_boottime));
538540
539541 seq_put_decimal_ull(m, "", pid_nr_ns(pid, ns));
540542 seq_puts(m, " (");
fs/proc/stat.c+3−0
@@ -10,6 +10,7 @@ · 9 unmodified lines
1010 #include <linux/seq_file.h>
1111 #include <linux/slab.h>
1212 #include <linux/time.h>
13+#include <linux/time_namespace.h>
1314 #include <linux/irqnr.h>
1415 #include <linux/sched/cputime.h>
1516 #include <linux/tick.h>
@@ -118,6 +119,8 @@ static int show_stat(struct seq_file *p, void *v) · 102 unmodified lines
118119 irq = softirq = steal = 0;
119120 guest = guest_nice = 0;
120121 getboottime64(&boottime);
122+ /* shift boot timestamp according to the timens offset */
123+ timens_sub_boottime(&boottime);
121124
122125 for_each_possible_cpu(i) {
123126 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
4545 struct user_namespace *user_ns,
4646 struct time_namespace *old_ns);
4747 void free_time_ns(struct kref *kref);
48−int timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk);
48+void timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk);
4949 struct vdso_data *arch_get_vdso_data(void *vvar_page);
5050
5151 static 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
7777 *ts = timespec64_add(*ts, ns_offsets->boottime);
7878 }
7979
80+static 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+
87+static 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+
8094 ktime_t do_timens_ktime_to_host(clockid_t clockid, ktime_t tim,
8195 struct timens_offsets *offsets);
8296
@@ -122,14 +136,22 @@ struct time_namespace *copy_time_ns(unsigned long flags, · 39 unmodified lines
122136 return old_ns;
123137 }
124138
125−static inline int timens_on_fork(struct nsproxy *nsproxy,
139+static inline void timens_on_fork(struct nsproxy *nsproxy,
126140 struct task_struct *tsk)
127141 {
128− return 0;
142+ return;
129143 }
130144
131145 static inline void timens_add_monotonic(struct timespec64 *ts) { }
132146 static inline void timens_add_boottime(struct timespec64 *ts) { }
147+
148+static inline u64 timens_add_boottime_ns(u64 nsec)
149+{
150+ return nsec;
151+}
152+
153+static inline void timens_sub_boottime(struct timespec64 *ts) { }
154+
133155 static inline ktime_t timens_ktime_to_host(clockid_t clockid, ktime_t tim)
134156 {
135157 return tim;
kernel/nsproxy.c+1−6
@@ -153,7 +153,6 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) · 152 unmodified lines
153153 struct nsproxy *old_ns = tsk->nsproxy;
154154 struct user_namespace *user_ns = task_cred_xxx(tsk, user_ns);
155155 struct nsproxy *new_ns;
156− int ret;
157156
158157 if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
159158 CLONE_NEWPID | CLONE_NEWNET |
@@ -180,11 +179,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) · 20 unmodified lines
180179 if (IS_ERR(new_ns))
181180 return PTR_ERR(new_ns);
182181
183− ret = timens_on_fork(new_ns, tsk);
184− if (ret) {
185− free_nsproxy(new_ns);
186− return ret;
187− }
182+ timens_on_fork(new_ns, tsk);
188183
189184 tsk->nsproxy = new_ns;
190185 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
308308 return 0;
309309 }
310310
311−int timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk)
311+void timens_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk)
312312 {
313313 struct ns_common *nsc = &nsproxy->time_ns_for_children->ns;
314314 struct time_namespace *ns = to_time_ns(nsc);
315315
316316 /* create_new_namespaces() already incremented the ref counter */
317317 if (nsproxy->time_ns == nsproxy->time_ns_for_children)
318− return 0;
318+ return;
319319
320320 get_time_ns(ns);
321321 put_time_ns(nsproxy->time_ns);
322322 nsproxy->time_ns = ns;
323323
324324 timens_commit(tsk, ns);
325−
326− return 0;
327325 }
328326
329327 static 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
9393 return 0;
9494 }
9595
96+static 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+
96123 static int check_uptime(void)
97124 {
98125 struct timespec uptime_new, uptime_old;
@@ -123,18 +150,47 @@ static int check_uptime(void) · 24 unmodified lines
123150 return 0;
124151 }
125152
153+static 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+
126181 int main(int argc, char *argv[])
127182 {
128183 int ret = 0;
129184
130185 nscheck();
131186
132− ksft_set_plan(1);
187+ ksft_set_plan(2);
133188
134189 if (init_namespaces())
135190 return 1;
136191
137192 ret |= check_uptime();
193+ ret |= check_stat_btime();
138194
139195 if (ret)
140196 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