* Re: [RFC PATCH] glibc: Perform rseq(2) registration at nptl init and thread creation
From: Mathieu Desnoyers @ 2018-09-19 21:01 UTC (permalink / raw)
To: Szabolcs Nagy
Cc: carlos, Florian Weimer, nd, Thomas Gleixner, Ben Maurer,
Peter Zijlstra, Paul E. McKenney, Boqun Feng, Will Deacon,
Dave Watson, Paul Turner, libc-alpha, linux-kernel, linux-api
In-Reply-To: <c58d4d6e-f22a-f5d9-e23a-5bd72cec1a86@arm.com>
----- On Sep 19, 2018, at 1:38 PM, Szabolcs Nagy szabolcs.nagy@arm.com wrote:
> On 19/09/18 15:44, Mathieu Desnoyers wrote:
>> Things to consider:
>>
>> - Move __rseq_refcount to an extra field at the end of __rseq_abi to
>> eliminate one symbol. This would require to wrap struct rseq into
>> e.g. struct rseq_lib or such, e.g.:
>>
>> struct rseq_lib {
>> struct rseq kabi;
>> int refcount;
>> };
>>
>> All libraries/programs which try to register rseq (glibc, early-adopter
>> applications, early-adopter libraries) should use the rseq refcount.
>> It becomes part of the ABI within a user-space process, but it's not
>> part of the ABI shared with the kernel per se.
>>
>> - Restructure how this code is organized so glibc keeps building on
>> non-Linux targets.
>>
>> - We do not need an atomic increment/decrement for the refcount per
>> se. Just being atomic with respect to the current thread (and nested
>> signals) would be enough. What is the proper API to use there ?
>> Should we expose struct rseq_lib in a public glibc header ? Should
>> we create a rseq(3) man page ?
>>
>> - Revisit use of "weak" symbol for __rseq_abi in glibc. Perhaps we
>> want a non-weak symbol there ? (and let all other early user
>> libraries use weak)
>>
>
> i don't think there is precedent for exposing tls symbol in glibc
> (e.g. errno is exposed via __errno_location function) so there
> might be issues with this (but i don't have immediate concerns).
>
>> diff --git a/nptl/pthread_create.c b/nptl/pthread_create.c
>> index fe75d04113..20ee197d94 100644
>> --- a/nptl/pthread_create.c
>> +++ b/nptl/pthread_create.c
>> @@ -52,6 +52,13 @@ static struct pthread *__nptl_last_event __attribute_used__;
>> /* Number of threads running. */
>> unsigned int __nptl_nthreads = 1;
>>
>> +__attribute__((weak, tls_model("initial-exec"))) __thread
>> +volatile struct rseq __rseq_abi = {
>> + .cpu_id = RSEQ_CPU_ID_UNINITIALIZED,
>> +};
>> +
>> +__attribute__((weak, tls_model("initial-exec"))) __thread
>> +volatile int __rseq_refcount;
>>
>
> note that libpthread.so is built with -ftls-model=initial-exec
Which would indeed make these annotations redundant. I'll remove
them.
> (and if it wasn't then you'd want to put the attribute on the
> declaration in the internal header file, not on the definition,
> so the actual tls accesses generate the right code)
This area is one where I'm still uneasy on my comprehension of
the details, especially that it goes in a different direction than
what you are recommending.
I've read through https://www.akkadia.org/drepper/tls.pdf Section 5
"Linker Optimizations" to try to figure it out, and I end up being
under the impression that applying the tls_model("initial-exec")
attribute to a symbol declaration in a header file does not have
much impact on the accesses that use that variable. Reading through
that section, it seems that the variable definition is the one that
matters, and then the compiler/linker/loader are tweaking the sites
that reference the TLS variable through code rewrite based on the
most efficient mechanism that each phase knows can be used at each
stage.
What am I missing ?
Thanks,
Mathieu
--
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com
^ permalink raw reply
* [RFC 20/20] timens/selftest: Add timer offsets test
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Andrei Vagin, Dmitry Safonov, Adrian Reber,
Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86, linux-kselftest
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
From: Andrei Vagin <avagin@openvz.org>
Check that timer_create takes into account clock offsets.
Cc: linux-kselftest@vger.kernel.org
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
tools/testing/selftests/timens/.gitignore | 1 +
tools/testing/selftests/timens/Makefile | 3 +-
tools/testing/selftests/timens/timer.c | 95 +++++++++++++++++++++++++++++++
3 files changed, 98 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/timens/timer.c
diff --git a/tools/testing/selftests/timens/.gitignore b/tools/testing/selftests/timens/.gitignore
index 94ffdd9cead7..3b7eda8f35ce 100644
--- a/tools/testing/selftests/timens/.gitignore
+++ b/tools/testing/selftests/timens/.gitignore
@@ -1,4 +1,5 @@
clock_nanosleep
procfs
timens
+timer
timerfd
diff --git a/tools/testing/selftests/timens/Makefile b/tools/testing/selftests/timens/Makefile
index f96f50d1fef8..ae1ffd24cc43 100644
--- a/tools/testing/selftests/timens/Makefile
+++ b/tools/testing/selftests/timens/Makefile
@@ -1,5 +1,6 @@
-TEST_GEN_PROGS := timens timerfd clock_nanosleep procfs
+TEST_GEN_PROGS := timens timerfd timer clock_nanosleep procfs
CFLAGS := -Wall -Werror
+LDFLAGS := -lrt
include ../lib.mk
diff --git a/tools/testing/selftests/timens/timer.c b/tools/testing/selftests/timens/timer.c
new file mode 100644
index 000000000000..e3a0951aadc8
--- /dev/null
+++ b/tools/testing/selftests/timens/timer.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <sched.h>
+
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <signal.h>
+#include <time.h>
+
+#include "log.h"
+
+#ifndef CLONE_NEWTIME
+#define CLONE_NEWTIME 0x00001000 /* New time namespace */
+#endif
+
+int run_test(int clockid)
+{
+ struct itimerspec new_value;
+ struct timespec now;
+ long long elapsed;
+ timer_t fd;
+ int i;
+
+ if (clock_gettime(clockid, &now) == -1)
+ return pr_perror("clock_gettime");
+
+ for (i = 0; i < 2; i++) {
+ struct sigevent sevp = {.sigev_notify = SIGEV_NONE};
+ int flags = 0;
+
+ pr_msg("timerfd_settime: %d", "INFO", clockid);
+ new_value.it_value.tv_sec = 3600;
+ new_value.it_value.tv_nsec = 0;
+ new_value.it_interval.tv_sec = 1;
+ new_value.it_interval.tv_nsec = 0;
+
+ if (i == 1) {
+ new_value.it_value.tv_sec += now.tv_sec;
+ new_value.it_value.tv_nsec += now.tv_nsec;
+ }
+
+ if (timer_create(clockid, &sevp, &fd) == -1)
+ return pr_perror("timerfd_create");
+
+ if (i == 1)
+ flags |= TIMER_ABSTIME;
+ if (timer_settime(fd, flags, &new_value, NULL) == -1)
+ return pr_perror("timerfd_settime");
+
+ if (timer_gettime(fd, &new_value) == -1)
+ return pr_perror("timerfd_gettime");
+
+ elapsed = new_value.it_value.tv_sec;
+ if (abs(elapsed - 3600) > 60) {
+ pr_fail("elapsed: %lld\n", elapsed);
+ return 1;
+ }
+ }
+
+ printf("PASS\n");
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct timespec tp;
+ int ret;
+
+ if (unshare(CLONE_NEWTIME))
+ return pr_perror("unshare");
+
+ if (clock_gettime(CLOCK_MONOTONIC, &tp))
+ return pr_perror("clock_gettime");
+ tp.tv_sec -= 70 * 24 * 3600;
+ if (clock_settime(CLOCK_MONOTONIC, &tp))
+ return pr_perror("clock_settime");
+
+ if (clock_gettime(CLOCK_BOOTTIME, &tp))
+ return pr_perror("clock_gettime");
+ tp.tv_sec -= 9 * 24 * 3600;
+ tp.tv_nsec = 0;
+ if (clock_settime(CLOCK_BOOTTIME, &tp))
+ return pr_perror("clock_settime");
+
+ ret = 0;
+ ret |= run_test(CLOCK_BOOTTIME);
+ ret |= run_test(CLOCK_MONOTONIC);
+ return ret;
+}
+
--
2.13.6
^ permalink raw reply related
* [RFC 19/20] timens/selftest: Add procfs selftest
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86, linux-kselftest
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
Currently only uptime check, but procfs checks for REALTIME might be
added in future.
Cc: linux-kselftest@vger.kernel.org
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
tools/testing/selftests/timens/.gitignore | 1 +
tools/testing/selftests/timens/Makefile | 2 +-
tools/testing/selftests/timens/procfs.c | 145 ++++++++++++++++++++++++++++++
3 files changed, 147 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/timens/procfs.c
diff --git a/tools/testing/selftests/timens/.gitignore b/tools/testing/selftests/timens/.gitignore
index 9b6c8ddac2c8..94ffdd9cead7 100644
--- a/tools/testing/selftests/timens/.gitignore
+++ b/tools/testing/selftests/timens/.gitignore
@@ -1,3 +1,4 @@
clock_nanosleep
+procfs
timens
timerfd
diff --git a/tools/testing/selftests/timens/Makefile b/tools/testing/selftests/timens/Makefile
index 76a1dc891184..f96f50d1fef8 100644
--- a/tools/testing/selftests/timens/Makefile
+++ b/tools/testing/selftests/timens/Makefile
@@ -1,4 +1,4 @@
-TEST_GEN_PROGS := timens timerfd clock_nanosleep
+TEST_GEN_PROGS := timens timerfd clock_nanosleep procfs
CFLAGS := -Wall -Werror
diff --git a/tools/testing/selftests/timens/procfs.c b/tools/testing/selftests/timens/procfs.c
new file mode 100644
index 000000000000..5067cbbddcc5
--- /dev/null
+++ b/tools/testing/selftests/timens/procfs.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "log.h"
+
+#ifndef CLONE_NEWTIME
+# define CLONE_NEWTIME 0x00001000
+#endif
+
+/*
+ * Test shouldn't be run for a day, so add 10 days to child
+ * time and check parent's time to be in the same day.
+ */
+#define MAX_TEST_TIME_SEC (60*5)
+#define DAY_IN_SEC (60*60*24)
+#define TEN_DAYS_IN_SEC (10*DAY_IN_SEC)
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+static int child_ns, parent_ns;
+
+static int switch_ns(int fd)
+{
+ if (setns(fd, CLONE_NEWTIME))
+ return pr_perror("setns()");
+
+ return 0;
+}
+
+static int init_namespaces(void)
+{
+ char path[] = "/proc/self/ns/time";
+ struct stat st1, st2;
+
+ parent_ns = open(path, O_RDONLY);
+ if (parent_ns <= 0)
+ return pr_perror("Unable to open %s", path);
+
+ if (fstat(parent_ns, &st1))
+ return pr_perror("Unable to stat the parent timens");
+
+ if (unshare(CLONE_NEWTIME))
+ return pr_perror("Can't unshare() timens");
+
+ child_ns = open(path, O_RDONLY);
+ if (child_ns <= 0)
+ return pr_perror("Unable to open %s", path);
+
+ if (fstat(child_ns, &st2))
+ return pr_perror("Unable to stat the timens");
+
+ if (st1.st_ino == st2.st_ino)
+ return pr_err("The same child_ns after CLONE_NEWTIME");
+
+ return 0;
+}
+
+static int read_proc_uptime(struct timespec *uptime)
+{
+ unsigned long up_sec, up_nsec;
+ FILE *proc;
+
+ proc = fopen("/proc/uptime", "r");
+ if (proc == NULL) {
+ pr_perror("Unable to open /proc/uptime");
+ return -1;
+ }
+
+ if (fscanf(proc, "%lu.%02lu", &up_sec, &up_nsec) != 2) {
+ if (errno) {
+ pr_perror("fscanf");
+ return -errno;
+ }
+ pr_err("failed to parse /proc/uptime");
+ return -1;
+ }
+ fclose(proc);
+
+ uptime->tv_sec = up_sec;
+ uptime->tv_nsec = up_nsec;
+ return 0;
+}
+
+static int check_uptime(void)
+{
+ struct timespec ts_btime, uptime_new, uptime_old;
+ time_t uptime_expected;
+ double prec = MAX_TEST_TIME_SEC;
+
+ if (switch_ns(parent_ns))
+ return pr_err("switch_ns(%d)", parent_ns);
+
+ if (clock_gettime(CLOCK_BOOTTIME, &ts_btime))
+ return pr_perror("clock_gettime()");
+
+ if (read_proc_uptime(&uptime_old))
+ return 1;
+
+ ts_btime.tv_sec += TEN_DAYS_IN_SEC;
+
+ if (switch_ns(child_ns))
+ return pr_err("switch_ns(%d)", child_ns);
+
+ if (clock_settime(CLOCK_BOOTTIME, &ts_btime))
+ return pr_perror("clock_settime()");
+
+ if (read_proc_uptime(&uptime_new))
+ return 1;
+
+ uptime_expected = uptime_old.tv_sec + TEN_DAYS_IN_SEC;
+ if (fabs(difftime(uptime_new.tv_sec, uptime_expected)) > prec) {
+ pr_fail("uptime in /proc/uptime: old %ld, new %ld [%ld]",
+ uptime_old.tv_sec, uptime_new.tv_sec,
+ uptime_old.tv_sec + TEN_DAYS_IN_SEC);
+ return 1;
+ }
+
+ pr_msg("Passed for /proc/uptime", "OK");
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+
+ if (init_namespaces())
+ return 1;
+
+ ret |= check_uptime();
+
+ return ret;
+}
--
2.13.6
^ permalink raw reply related
* [RFC 18/20] selftest/timens: Add test for clock_nanosleep
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Andrei Vagin, Dmitry Safonov, Adrian Reber,
Andrei Vagin, Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86, linux-kselftest
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
From: Andrei Vagin <avagin@gmail.com>
Check that clock_nanosleep() takes into account clock offsets.
Cc: linux-kselftest@vger.kernel.org
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
tools/testing/selftests/timens/.gitignore | 1 +
tools/testing/selftests/timens/Makefile | 2 +-
tools/testing/selftests/timens/clock_nanosleep.c | 98 ++++++++++++++++++++++++
3 files changed, 100 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/timens/clock_nanosleep.c
diff --git a/tools/testing/selftests/timens/.gitignore b/tools/testing/selftests/timens/.gitignore
index b609f6ee9fb9..9b6c8ddac2c8 100644
--- a/tools/testing/selftests/timens/.gitignore
+++ b/tools/testing/selftests/timens/.gitignore
@@ -1,2 +1,3 @@
+clock_nanosleep
timens
timerfd
diff --git a/tools/testing/selftests/timens/Makefile b/tools/testing/selftests/timens/Makefile
index 66b90cd28e5c..76a1dc891184 100644
--- a/tools/testing/selftests/timens/Makefile
+++ b/tools/testing/selftests/timens/Makefile
@@ -1,4 +1,4 @@
-TEST_GEN_PROGS := timens timerfd
+TEST_GEN_PROGS := timens timerfd clock_nanosleep
CFLAGS := -Wall -Werror
diff --git a/tools/testing/selftests/timens/clock_nanosleep.c b/tools/testing/selftests/timens/clock_nanosleep.c
new file mode 100644
index 000000000000..5af780b4cfe0
--- /dev/null
+++ b/tools/testing/selftests/timens/clock_nanosleep.c
@@ -0,0 +1,98 @@
+#define _GNU_SOURCE
+#include <sched.h>
+
+#include <sys/timerfd.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "log.h"
+
+#ifndef CLONE_NEWTIME
+#define CLONE_NEWTIME 0x00001000
+#endif
+
+static long long get_elapsed_time(int clockid, struct timespec *start)
+{
+ struct timespec curr;
+ long long secs, nsecs;
+
+ if (clock_gettime(clockid, &curr) == -1)
+ return pr_perror("clock_gettime");
+
+ secs = curr.tv_sec - start->tv_sec;
+ nsecs = curr.tv_nsec - start->tv_nsec;
+ if (nsecs < 0) {
+ secs--;
+ nsecs += 1000000000;
+ }
+ if (nsecs > 1000000000) {
+ secs++;
+ nsecs -= 1000000000;
+ }
+ return secs * 1000 + nsecs / 1000000;
+}
+
+int run_test(int clockid)
+{
+ long long elapsed;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ struct timespec now = {};
+ struct timespec start;
+
+ if (clock_gettime(clockid, &start) == -1)
+ return pr_perror("clock_gettime");
+
+
+ if (i == 1) {
+ now.tv_sec = start.tv_sec;
+ now.tv_nsec = start.tv_nsec;
+ }
+
+ printf("clock_nanosleep: %d\n", clockid);
+ now.tv_sec += 2;
+ clock_nanosleep(clockid, i ? TIMER_ABSTIME : 0, &now, NULL);
+
+ elapsed = get_elapsed_time(clockid, &start);
+ if (elapsed < 1900 || elapsed > 2100) {
+ pr_fail("elapsed %lld\n", elapsed);
+ return 1;
+ }
+ }
+
+ printf("PASS\n");
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct timespec tp;
+ int ret;
+
+ if (unshare(CLONE_NEWTIME))
+ return pr_perror("unshare");;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &tp))
+ return pr_perror("clock_gettime");
+ tp.tv_sec += 7 * 24 * 3600;
+ if (clock_settime(CLOCK_MONOTONIC, &tp))
+ return pr_perror("clock_settime");
+
+ if (clock_gettime(CLOCK_BOOTTIME, &tp))
+ return pr_perror("clock_gettime");
+ tp.tv_sec += 9 * 24 * 3600;
+ tp.tv_nsec = 0;
+ if (clock_settime(CLOCK_BOOTTIME, &tp))
+ return pr_perror("clock_settime");
+
+ ret = 0;
+ ret |= run_test(CLOCK_MONOTONIC);
+ return ret;
+}
+
--
2.13.6
^ permalink raw reply related
* [RFC 17/20] selftest/timens: Add test for timerfd
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Andrei Vagin, Dmitry Safonov, Adrian Reber,
Andrei Vagin, Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86, linux-kselftest
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
From: Andrei Vagin <avagin@gmail.com>
Check that timerfd_create takes into account clock offsets.
Cc: linux-kselftest@vger.kernel.org
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
tools/testing/selftests/timens/.gitignore | 1 +
tools/testing/selftests/timens/Makefile | 2 +-
tools/testing/selftests/timens/timerfd.c | 96 +++++++++++++++++++++++++++++++
3 files changed, 98 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/timens/timerfd.c
diff --git a/tools/testing/selftests/timens/.gitignore b/tools/testing/selftests/timens/.gitignore
index 27a693229ce1..b609f6ee9fb9 100644
--- a/tools/testing/selftests/timens/.gitignore
+++ b/tools/testing/selftests/timens/.gitignore
@@ -1 +1,2 @@
timens
+timerfd
diff --git a/tools/testing/selftests/timens/Makefile b/tools/testing/selftests/timens/Makefile
index b877efb78974..66b90cd28e5c 100644
--- a/tools/testing/selftests/timens/Makefile
+++ b/tools/testing/selftests/timens/Makefile
@@ -1,4 +1,4 @@
-TEST_GEN_PROGS := timens
+TEST_GEN_PROGS := timens timerfd
CFLAGS := -Wall -Werror
diff --git a/tools/testing/selftests/timens/timerfd.c b/tools/testing/selftests/timens/timerfd.c
new file mode 100644
index 000000000000..914a4cd9a0df
--- /dev/null
+++ b/tools/testing/selftests/timens/timerfd.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <sched.h>
+
+#include <sys/timerfd.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "log.h"
+
+#ifndef CLONE_NEWTIME
+# define CLONE_NEWTIME 0x00001000
+#endif
+
+int run_test(int clockid)
+{
+ struct itimerspec new_value;
+ struct timespec now;
+ long long elapsed;
+ int fd, i;
+
+ if (clock_gettime(clockid, &now))
+ return pr_perror("clock_gettime");
+
+ for (i = 0; i < 2; i++) {
+ int flags = 0;
+
+ pr_msg("timerfd_settime: %d", "INFO", clockid);
+ new_value.it_value.tv_sec = 3600;
+ new_value.it_value.tv_nsec = 0;
+ new_value.it_interval.tv_sec = 1;
+ new_value.it_interval.tv_nsec = 0;
+
+ if (i == 1) {
+ new_value.it_value.tv_sec += now.tv_sec;
+ new_value.it_value.tv_nsec += now.tv_nsec;
+ }
+
+ fd = timerfd_create(clockid, 0);
+ if (fd == -1)
+ return pr_perror("timerfd_create");
+
+ if (i == 1)
+ flags |= TFD_TIMER_ABSTIME;
+
+ if (timerfd_settime(fd, flags, &new_value, NULL))
+ return pr_perror("timerfd_settime");
+
+ if (timerfd_gettime(fd, &new_value))
+ return pr_perror("timerfd_gettime");
+
+ elapsed = new_value.it_value.tv_sec;
+ if (abs(elapsed - 3600) > 60) {
+ printf("FAIL\n");
+ return 1;
+ }
+
+ close(fd);
+ }
+
+ printf("PASS\n");
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct timespec tp;
+ int ret;
+
+ if (unshare(CLONE_NEWTIME))
+ return pr_perror("unshare");
+
+ if (clock_gettime(CLOCK_MONOTONIC, &tp))
+ return pr_perror("clock_gettime");
+ tp.tv_sec = 7 * 24 * 3600;
+ if (clock_settime(CLOCK_MONOTONIC, &tp))
+ return pr_perror("clock_settime");
+
+ if (clock_gettime(CLOCK_BOOTTIME, &tp))
+ return pr_perror("clock_gettime");
+ tp.tv_sec += 9 * 24 * 3600;
+ tp.tv_nsec = 0;
+ if (clock_settime(CLOCK_BOOTTIME, &tp))
+ return pr_perror("clock_settime");
+
+ ret = 0;
+ ret |= run_test(CLOCK_BOOTTIME);
+ ret |= run_test(CLOCK_MONOTONIC);
+ return ret;
+}
+
--
2.13.6
^ permalink raw reply related
* [RFC 16/20] selftest: Add Time Namespace test for supported clocks
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86, linux-kselftest
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
This test checks that all supported clocks can be changed by
clock_settime.
Cc: linux-kselftest@vger.kernel.org
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Co-developed-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
tools/testing/selftests/timens/.gitignore | 1 +
tools/testing/selftests/timens/Makefile | 5 +
tools/testing/selftests/timens/config | 1 +
tools/testing/selftests/timens/log.h | 21 ++++
tools/testing/selftests/timens/timens.c | 196 ++++++++++++++++++++++++++++++
5 files changed, 224 insertions(+)
create mode 100644 tools/testing/selftests/timens/.gitignore
create mode 100644 tools/testing/selftests/timens/Makefile
create mode 100644 tools/testing/selftests/timens/config
create mode 100644 tools/testing/selftests/timens/log.h
create mode 100644 tools/testing/selftests/timens/timens.c
diff --git a/tools/testing/selftests/timens/.gitignore b/tools/testing/selftests/timens/.gitignore
new file mode 100644
index 000000000000..27a693229ce1
--- /dev/null
+++ b/tools/testing/selftests/timens/.gitignore
@@ -0,0 +1 @@
+timens
diff --git a/tools/testing/selftests/timens/Makefile b/tools/testing/selftests/timens/Makefile
new file mode 100644
index 000000000000..b877efb78974
--- /dev/null
+++ b/tools/testing/selftests/timens/Makefile
@@ -0,0 +1,5 @@
+TEST_GEN_PROGS := timens
+
+CFLAGS := -Wall -Werror
+
+include ../lib.mk
diff --git a/tools/testing/selftests/timens/config b/tools/testing/selftests/timens/config
new file mode 100644
index 000000000000..4480620f6f49
--- /dev/null
+++ b/tools/testing/selftests/timens/config
@@ -0,0 +1 @@
+CONFIG_TIME_NS=y
diff --git a/tools/testing/selftests/timens/log.h b/tools/testing/selftests/timens/log.h
new file mode 100644
index 000000000000..05fec7f97870
--- /dev/null
+++ b/tools/testing/selftests/timens/log.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __SELFTEST_TIMENS_LOG_H__
+#define __SELFTEST_TIMENS_LOG_H__
+
+#define pr_msg(fmt, lvl, ...) \
+ fprintf(stderr, "[%s] (%s:%d)\t" fmt "\n", \
+ lvl, __FILE__, __LINE__, ##__VA_ARGS__)
+
+#define pr_p(func, fmt, ...) func(fmt ": %m", ##__VA_ARGS__)
+
+#define pr_err(fmt, ...) \
+ ({ \
+ pr_msg(fmt, "ERR", ##__VA_ARGS__) \
+ -1; \
+ })
+#define pr_fail(fmt, ...) pr_msg(fmt, "FAIL", ##__VA_ARGS__)
+
+#define pr_perror(fmt, ...) pr_p(pr_err, fmt, ##__VA_ARGS__)
+
+#endif
diff --git a/tools/testing/selftests/timens/timens.c b/tools/testing/selftests/timens/timens.c
new file mode 100644
index 000000000000..dfa6701214b1
--- /dev/null
+++ b/tools/testing/selftests/timens/timens.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "log.h"
+
+#ifndef CLONE_NEWTIME
+# define CLONE_NEWTIME 0x00001000
+#endif
+
+/*
+ * Test shouldn't be run for a day, so add 10 days to child
+ * time and check parent's time to be in the same day.
+ */
+#define DAY_IN_SEC (60*60*24)
+#define TEN_DAYS_IN_SEC (10*DAY_IN_SEC)
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+#define CLOCK_TYPES \
+ ct(CLOCK_BOOTTIME), \
+ ct(CLOCK_MONOTONIC), \
+ ct(CLOCK_MONOTONIC_COARSE), \
+ ct(CLOCK_MONOTONIC_RAW), \
+
+
+#define ct(clock) clock
+static clockid_t clocks[] = {
+ CLOCK_TYPES
+};
+#undef ct
+#define ct(clock) #clock
+static char *clock_names[] = {
+ CLOCK_TYPES
+};
+
+static int child_ns, parent_ns;
+
+static int switch_ns(int fd)
+{
+ if (setns(fd, CLONE_NEWTIME)) {
+ pr_perror("setns()");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int init_namespaces(void)
+{
+ char path[] = "/proc/self/ns/time";
+ struct stat st1, st2;
+
+ parent_ns = open(path, O_RDONLY);
+ if (parent_ns <= 0)
+ return pr_perror("Unable to open %s", path);
+
+ if (fstat(parent_ns, &st1))
+ return pr_perror("Unable to stat the parent timens");
+
+ if (unshare(CLONE_NEWTIME))
+ return pr_perror("Can't unshare() timens");
+
+ child_ns = open(path, O_RDONLY);
+ if (child_ns <= 0)
+ return pr_perror("Unable to open %s", path);
+
+ if (fstat(child_ns, &st2))
+ return pr_perror("Unable to stat the timens");
+
+ if (st1.st_ino == st2.st_ino)
+ return pr_perror("The same child_ns after CLONE_NEWTIME");
+
+ return 0;
+}
+
+static int _gettime(clockid_t clk_id, struct timespec *res, bool raw_syscall)
+{
+ int err;
+
+ if (!raw_syscall) {
+ if (clock_gettime(clk_id, res)) {
+ pr_perror("clock_gettime(%d)", (int)clk_id);
+ return -1;
+ }
+ return 0;
+ }
+
+ err = syscall(SYS_clock_gettime, clk_id, res);
+ if (err)
+ pr_perror("syscall(SYS_clock_gettime(%d))", (int)clk_id);
+
+ return err;
+}
+
+static int _settime(clockid_t clk_id, struct timespec *res, bool raw_syscall)
+{
+ int err;
+
+ if (!raw_syscall) {
+ if (clock_settime(clk_id, res))
+ return pr_perror("clock_settime(%d)", (int)clk_id);
+ return 0;
+ }
+
+ err = syscall(SYS_clock_settime, clk_id, res);
+ if (err)
+ pr_perror("syscall(SYS_clock_settime(%d))", (int)clk_id);
+
+ return err;
+}
+
+static int test_gettime(clockid_t clock_index, bool raw_syscall, time_t offset)
+{
+ struct timespec child_ts_new, parent_ts_old, cur_ts;
+ char *entry = raw_syscall ? "syscall" : "vdso";
+ double precision = 0.0;
+
+ switch (clocks[clock_index]) {
+ case CLOCK_MONOTONIC_COARSE:
+ case CLOCK_MONOTONIC_RAW:
+ precision = -2.0;
+ break;
+ }
+
+ if (switch_ns(parent_ns))
+ return pr_err("switch_ns(%d)", child_ns);
+
+ if (_gettime(clocks[clock_index], &parent_ts_old, raw_syscall))
+ return -1;
+
+ if (switch_ns(child_ns))
+ return pr_err("switch_ns(%d)", child_ns);
+
+ child_ts_new.tv_nsec = parent_ts_old.tv_nsec;
+ child_ts_new.tv_sec = parent_ts_old.tv_sec + offset;
+
+ if (_settime(clocks[clock_index], &child_ts_new, raw_syscall))
+ return -1;
+
+ if (_gettime(clocks[clock_index], &cur_ts, raw_syscall))
+ return -1;
+
+ if (difftime(cur_ts.tv_sec, child_ts_new.tv_sec) < precision) {
+ pr_fail("Child's %s (%s) time has not changed: %lu -> %lu [%lu]",
+ clock_names[clock_index], entry, parent_ts_old.tv_sec,
+ child_ts_new.tv_sec, cur_ts.tv_sec);
+ return -1;
+ }
+
+ if (switch_ns(parent_ns))
+ return pr_err("switch_ns(%d)", parent_ns);
+
+ if (_gettime(clocks[clock_index], &cur_ts, raw_syscall))
+ return -1;
+
+ if (difftime(cur_ts.tv_sec, parent_ts_old.tv_sec) > DAY_IN_SEC) {
+ pr_fail("Parent's %s (%s) time has changed: %lu -> %lu [%lu]",
+ clock_names[clock_index], entry, parent_ts_old.tv_sec,
+ child_ts_new.tv_sec, cur_ts.tv_sec);
+ /* Let's play nice and put it closer to original */
+ clock_settime(clocks[clock_index], &cur_ts);
+ return -1;
+ }
+
+ pr_msg("Passed for %s (%s)", "OK", clock_names[clock_index], entry);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ unsigned int i;
+ int ret = 0;
+
+ if (init_namespaces())
+ return 1;
+
+ for (i = 0; i < ARRAY_SIZE(clocks); i++) {
+ ret |= test_gettime(i, true, TEN_DAYS_IN_SEC);
+ ret |= test_gettime(i, true, -TEN_DAYS_IN_SEC);
+ ret |= test_gettime(i, false, TEN_DAYS_IN_SEC);
+ ret |= test_gettime(i, false, -TEN_DAYS_IN_SEC);
+ }
+
+ return !!ret;
+}
--
2.13.6
^ permalink raw reply related
* [RFC 15/20] timens: Optimize zero-offsets
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
Fall through on host or in ns without time set.
Add TIMENS_FALLBACK_SYSCALL which might be wired up if timens offsets
should be unknown for userspace (will result in fall-back to syscalls).
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/x86/entry/vdso/vclock_gettime.c | 17 +++++++++++++----
include/linux/timens_offsets.h | 12 ++++++++++--
kernel/time/posix-timers.c | 21 ++++++++++++---------
kernel/time_namespace.c | 2 +-
4 files changed, 36 insertions(+), 16 deletions(-)
diff --git a/arch/x86/entry/vdso/vclock_gettime.c b/arch/x86/entry/vdso/vclock_gettime.c
index a265e2737a9a..458cb1992e2e 100644
--- a/arch/x86/entry/vdso/vclock_gettime.c
+++ b/arch/x86/entry/vdso/vclock_gettime.c
@@ -252,17 +252,25 @@ notrace void set_normalized_timespec(struct timespec *ts, time_t sec, s64 nsec)
ts->tv_nsec = nsec;
}
-notrace static __always_inline void monotonic_to_ns(struct timespec *ts)
+notrace static __always_inline int monotonic_to_ns(struct timespec *ts)
{
#ifdef CONFIG_TIME_NS
struct timens_offsets *timens = (struct timens_offsets *) &timens_page;
struct timespec offset;
+ /* Optimization: time is the same as on host, return right away */
+ if (!(timens->flags & TIMENS_USE_OFFSETS))
+ return 0;
+
+ if (timens->flags & TIMENS_FALLBACK_SYSCALL)
+ return -1;
+
offset = timespec64_to_timespec(timens->monotonic_time_offset);
*ts = timespec_add(*ts, offset);
#endif
+ return 0;
}
notrace static int __always_inline do_monotonic(struct timespec *ts)
@@ -283,8 +291,6 @@ notrace static int __always_inline do_monotonic(struct timespec *ts)
ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
ts->tv_nsec = ns;
- monotonic_to_ns(ts);
-
return mode;
}
@@ -306,7 +312,6 @@ notrace static void do_monotonic_coarse(struct timespec *ts)
ts->tv_sec = gtod->monotonic_time_coarse_sec;
ts->tv_nsec = gtod->monotonic_time_coarse_nsec;
} while (unlikely(gtod_read_retry(gtod, seq)));
- monotonic_to_ns(ts);
}
notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
@@ -319,12 +324,16 @@ notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
case CLOCK_MONOTONIC:
if (do_monotonic(ts) == VCLOCK_NONE)
goto fallback;
+ if (monotonic_to_ns(ts))
+ goto fallback;
break;
case CLOCK_REALTIME_COARSE:
do_realtime_coarse(ts);
break;
case CLOCK_MONOTONIC_COARSE:
do_monotonic_coarse(ts);
+ if (monotonic_to_ns(ts))
+ goto fallback;
break;
default:
goto fallback;
diff --git a/include/linux/timens_offsets.h b/include/linux/timens_offsets.h
index 92a8ea5601eb..8c43e7c3e632 100644
--- a/include/linux/timens_offsets.h
+++ b/include/linux/timens_offsets.h
@@ -2,6 +2,13 @@
#ifndef _LINUX_TIME_OFFSETS_H
#define _LINUX_TIME_OFFSETS_H
+enum {
+ /* We're in namespace - add offsets from vvar */
+ TIMENS_USE_OFFSETS = 1,
+ /* Don't expose host's offsets, fall back to syscall - slow */
+ TIMENS_FALLBACK_SYSCALL = 2, /* TODO if anyone actually interested */
+};
+
/*
* Time offsets need align as they're placed on vvar page,
* which should have tail paddings on ia32 vdso.
@@ -10,8 +17,9 @@
* to timespec because of a padding occuring between the fields.
*/
struct timens_offsets {
- struct timespec64 monotonic_time_offset __aligned(8);
- struct timespec64 monotonic_boottime_offset __aligned(8);
+ u64 flags;
+ struct timespec64 monotonic_time_offset __aligned(8);
+ struct timespec64 monotonic_boottime_offset __aligned(8);
};
#endif
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 701cb0602b7a..576dbd24c498 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -210,7 +210,7 @@ static void common_timens_adjust(clockid_t which_clock, struct timespec64 *tp)
{
struct timens_offsets *ns_offsets = current->nsproxy->time_ns->offsets;
- if (!ns_offsets)
+ if (!ns_offsets || !(ns_offsets->flags & TIMENS_USE_OFFSETS))
return;
switch (which_clock) {
@@ -234,15 +234,16 @@ static int posix_ktime_set_ts(clockid_t which_clock,
struct timens_offsets *ns_offsets = current->nsproxy->time_ns->offsets;
struct timespec64 ktp;
+ if (!ns_offsets)
+ return -EINVAL;
+
if (!ns_capable(current->nsproxy->time_ns->user_ns, CAP_SYS_TIME))
return -EPERM;
ktime_get_ts64(&ktp);
- if (ns_offsets)
- ns_offsets->monotonic_time_offset = timespec64_sub(*tp, ktp);
- else
- return -EINVAL;
+ ns_offsets->monotonic_time_offset = timespec64_sub(*tp, ktp);
+ ns_offsets->flags |= TIMENS_USE_OFFSETS;
return 0;
}
@@ -296,15 +297,17 @@ static int posix_set_boottime(clockid_t which_clock, const struct timespec64 *tp
struct timens_offsets *ns_offsets = current->nsproxy->time_ns->offsets;
struct timespec64 ktp;
+ if (!ns_offsets)
+ return -EINVAL;
+
if (!ns_capable(current->nsproxy->time_ns->user_ns, CAP_SYS_TIME))
return -EPERM;
ktime_get_boottime_ts64(&ktp);
- if (ns_offsets)
- ns_offsets->monotonic_boottime_offset = timespec64_sub(*tp, ktp);
- else
- return -EINVAL;
+ ns_offsets->monotonic_boottime_offset = timespec64_sub(*tp, ktp);
+ ns_offsets->flags |= TIMENS_USE_OFFSETS;
+
return 0;
}
diff --git a/kernel/time_namespace.c b/kernel/time_namespace.c
index f88ae0e17d92..4052bdcec110 100644
--- a/kernel/time_namespace.c
+++ b/kernel/time_namespace.c
@@ -171,7 +171,7 @@ static void clock_timens_fixup(int clockid, struct timespec64 *val, bool to_ns)
struct timens_offsets *ns_offsets = current->nsproxy->time_ns->offsets;
struct timespec64 *offsets = NULL;
- if (!ns_offsets)
+ if (!ns_offsets || !(ns_offsets->flags & TIMENS_USE_OFFSETS))
return;
if (val->tv_sec == 0 && val->tv_nsec == 0)
--
2.13.6
^ permalink raw reply related
* [RFC 14/20] timens: Add align for timens_offsets
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
Align offsets so that Time Namespace will work for ia32 applications on
x86_64 host.
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
include/linux/timens_offsets.h | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/include/linux/timens_offsets.h b/include/linux/timens_offsets.h
index 777530c46852..92a8ea5601eb 100644
--- a/include/linux/timens_offsets.h
+++ b/include/linux/timens_offsets.h
@@ -2,9 +2,16 @@
#ifndef _LINUX_TIME_OFFSETS_H
#define _LINUX_TIME_OFFSETS_H
+/*
+ * Time offsets need align as they're placed on vvar page,
+ * which should have tail paddings on ia32 vdso.
+ * Otherwise as u64 has align(4), vvar offsets will differ.
+ * On 64-bit big-endian systems vdso should convert to timespec64
+ * to timespec because of a padding occuring between the fields.
+ */
struct timens_offsets {
- struct timespec64 monotonic_time_offset;
- struct timespec64 monotonic_boottime_offset;
+ struct timespec64 monotonic_time_offset __aligned(8);
+ struct timespec64 monotonic_boottime_offset __aligned(8);
};
#endif
--
2.13.6
^ permalink raw reply related
* [RFC 13/20] posix-timers/timens: Take into account clock offsets
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Andrei Vagin, Dmitry Safonov, Adrian Reber,
Andrei Vagin, Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
From: Andrei Vagin <avagin@gmail.com>
Provide a helper that will convert clocks to time namespace.
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
kernel/time/posix-timers.c | 52 +++++++++++++++++++++++++++++++---------------
kernel/time/posix-timers.h | 2 ++
2 files changed, 37 insertions(+), 17 deletions(-)
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index d38835a21c5d..701cb0602b7a 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -206,12 +206,26 @@ static int posix_clock_realtime_adj(const clockid_t which_clock,
return do_adjtimex(t);
}
-static void timens_adjust_monotonic(struct timespec64 *tp)
+static void common_timens_adjust(clockid_t which_clock, struct timespec64 *tp)
{
struct timens_offsets *ns_offsets = current->nsproxy->time_ns->offsets;
- if (ns_offsets)
+ if (!ns_offsets)
+ return;
+
+ switch (which_clock) {
+ case CLOCK_MONOTONIC:
+ case CLOCK_MONOTONIC_RAW:
+ case CLOCK_MONOTONIC_COARSE:
*tp = timespec64_add(*tp, ns_offsets->monotonic_time_offset);
+ break;
+ case CLOCK_BOOTTIME:
+ *tp = timespec64_add(*tp, ns_offsets->monotonic_boottime_offset);
+ break;
+ default:
+ WARN_ONCE(1, "Time Namespace offset for %d is not realized",
+ which_clock);
+ }
}
static int posix_ktime_set_ts(clockid_t which_clock,
@@ -239,7 +253,6 @@ static int posix_ktime_set_ts(clockid_t which_clock,
static int posix_ktime_get_ts(clockid_t which_clock, struct timespec64 *tp)
{
ktime_get_ts64(tp);
- timens_adjust_monotonic(tp);
return 0;
}
@@ -249,7 +262,6 @@ static int posix_ktime_get_ts(clockid_t which_clock, struct timespec64 *tp)
static int posix_get_monotonic_raw(clockid_t which_clock, struct timespec64 *tp)
{
ktime_get_raw_ts64(tp);
- timens_adjust_monotonic(tp);
return 0;
}
@@ -264,7 +276,6 @@ static int posix_get_monotonic_coarse(clockid_t which_clock,
struct timespec64 *tp)
{
ktime_get_coarse_ts64(tp);
- timens_adjust_monotonic(tp);
return 0;
}
@@ -276,15 +287,7 @@ static int posix_get_coarse_res(const clockid_t which_clock, struct timespec64 *
static int posix_get_boottime(const clockid_t which_clock, struct timespec64 *tp)
{
- struct timens_offsets *ns_offsets = current->nsproxy->time_ns->offsets;
-
ktime_get_boottime_ts64(tp);
-
- if (!ns_offsets)
- return 0;
-
- *tp = timespec64_add(*tp, ns_offsets->monotonic_boottime_offset);
-
return 0;
}
@@ -933,10 +936,6 @@ static int do_timer_settime(timer_t timer_id, int flags,
unsigned long flag;
int error = 0;
- if (!timespec64_valid(&new_spec64->it_interval) ||
- !timespec64_valid(&new_spec64->it_value))
- return -EINVAL;
-
if (old_spec64)
memset(old_spec64, 0, sizeof(*old_spec64));
retry:
@@ -944,6 +943,15 @@ static int do_timer_settime(timer_t timer_id, int flags,
if (!timr)
return -EINVAL;
+ if (flags & TIMER_ABSTIME)
+ timens_clock_to_host(timr->it_clock, &new_spec64->it_value);
+
+ if (!timespec64_valid(&new_spec64->it_interval) ||
+ !timespec64_valid(&new_spec64->it_value)) {
+ unlock_timer(timr, flag);
+ return -EINVAL;
+ }
+
kc = timr->kclock;
if (WARN_ON_ONCE(!kc || !kc->timer_set))
error = -EINVAL;
@@ -1121,6 +1129,9 @@ SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
error = kc->clock_get(which_clock, &kernel_tp);
+ if (!error && kc->clock_timens_adjust)
+ kc->clock_timens_adjust(which_clock, &kernel_tp);
+
if (!error && put_timespec64(&kernel_tp, tp))
error = -EFAULT;
@@ -1197,6 +1208,9 @@ COMPAT_SYSCALL_DEFINE2(clock_gettime, clockid_t, which_clock,
err = kc->clock_get(which_clock, &ts);
+ if (!err && kc->clock_timens_adjust)
+ kc->clock_timens_adjust(which_clock, &ts);
+
if (!err && compat_put_timespec64(&ts, tp))
err = -EFAULT;
@@ -1340,6 +1354,7 @@ static const struct k_clock clock_monotonic = {
.clock_getres = posix_get_hrtimer_res,
.clock_get = posix_ktime_get_ts,
.clock_set = posix_ktime_set_ts,
+ .clock_timens_adjust = common_timens_adjust,
.nsleep = common_nsleep,
.timer_create = common_timer_create,
.timer_set = common_timer_set,
@@ -1356,6 +1371,7 @@ static const struct k_clock clock_monotonic_raw = {
.clock_getres = posix_get_hrtimer_res,
.clock_get = posix_get_monotonic_raw,
.clock_set = posix_ktime_set_ts,
+ .clock_timens_adjust = common_timens_adjust,
};
static const struct k_clock clock_realtime_coarse = {
@@ -1367,6 +1383,7 @@ static const struct k_clock clock_monotonic_coarse = {
.clock_getres = posix_get_coarse_res,
.clock_get = posix_get_monotonic_coarse,
.clock_set = posix_ktime_set_ts,
+ .clock_timens_adjust = common_timens_adjust,
};
static const struct k_clock clock_tai = {
@@ -1388,6 +1405,7 @@ static const struct k_clock clock_boottime = {
.clock_getres = posix_get_hrtimer_res,
.clock_get = posix_get_boottime,
.clock_set = posix_set_boottime,
+ .clock_timens_adjust = common_timens_adjust,
.nsleep = common_nsleep,
.timer_create = common_timer_create,
.timer_set = common_timer_set,
diff --git a/kernel/time/posix-timers.h b/kernel/time/posix-timers.h
index ddb21145211a..308774bea32a 100644
--- a/kernel/time/posix-timers.h
+++ b/kernel/time/posix-timers.h
@@ -8,6 +8,8 @@ struct k_clock {
const struct timespec64 *tp);
int (*clock_get)(const clockid_t which_clock,
struct timespec64 *tp);
+ void (*clock_timens_adjust)(const clockid_t which_clock,
+ struct timespec64 *tp);
int (*clock_adj)(const clockid_t which_clock, struct timex *tx);
int (*timer_create)(struct k_itimer *timer);
int (*nsleep)(const clockid_t which_clock, int flags,
--
2.13.6
^ permalink raw reply related
* [RFC 12/20] x86/vdso: Look for vvar vma to purge timens page
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
As vvar vma may be moved away from vdso, let's search it, rather than
calculate purge address from vdso position.
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/x86/entry/vdso/vma.c | 39 +++++++++++++++++++++++++--------------
1 file changed, 25 insertions(+), 14 deletions(-)
diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c
index 90eadcfcb7f5..d1e2392a4905 100644
--- a/arch/x86/entry/vdso/vma.c
+++ b/arch/x86/entry/vdso/vma.c
@@ -159,7 +159,18 @@ static int vvar_fault(const struct vm_special_mapping *sm,
return VM_FAULT_SIGBUS;
}
-static void clear_flush_timens_pte(struct mm_struct *mm, unsigned long addr)
+static const struct vm_special_mapping vdso_mapping = {
+ .name = "[vdso]",
+ .fault = vdso_fault,
+ .mremap = vdso_mremap,
+};
+static const struct vm_special_mapping vvar_mapping = {
+ .name = "[vvar]",
+ .fault = vvar_fault,
+ .mremap = vvar_mremap,
+};
+
+static void vvar_flush_timens_pte(struct mm_struct *mm, unsigned long addr)
{
spinlock_t *ptl;
pte_t *ptep;
@@ -175,31 +186,31 @@ int vvar_purge_timens(struct task_struct *task)
{
struct mm_struct *mm = task->mm;
const struct vdso_image *image;
+ struct vm_area_struct *vma;
unsigned long addr;
if (down_write_killable(&mm->mmap_sem))
return -EINTR;
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ if (vma_is_special_mapping(vma, &vvar_mapping))
+ break;
+ }
+
+ /* vvar is unmapped */
+ if (!vma || !vma_is_special_mapping(vma, &vvar_mapping))
+ goto out;
+
image = mm->context.vdso_image;
- addr = (unsigned long)mm->context.vdso + image->sym_timens_page;
- clear_flush_timens_pte(mm, addr);
+ addr = vma->vm_end + image->sym_timens_page;
+ vvar_flush_timens_pte(mm, addr);
+out:
up_write(&mm->mmap_sem);
return 0;
}
-static const struct vm_special_mapping vdso_mapping = {
- .name = "[vdso]",
- .fault = vdso_fault,
- .mremap = vdso_mremap,
-};
-static const struct vm_special_mapping vvar_mapping = {
- .name = "[vvar]",
- .fault = vvar_fault,
- .mremap = vvar_mremap,
-};
-
/*
* Add vdso and vvar mappings to current process.
* @image - blob to map
--
2.13.6
^ permalink raw reply related
* [RFC 11/20] x86/vdso: Purge timens page on setns()/unshare()/clone()
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
Find page with timens offsets on vvar and flush mapping for it during
entering/creating another time namespace.
Prevents application to have stale mapping from old namespace.
(as old namespace might be destroyed on the moment of userspace access,
it also prevents leaks from kernel).
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/x86/entry/vdso/vma.c | 31 +++++++++++++++++++++++++++++++
arch/x86/include/asm/vdso.h | 1 +
kernel/time_namespace.c | 12 ++++++++++++
3 files changed, 44 insertions(+)
diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c
index 0f92227a4a7e..90eadcfcb7f5 100644
--- a/arch/x86/entry/vdso/vma.c
+++ b/arch/x86/entry/vdso/vma.c
@@ -25,6 +25,7 @@
#include <asm/cpufeature.h>
#include <asm/mshyperv.h>
#include <asm/page.h>
+#include <asm/tlbflush.h>
#if defined(CONFIG_X86_64)
unsigned int __read_mostly vdso64_enabled = 1;
@@ -158,6 +159,36 @@ static int vvar_fault(const struct vm_special_mapping *sm,
return VM_FAULT_SIGBUS;
}
+static void clear_flush_timens_pte(struct mm_struct *mm, unsigned long addr)
+{
+ spinlock_t *ptl;
+ pte_t *ptep;
+
+ if (follow_pte_pmd(mm, addr, NULL, NULL, &ptep, NULL, &ptl))
+ return; /* no pte found */
+ ptep_get_and_clear(mm, addr, ptep);
+ pte_unmap_unlock(ptep, ptl);
+ flush_tlb_mm_range(mm, addr, addr + PAGE_SIZE, VM_NONE);
+}
+
+int vvar_purge_timens(struct task_struct *task)
+{
+ struct mm_struct *mm = task->mm;
+ const struct vdso_image *image;
+ unsigned long addr;
+
+ if (down_write_killable(&mm->mmap_sem))
+ return -EINTR;
+
+ image = mm->context.vdso_image;
+
+ addr = (unsigned long)mm->context.vdso + image->sym_timens_page;
+ clear_flush_timens_pte(mm, addr);
+
+ up_write(&mm->mmap_sem);
+ return 0;
+}
+
static const struct vm_special_mapping vdso_mapping = {
.name = "[vdso]",
.fault = vdso_fault,
diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h
index 619322065b8e..98b02481137c 100644
--- a/arch/x86/include/asm/vdso.h
+++ b/arch/x86/include/asm/vdso.h
@@ -45,6 +45,7 @@ extern const struct vdso_image vdso_image_32;
extern void __init init_vdso_image(const struct vdso_image *image);
extern int map_vdso_once(const struct vdso_image *image, unsigned long addr);
+extern int vvar_purge_timens(struct task_struct *task);
#endif /* __ASSEMBLER__ */
diff --git a/kernel/time_namespace.c b/kernel/time_namespace.c
index f96871cb8124..f88ae0e17d92 100644
--- a/kernel/time_namespace.c
+++ b/kernel/time_namespace.c
@@ -14,6 +14,7 @@
#include <linux/proc_ns.h>
#include <linux/sched/task.h>
#include <linux/mm.h>
+#include <asm/vdso.h>
static struct ucounts *inc_time_namespaces(struct user_namespace *ns)
{
@@ -91,9 +92,15 @@ static struct time_namespace *clone_time_ns(struct user_namespace *user_ns,
struct time_namespace *copy_time_ns(unsigned long flags,
struct user_namespace *user_ns, struct time_namespace *old_ns)
{
+ int ret;
+
if (!(flags & CLONE_NEWTIME))
return get_time_ns(old_ns);
+ ret = vvar_purge_timens(current);
+ if (ret)
+ return ERR_PTR(ret);
+
return clone_time_ns(user_ns, old_ns);
}
@@ -138,11 +145,16 @@ static void timens_put(struct ns_common *ns)
static int timens_install(struct nsproxy *nsproxy, struct ns_common *new)
{
struct time_namespace *ns = to_time_ns(new);
+ int ret;
if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
!ns_capable(current_user_ns(), CAP_SYS_ADMIN))
return -EPERM;
+ ret = vvar_purge_timens(current);
+ if (ret)
+ return ret;
+
get_time_ns(ns);
put_time_ns(nsproxy->time_ns);
nsproxy->time_ns = ns;
--
2.13.6
^ permalink raw reply related
* [RFC 10/20] x86/vdso: Use set_normalized_timespec() to avoid 32 bit overflow
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Andrei Vagin, Dmitry Safonov, Adrian Reber,
Andrei Vagin, Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
From: Andrei Vagin <avagin@gmail.com>
ts->tv_nsec + offset->tv_nsec
On 32 bit machines that sum can be larger than (1 << 31) and therefor
result in a negative value which screws up the result completely.
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/x86/entry/vdso/vclock_gettime.c | 35 ++++++++++++++++++++++++++---------
1 file changed, 26 insertions(+), 9 deletions(-)
diff --git a/arch/x86/entry/vdso/vclock_gettime.c b/arch/x86/entry/vdso/vclock_gettime.c
index 0594266740b9..a265e2737a9a 100644
--- a/arch/x86/entry/vdso/vclock_gettime.c
+++ b/arch/x86/entry/vdso/vclock_gettime.c
@@ -231,20 +231,37 @@ notrace static int __always_inline do_realtime(struct timespec *ts)
return mode;
}
+notrace void set_normalized_timespec(struct timespec *ts, time_t sec, s64 nsec)
+{
+ while (nsec >= NSEC_PER_SEC) {
+ /*
+ * The following asm() prevents the compiler from
+ * optimising this loop into a modulo operation. See
+ * also __iter_div_u64_rem() in include/linux/time.h
+ */
+ asm("" : "+rm"(nsec));
+ nsec -= NSEC_PER_SEC;
+ ++sec;
+ }
+ while (nsec < 0) {
+ asm("" : "+rm"(nsec));
+ nsec += NSEC_PER_SEC;
+ --sec;
+ }
+ ts->tv_sec = sec;
+ ts->tv_nsec = nsec;
+}
+
notrace static __always_inline void monotonic_to_ns(struct timespec *ts)
{
#ifdef CONFIG_TIME_NS
struct timens_offsets *timens = (struct timens_offsets *) &timens_page;
+ struct timespec offset;
+
+ offset = timespec64_to_timespec(timens->monotonic_time_offset);
+
+ *ts = timespec_add(*ts, offset);
- ts->tv_sec += timens->monotonic_time_offset.tv_sec;
- ts->tv_nsec += timens->monotonic_time_offset.tv_nsec;
- if (ts->tv_nsec > NSEC_PER_SEC) {
- ts->tv_nsec -= NSEC_PER_SEC;
- ts->tv_sec++;
- } else if (ts->tv_nsec < 0) {
- ts->tv_nsec += NSEC_PER_SEC;
- ts->tv_sec--;
- }
#endif
}
--
2.13.6
^ permalink raw reply related
* [RFC 09/20] x86/vdso/timens: Add offsets page in vvar
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Andrei Vagin, Dmitry Safonov, Adrian Reber,
Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
From: Andrei Vagin <avagin@openvz.org>
As modern applications fetch time from vdso without entering the kernel,
it's needed to provide offsets for userspace code.
Allocate a page for timens offsets when constructing time namespace.
As vdso mappings are platform-specific, add Kconfig dependency for arch.
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Co-developed-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/Kconfig | 5 +++++
arch/x86/Kconfig | 1 +
arch/x86/entry/vdso/vclock_gettime.c | 26 ++++++++++++++++++++++++++
arch/x86/entry/vdso/vdso-layout.lds.S | 9 ++++++++-
arch/x86/entry/vdso/vdso2c.c | 3 +++
arch/x86/entry/vdso/vma.c | 12 ++++++++++++
arch/x86/include/asm/vdso.h | 1 +
init/Kconfig | 1 +
8 files changed, 57 insertions(+), 1 deletion(-)
diff --git a/arch/Kconfig b/arch/Kconfig
index 6801123932a5..411df0227a1d 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -681,6 +681,11 @@ config HAVE_ARCH_HASH
config ISA_BUS_API
def_bool ISA
+config ARCH_HAS_VDSO_TIME_NS
+ bool
+ help
+ VDSO can add time-ns offsets without entering kernel.
+
#
# ABI hall of shame
#
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 1a0be022f91d..4bcbdd1f1200 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -70,6 +70,7 @@ config X86
select ARCH_HAS_STRICT_MODULE_RWX
select ARCH_HAS_SYNC_CORE_BEFORE_USERMODE
select ARCH_HAS_UBSAN_SANITIZE_ALL
+ select ARCH_HAS_VDSO_TIME_NS
select ARCH_HAS_ZONE_DEVICE if X86_64
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select ARCH_MIGHT_HAVE_ACPI_PDC if ACPI
diff --git a/arch/x86/entry/vdso/vclock_gettime.c b/arch/x86/entry/vdso/vclock_gettime.c
index f19856d95c60..0594266740b9 100644
--- a/arch/x86/entry/vdso/vclock_gettime.c
+++ b/arch/x86/entry/vdso/vclock_gettime.c
@@ -21,6 +21,7 @@
#include <linux/math64.h>
#include <linux/time.h>
#include <linux/kernel.h>
+#include <linux/timens_offsets.h>
#define gtod (&VVAR(vsyscall_gtod_data))
@@ -38,6 +39,11 @@ extern u8 hvclock_page
__attribute__((visibility("hidden")));
#endif
+#ifdef CONFIG_TIME_NS
+extern u8 timens_page
+ __attribute__((visibility("hidden")));
+#endif
+
#ifndef BUILD_VDSO32
notrace static long vdso_fallback_gettime(long clock, struct timespec *ts)
@@ -225,6 +231,23 @@ notrace static int __always_inline do_realtime(struct timespec *ts)
return mode;
}
+notrace static __always_inline void monotonic_to_ns(struct timespec *ts)
+{
+#ifdef CONFIG_TIME_NS
+ struct timens_offsets *timens = (struct timens_offsets *) &timens_page;
+
+ ts->tv_sec += timens->monotonic_time_offset.tv_sec;
+ ts->tv_nsec += timens->monotonic_time_offset.tv_nsec;
+ if (ts->tv_nsec > NSEC_PER_SEC) {
+ ts->tv_nsec -= NSEC_PER_SEC;
+ ts->tv_sec++;
+ } else if (ts->tv_nsec < 0) {
+ ts->tv_nsec += NSEC_PER_SEC;
+ ts->tv_sec--;
+ }
+#endif
+}
+
notrace static int __always_inline do_monotonic(struct timespec *ts)
{
unsigned long seq;
@@ -243,6 +266,8 @@ notrace static int __always_inline do_monotonic(struct timespec *ts)
ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
ts->tv_nsec = ns;
+ monotonic_to_ns(ts);
+
return mode;
}
@@ -264,6 +289,7 @@ notrace static void do_monotonic_coarse(struct timespec *ts)
ts->tv_sec = gtod->monotonic_time_coarse_sec;
ts->tv_nsec = gtod->monotonic_time_coarse_nsec;
} while (unlikely(gtod_read_retry(gtod, seq)));
+ monotonic_to_ns(ts);
}
notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
diff --git a/arch/x86/entry/vdso/vdso-layout.lds.S b/arch/x86/entry/vdso/vdso-layout.lds.S
index acfd5ba7d943..e5c2e9deca03 100644
--- a/arch/x86/entry/vdso/vdso-layout.lds.S
+++ b/arch/x86/entry/vdso/vdso-layout.lds.S
@@ -17,6 +17,12 @@
#define NUM_FAKE_SHDRS 13
+#ifdef CONFIG_TIME_NS
+# define TIMENS_SZ PAGE_SIZE
+#else
+# define TIMENS_SZ 0
+#endif
+
SECTIONS
{
/*
@@ -26,7 +32,7 @@ SECTIONS
* segment.
*/
- vvar_start = . - 3 * PAGE_SIZE;
+ vvar_start = . - (3 * PAGE_SIZE + TIMENS_SZ);
vvar_page = vvar_start;
/* Place all vvars at the offsets in asm/vvar.h. */
@@ -38,6 +44,7 @@ SECTIONS
pvclock_page = vvar_start + PAGE_SIZE;
hvclock_page = vvar_start + 2 * PAGE_SIZE;
+ timens_page = vvar_start + 3 * PAGE_SIZE;
. = SIZEOF_HEADERS;
diff --git a/arch/x86/entry/vdso/vdso2c.c b/arch/x86/entry/vdso/vdso2c.c
index 4674f58581a1..6c67cde7fe99 100644
--- a/arch/x86/entry/vdso/vdso2c.c
+++ b/arch/x86/entry/vdso/vdso2c.c
@@ -76,6 +76,7 @@ enum {
sym_hpet_page,
sym_pvclock_page,
sym_hvclock_page,
+ sym_timens_page,
sym_VDSO_FAKE_SECTION_TABLE_START,
sym_VDSO_FAKE_SECTION_TABLE_END,
};
@@ -85,6 +86,7 @@ const int special_pages[] = {
sym_hpet_page,
sym_pvclock_page,
sym_hvclock_page,
+ sym_timens_page,
};
struct vdso_sym {
@@ -98,6 +100,7 @@ struct vdso_sym required_syms[] = {
[sym_hpet_page] = {"hpet_page", true},
[sym_pvclock_page] = {"pvclock_page", true},
[sym_hvclock_page] = {"hvclock_page", true},
+ [sym_timens_page] = {"timens_page", true},
[sym_VDSO_FAKE_SECTION_TABLE_START] = {
"VDSO_FAKE_SECTION_TABLE_START", false
},
diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c
index 8cc0395687b0..0f92227a4a7e 100644
--- a/arch/x86/entry/vdso/vma.c
+++ b/arch/x86/entry/vdso/vma.c
@@ -14,6 +14,7 @@
#include <linux/elf.h>
#include <linux/cpu.h>
#include <linux/ptrace.h>
+#include <linux/time_namespace.h>
#include <asm/pvclock.h>
#include <asm/vgtod.h>
#include <asm/proto.h>
@@ -23,6 +24,7 @@
#include <asm/desc.h>
#include <asm/cpufeature.h>
#include <asm/mshyperv.h>
+#include <asm/page.h>
#if defined(CONFIG_X86_64)
unsigned int __read_mostly vdso64_enabled = 1;
@@ -138,6 +140,16 @@ static int vvar_fault(const struct vm_special_mapping *sm,
if (tsc_pg && vclock_was_used(VCLOCK_HVCLOCK))
ret = vm_insert_pfn(vma, vmf->address,
vmalloc_to_pfn(tsc_pg));
+ } else if (sym_offset == image->sym_timens_page) {
+ struct time_namespace *ns = current->nsproxy->time_ns;
+ unsigned long pfn;
+
+ if (!ns->offsets)
+ pfn = page_to_pfn(ZERO_PAGE(0));
+ else
+ pfn = page_to_pfn(virt_to_page(ns->offsets));
+
+ ret = vm_insert_pfn(vma, vmf->address, pfn);
}
if (ret == 0 || ret == -EBUSY)
diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h
index 27566e57e87d..619322065b8e 100644
--- a/arch/x86/include/asm/vdso.h
+++ b/arch/x86/include/asm/vdso.h
@@ -22,6 +22,7 @@ struct vdso_image {
long sym_hpet_page;
long sym_pvclock_page;
long sym_hvclock_page;
+ long sym_timens_page;
long sym_VDSO32_NOTE_MASK;
long sym___kernel_sigreturn;
long sym___kernel_rt_sigreturn;
diff --git a/init/Kconfig b/init/Kconfig
index dc2b40f7d73f..c9b250475ddb 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -929,6 +929,7 @@ config UTS_NS
config TIME_NS
bool "TIME namespace"
+ depends on ARCH_HAS_VDSO_TIME_NS
default y
help
In this namespace boottime and monotonic clocks can be set.
--
2.13.6
^ permalink raw reply related
* [RFC 08/20] x86/vdso: Restrict splitting vvar vma
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
As offsets differ between time namespaces, we will need to flush vvar
mapping for timens page during setns(), unshare(), clone(NEW_TIME).
Forcing userspace to mremap() either all vvar or nothing and the same
for munmap() will simplify searching for timens page to flush.
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
arch/x86/entry/vdso/vma.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c
index 5b8b556dbb12..8cc0395687b0 100644
--- a/arch/x86/entry/vdso/vma.c
+++ b/arch/x86/entry/vdso/vma.c
@@ -84,6 +84,18 @@ static int vdso_mremap(const struct vm_special_mapping *sm,
return 0;
}
+static int vvar_mremap(const struct vm_special_mapping *sm,
+ struct vm_area_struct *new_vma)
+{
+ unsigned long new_size = new_vma->vm_end - new_vma->vm_start;
+ const struct vdso_image *image = current->mm->context.vdso_image;
+
+ if (new_size != -image->sym_vvar_start)
+ return -EINVAL;
+
+ return 0;
+}
+
static int vvar_fault(const struct vm_special_mapping *sm,
struct vm_area_struct *vma, struct vm_fault *vmf)
{
@@ -142,6 +154,7 @@ static const struct vm_special_mapping vdso_mapping = {
static const struct vm_special_mapping vvar_mapping = {
.name = "[vvar]",
.fault = vvar_fault,
+ .mremap = vvar_mremap,
};
/*
--
2.13.6
^ permalink raw reply related
* [RFC 07/20] timens: Shift /proc/uptime
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86, Alexey Dobriyan
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
Respect boottime inside time namespace for /proc/uptime
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
fs/proc/uptime.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/fs/proc/uptime.c b/fs/proc/uptime.c
index a4c2791ab70b..4421ec058472 100644
--- a/fs/proc/uptime.c
+++ b/fs/proc/uptime.c
@@ -5,6 +5,7 @@
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/time.h>
+#include <linux/time_namespace.h>
#include <linux/kernel_stat.h>
static int uptime_proc_show(struct seq_file *m, void *v)
@@ -20,6 +21,8 @@ static int uptime_proc_show(struct seq_file *m, void *v)
nsec += (__force u64) kcpustat_cpu(i).cpustat[CPUTIME_IDLE];
ktime_get_boottime_ts64(&uptime);
+ timens_clock_from_host(CLOCK_BOOTTIME, &uptime);
+
idle.tv_sec = div_u64_rem(nsec, NSEC_PER_SEC, &rem);
idle.tv_nsec = rem;
seq_printf(m, "%lu.%02lu %lu.%02lu\n",
--
2.13.6
^ permalink raw reply related
* [RFC 06/20] kernel: Take into account timens clock offsets in clock_nanosleep
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Andrei Vagin, Dmitry Safonov, Adrian Reber,
Andrei Vagin, Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
From: Andrei Vagin <avagin@gmail.com>
Wire up clock_nanosleep to timens offsets.
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
kernel/time/hrtimer.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index e1a549c9e399..4fe80c1325b2 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -51,6 +51,7 @@
#include <linux/timer.h>
#include <linux/freezer.h>
#include <linux/compat.h>
+#include <linux/time_namespace.h>
#include <linux/uaccess.h>
@@ -1730,9 +1731,16 @@ long hrtimer_nanosleep(const struct timespec64 *rqtp,
{
struct restart_block *restart;
struct hrtimer_sleeper t;
+ struct timespec64 tp;
int ret = 0;
u64 slack;
+ if (!(mode & HRTIMER_MODE_REL)) {
+ tp = *rqtp;
+ rqtp = &tp;
+ timens_clock_to_host(clockid, &tp);
+ }
+
slack = current->timer_slack_ns;
if (dl_task(current) || rt_task(current))
slack = 0;
--
2.13.6
^ permalink raw reply related
* [RFC 05/20] timerfd/timens: Take into account ns clock offsets
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Andrei Vagin, Dmitry Safonov, Adrian Reber,
Andrei Vagin, Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
From: Andrei Vagin <avagin@gmail.com>
Make timerfd respect timens offsets.
Provide two helpers timens_clock_to_host() timens_clock_from_host() that
are useful to wire up timens to different kernel subsystems.
Following patches will use timens_clock_from_host(), added here for
completeness.
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Co-developed-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
fs/timerfd.c | 16 +++++++++++-----
include/linux/time_namespace.h | 11 +++++++++++
kernel/time_namespace.c | 39 +++++++++++++++++++++++++++++++++++++++
3 files changed, 61 insertions(+), 5 deletions(-)
diff --git a/fs/timerfd.c b/fs/timerfd.c
index d69ad801eb80..001ab7a0fd8b 100644
--- a/fs/timerfd.c
+++ b/fs/timerfd.c
@@ -26,6 +26,7 @@
#include <linux/syscalls.h>
#include <linux/compat.h>
#include <linux/rcupdate.h>
+#include <linux/time_namespace.h>
struct timerfd_ctx {
union {
@@ -433,22 +434,27 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
}
static int do_timerfd_settime(int ufd, int flags,
- const struct itimerspec64 *new,
+ struct itimerspec64 *new,
struct itimerspec64 *old)
{
struct fd f;
struct timerfd_ctx *ctx;
int ret;
- if ((flags & ~TFD_SETTIME_FLAGS) ||
- !itimerspec64_valid(new))
- return -EINVAL;
-
ret = timerfd_fget(ufd, &f);
if (ret)
return ret;
ctx = f.file->private_data;
+ if (flags & TFD_TIMER_ABSTIME)
+ timens_clock_to_host(ctx->clockid, &new->it_value);
+
+ if ((flags & ~TFD_SETTIME_FLAGS) ||
+ !itimerspec64_valid(new)) {
+ fdput(f);
+ return -EINVAL;
+ }
+
if (isalarm(ctx) && !capable(CAP_WAKE_ALARM)) {
fdput(f);
return -EPERM;
diff --git a/include/linux/time_namespace.h b/include/linux/time_namespace.h
index 4960c54f1b33..910711d1c39d 100644
--- a/include/linux/time_namespace.h
+++ b/include/linux/time_namespace.h
@@ -38,6 +38,9 @@ static inline void put_time_ns(struct time_namespace *ns)
kref_put(&ns->kref, free_time_ns);
}
+void timens_clock_to_host(int clockid, struct timespec64 *val);
+void timens_clock_from_host(int clockid, struct timespec64 *val);
+
#else
static inline void get_time_ns(struct time_namespace *ns)
{
@@ -56,6 +59,14 @@ static inline struct time_namespace *copy_time_ns(unsigned long flags,
return old_ns;
}
+static inline void timens_clock_to_host(int clockid, struct timespec64 *val)
+{
+}
+
+static inline void timens_clock_from_host(int clockid, struct timespec64 *val)
+{
+}
+
#endif
#endif /* _LINUX_TIMENS_H */
diff --git a/kernel/time_namespace.c b/kernel/time_namespace.c
index a985529754b4..f96871cb8124 100644
--- a/kernel/time_namespace.c
+++ b/kernel/time_namespace.c
@@ -154,6 +154,45 @@ static struct user_namespace *timens_owner(struct ns_common *ns)
return to_time_ns(ns)->user_ns;
}
+static void clock_timens_fixup(int clockid, struct timespec64 *val, bool to_ns)
+{
+ struct timens_offsets *ns_offsets = current->nsproxy->time_ns->offsets;
+ struct timespec64 *offsets = NULL;
+
+ if (!ns_offsets)
+ return;
+
+ if (val->tv_sec == 0 && val->tv_nsec == 0)
+ return;
+
+ switch (clockid) {
+ case CLOCK_MONOTONIC:
+ offsets = &ns_offsets->monotonic_time_offset;
+ break;
+ case CLOCK_BOOTTIME:
+ offsets = &ns_offsets->monotonic_boottime_offset;
+ break;
+ }
+
+ if (!offsets)
+ return;
+
+ if (to_ns)
+ *val = timespec64_add(*val, *offsets);
+ else
+ *val = timespec64_sub(*val, *offsets);
+}
+
+void timens_clock_to_host(int clockid, struct timespec64 *val)
+{
+ clock_timens_fixup(clockid, val, false);
+}
+
+void timens_clock_from_host(int clockid, struct timespec64 *val)
+{
+ clock_timens_fixup(clockid, val, true);
+}
+
const struct proc_ns_operations timens_operations = {
.name = "time",
.type = CLONE_NEWTIME,
--
2.13.6
^ permalink raw reply related
* [RFC 04/20] timens: Introduce CLOCK_BOOTTIME offset
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Andrei Vagin, Dmitry Safonov, Adrian Reber,
Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
From: Andrei Vagin <avagin@openvz.org>
Adds boottime virtualisation for time namespace.
Provide clock_set() API to set boottime clock inside ns.
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
include/linux/timens_offsets.h | 1 +
kernel/time/posix-timers.c | 26 ++++++++++++++++++++++++++
2 files changed, 27 insertions(+)
diff --git a/include/linux/timens_offsets.h b/include/linux/timens_offsets.h
index 248b0c0bb92a..777530c46852 100644
--- a/include/linux/timens_offsets.h
+++ b/include/linux/timens_offsets.h
@@ -4,6 +4,7 @@
struct timens_offsets {
struct timespec64 monotonic_time_offset;
+ struct timespec64 monotonic_boottime_offset;
};
#endif
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 3c1f98760dec..d38835a21c5d 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -276,7 +276,32 @@ static int posix_get_coarse_res(const clockid_t which_clock, struct timespec64 *
static int posix_get_boottime(const clockid_t which_clock, struct timespec64 *tp)
{
+ struct timens_offsets *ns_offsets = current->nsproxy->time_ns->offsets;
+
ktime_get_boottime_ts64(tp);
+
+ if (!ns_offsets)
+ return 0;
+
+ *tp = timespec64_add(*tp, ns_offsets->monotonic_boottime_offset);
+
+ return 0;
+}
+
+static int posix_set_boottime(clockid_t which_clock, const struct timespec64 *tp)
+{
+ struct timens_offsets *ns_offsets = current->nsproxy->time_ns->offsets;
+ struct timespec64 ktp;
+
+ if (!ns_capable(current->nsproxy->time_ns->user_ns, CAP_SYS_TIME))
+ return -EPERM;
+
+ ktime_get_boottime_ts64(&ktp);
+
+ if (ns_offsets)
+ ns_offsets->monotonic_boottime_offset = timespec64_sub(*tp, ktp);
+ else
+ return -EINVAL;
return 0;
}
@@ -1362,6 +1387,7 @@ static const struct k_clock clock_tai = {
static const struct k_clock clock_boottime = {
.clock_getres = posix_get_hrtimer_res,
.clock_get = posix_get_boottime,
+ .clock_set = posix_set_boottime,
.nsleep = common_nsleep,
.timer_create = common_timer_create,
.timer_set = common_timer_set,
--
2.13.6
^ permalink raw reply related
* [RFC 03/20] timens: Introduce CLOCK_MONOTONIC offsets
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Andrei Vagin, Dmitry Safonov, Adrian Reber,
Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
From: Andrei Vagin <avagin@openvz.org>
Adds monotonic time virtualisation for time namespace.
Provide clock_set() API to set monotonic time inside ns.
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Co-developed-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
include/linux/timens_offsets.h | 1 +
kernel/time/posix-timers.c | 34 ++++++++++++++++++++++++++++++++++
2 files changed, 35 insertions(+)
diff --git a/include/linux/timens_offsets.h b/include/linux/timens_offsets.h
index 7d7cb68ea778..248b0c0bb92a 100644
--- a/include/linux/timens_offsets.h
+++ b/include/linux/timens_offsets.h
@@ -3,6 +3,7 @@
#define _LINUX_TIME_OFFSETS_H
struct timens_offsets {
+ struct timespec64 monotonic_time_offset;
};
#endif
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 4b9127e95430..3c1f98760dec 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -51,6 +51,7 @@
#include <linux/hashtable.h>
#include <linux/compat.h>
#include <linux/nospec.h>
+#include <linux/time_namespace.h>
#include "timekeeping.h"
#include "posix-timers.h"
@@ -205,12 +206,40 @@ static int posix_clock_realtime_adj(const clockid_t which_clock,
return do_adjtimex(t);
}
+static void timens_adjust_monotonic(struct timespec64 *tp)
+{
+ struct timens_offsets *ns_offsets = current->nsproxy->time_ns->offsets;
+
+ if (ns_offsets)
+ *tp = timespec64_add(*tp, ns_offsets->monotonic_time_offset);
+}
+
+static int posix_ktime_set_ts(clockid_t which_clock,
+ const struct timespec64 *tp)
+{
+ struct timens_offsets *ns_offsets = current->nsproxy->time_ns->offsets;
+ struct timespec64 ktp;
+
+ if (!ns_capable(current->nsproxy->time_ns->user_ns, CAP_SYS_TIME))
+ return -EPERM;
+
+ ktime_get_ts64(&ktp);
+
+ if (ns_offsets)
+ ns_offsets->monotonic_time_offset = timespec64_sub(*tp, ktp);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
/*
* Get monotonic time for posix timers
*/
static int posix_ktime_get_ts(clockid_t which_clock, struct timespec64 *tp)
{
ktime_get_ts64(tp);
+ timens_adjust_monotonic(tp);
return 0;
}
@@ -220,6 +249,7 @@ static int posix_ktime_get_ts(clockid_t which_clock, struct timespec64 *tp)
static int posix_get_monotonic_raw(clockid_t which_clock, struct timespec64 *tp)
{
ktime_get_raw_ts64(tp);
+ timens_adjust_monotonic(tp);
return 0;
}
@@ -234,6 +264,7 @@ static int posix_get_monotonic_coarse(clockid_t which_clock,
struct timespec64 *tp)
{
ktime_get_coarse_ts64(tp);
+ timens_adjust_monotonic(tp);
return 0;
}
@@ -1283,6 +1314,7 @@ static const struct k_clock clock_realtime = {
static const struct k_clock clock_monotonic = {
.clock_getres = posix_get_hrtimer_res,
.clock_get = posix_ktime_get_ts,
+ .clock_set = posix_ktime_set_ts,
.nsleep = common_nsleep,
.timer_create = common_timer_create,
.timer_set = common_timer_set,
@@ -1298,6 +1330,7 @@ static const struct k_clock clock_monotonic = {
static const struct k_clock clock_monotonic_raw = {
.clock_getres = posix_get_hrtimer_res,
.clock_get = posix_get_monotonic_raw,
+ .clock_set = posix_ktime_set_ts,
};
static const struct k_clock clock_realtime_coarse = {
@@ -1308,6 +1341,7 @@ static const struct k_clock clock_realtime_coarse = {
static const struct k_clock clock_monotonic_coarse = {
.clock_getres = posix_get_coarse_res,
.clock_get = posix_get_monotonic_coarse,
+ .clock_set = posix_ktime_set_ts,
};
static const struct k_clock clock_tai = {
--
2.13.6
^ permalink raw reply related
* [RFC 02/20] timens: Add timens_offsets
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Andrei Vagin, Dmitry Safonov, Adrian Reber,
Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
From: Andrei Vagin <avagin@openvz.org>
Introduce offsets for time namespace. They will contain adjustment
needed to convert clocks to/from host's.
Allocate one page for each time namespace that will be premapped into
userspace with vvar pages.
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
include/linux/time_namespace.h | 2 ++
include/linux/timens_offsets.h | 8 ++++++++
kernel/time_namespace.c | 14 ++++++++++++--
3 files changed, 22 insertions(+), 2 deletions(-)
create mode 100644 include/linux/timens_offsets.h
diff --git a/include/linux/time_namespace.h b/include/linux/time_namespace.h
index bf98f35efe07..4960c54f1b33 100644
--- a/include/linux/time_namespace.h
+++ b/include/linux/time_namespace.h
@@ -8,6 +8,7 @@
#include <linux/nsproxy.h>
#include <linux/ns_common.h>
#include <linux/err.h>
+#include <linux/timens_offsets.h>
struct user_namespace;
extern struct user_namespace init_user_ns;
@@ -17,6 +18,7 @@ struct time_namespace {
struct user_namespace *user_ns;
struct ucounts *ucounts;
struct ns_common ns;
+ struct timens_offsets *offsets;
} __randomize_layout;
extern struct time_namespace init_time_ns;
diff --git a/include/linux/timens_offsets.h b/include/linux/timens_offsets.h
new file mode 100644
index 000000000000..7d7cb68ea778
--- /dev/null
+++ b/include/linux/timens_offsets.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_TIME_OFFSETS_H
+#define _LINUX_TIME_OFFSETS_H
+
+struct timens_offsets {
+};
+
+#endif
diff --git a/kernel/time_namespace.c b/kernel/time_namespace.c
index 902cd9c22159..a985529754b4 100644
--- a/kernel/time_namespace.c
+++ b/kernel/time_namespace.c
@@ -13,6 +13,7 @@
#include <linux/user_namespace.h>
#include <linux/proc_ns.h>
#include <linux/sched/task.h>
+#include <linux/mm.h>
static struct ucounts *inc_time_namespaces(struct user_namespace *ns)
{
@@ -44,6 +45,7 @@ static struct time_namespace *clone_time_ns(struct user_namespace *user_ns,
{
struct time_namespace *ns;
struct ucounts *ucounts;
+ struct page *page;
int err;
err = -ENOSPC;
@@ -56,15 +58,22 @@ static struct time_namespace *clone_time_ns(struct user_namespace *user_ns,
if (!ns)
goto fail_dec;
+ page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!page)
+ goto fail_free;
+ ns->offsets = page_address(page);
+ BUILD_BUG_ON(sizeof(*ns->offsets) > PAGE_SIZE);
+
err = ns_alloc_inum(&ns->ns);
if (err)
- goto fail_free;
+ goto fail_page;
ns->ucounts = ucounts;
ns->ns.ops = &timens_operations;
ns->user_ns = get_user_ns(user_ns);
return ns;
-
+fail_page:
+ free_page((unsigned long)ns->offsets);
fail_free:
kfree(ns);
fail_dec:
@@ -93,6 +102,7 @@ void free_time_ns(struct kref *kref)
struct time_namespace *ns;
ns = container_of(kref, struct time_namespace, kref);
+ free_page((unsigned long)ns->offsets);
dec_time_namespaces(ns->ucounts);
put_user_ns(ns->user_ns);
ns_free_inum(&ns->ns);
--
2.13.6
^ permalink raw reply related
* [RFC 01/20] ns: Introduce Time Namespace
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Andrei Vagin, Dmitry Safonov, Adrian Reber,
Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86
In-Reply-To: <20180919205037.9574-1-dima@arista.com>
From: Andrei Vagin <avagin@openvz.org>
Time Namespace isolates clock values.
The kernel provides access to several clocks CLOCK_REALTIME,
CLOCK_MONOTONIC, CLOCK_BOOTTIME, etc.
CLOCK_REALTIME
System-wide clock that measures real (i.e., wall-clock) time.
CLOCK_MONOTONIC
Clock that cannot be set and represents monotonic time since
some unspecified starting point.
CLOCK_BOOTTIME
Identical to CLOCK_MONOTONIC, except it also includes any time
that the system is suspended.
For many users, the time namespace means the ability to changes time in
a container (CLOCK_REALTIME).
But in a context of the checkpoint/restore functionality, monotonic and
bootime clocks become interesting. Both clocks are monotonic with
unspecified staring points. These clocks are widely used to measure time
slices, set timers. After restoring or migrating processes, we have to
guarantee that they never go backward. In an ideal case, the behavior of
these clocks should be the same as for a case when a whole system is
suspended. All this means that we need to be able to set CLOCK_MONOTONIC
and CLOCK_BOOTTIME clocks, what can be done by adding per-namespace
offsets for clocks.
Link: https://criu.org/Time_namespace
Link: https://lists.openvz.org/pipermail/criu/2018-June/041504.html
Signed-off-by: Andrei Vagin <avagin@openvz.org>
Co-developed-by: Dmitry Safonov <dima@arista.com>
Signed-off-by: Dmitry Safonov <dima@arista.com>
---
fs/proc/namespaces.c | 3 +
include/linux/nsproxy.h | 1 +
include/linux/proc_ns.h | 1 +
include/linux/time_namespace.h | 59 ++++++++++++++
include/linux/user_namespace.h | 1 +
include/uapi/linux/sched.h | 1 +
init/Kconfig | 7 ++
kernel/Makefile | 1 +
kernel/fork.c | 3 +-
kernel/nsproxy.c | 19 ++++-
kernel/time_namespace.c | 169 +++++++++++++++++++++++++++++++++++++++++
11 files changed, 262 insertions(+), 3 deletions(-)
create mode 100644 include/linux/time_namespace.h
create mode 100644 kernel/time_namespace.c
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
index dd2b35f78b09..faee2facb4f3 100644
--- a/fs/proc/namespaces.c
+++ b/fs/proc/namespaces.c
@@ -33,6 +33,9 @@ static const struct proc_ns_operations *ns_entries[] = {
#ifdef CONFIG_CGROUPS
&cgroupns_operations,
#endif
+#ifdef CONFIG_TIME_NS
+ &timens_operations,
+#endif
};
static const char *proc_ns_get_link(struct dentry *dentry,
diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h
index 2ae1b1a4d84d..5355229c0ce7 100644
--- a/include/linux/nsproxy.h
+++ b/include/linux/nsproxy.h
@@ -35,6 +35,7 @@ struct nsproxy {
struct mnt_namespace *mnt_ns;
struct pid_namespace *pid_ns_for_children;
struct net *net_ns;
+ struct time_namespace *time_ns;
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..b97b802ab04d 100644
--- a/include/linux/proc_ns.h
+++ b/include/linux/proc_ns.h
@@ -32,6 +32,7 @@ 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;
/*
* We always define these enumerators
diff --git a/include/linux/time_namespace.h b/include/linux/time_namespace.h
new file mode 100644
index 000000000000..bf98f35efe07
--- /dev/null
+++ b/include/linux/time_namespace.h
@@ -0,0 +1,59 @@
+/* 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 time_namespace {
+ struct kref kref;
+ struct user_namespace *user_ns;
+ struct ucounts *ucounts;
+ struct ns_common ns;
+} __randomize_layout;
+extern struct time_namespace init_time_ns;
+
+#ifdef CONFIG_TIME_NS
+static inline struct time_namespace *get_time_ns(struct time_namespace *ns)
+{
+ kref_get(&ns->kref);
+ return ns;
+}
+
+extern struct time_namespace *copy_time_ns(unsigned long flags,
+ struct user_namespace *user_ns, struct time_namespace *old_ns);
+extern void free_time_ns(struct kref *kref);
+
+static inline void put_time_ns(struct time_namespace *ns)
+{
+ kref_put(&ns->kref, free_time_ns);
+}
+
+#else
+static inline void get_time_ns(struct time_namespace *ns)
+{
+}
+
+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;
+}
+
+#endif
+
+#endif /* _LINUX_TIMENS_H */
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index d6b74b91096b..bf84f93dc411 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 22627f80063e..adffac53c76e 100644
--- a/include/uapi/linux/sched.h
+++ b/include/uapi/linux/sched.h
@@ -10,6 +10,7 @@
#define CLONE_FS 0x00000200 /* set if fs info shared between processes */
#define CLONE_FILES 0x00000400 /* set if open files shared between processes */
#define CLONE_SIGHAND 0x00000800 /* set if signal handlers and blocked signals shared */
+#define CLONE_NEWTIME 0x00001000 /* New time namespace */
#define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */
#define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */
#define CLONE_PARENT 0x00008000 /* set if we want to have the same parent as the cloner */
diff --git a/init/Kconfig b/init/Kconfig
index 1e234e2f1cba..dc2b40f7d73f 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -927,6 +927,13 @@ config UTS_NS
In this namespace tasks see different info provided with the
uname() system call
+config TIME_NS
+ bool "TIME namespace"
+ default y
+ help
+ In this namespace boottime and monotonic clocks can be set.
+ The time will keep going with the same pace.
+
config IPC_NS
bool "IPC namespace"
depends on (SYSVIPC || POSIX_MQUEUE)
diff --git a/kernel/Makefile b/kernel/Makefile
index 7a63d567fdb5..bc92feb6987d 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
obj-$(CONFIG_COMPAT) += compat.o
obj-$(CONFIG_CGROUPS) += cgroup/
obj-$(CONFIG_UTS_NS) += utsname.o
+obj-$(CONFIG_TIME_NS) += time_namespace.o
obj-$(CONFIG_USER_NS) += user_namespace.o
obj-$(CONFIG_PID_NS) += pid_namespace.o
obj-$(CONFIG_IKCONFIG) += configs.o
diff --git a/kernel/fork.c b/kernel/fork.c
index f0b58479534f..384f88912b63 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -2365,7 +2365,8 @@ static int check_unshare_flags(unsigned long unshare_flags)
if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET|
- CLONE_NEWUSER|CLONE_NEWPID|CLONE_NEWCGROUP))
+ CLONE_NEWUSER|CLONE_NEWPID|CLONE_NEWCGROUP|
+ CLONE_NEWTIME))
return -EINVAL;
/*
* Not implemented, but pretend it works if there is nothing
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
index f6c5d330059a..5e482e538365 100644
--- a/kernel/nsproxy.c
+++ b/kernel/nsproxy.c
@@ -22,6 +22,7 @@
#include <linux/pid_namespace.h>
#include <net/net_namespace.h>
#include <linux/ipc_namespace.h>
+#include <linux/time_namespace.h>
#include <linux/proc_ns.h>
#include <linux/file.h>
#include <linux/syscalls.h>
@@ -44,6 +45,9 @@ struct nsproxy init_nsproxy = {
#ifdef CONFIG_CGROUPS
.cgroup_ns = &init_cgroup_ns,
#endif
+#ifdef CONFIG_TIME_NS
+ .time_ns = &init_time_ns,
+#endif
};
static inline struct nsproxy *create_nsproxy(void)
@@ -110,8 +114,16 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
goto out_net;
}
+ new_nsp->time_ns = copy_time_ns(flags, user_ns, tsk->nsproxy->time_ns);
+ if (IS_ERR(new_nsp->time_ns)) {
+ err = PTR_ERR(new_nsp->time_ns);
+ goto out_time;
+ }
+
return new_nsp;
+out_time:
+ put_net(new_nsp->net_ns);
out_net:
put_cgroup_ns(new_nsp->cgroup_ns);
out_cgroup:
@@ -143,7 +155,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
CLONE_NEWPID | CLONE_NEWNET |
- CLONE_NEWCGROUP)))) {
+ CLONE_NEWCGROUP | CLONE_NEWTIME)))) {
get_nsproxy(old_ns);
return 0;
}
@@ -180,6 +192,8 @@ void free_nsproxy(struct nsproxy *ns)
put_ipc_ns(ns->ipc_ns);
if (ns->pid_ns_for_children)
put_pid_ns(ns->pid_ns_for_children);
+ if (ns->time_ns)
+ put_time_ns(ns->time_ns);
put_cgroup_ns(ns->cgroup_ns);
put_net(ns->net_ns);
kmem_cache_free(nsproxy_cachep, ns);
@@ -196,7 +210,8 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags,
int err = 0;
if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
- CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWCGROUP)))
+ CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWCGROUP |
+ CLONE_NEWTIME)))
return 0;
user_ns = new_cred ? new_cred->user_ns : current_user_ns();
diff --git a/kernel/time_namespace.c b/kernel/time_namespace.c
new file mode 100644
index 000000000000..902cd9c22159
--- /dev/null
+++ b/kernel/time_namespace.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Andrei Vagin <avagin@openvz.org>
+ * Author: Dmitry Safonov <dima@arista.com>
+ */
+
+#include <linux/export.h>
+#include <linux/time.h>
+#include <linux/time_namespace.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/cred.h>
+#include <linux/user_namespace.h>
+#include <linux/proc_ns.h>
+#include <linux/sched/task.h>
+
+static struct ucounts *inc_time_namespaces(struct user_namespace *ns)
+{
+ return inc_ucount(ns, current_euid(), UCOUNT_TIME_NAMESPACES);
+}
+
+static void dec_time_namespaces(struct ucounts *ucounts)
+{
+ dec_ucount(ucounts, UCOUNT_TIME_NAMESPACES);
+}
+
+static struct time_namespace *create_time_ns(void)
+{
+ struct time_namespace *time_ns;
+
+ time_ns = kmalloc(sizeof(struct time_namespace), GFP_KERNEL);
+ if (time_ns)
+ kref_init(&time_ns->kref);
+ return time_ns;
+}
+
+/*
+ * Clone a new ns copying an original timename, setting refcount to 1
+ * @old_ns: namespace to clone
+ * Return ERR_PTR(-ENOMEM) on error (failure to allocate), new ns otherwise
+ */
+static struct time_namespace *clone_time_ns(struct user_namespace *user_ns,
+ struct time_namespace *old_ns)
+{
+ struct time_namespace *ns;
+ struct ucounts *ucounts;
+ int err;
+
+ err = -ENOSPC;
+ ucounts = inc_time_namespaces(user_ns);
+ if (!ucounts)
+ goto fail;
+
+ err = -ENOMEM;
+ ns = create_time_ns();
+ if (!ns)
+ goto fail_dec;
+
+ err = ns_alloc_inum(&ns->ns);
+ if (err)
+ goto fail_free;
+
+ ns->ucounts = ucounts;
+ ns->ns.ops = &timens_operations;
+ ns->user_ns = get_user_ns(user_ns);
+ return ns;
+
+fail_free:
+ kfree(ns);
+fail_dec:
+ dec_time_namespaces(ucounts);
+fail:
+ return ERR_PTR(err);
+}
+
+/*
+ * Copy task tsk's time namespace, or clone it if flags
+ * specifies CLONE_NEWTIME. In latter case, changes to the
+ * timename of this process won't be seen by parent, and vice
+ * versa.
+ */
+struct time_namespace *copy_time_ns(unsigned long flags,
+ struct user_namespace *user_ns, struct time_namespace *old_ns)
+{
+ if (!(flags & CLONE_NEWTIME))
+ return get_time_ns(old_ns);
+
+ return clone_time_ns(user_ns, old_ns);
+}
+
+void free_time_ns(struct kref *kref)
+{
+ struct time_namespace *ns;
+
+ ns = container_of(kref, struct time_namespace, kref);
+ dec_time_namespaces(ns->ucounts);
+ put_user_ns(ns->user_ns);
+ ns_free_inum(&ns->ns);
+ kfree(ns);
+}
+
+static inline struct time_namespace *to_time_ns(struct ns_common *ns)
+{
+ return container_of(ns, struct time_namespace, ns);
+}
+
+static struct ns_common *timens_get(struct task_struct *task)
+{
+ struct time_namespace *ns = NULL;
+ struct nsproxy *nsproxy;
+
+ task_lock(task);
+ nsproxy = task->nsproxy;
+ if (nsproxy) {
+ ns = nsproxy->time_ns;
+ get_time_ns(ns);
+ }
+ task_unlock(task);
+
+ return ns ? &ns->ns : NULL;
+}
+
+static void timens_put(struct ns_common *ns)
+{
+ put_time_ns(to_time_ns(ns));
+}
+
+static int timens_install(struct nsproxy *nsproxy, struct ns_common *new)
+{
+ struct time_namespace *ns = to_time_ns(new);
+
+ if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
+ !ns_capable(current_user_ns(), CAP_SYS_ADMIN))
+ return -EPERM;
+
+ get_time_ns(ns);
+ put_time_ns(nsproxy->time_ns);
+ nsproxy->time_ns = ns;
+ return 0;
+}
+
+static struct user_namespace *timens_owner(struct ns_common *ns)
+{
+ return to_time_ns(ns)->user_ns;
+}
+
+const struct proc_ns_operations timens_operations = {
+ .name = "time",
+ .type = CLONE_NEWTIME,
+ .get = timens_get,
+ .put = timens_put,
+ .install = timens_install,
+ .owner = timens_owner,
+};
+
+struct time_namespace init_time_ns = {
+ .kref = KREF_INIT(2),
+ .user_ns = &init_user_ns,
+ .ns.inum = PROC_UTS_INIT_INO,
+#ifdef CONFIG_UTS_NS
+ .ns.ops = &timens_operations,
+#endif
+};
+
+static int __init time_ns_init(void)
+{
+ return 0;
+}
+subsys_initcall(time_ns_init);
--
2.13.6
^ permalink raw reply related
* [RFC 00/20] ns: Introduce Time Namespace
From: Dmitry Safonov @ 2018-09-19 20:50 UTC (permalink / raw)
To: linux-kernel
Cc: Dmitry Safonov, Dmitry Safonov, Adrian Reber, Andrei Vagin,
Andy Lutomirski, Christian Brauner, Cyrill Gorcunov,
Eric W. Biederman, H. Peter Anvin, Ingo Molnar, Jeff Dike,
Oleg Nesterov, Pavel Emelyanov, Shuah Khan, Thomas Gleixner,
containers, criu, linux-api, x86, Alexey Dobriyan,
linux-kselftest
Discussions around time virtualization are there for a long time.
The first attempt to implement time namespace was in 2006 by Jeff Dike.
>From that time, the topic appears on and off in various discussions.
There are two main use cases for time namespaces:
1. change date and time inside a container;
2. adjust clocks for a container restored from a checkpoint.
“It seems like this might be one of the last major obstacles keeping
migration from being used in production systems, given that not all
containers and connections can be migrated as long as a time dependency
is capable of messing it up.” (by github.com/dav-ell)
The kernel provides access to several clocks: CLOCK_REALTIME,
CLOCK_MONOTONIC, CLOCK_BOOTTIME. Last two clocks are monotonous, but the
start points for them are not defined and are different for each running
system. When a container is migrated from one node to another, all
clocks have to be restored into consistent states; in other words, they
have to continue running from the same points where they have been
dumped.
The main idea behind this patch set is adding per-namespace offsets for
system clocks. When a process in a non-root time namespace requests
time of a clock, a namespace offset is added to the current value of
this clock on a host and the sum is returned.
All offsets are placed on a separate page, this allows up to map it as
part of vvar into user processes and use offsets from vdso calls.
Now offsets are implemented for CLOCK_MONOTONIC and CLOCK_BOOTTIME
clocks.
Questions to discuss:
* Clone flags exhaustion. Currently there is only one unused clone flag
bit left, and it may be worth to use it to extend arguments of the clone
system call.
* Realtime clock implementation details:
Is having a simple offset enough?
What to do when date and time is changed on the host?
Is there a need to adjust vfs modification and creation times?
Implementation for adjtime() syscall.
Cc: Dmitry Safonov <0x7f454c46@gmail.com>
Cc: Adrian Reber <adrian@lisas.de>
Cc: Andrei Vagin <avagin@openvz.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Christian Brauner <christian.brauner@ubuntu.com>
Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jeff Dike <jdike@addtoit.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Pavel Emelyanov <xemul@virtuozzo.com>
Cc: Shuah Khan <shuah@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: containers@lists.linux-foundation.org
Cc: criu@openvz.org
Cc: linux-api@vger.kernel.org
Cc: x86@kernel.org
Andrei Vagin (12):
ns: Introduce Time Namespace
timens: Add timens_offsets
timens: Introduce CLOCK_MONOTONIC offsets
timens: Introduce CLOCK_BOOTTIME offset
timerfd/timens: Take into account ns clock offsets
kernel: Take into account timens clock offsets in clock_nanosleep
x86/vdso/timens: Add offsets page in vvar
x86/vdso: Use set_normalized_timespec() to avoid 32 bit overflow
posix-timers/timens: Take into account clock offsets
selftest/timens: Add test for timerfd
selftest/timens: Add test for clock_nanosleep
timens/selftest: Add timer offsets test
Dmitry Safonov (8):
timens: Shift /proc/uptime
x86/vdso: Restrict splitting vvar vma
x86/vdso: Purge timens page on setns()/unshare()/clone()
x86/vdso: Look for vvar vma to purge timens page
timens: Add align for timens_offsets
timens: Optimize zero-offsets
selftest: Add Time Namespace test for supported clocks
timens/selftest: Add procfs selftest
arch/Kconfig | 5 +
arch/x86/Kconfig | 1 +
arch/x86/entry/vdso/vclock_gettime.c | 52 +++++
arch/x86/entry/vdso/vdso-layout.lds.S | 9 +-
arch/x86/entry/vdso/vdso2c.c | 3 +
arch/x86/entry/vdso/vma.c | 67 +++++++
arch/x86/include/asm/vdso.h | 2 +
fs/proc/namespaces.c | 3 +
fs/proc/uptime.c | 3 +
fs/timerfd.c | 16 +-
include/linux/nsproxy.h | 1 +
include/linux/proc_ns.h | 1 +
include/linux/time_namespace.h | 72 +++++++
include/linux/timens_offsets.h | 25 +++
include/linux/user_namespace.h | 1 +
include/uapi/linux/sched.h | 1 +
init/Kconfig | 8 +
kernel/Makefile | 1 +
kernel/fork.c | 3 +-
kernel/nsproxy.c | 19 +-
kernel/time/hrtimer.c | 8 +
kernel/time/posix-timers.c | 89 ++++++++-
kernel/time/posix-timers.h | 2 +
kernel/time_namespace.c | 230 +++++++++++++++++++++++
tools/testing/selftests/timens/.gitignore | 5 +
tools/testing/selftests/timens/Makefile | 6 +
tools/testing/selftests/timens/clock_nanosleep.c | 98 ++++++++++
tools/testing/selftests/timens/config | 1 +
tools/testing/selftests/timens/log.h | 21 +++
tools/testing/selftests/timens/procfs.c | 145 ++++++++++++++
tools/testing/selftests/timens/timens.c | 196 +++++++++++++++++++
tools/testing/selftests/timens/timer.c | 95 ++++++++++
tools/testing/selftests/timens/timerfd.c | 96 ++++++++++
33 files changed, 1272 insertions(+), 13 deletions(-)
create mode 100644 include/linux/time_namespace.h
create mode 100644 include/linux/timens_offsets.h
create mode 100644 kernel/time_namespace.c
create mode 100644 tools/testing/selftests/timens/.gitignore
create mode 100644 tools/testing/selftests/timens/Makefile
create mode 100644 tools/testing/selftests/timens/clock_nanosleep.c
create mode 100644 tools/testing/selftests/timens/config
create mode 100644 tools/testing/selftests/timens/log.h
create mode 100644 tools/testing/selftests/timens/procfs.c
create mode 100644 tools/testing/selftests/timens/timens.c
create mode 100644 tools/testing/selftests/timens/timer.c
create mode 100644 tools/testing/selftests/timens/timerfd.c
--
2.13.6
^ permalink raw reply
* Re: [RFC PATCH] glibc: Perform rseq(2) registration at nptl init and thread creation
From: Mathieu Desnoyers @ 2018-09-19 20:10 UTC (permalink / raw)
To: Joseph Myers
Cc: Szabolcs Nagy, carlos, Florian Weimer, nd, Thomas Gleixner,
Ben Maurer, Peter Zijlstra, Paul E. McKenney, Boqun Feng,
Will Deacon, Dave Watson, Paul Turner, libc-alpha, linux-kernel,
linux-api
In-Reply-To: <alpine.DEB.2.21.1809191944470.4536@digraph.polyomino.org.uk>
----- On Sep 19, 2018, at 3:49 PM, Joseph Myers joseph@codesourcery.com wrote:
> On Wed, 19 Sep 2018, Szabolcs Nagy wrote:
>
>> i don't think there is precedent for exposing tls symbol in glibc
>> (e.g. errno is exposed via __errno_location function) so there
>> might be issues with this (but i don't have immediate concerns).
>
> There have been suggestions to expose TLS errno - but also suggestions
> that use of __errno_location is more efficient, at least in terms of code
> size everywhere errno is accessed (for some ABIs, anyway).
AFAIU, the trade-off is different between the errno use-case and the
rseq use-case.
If my understanding is correct, errno is not supposed to be used in
fast-paths, only when an error is returned. So size is more important than
speed there.
Comparatively, rseq is _meant_ to speed up fast-paths. A per-cpu statistics
counter can be incremented in 2ns with rseq on a Intel E5-2630, which is faster
than a simple function call.
So for rseq, we should favor speed over space, which means the user applications
and libraries would need access to the TLS symbol without requiring an
accessor function. That's also why I'm using the initial-exec tls model
rather than the global-dynamic: I want to make sure no function call is
generated there.
> The ABI tests have code that would list .tbss symbols as "T" in ABI test
> baselines, but no existing ABI baselines use that.
Thanks,
Mathieu
--
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com
^ permalink raw reply
* Re: [PATCH v6 4/5] seccomp: add support for passing fds via USER_NOTIF
From: Andy Lutomirski @ 2018-09-19 19:58 UTC (permalink / raw)
To: Tycho Andersen
Cc: Kees Cook, LKML, Linux Containers, Linux API, Oleg Nesterov,
Eric W . Biederman, Serge E . Hallyn, Christian Brauner,
Tyler Hicks, Akihiro Suda, Jann Horn
In-Reply-To: <20180919143842.GN4672@cisco>
On Wed, Sep 19, 2018 at 7:38 AM, Tycho Andersen <tycho@tycho.ws> wrote:
> On Wed, Sep 19, 2018 at 07:19:56AM -0700, Andy Lutomirski wrote:
>>
>>
>> > On Sep 19, 2018, at 2:55 AM, Tycho Andersen <tycho@tycho.ws> wrote:
>> >
>> >> On Wed, Sep 12, 2018 at 04:52:38PM -0700, Andy Lutomirski wrote:
>> >>> On Thu, Sep 6, 2018 at 8:28 AM, Tycho Andersen <tycho@tycho.ws> wrote:
>> >>> The idea here is that the userspace handler should be able to pass an fd
>> >>> back to the trapped task, for example so it can be returned from socket().
>> >>>
>> >>> I've proposed one API here, but I'm open to other options. In particular,
>> >>> this only lets you return an fd from a syscall, which may not be enough in
>> >>> all cases. For example, if an fd is written to an output parameter instead
>> >>> of returned, the current API can't handle this. Another case is that
>> >>> netlink takes as input fds sometimes (IFLA_NET_NS_FD, e.g.). If netlink
>> >>> ever decides to install an fd and output it, we wouldn't be able to handle
>> >>> this either.
>> >>
>> >> An alternative could be to have an API (an ioctl on the listener,
>> >> perhaps) that just copies an fd into the tracee. There would be the
>> >> obvious set of options: do we replace an existing fd or allocate a new
>> >> one, and is it CLOEXEC. Then the tracer could add an fd and then
>> >> return it just like it's a regular number.
>> >>
>> >> I feel like this would be more flexible and conceptually simpler, but
>> >> maybe a little slower for the common cases. What do you think?
>> >
>> > I'm just implementing this now, and there's one question: when do we
>> > actually do the fd install? Should we do it when the user calls
>> > SECCOMP_NOTIF_PUT_FD, or when the actual response is sent? It feels
>> > like we should do it when the response is sent, instead of doing it
>> > right when SECCOMP_NOTIF_PUT_FD is called, since if there's a
>> > subsequent signal and the tracer decides to discard the response,
>> > we'll have to implement some delete mechanism to delete the fd, but it
>> > would have already been visible to the process, etc. So I'll go
>> > forward with this unless there are strong objections, but I thought
>> > I'd point it out just to avoid another round trip.
>> >
>> >
>>
>> Can you do that non-racily? That is, you need to commit to an fd *number* right away, but what if another thread uses the number before you actually install the fd?
>
> I was thinking we could just do an __alloc_fd() and then do the
> fd_install() when the response is sent or clean up the case that the
> listener or task dies. I haven't actually tried to run the code yet,
> so it's possible the locking won't work :)
I would be very surprised if the locking works. How can you run a
thread in a process when another thread has allocated but not
installed an fd and is blocked for an arbitrarily long time?
>
>> Do we really allow non-“kill” signals to interrupt the whole process? It might be the case that we don’t really need to clean up from signals if there’s a guarantee that the thread dies.
>
> Yes, we do, because of this: https://lkml.org/lkml/2018/3/15/1122
>
I'm still not sure I see the problem. Suppose I'm implementing a user
notifier for a nasty syscall like recvmsg(). If I'm the tracer, by
the time I decide to install an fd, I've committed to returning
something other than -EINTR, even if a non-fatal signal is sent before
I finish. No rollback should be necessary.
In the (unlikely?) event that some tracer needs to be able to rollback
an fd installation to return -EINTR, a SECCOMP_NOTIF_CLOSE_FD
operation should be good enough, I think. Or maybe PUT_FD can put -1
to delete an fd.
--Andy
^ permalink raw reply
* Re: [RFC PATCH] glibc: Perform rseq(2) registration at nptl init and thread creation
From: Joseph Myers @ 2018-09-19 19:49 UTC (permalink / raw)
To: Szabolcs Nagy
Cc: Mathieu Desnoyers, Carlos O'Donell, Florian Weimer, nd,
Thomas Gleixner, Ben Maurer, Peter Zijlstra, Paul E. McKenney,
Boqun Feng, Will Deacon, Dave Watson, Paul Turner, libc-alpha,
linux-kernel, linux-api
In-Reply-To: <c58d4d6e-f22a-f5d9-e23a-5bd72cec1a86@arm.com>
On Wed, 19 Sep 2018, Szabolcs Nagy wrote:
> i don't think there is precedent for exposing tls symbol in glibc
> (e.g. errno is exposed via __errno_location function) so there
> might be issues with this (but i don't have immediate concerns).
There have been suggestions to expose TLS errno - but also suggestions
that use of __errno_location is more efficient, at least in terms of code
size everywhere errno is accessed (for some ABIs, anyway).
The ABI tests have code that would list .tbss symbols as "T" in ABI test
baselines, but no existing ABI baselines use that.
--
Joseph S. Myers
joseph@codesourcery.com
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox