clocksource: Add common vdso clock mode storage
authorThomas Gleixner <tglx@linutronix.de>
Fri, 7 Feb 2020 12:38:55 +0000 (13:38 +0100)
committerThomas Gleixner <tglx@linutronix.de>
Mon, 17 Feb 2020 13:40:23 +0000 (14:40 +0100)
All architectures which use the generic VDSO code have their own storage
for the VDSO clock mode. That's pointless and just requires duplicate code.

Provide generic storage for it. The new Kconfig symbol is intermediate and
will be removed once all architectures are converted over.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
Reviewed-by: Vincenzo Frascino <vincenzo.frascino@arm.com>
Link: https://lkml.kernel.org/r/20200207124403.028046322@linutronix.de
include/linux/clocksource.h
kernel/time/clocksource.c
kernel/time/vsyscall.c
lib/vdso/Kconfig
lib/vdso/gettimeofday.c

index 2c4574b517d2c457171d7bec8bfea54ffa65b80e..6d5ed1b4d24de91d1bffffde4b9f1fa258ecf9f5 100644 (file)
 struct clocksource;
 struct module;
 
-#ifdef CONFIG_ARCH_CLOCKSOURCE_DATA
+#if defined(CONFIG_ARCH_CLOCKSOURCE_DATA) || \
+    defined(CONFIG_GENERIC_VDSO_CLOCK_MODE)
 #include <asm/clocksource.h>
 #endif
 
+enum vdso_clock_mode {
+       VDSO_CLOCKMODE_NONE,
+#ifdef CONFIG_GENERIC_VDSO_CLOCK_MODE
+       VDSO_ARCH_CLOCKMODES,
+#endif
+       VDSO_CLOCKMODE_MAX,
+};
+
 /**
  * struct clocksource - hardware abstraction for a free running counter
  *     Provides mostly state-free accessors to the underlying hardware.
@@ -97,6 +106,7 @@ struct clocksource {
        const char              *name;
        struct list_head        list;
        int                     rating;
+       enum vdso_clock_mode    vdso_clock_mode;
        unsigned long           flags;
 
        int                     (*enable)(struct clocksource *cs);
index 428beb69426a85577085ee6f644ab785ebc700e9..7cb09c4cf21c8cd4f55f00bf0aeb311e4523b2a5 100644 (file)
@@ -928,6 +928,15 @@ int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq)
 
        clocksource_arch_init(cs);
 
+#ifdef CONFIG_GENERIC_VDSO_CLOCK_MODE
+       if (cs->vdso_clock_mode < 0 ||
+           cs->vdso_clock_mode >= VDSO_CLOCKMODE_MAX) {
+               pr_warn("clocksource %s registered with invalid VDSO mode %d. Disabling VDSO support.\n",
+                       cs->name, cs->vdso_clock_mode);
+               cs->vdso_clock_mode = VDSO_CLOCKMODE_NONE;
+       }
+#endif
+
        /* Initialize mult/shift and max_idle_ns */
        __clocksource_update_freq_scale(cs, scale, freq);
 
index 9577c89179cd1309b99d8224bc21a6622407d210..f9a5178c69bbc49f6e6da345383c556dbcb9b707 100644 (file)
@@ -71,13 +71,19 @@ void update_vsyscall(struct timekeeper *tk)
 {
        struct vdso_data *vdata = __arch_get_k_vdso_data();
        struct vdso_timestamp *vdso_ts;
+       s32 clock_mode;
        u64 nsec;
 
        /* copy vsyscall data */
        vdso_write_begin(vdata);
 
-       vdata[CS_HRES_COARSE].clock_mode        = __arch_get_clock_mode(tk);
-       vdata[CS_RAW].clock_mode                = __arch_get_clock_mode(tk);
+#ifdef CONFIG_GENERIC_VDSO_CLOCK_MODE
+       clock_mode = tk->tkr_mono.clock->vdso_clock_mode;
+#else
+       clock_mode = __arch_get_clock_mode(tk);
+#endif
+       vdata[CS_HRES_COARSE].clock_mode        = clock_mode;
+       vdata[CS_RAW].clock_mode                = clock_mode;
 
        /* CLOCK_REALTIME also required for time() */
        vdso_ts         = &vdata[CS_HRES_COARSE].basetime[CLOCK_REALTIME];
index d883ac29950897801f3a597cc90d42ae6d696524..d9f43c84fcc605f66ef66f75c0a52b1a7ca7fdb7 100644 (file)
@@ -30,4 +30,7 @@ config GENERIC_VDSO_TIME_NS
          Selected by architectures which support time namespaces in the
          VDSO
 
+config GENERIC_VDSO_CLOCK_MODE
+       bool
+
 endif
index 5804e4e168e793ecffe0d984295dcd695a9c4582..3f2d8b859130290bde1241a608406f7539994e1c 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/time.h>
 #include <linux/kernel.h>
 #include <linux/hrtimer_defs.h>
+#include <linux/clocksource.h>
 #include <vdso/datapage.h>
 #include <vdso/helpers.h>
 
@@ -64,10 +65,14 @@ static int do_hres_timens(const struct vdso_data *vdns, clockid_t clk,
 
        do {
                seq = vdso_read_begin(vd);
+               if (IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) &&
+                   vd->clock_mode == VDSO_CLOCKMODE_NONE)
+                       return -1;
                cycles = __arch_get_hw_counter(vd->clock_mode);
                ns = vdso_ts->nsec;
                last = vd->cycle_last;
-               if (unlikely((s64)cycles < 0))
+               if (!IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) &&
+                   unlikely((s64)cycles < 0))
                        return -1;
 
                ns += vdso_calc_delta(cycles, last, vd->mask, vd->mult);
@@ -132,10 +137,14 @@ static __always_inline int do_hres(const struct vdso_data *vd, clockid_t clk,
                }
                smp_rmb();
 
+               if (IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) &&
+                   vd->clock_mode == VDSO_CLOCKMODE_NONE)
+                       return -1;
                cycles = __arch_get_hw_counter(vd->clock_mode);
                ns = vdso_ts->nsec;
                last = vd->cycle_last;
-               if (unlikely((s64)cycles < 0))
+               if (!IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) &&
+                   unlikely((s64)cycles < 0))
                        return -1;
 
                ns += vdso_calc_delta(cycles, last, vd->mask, vd->mult);