1/* $OpenBSD: dt_prov_profile.c,v 1.8 2024/04/06 11:18:02 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 27struct dt_probe *dtpp_profile; /* per-CPU profile probe */ 28struct dt_probe *dtpp_interval; /* global periodic probe */ 29 30/* Flags that make sense for this provider */ 31#define DTEVT_PROV_PROFILE DTEVT_COMMON 32 33int dt_prov_profile_alloc(struct dt_probe *, struct dt_softc *, 34 struct dt_pcb_list *, struct dtioc_req *); 35 36struct 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 44struct 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 52int 53dt_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 66int 67dt_prov_profile_alloc(struct dt_probe *dtp, struct dt_softc *sc, 68 struct dt_pcb_list *plist, struct dtioc_req *dtrq) 69{ 70 struct dt_pcb *dp; 71 struct cpu_info *ci; 72 CPU_INFO_ITERATOR cii; 73 extern int hz; 74 75 KASSERT(TAILQ_EMPTY(plist)); 76 KASSERT(dtp == dtpp_profile || dtp == dtpp_interval); 77 78 if (dtrq->dtrq_rate <= 0 || dtrq->dtrq_rate > hz) 79 return EOPNOTSUPP; 80 81 CPU_INFO_FOREACH(cii, ci) { 82 if (!CPU_IS_PRIMARY(ci) && (dtp == dtpp_interval)) 83 continue; 84 85 dp = dt_pcb_alloc(dtp, sc); 86 if (dp == NULL) { 87 dt_pcb_purge(plist); 88 return ENOMEM; 89 } 90 91 dp->dp_nsecs = SEC_TO_NSEC(1) / dtrq->dtrq_rate; 92 dp->dp_cpu = ci; 93 94 dp->dp_evtflags = dtrq->dtrq_evtflags & DTEVT_PROV_PROFILE; 95 TAILQ_INSERT_HEAD(plist, dp, dp_snext); 96 } 97 98 return 0; 99} 100 101void 102dt_clock(struct clockrequest *cr, void *cf, void *arg) 103{ 104 uint64_t count, i; 105 struct dt_evt *dtev; 106 struct dt_pcb *dp = arg; 107 108 count = clockrequest_advance(cr, dp->dp_nsecs); 109 for (i = 0; i < count; i++) { 110 dtev = dt_pcb_ring_get(dp, 1); 111 if (dtev == NULL) 112 return; 113 dt_pcb_ring_consume(dp, dtev); 114 } 115} 116