timekeeping/vsyscall: Provide vdso_update_begin/end()
authorThomas Gleixner <tglx@linutronix.de>
Tue, 4 Aug 2020 15:01:23 +0000 (17:01 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Thu, 6 Aug 2020 08:57:30 +0000 (10:57 +0200)
Architectures can have the requirement to add additional architecture
specific data to the VDSO data page which needs to be updated independent
of the timekeeper updates.

To protect these updates vs. concurrent readers and a conflicting update
through timekeeping, provide helper functions to make such updates safe.

vdso_update_begin() takes the timekeeper_lock to protect against a
potential update from timekeeper code and increments the VDSO sequence
count to signal data inconsistency to concurrent readers. vdso_update_end()
makes the sequence count even again to signal data consistency and drops
the timekeeper lock.

[ Sven: Add interrupt disable handling to the functions ]

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Sven Schnelle <svens@linux.ibm.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lkml.kernel.org/r/20200804150124.41692-3-svens@linux.ibm.com
include/vdso/vsyscall.h
kernel/time/timekeeping.c
kernel/time/timekeeping_internal.h
kernel/time/vsyscall.c

index 2c6134e0c23d17dd8fda9c83c4763ab79c968e59..b0fdc9c6bf439a832cac2d8d2dc7bd31375efd27 100644 (file)
@@ -6,6 +6,9 @@
 
 #include <asm/vdso/vsyscall.h>
 
+unsigned long vdso_update_begin(void);
+void vdso_update_end(unsigned long flags);
+
 #endif /* !__ASSEMBLY__ */
 
 #endif /* __VDSO_VSYSCALL_H */
index 63a632f9896c38133e4877dc828390af052105f1..4c7212f3c60350e2d8671dbf524d2a66ca7f125b 100644 (file)
@@ -50,7 +50,7 @@ static struct {
        .seq = SEQCNT_ZERO(tk_core.seq),
 };
 
-static DEFINE_RAW_SPINLOCK(timekeeper_lock);
+DEFINE_RAW_SPINLOCK(timekeeper_lock);
 static struct timekeeper shadow_timekeeper;
 
 /**
index bcbb52db22565971d1a4885bd8e177c21265e6a5..4ca2787d1642e2f52bf985607ca3b03785cf9a50 100644 (file)
@@ -1,12 +1,14 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 #ifndef _TIMEKEEPING_INTERNAL_H
 #define _TIMEKEEPING_INTERNAL_H
-/*
- * timekeeping debug functions
- */
+
 #include <linux/clocksource.h>
+#include <linux/spinlock.h>
 #include <linux/time.h>
 
+/*
+ * timekeeping debug functions
+ */
 #ifdef CONFIG_DEBUG_FS
 extern void tk_debug_account_sleep_time(const struct timespec64 *t);
 #else
@@ -31,4 +33,7 @@ static inline u64 clocksource_delta(u64 now, u64 last, u64 mask)
 }
 #endif
 
+/* Semi public for serialization of non timekeeper VDSO updates. */
+extern raw_spinlock_t timekeeper_lock;
+
 #endif /* _TIMEKEEPING_INTERNAL_H */
index 54ce6eb2ca36db6f34a07ac8dac3ae6f0814a01b..88e6b8ed6ca5ccd4271b96cc1e5fdb867920e88f 100644 (file)
@@ -13,6 +13,8 @@
 #include <vdso/helpers.h>
 #include <vdso/vsyscall.h>
 
+#include "timekeeping_internal.h"
+
 static inline void update_vdso_data(struct vdso_data *vdata,
                                    struct timekeeper *tk)
 {
@@ -127,3 +129,42 @@ void update_vsyscall_tz(void)
 
        __arch_sync_vdso_data(vdata);
 }
+
+/**
+ * vdso_update_begin - Start of a VDSO update section
+ *
+ * Allows architecture code to safely update the architecture specific VDSO
+ * data. Disables interrupts, acquires timekeeper lock to serialize against
+ * concurrent updates from timekeeping and invalidates the VDSO data
+ * sequence counter to prevent concurrent readers from accessing
+ * inconsistent data.
+ *
+ * Returns: Saved interrupt flags which need to be handed in to
+ * vdso_update_end().
+ */
+unsigned long vdso_update_begin(void)
+{
+       struct vdso_data *vdata = __arch_get_k_vdso_data();
+       unsigned long flags;
+
+       raw_spin_lock_irqsave(&timekeeper_lock, flags);
+       vdso_write_begin(vdata);
+       return flags;
+}
+
+/**
+ * vdso_update_end - End of a VDSO update section
+ * @flags:     Interrupt flags as returned from vdso_update_begin()
+ *
+ * Pairs with vdso_update_begin(). Marks vdso data consistent, invokes data
+ * synchronization if the architecture requires it, drops timekeeper lock
+ * and restores interrupt flags.
+ */
+void vdso_update_end(unsigned long flags)
+{
+       struct vdso_data *vdata = __arch_get_k_vdso_data();
+
+       vdso_write_end(vdata);
+       __arch_sync_vdso_data(vdata);
+       raw_spin_unlock_irqrestore(&timekeeper_lock, flags);
+}