diff options
author | Scott Soule Cheloha <cheloha@cvs.openbsd.org> | 2024-02-09 17:42:19 +0000 |
---|---|---|
committer | Scott Soule Cheloha <cheloha@cvs.openbsd.org> | 2024-02-09 17:42:19 +0000 |
commit | b00a000146266373c5f6de452982c161e7924230 (patch) | |
tree | a4ccd66656c221f0dd9318bba0b41ae3f02eb49f /sys/dev/dt | |
parent | 5734a295f3fc1ba34d5cef01ff26ba34aa99bade (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.c | 24 | ||||
-rw-r--r-- | sys/dev/dt/dt_prov_profile.c | 68 | ||||
-rw-r--r-- | sys/dev/dt/dtvar.h | 10 |
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? */ |