xref: /openbsd-src/sys/dev/dt/dt_prov_profile.c (revision 139d07b5d2b54520acbf93ed1ea9df2b42a216fe)
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