1 /* $OpenBSD: dt_prov_profile.c,v 1.10 2025/01/23 11:17:32 mpi Exp $ */ 2 3 /* 4 * Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/systm.h> 21 #include <sys/param.h> 22 #include <sys/atomic.h> 23 #include <sys/clockintr.h> 24 25 #include <dev/dt/dtvar.h> 26 27 struct dt_probe *dtpp_profile; /* per-CPU profile probe */ 28 struct dt_probe *dtpp_interval; /* global periodic probe */ 29 30 /* Flags that make sense for this provider */ 31 #define DTEVT_PROV_PROFILE DTEVT_COMMON 32 33 int dt_prov_profile_alloc(struct dt_probe *, struct dt_softc *, 34 struct dt_pcb_list *, struct dtioc_req *); 35 36 struct dt_provider dt_prov_profile = { 37 .dtpv_name = "profile", 38 .dtpv_alloc = dt_prov_profile_alloc, 39 .dtpv_enter = NULL, 40 .dtpv_leave = NULL, 41 .dtpv_dealloc = NULL, 42 }; 43 44 struct dt_provider dt_prov_interval = { 45 .dtpv_name = "interval", 46 .dtpv_alloc = dt_prov_profile_alloc, 47 .dtpv_enter = NULL, 48 .dtpv_leave = NULL, 49 .dtpv_dealloc = NULL, 50 }; 51 52 int 53 dt_prov_profile_init(void) 54 { 55 dtpp_profile = dt_dev_alloc_probe("hz", "97", &dt_prov_profile); 56 if (dtpp_profile == NULL) 57 return 0; 58 dt_dev_register_probe(dtpp_profile); 59 dtpp_interval = dt_dev_alloc_probe("hz", "1", &dt_prov_interval); 60 if (dtpp_interval == NULL) 61 return 1; 62 dt_dev_register_probe(dtpp_interval); 63 return 2; 64 } 65 66 int 67 dt_prov_profile_alloc(struct dt_probe *dtp, struct dt_softc *sc, 68 struct dt_pcb_list *plist, struct dtioc_req *dtrq) 69 { 70 uint64_t nsecs; 71 struct dt_pcb *dp; 72 struct cpu_info *ci; 73 CPU_INFO_ITERATOR cii; 74 75 KASSERT(TAILQ_EMPTY(plist)); 76 KASSERT(dtp == dtpp_profile || dtp == dtpp_interval); 77 78 nsecs = dtrq->dtrq_nsecs; 79 if (nsecs < USEC_TO_NSEC(200) || nsecs > SEC_TO_NSEC(UINT32_MAX)) 80 return EINVAL; 81 82 CPU_INFO_FOREACH(cii, ci) { 83 if (!CPU_IS_PRIMARY(ci) && (dtp == dtpp_interval)) 84 continue; 85 86 dp = dt_pcb_alloc(dtp, sc); 87 if (dp == NULL) { 88 dt_pcb_purge(plist); 89 return ENOMEM; 90 } 91 92 dp->dp_nsecs = nsecs; 93 dp->dp_cpu = ci; 94 95 dp->dp_evtflags = dtrq->dtrq_evtflags & DTEVT_PROV_PROFILE; 96 TAILQ_INSERT_HEAD(plist, dp, dp_snext); 97 } 98 99 return 0; 100 } 101 102 void 103 dt_clock(struct clockrequest *cr, void *cf, void *arg) 104 { 105 uint64_t count; 106 struct dt_evt *dtev; 107 struct dt_pcb *dp = arg; 108 109 count = clockrequest_advance(cr, dp->dp_nsecs); 110 if (count == 0) 111 return; 112 else if (count > 1) 113 dt_pcb_ring_skiptick(dp, count - 1); 114 115 dtev = dt_pcb_ring_get(dp, 1); 116 if (dtev == NULL) 117 return; 118 dt_pcb_ring_consume(dp, dtev); 119 } 120