dt(4): move interval/profile entry points to dedicated clockintr callback
authorcheloha <cheloha@openbsd.org>
Fri, 9 Feb 2024 17:42:18 +0000 (17:42 +0000)
committercheloha <cheloha@openbsd.org>
Fri, 9 Feb 2024 17:42:18 +0000 (17:42 +0000)
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@

sys/dev/dt/dt_dev.c
sys/dev/dt/dt_prov_profile.c
sys/dev/dt/dtvar.h
sys/kern/kern_clock.c

index 12c5390..2383d3a 100644 (file)
@@ -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>
  *     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);
index d6db079..1388770 100644 (file)
@@ -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;
 }
index 9b5d3de..f69a41c 100644 (file)
@@ -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? */
 
index e1669ba..f6f98b7 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: kern_clock.c,v 1.121 2023/10/17 00:04:02 cheloha Exp $        */
+/*     $OpenBSD: kern_clock.c,v 1.122 2024/02/09 17:42:18 cheloha Exp $        */
 /*     $NetBSD: kern_clock.c,v 1.34 1996/06/09 04:51:03 briggs Exp $   */
 
 /*-
 #include <sys/sched.h>
 #include <sys/timetc.h>
 
-#include "dt.h"
-#if NDT > 0
-#include <dev/dt/dtvar.h>
-#endif
-
 /*
  * Clock handling routines.
  *
@@ -145,12 +140,6 @@ initclocks(void)
 void
 hardclock(struct clockframe *frame)
 {
-#if NDT > 0
-       DT_ENTER(profile, NULL);
-       if (CPU_IS_PRIMARY(curcpu()))
-               DT_ENTER(interval, NULL);
-#endif
-
        /*
         * If we are not the primary CPU, we're not allowed to do
         * any more work.