]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
vdso/timens: Move functions to new file
authorThomas Weißschuh <thomas.weissschuh@linutronix.de>
Thu, 26 Mar 2026 11:42:30 +0000 (12:42 +0100)
committerThomas Gleixner <tglx@kernel.org>
Thu, 26 Mar 2026 14:44:22 +0000 (15:44 +0100)
As a preparation of the untangling of time namespaces and the vDSO, move
the glue functions between those subsystems into a new file.

While at it, switch the mutex lock and mmap_read_lock() in the vDSO
namespace code to guard().

Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Link: https://patch.msgid.link/20260326-vdso-timens-decoupling-v2-1-c82693a7775f@linutronix.de
MAINTAINERS
include/linux/time_namespace.h
kernel/time/Makefile
kernel/time/namespace.c
kernel/time/namespace_internal.h [new file with mode: 0644]
kernel/time/namespace_vdso.c [new file with mode: 0644]
lib/vdso/datastore.c

index 77fdfcb55f0607712cc4c5590fb7cd1d13d71105..6ad74a5196d1d514b66bb3918639a5a559db1052 100644 (file)
@@ -10768,6 +10768,7 @@ S:      Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git timers/vdso
 F:     include/asm-generic/vdso/vsyscall.h
 F:     include/vdso/
+F:     kernel/time/namespace_vdso.c
 F:     kernel/time/vsyscall.c
 F:     lib/vdso/
 F:     tools/testing/selftests/vDSO/
@@ -21000,6 +21001,7 @@ F:      include/trace/events/timer*
 F:     kernel/time/itimer.c
 F:     kernel/time/posix-*
 F:     kernel/time/namespace.c
+F:     kernel/time/namespace_vdso.c
 
 POWER MANAGEMENT CORE
 M:     "Rafael J. Wysocki" <rafael@kernel.org>
index c514d0e5a45cb266961ed53a4c4689f9907699f8..0421bf1b13d7a9116de46c7d1aaed16eecd687b4 100644 (file)
@@ -38,8 +38,6 @@ static inline struct time_namespace *to_time_ns(struct ns_common *ns)
        return container_of(ns, struct time_namespace, ns);
 }
 void __init time_ns_init(void);
-extern int vdso_join_timens(struct task_struct *task,
-                           struct time_namespace *ns);
 extern void timens_commit(struct task_struct *tsk, struct time_namespace *ns);
 
 static inline struct time_namespace *get_time_ns(struct time_namespace *ns)
@@ -117,12 +115,6 @@ static inline void __init time_ns_init(void)
 {
 }
 
-static inline int vdso_join_timens(struct task_struct *task,
-                                  struct time_namespace *ns)
-{
-       return 0;
-}
-
 static inline void timens_commit(struct task_struct *tsk,
                                 struct time_namespace *ns)
 {
index f7d52d9543cc7a2d1a18db179229a8d7936317d3..662bccb3b7f9a0e08c3ad5639211c772a980e654 100644 (file)
@@ -29,6 +29,6 @@ endif
 obj-$(CONFIG_GENERIC_GETTIMEOFDAY)             += vsyscall.o
 obj-$(CONFIG_DEBUG_FS)                         += timekeeping_debug.o
 obj-$(CONFIG_TEST_UDELAY)                      += test_udelay.o
-obj-$(CONFIG_TIME_NS)                          += namespace.o
+obj-$(CONFIG_TIME_NS)                          += namespace.o namespace_vdso.o
 obj-$(CONFIG_TEST_CLOCKSOURCE_WATCHDOG)                += clocksource-wdtest.o
 obj-$(CONFIG_TIME_KUNIT_TEST)                  += time_test.o
index 652744e00eb4c0b1bf29f0098f17543b93b56e9d..903f55a2dfb2a787caf527ac53f86a55a17308d8 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/err.h>
 #include <linux/mm.h>
 
-#include <vdso/datapage.h>
+#include "namespace_internal.h"
 
 ktime_t do_timens_ktime_to_host(clockid_t clockid, ktime_t tim,
                                struct timens_offsets *ns_offsets)
@@ -138,117 +138,7 @@ struct time_namespace *copy_time_ns(u64 flags,
        return clone_time_ns(user_ns, old_ns);
 }
 
-static struct timens_offset offset_from_ts(struct timespec64 off)
-{
-       struct timens_offset ret;
-
-       ret.sec = off.tv_sec;
-       ret.nsec = off.tv_nsec;
-
-       return ret;
-}
-
-/*
- * A time namespace VVAR page has the same layout as the VVAR page which
- * contains the system wide VDSO data.
- *
- * For a normal task the VVAR pages are installed in the normal ordering:
- *     VVAR
- *     PVCLOCK
- *     HVCLOCK
- *     TIMENS   <- Not really required
- *
- * Now for a timens task the pages are installed in the following order:
- *     TIMENS
- *     PVCLOCK
- *     HVCLOCK
- *     VVAR
- *
- * The check for vdso_clock->clock_mode is in the unlikely path of
- * the seq begin magic. So for the non-timens case most of the time
- * 'seq' is even, so the branch is not taken.
- *
- * If 'seq' is odd, i.e. a concurrent update is in progress, the extra check
- * for vdso_clock->clock_mode is a non-issue. The task is spin waiting for the
- * update to finish and for 'seq' to become even anyway.
- *
- * Timens page has vdso_clock->clock_mode set to VDSO_CLOCKMODE_TIMENS which
- * enforces the time namespace handling path.
- */
-static void timens_setup_vdso_clock_data(struct vdso_clock *vc,
-                                        struct time_namespace *ns)
-{
-       struct timens_offset *offset = vc->offset;
-       struct timens_offset monotonic = offset_from_ts(ns->offsets.monotonic);
-       struct timens_offset boottime = offset_from_ts(ns->offsets.boottime);
-
-       vc->seq                         = 1;
-       vc->clock_mode                  = VDSO_CLOCKMODE_TIMENS;
-       offset[CLOCK_MONOTONIC]         = monotonic;
-       offset[CLOCK_MONOTONIC_RAW]     = monotonic;
-       offset[CLOCK_MONOTONIC_COARSE]  = monotonic;
-       offset[CLOCK_BOOTTIME]          = boottime;
-       offset[CLOCK_BOOTTIME_ALARM]    = boottime;
-}
-
-struct page *find_timens_vvar_page(struct vm_area_struct *vma)
-{
-       if (likely(vma->vm_mm == current->mm))
-               return current->nsproxy->time_ns->vvar_page;
-
-       /*
-        * VM_PFNMAP | VM_IO protect .fault() handler from being called
-        * through interfaces like /proc/$pid/mem or
-        * process_vm_{readv,writev}() as long as there's no .access()
-        * in special_mapping_vmops().
-        * For more details check_vma_flags() and __access_remote_vm()
-        */
-
-       WARN(1, "vvar_page accessed remotely");
-
-       return NULL;
-}
-
-/*
- * Protects possibly multiple offsets writers racing each other
- * and tasks entering the namespace.
- */
-static DEFINE_MUTEX(offset_lock);
-
-static void timens_set_vvar_page(struct task_struct *task,
-                               struct time_namespace *ns)
-{
-       struct vdso_time_data *vdata;
-       struct vdso_clock *vc;
-       unsigned int i;
-
-       if (ns == &init_time_ns)
-               return;
-
-       /* Fast-path, taken by every task in namespace except the first. */
-       if (likely(ns->frozen_offsets))
-               return;
-
-       mutex_lock(&offset_lock);
-       /* Nothing to-do: vvar_page has been already initialized. */
-       if (ns->frozen_offsets)
-               goto out;
-
-       ns->frozen_offsets = true;
-       vdata = page_address(ns->vvar_page);
-       vc = vdata->clock_data;
-
-       for (i = 0; i < CS_BASES; i++)
-               timens_setup_vdso_clock_data(&vc[i], ns);
-
-       if (IS_ENABLED(CONFIG_POSIX_AUX_CLOCKS)) {
-               for (i = 0; i < ARRAY_SIZE(vdata->aux_clock_data); i++)
-                       timens_setup_vdso_clock_data(&vdata->aux_clock_data[i], ns);
-       }
-
-out:
-       mutex_unlock(&offset_lock);
-}
+DEFINE_MUTEX(timens_offset_lock);
 
 void free_time_ns(struct time_namespace *ns)
 {
@@ -298,12 +188,6 @@ static void timens_put(struct ns_common *ns)
        put_time_ns(to_time_ns(ns));
 }
 
-void timens_commit(struct task_struct *tsk, struct time_namespace *ns)
-{
-       timens_set_vvar_page(tsk, ns);
-       vdso_join_timens(tsk, ns);
-}
-
 static int timens_install(struct nsset *nsset, struct ns_common *new)
 {
        struct nsproxy *nsproxy = nsset->nsproxy;
@@ -428,7 +312,7 @@ int proc_timens_set_offset(struct file *file, struct task_struct *p,
                        goto out;
        }
 
-       mutex_lock(&offset_lock);
+       mutex_lock(&timens_offset_lock);
        if (time_ns->frozen_offsets) {
                err = -EACCES;
                goto out_unlock;
@@ -453,7 +337,7 @@ int proc_timens_set_offset(struct file *file, struct task_struct *p,
        }
 
 out_unlock:
-       mutex_unlock(&offset_lock);
+       mutex_unlock(&timens_offset_lock);
 out:
        put_time_ns(time_ns);
 
diff --git a/kernel/time/namespace_internal.h b/kernel/time/namespace_internal.h
new file mode 100644 (file)
index 0000000..e85da11
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _TIME_NAMESPACE_INTERNAL_H
+#define _TIME_NAMESPACE_INTERNAL_H
+
+#include <linux/mutex.h>
+
+/*
+ * Protects possibly multiple offsets writers racing each other
+ * and tasks entering the namespace.
+ */
+extern struct mutex timens_offset_lock;
+
+#endif /* _TIME_NAMESPACE_INTERNAL_H */
diff --git a/kernel/time/namespace_vdso.c b/kernel/time/namespace_vdso.c
new file mode 100644 (file)
index 0000000..0e154f9
--- /dev/null
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Andrei Vagin <avagin@openvz.org>
+ * Author: Dmitry Safonov <dima@arista.com>
+ */
+
+#include <linux/cleanup.h>
+#include <linux/mm.h>
+#include <linux/time_namespace.h>
+#include <linux/time.h>
+#include <linux/vdso_datastore.h>
+
+#include <vdso/clocksource.h>
+#include <vdso/datapage.h>
+
+#include "namespace_internal.h"
+
+static struct timens_offset offset_from_ts(struct timespec64 off)
+{
+       struct timens_offset ret;
+
+       ret.sec = off.tv_sec;
+       ret.nsec = off.tv_nsec;
+
+       return ret;
+}
+
+/*
+ * A time namespace VVAR page has the same layout as the VVAR page which
+ * contains the system wide VDSO data.
+ *
+ * For a normal task the VVAR pages are installed in the normal ordering:
+ *     VVAR
+ *     PVCLOCK
+ *     HVCLOCK
+ *     TIMENS   <- Not really required
+ *
+ * Now for a timens task the pages are installed in the following order:
+ *     TIMENS
+ *     PVCLOCK
+ *     HVCLOCK
+ *     VVAR
+ *
+ * The check for vdso_clock->clock_mode is in the unlikely path of
+ * the seq begin magic. So for the non-timens case most of the time
+ * 'seq' is even, so the branch is not taken.
+ *
+ * If 'seq' is odd, i.e. a concurrent update is in progress, the extra check
+ * for vdso_clock->clock_mode is a non-issue. The task is spin waiting for the
+ * update to finish and for 'seq' to become even anyway.
+ *
+ * Timens page has vdso_clock->clock_mode set to VDSO_CLOCKMODE_TIMENS which
+ * enforces the time namespace handling path.
+ */
+static void timens_setup_vdso_clock_data(struct vdso_clock *vc,
+                                        struct time_namespace *ns)
+{
+       struct timens_offset *offset = vc->offset;
+       struct timens_offset monotonic = offset_from_ts(ns->offsets.monotonic);
+       struct timens_offset boottime = offset_from_ts(ns->offsets.boottime);
+
+       vc->seq                         = 1;
+       vc->clock_mode                  = VDSO_CLOCKMODE_TIMENS;
+       offset[CLOCK_MONOTONIC]         = monotonic;
+       offset[CLOCK_MONOTONIC_RAW]     = monotonic;
+       offset[CLOCK_MONOTONIC_COARSE]  = monotonic;
+       offset[CLOCK_BOOTTIME]          = boottime;
+       offset[CLOCK_BOOTTIME_ALARM]    = boottime;
+}
+
+struct page *find_timens_vvar_page(struct vm_area_struct *vma)
+{
+       if (likely(vma->vm_mm == current->mm))
+               return current->nsproxy->time_ns->vvar_page;
+
+       /*
+        * VM_PFNMAP | VM_IO protect .fault() handler from being called
+        * through interfaces like /proc/$pid/mem or
+        * process_vm_{readv,writev}() as long as there's no .access()
+        * in special_mapping_vmops().
+        * For more details check_vma_flags() and __access_remote_vm()
+        */
+
+       WARN(1, "vvar_page accessed remotely");
+
+       return NULL;
+}
+
+static void timens_set_vvar_page(struct task_struct *task,
+                               struct time_namespace *ns)
+{
+       struct vdso_time_data *vdata;
+       struct vdso_clock *vc;
+       unsigned int i;
+
+       if (ns == &init_time_ns)
+               return;
+
+       /* Fast-path, taken by every task in namespace except the first. */
+       if (likely(ns->frozen_offsets))
+               return;
+
+       guard(mutex)(&timens_offset_lock);
+       /* Nothing to-do: vvar_page has been already initialized. */
+       if (ns->frozen_offsets)
+               return;
+
+       ns->frozen_offsets = true;
+       vdata = page_address(ns->vvar_page);
+       vc = vdata->clock_data;
+
+       for (i = 0; i < CS_BASES; i++)
+               timens_setup_vdso_clock_data(&vc[i], ns);
+
+       if (IS_ENABLED(CONFIG_POSIX_AUX_CLOCKS)) {
+               for (i = 0; i < ARRAY_SIZE(vdata->aux_clock_data); i++)
+                       timens_setup_vdso_clock_data(&vdata->aux_clock_data[i], ns);
+       }
+}
+
+/*
+ * The vvar page layout depends on whether a task belongs to the root or
+ * non-root time namespace. Whenever a task changes its namespace, the VVAR
+ * page tables are cleared and then they will be re-faulted with a
+ * corresponding layout.
+ * See also the comment near timens_setup_vdso_clock_data() for details.
+ */
+static int vdso_join_timens(struct task_struct *task, struct time_namespace *ns)
+{
+       struct mm_struct *mm = task->mm;
+       struct vm_area_struct *vma;
+       VMA_ITERATOR(vmi, mm, 0);
+
+       guard(mmap_read_lock)(mm);
+       for_each_vma(vmi, vma) {
+               if (vma_is_special_mapping(vma, &vdso_vvar_mapping))
+                       zap_vma_pages(vma);
+       }
+       return 0;
+}
+
+void timens_commit(struct task_struct *tsk, struct time_namespace *ns)
+{
+       timens_set_vvar_page(tsk, ns);
+       vdso_join_timens(tsk, ns);
+}
index faebf5b7cd6ecc39062a5153e0ecd886497fddc3..cf5d784a4a5a14889ef6b61281f979af747f9242 100644 (file)
@@ -132,28 +132,3 @@ struct vm_area_struct *vdso_install_vvar_mapping(struct mm_struct *mm, unsigned
                                        VM_MIXEDMAP | VM_SEALED_SYSMAP,
                                        &vdso_vvar_mapping);
 }
-
-#ifdef CONFIG_TIME_NS
-/*
- * The vvar page layout depends on whether a task belongs to the root or
- * non-root time namespace. Whenever a task changes its namespace, the VVAR
- * page tables are cleared and then they will be re-faulted with a
- * corresponding layout.
- * See also the comment near timens_setup_vdso_clock_data() for details.
- */
-int vdso_join_timens(struct task_struct *task, struct time_namespace *ns)
-{
-       struct mm_struct *mm = task->mm;
-       struct vm_area_struct *vma;
-       VMA_ITERATOR(vmi, mm, 0);
-
-       mmap_read_lock(mm);
-       for_each_vma(vmi, vma) {
-               if (vma_is_special_mapping(vma, &vdso_vvar_mapping))
-                       zap_vma_pages(vma);
-       }
-       mmap_read_unlock(mm);
-
-       return 0;
-}
-#endif