summaryrefslogtreecommitdiffstats
path: root/drivers/ptp/ptp_mock.c
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2026-09-02 09:55:01 -0400
committerSteven Rostedt <rostedt@goodmis.org>2026-09-02 11:08:21 -0400
commit4617721c502b2ddaa4e324e86da4997edf738fa5 (patch)
tree350aa83b47da0b574a814795392b20b0bed83cbc /drivers/ptp/ptp_mock.c
downloadlinux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz
linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since it can be declared outside of the ftrace.c code, it calls ftrace_ops_init() on the ops in every global function. The issue is that if two tasks call it on the same ops at the same time it is possible to have the initialization of one corrupt the initialization of the other call. Create a ops_mutex to use to synchronize every initialization of the ftrace_ops. The mutex is taken within checking the ftrace_ops flag that states it was initializied but the flag is checked again after the mutex has been taken. Checking first outside the mutex allows it to shortcut having to take the mutex. But then the check needs to be done again after the mute is taken in case of races. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock") Reported-by: sashiko-bot@kernel.org Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/ Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'drivers/ptp/ptp_mock.c')
-rw-r--r--drivers/ptp/ptp_mock.c175
1 files changed, 175 insertions, 0 deletions
diff --git a/drivers/ptp/ptp_mock.c b/drivers/ptp/ptp_mock.c
new file mode 100644
index 000000000..4d66b6147
--- /dev/null
+++ b/drivers/ptp/ptp_mock.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2023 NXP
+ *
+ * Mock-up PTP Hardware Clock driver for virtual network devices
+ *
+ * Create a PTP clock which offers PTP time manipulation operations
+ * using a timecounter/cyclecounter on top of CLOCK_MONOTONIC_RAW.
+ */
+
+#include <linux/ptp_clock_kernel.h>
+#include <linux/ptp_mock.h>
+#include <linux/timecounter.h>
+
+/* Clamp scaled_ppm between -2,097,152,000 and 2,097,152,000,
+ * and thus "adj" between -68,719,476 and 68,719,476
+ */
+#define MOCK_PHC_MAX_ADJ_PPB 32000000
+/* Timestamps from ktime_get_raw() have 1 ns resolution, so the scale factor
+ * (MULT >> SHIFT) needs to be 1. Pick SHIFT as 31 bits, which translates
+ * MULT(freq 0) into 0x80000000.
+ */
+#define MOCK_PHC_CC_SHIFT 31
+#define MOCK_PHC_CC_MULT (1 << MOCK_PHC_CC_SHIFT)
+#define MOCK_PHC_FADJ_SHIFT 9
+#define MOCK_PHC_FADJ_DENOMINATOR 15625ULL
+
+/* The largest cycle_delta that timecounter_read_delta() can handle without a
+ * 64-bit overflow during the multiplication with cc->mult, given the max "adj"
+ * we permit, is ~8.3 seconds. Make sure readouts are more frequent than that.
+ */
+#define MOCK_PHC_REFRESH_INTERVAL (HZ * 5)
+
+#define info_to_phc(d) container_of((d), struct mock_phc, info)
+
+struct mock_phc {
+ struct ptp_clock_info info;
+ struct ptp_clock *clock;
+ struct timecounter tc;
+ struct cyclecounter cc;
+ spinlock_t lock;
+};
+
+static u64 mock_phc_cc_read(struct cyclecounter *cc)
+{
+ return ktime_get_raw_ns();
+}
+
+static int mock_phc_adjfine(struct ptp_clock_info *info, long scaled_ppm)
+{
+ struct mock_phc *phc = info_to_phc(info);
+ s64 adj;
+
+ adj = (s64)scaled_ppm << MOCK_PHC_FADJ_SHIFT;
+ adj = div_s64(adj, MOCK_PHC_FADJ_DENOMINATOR);
+
+ spin_lock(&phc->lock);
+ timecounter_read(&phc->tc);
+ phc->cc.mult = MOCK_PHC_CC_MULT + adj;
+ spin_unlock(&phc->lock);
+
+ return 0;
+}
+
+static int mock_phc_adjtime(struct ptp_clock_info *info, s64 delta)
+{
+ struct mock_phc *phc = info_to_phc(info);
+
+ spin_lock(&phc->lock);
+ timecounter_adjtime(&phc->tc, delta);
+ spin_unlock(&phc->lock);
+
+ return 0;
+}
+
+static int mock_phc_settime64(struct ptp_clock_info *info,
+ const struct timespec64 *ts)
+{
+ struct mock_phc *phc = info_to_phc(info);
+ u64 ns = timespec64_to_ns(ts);
+
+ spin_lock(&phc->lock);
+ timecounter_init(&phc->tc, &phc->cc, ns);
+ spin_unlock(&phc->lock);
+
+ return 0;
+}
+
+static int mock_phc_gettime64(struct ptp_clock_info *info, struct timespec64 *ts)
+{
+ struct mock_phc *phc = info_to_phc(info);
+ u64 ns;
+
+ spin_lock(&phc->lock);
+ ns = timecounter_read(&phc->tc);
+ spin_unlock(&phc->lock);
+
+ *ts = ns_to_timespec64(ns);
+
+ return 0;
+}
+
+static long mock_phc_refresh(struct ptp_clock_info *info)
+{
+ struct timespec64 ts;
+
+ mock_phc_gettime64(info, &ts);
+
+ return MOCK_PHC_REFRESH_INTERVAL;
+}
+
+int mock_phc_index(struct mock_phc *phc)
+{
+ return ptp_clock_index(phc->clock);
+}
+EXPORT_SYMBOL_GPL(mock_phc_index);
+
+struct mock_phc *mock_phc_create(struct device *dev)
+{
+ struct mock_phc *phc;
+ int err;
+
+ phc = kzalloc_obj(*phc);
+ if (!phc) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ phc->info = (struct ptp_clock_info) {
+ .owner = THIS_MODULE,
+ .name = "Mock-up PTP clock",
+ .max_adj = MOCK_PHC_MAX_ADJ_PPB,
+ .adjfine = mock_phc_adjfine,
+ .adjtime = mock_phc_adjtime,
+ .gettime64 = mock_phc_gettime64,
+ .settime64 = mock_phc_settime64,
+ .do_aux_work = mock_phc_refresh,
+ };
+
+ phc->cc = (struct cyclecounter) {
+ .read = mock_phc_cc_read,
+ .mask = CYCLECOUNTER_MASK(64),
+ .mult = MOCK_PHC_CC_MULT,
+ .shift = MOCK_PHC_CC_SHIFT,
+ };
+
+ spin_lock_init(&phc->lock);
+ timecounter_init(&phc->tc, &phc->cc, 0);
+
+ phc->clock = ptp_clock_register(&phc->info, dev);
+ if (IS_ERR(phc->clock)) {
+ err = PTR_ERR(phc->clock);
+ goto out_free_phc;
+ }
+
+ ptp_schedule_worker(phc->clock, MOCK_PHC_REFRESH_INTERVAL);
+
+ return phc;
+
+out_free_phc:
+ kfree(phc);
+out:
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(mock_phc_create);
+
+void mock_phc_destroy(struct mock_phc *phc)
+{
+ ptp_clock_unregister(phc->clock);
+ kfree(phc);
+}
+EXPORT_SYMBOL_GPL(mock_phc_destroy);
+
+MODULE_DESCRIPTION("Mock-up PTP Hardware Clock driver");
+MODULE_LICENSE("GPL");