summaryrefslogtreecommitdiff
path: root/sys/dev/dt
diff options
context:
space:
mode:
authorScott Soule Cheloha <cheloha@cvs.openbsd.org>2024-02-09 17:42:19 +0000
committerScott Soule Cheloha <cheloha@cvs.openbsd.org>2024-02-09 17:42:19 +0000
commitb00a000146266373c5f6de452982c161e7924230 (patch)
treea4ccd66656c221f0dd9318bba0b41ae3f02eb49f /sys/dev/dt
parent5734a295f3fc1ba34d5cef01ff26ba34aa99bade (diff)
dt(4): move interval/profile entry points to dedicated clockintr callback
To improve the utility of dt(4)'s interval and profile probes we need to move the probe entry points from the fixed-frequency hardclock() to a dedicated clock interrupt callback so that the probes can fire at arbitrary frequencies. - Remove entry points for interval/profile probes from hardclock(). - Merge dt_prov_profile_enter(), dt_prov_interval_enter(), and dt_prov_profile_fire() into one function, dt_clock(). This is the now-unified callback for interval/profile probes. dt_clock() will consume multiple events during a single execution if it is delayed, but on platforms with high quality interrupt clocks this should be rare. - Each struct dt_pcb gets its own clockintr handle, dp_clockintr. - In struct dt_pcb, replace dp_maxtick/dp_nticks with dp_nsecs, the PCB's sampling period. Aynchronous probes must initialize dp_nsecs to a non-zero value during dtpv_alloc(). - In struct dt_pcb, replace dp_cpuid with dp_cpu so that dt_ioctl_record_start() knows where to bind the PCB's dp_clockintr. - dt_ioctl_record_start() binds, staggers, and starts all interval/profile PCBs on the given dt_softc. Each dp_clockintr is given a reference to its enclosing PCB so that dt_clock() doesn't need to search for it. The staggering sort-of simulates the current behavior under hardclock(). - dt_ioctl_record_stop() unbinds all interval/profile PCBs. The CL_BARRIER ensures that dp_clockintr's PCB reference is not in use by dt_clock() so that the PCB may be safely freed upon return from dt_ioctl_record_stop(). Blocking while holding dt_lock is not ideal, but in practice blocking in this spot is rare and dt_clock() completes quickly on all but the oldest hardware. An extremely unlucky thread could block for every interval/profile PCB on the softc, but this is implausible. DT_FA_PROFILE values are up-to-date for amd64, i386, and macppc. Somebody with the right hardware needs to check-and-maybe-fix the values on octeon, powerpc64, and sparc64. Joint effort with mpi@. Thread: https://marc.info/?l=openbsd-tech&m=170629371821879&w=2 ok mpi@
Diffstat (limited to 'sys/dev/dt')
-rw-r--r--sys/dev/dt/dt_dev.c24
-rw-r--r--sys/dev/dt/dt_prov_profile.c68
-rw-r--r--sys/dev/dt/dtvar.h10
3 files changed, 43 insertions, 59 deletions
diff --git a/sys/dev/dt/dt_dev.c b/sys/dev/dt/dt_dev.c
index 12c5390cdbb..2383d3a732e 100644
--- a/sys/dev/dt/dt_dev.c
+++ b/sys/dev/dt/dt_dev.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dt_dev.c,v 1.29 2024/01/02 16:32:48 bluhm Exp $ */
+/* $OpenBSD: dt_dev.c,v 1.30 2024/02/09 17:42:18 cheloha Exp $ */
/*
* Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/param.h>
+#include <sys/clockintr.h>
#include <sys/device.h>
#include <sys/exec_elf.h>
#include <sys/malloc.h>
@@ -56,13 +57,13 @@
* proc_trampoline+0x1c
*/
#if defined(__amd64__)
-#define DT_FA_PROFILE 7
+#define DT_FA_PROFILE 5
#define DT_FA_STATIC 2
#elif defined(__i386__)
-#define DT_FA_PROFILE 8
+#define DT_FA_PROFILE 5
#define DT_FA_STATIC 2
#elif defined(__macppc__)
-#define DT_FA_PROFILE 7
+#define DT_FA_PROFILE 5
#define DT_FA_STATIC 2
#elif defined(__octeon__)
#define DT_FA_PROFILE 6
@@ -492,6 +493,14 @@ dt_ioctl_record_start(struct dt_softc *sc)
SMR_SLIST_INSERT_HEAD_LOCKED(&dtp->dtp_pcbs, dp, dp_pnext);
dtp->dtp_recording++;
dtp->dtp_prov->dtpv_recording++;
+
+ if (dp->dp_nsecs != 0) {
+ clockintr_bind(&dp->dp_clockintr, dp->dp_cpu, dt_clock,
+ dp);
+ clockintr_stagger(&dp->dp_clockintr, dp->dp_nsecs,
+ CPU_INFO_UNIT(dp->dp_cpu), MAXCPUS);
+ clockintr_advance(&dp->dp_clockintr, dp->dp_nsecs);
+ }
}
rw_exit_write(&dt_lock);
@@ -518,6 +527,13 @@ dt_ioctl_record_stop(struct dt_softc *sc)
TAILQ_FOREACH(dp, &sc->ds_pcbs, dp_snext) {
struct dt_probe *dtp = dp->dp_dtp;
+ /*
+ * Set an execution barrier to ensure the shared
+ * reference to dp is inactive.
+ */
+ if (dp->dp_nsecs != 0)
+ clockintr_unbind(&dp->dp_clockintr, CL_BARRIER);
+
dtp->dtp_recording--;
dtp->dtp_prov->dtpv_recording--;
SMR_SLIST_REMOVE_LOCKED(&dtp->dtp_pcbs, dp, dt_pcb, dp_pnext);
diff --git a/sys/dev/dt/dt_prov_profile.c b/sys/dev/dt/dt_prov_profile.c
index d6db0797bf0..1388770fb39 100644
--- a/sys/dev/dt/dt_prov_profile.c
+++ b/sys/dev/dt/dt_prov_profile.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dt_prov_profile.c,v 1.5 2023/04/26 16:53:59 claudio Exp $ */
+/* $OpenBSD: dt_prov_profile.c,v 1.6 2024/02/09 17:42:18 cheloha Exp $ */
/*
* Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
@@ -20,6 +20,7 @@
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/atomic.h>
+#include <sys/clockintr.h>
#include <dev/dt/dtvar.h>
@@ -31,13 +32,11 @@ struct dt_probe *dtpp_interval; /* global periodic probe */
int dt_prov_profile_alloc(struct dt_probe *, struct dt_softc *,
struct dt_pcb_list *, struct dtioc_req *);
-int dt_prov_profile_enter(struct dt_provider *, ...);
-int dt_prov_interval_enter(struct dt_provider *, ...);
struct dt_provider dt_prov_profile = {
.dtpv_name = "profile",
.dtpv_alloc = dt_prov_profile_alloc,
- .dtpv_enter = dt_prov_profile_enter,
+ .dtpv_enter = NULL,
.dtpv_leave = NULL,
.dtpv_dealloc = NULL,
};
@@ -45,7 +44,7 @@ struct dt_provider dt_prov_profile = {
struct dt_provider dt_prov_interval = {
.dtpv_name = "interval",
.dtpv_alloc = dt_prov_profile_alloc,
- .dtpv_enter = dt_prov_interval_enter,
+ .dtpv_enter = NULL,
.dtpv_leave = NULL,
.dtpv_dealloc = NULL,
};
@@ -90,8 +89,8 @@ dt_prov_profile_alloc(struct dt_probe *dtp, struct dt_softc *sc,
return ENOMEM;
}
- dp->dp_maxtick = hz / dtrq->dtrq_rate;
- dp->dp_cpuid = ci->ci_cpuid;
+ dp->dp_nsecs = SEC_TO_NSEC(1) / dtrq->dtrq_rate;
+ dp->dp_cpu = ci;
dp->dp_filter = dtrq->dtrq_filter;
dp->dp_evtflags = dtrq->dtrq_evtflags & DTEVT_PROV_PROFILE;
@@ -101,51 +100,18 @@ dt_prov_profile_alloc(struct dt_probe *dtp, struct dt_softc *sc,
return 0;
}
-static inline void
-dt_prov_profile_fire(struct dt_pcb *dp)
+void
+dt_clock(struct clockrequest *cr, void *cf, void *arg)
{
+ uint64_t count, i;
struct dt_evt *dtev;
-
- if (++dp->dp_nticks < dp->dp_maxtick)
- return;
-
- dtev = dt_pcb_ring_get(dp, 1);
- if (dtev == NULL)
- return;
- dt_pcb_ring_consume(dp, dtev);
- dp->dp_nticks = 0;
-}
-
-int
-dt_prov_profile_enter(struct dt_provider *dtpv, ...)
-{
- struct cpu_info *ci = curcpu();
- struct dt_pcb *dp;
-
- KASSERT(dtpv == &dt_prov_profile);
-
- smr_read_enter();
- SMR_SLIST_FOREACH(dp, &dtpp_profile->dtp_pcbs, dp_pnext) {
- if (dp->dp_cpuid != ci->ci_cpuid)
- continue;
-
- dt_prov_profile_fire(dp);
+ struct dt_pcb *dp = arg;
+
+ count = clockrequest_advance(cr, dp->dp_nsecs);
+ for (i = 0; i < count; i++) {
+ dtev = dt_pcb_ring_get(dp, 1);
+ if (dtev == NULL)
+ return;
+ dt_pcb_ring_consume(dp, dtev);
}
- smr_read_leave();
- return 0;
-}
-
-int
-dt_prov_interval_enter(struct dt_provider *dtpv, ...)
-{
- struct dt_pcb *dp;
-
- KASSERT(dtpv == &dt_prov_interval);
-
- smr_read_enter();
- SMR_SLIST_FOREACH(dp, &dtpp_interval->dtp_pcbs, dp_pnext) {
- dt_prov_profile_fire(dp);
- }
- smr_read_leave();
- return 0;
}
diff --git a/sys/dev/dt/dtvar.h b/sys/dev/dt/dtvar.h
index 9b5d3de08ee..f69a41c6506 100644
--- a/sys/dev/dt/dtvar.h
+++ b/sys/dev/dt/dtvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: dtvar.h,v 1.17 2023/04/26 16:53:59 claudio Exp $ */
+/* $OpenBSD: dtvar.h,v 1.18 2024/02/09 17:42:18 cheloha Exp $ */
/*
* Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
@@ -175,6 +175,7 @@ int dtioc_req_isvalid(struct dtioc_req *);
* userland read(2)s them.
*
* Locks used to protect struct members in this file:
+ * D dt_lock
* I immutable after creation
* K kernel lock
* K,S kernel lock for writing and SMR for reading
@@ -197,9 +198,9 @@ struct dt_pcb {
struct dt_filter dp_filter; /* [I] filter to match */
/* Provider specific fields. */
- unsigned int dp_cpuid; /* [I] on which CPU */
- unsigned int dp_maxtick; /* [I] freq. of profiling */
- unsigned int dp_nticks; /* [c] current tick count */
+ struct clockintr dp_clockintr; /* [D] profiling handle */
+ uint64_t dp_nsecs; /* [I] profiling period */
+ struct cpu_info *dp_cpu; /* [I] on which CPU */
/* Counters */
uint64_t dp_dropevt; /* [m] # dropped event */
@@ -270,6 +271,7 @@ struct dt_probe *dt_dev_alloc_probe(const char *, const char *,
struct dt_provider *);
void dt_dev_register_probe(struct dt_probe *);
+void dt_clock(struct clockrequest *, void *, void *);
extern volatile uint32_t dt_tracing; /* currently tracing? */