xref: /freebsd/sys/kern/kern_et.c (revision 580744621f33383027108364dcadad718df46ffe)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2010-2013 Alexander Motin <mav@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer,
12  *    without modification, immediately at the beginning of the file.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/sbuf.h>
35 #include <sys/sysctl.h>
36 #include <sys/systm.h>
37 #include <sys/queue.h>
38 #include <sys/timeet.h>
39 
40 #include "opt_timer.h"
41 
42 SLIST_HEAD(et_eventtimers_list, eventtimer);
43 static struct et_eventtimers_list eventtimers = SLIST_HEAD_INITIALIZER(et_eventtimers);
44 
45 struct mtx	et_eventtimers_mtx;
46 MTX_SYSINIT(et_eventtimers_init, &et_eventtimers_mtx, "et_mtx", MTX_DEF);
47 
48 SYSCTL_NODE(_kern, OID_AUTO, eventtimer, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
49     "Event timers");
50 static SYSCTL_NODE(_kern_eventtimer, OID_AUTO, et,
51     CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
52     "");
53 
54 /*
55  * Register a new event timer hardware.
56  */
57 int
58 et_register(struct eventtimer *et)
59 {
60 	struct eventtimer *tmp, *next;
61 
62 	if (et->et_quality >= 0 || bootverbose) {
63 		if (et->et_frequency == 0) {
64 			printf("Event timer \"%s\" quality %d\n",
65 			    et->et_name, et->et_quality);
66 		} else {
67 			printf("Event timer \"%s\" "
68 			    "frequency %ju Hz quality %d\n",
69 			    et->et_name, (uintmax_t)et->et_frequency,
70 			    et->et_quality);
71 		}
72 	}
73 	KASSERT(et->et_start, ("et_register: timer has no start function"));
74 	et->et_sysctl = SYSCTL_ADD_NODE_WITH_LABEL(NULL,
75 	    SYSCTL_STATIC_CHILDREN(_kern_eventtimer_et), OID_AUTO, et->et_name,
76 	    CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
77 	    "event timer description", "eventtimer");
78 	SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO,
79 	    "flags", CTLFLAG_RD, &(et->et_flags), 0,
80 	    "Event timer capabilities");
81 	SYSCTL_ADD_UQUAD(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO,
82 	    "frequency", CTLFLAG_RD, &(et->et_frequency),
83 	    "Event timer base frequency");
84 	SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(et->et_sysctl), OID_AUTO,
85 	    "quality", CTLFLAG_RD, &(et->et_quality), 0,
86 	    "Goodness of event timer");
87 	ET_LOCK();
88 	if (SLIST_EMPTY(&eventtimers) ||
89 	    SLIST_FIRST(&eventtimers)->et_quality < et->et_quality) {
90 		SLIST_INSERT_HEAD(&eventtimers, et, et_all);
91 	} else {
92 		SLIST_FOREACH(tmp, &eventtimers, et_all) {
93 			next = SLIST_NEXT(tmp, et_all);
94 			if (next == NULL || next->et_quality < et->et_quality) {
95 				SLIST_INSERT_AFTER(tmp, et, et_all);
96 				break;
97 			}
98 		}
99 	}
100 	ET_UNLOCK();
101 	return (0);
102 }
103 
104 /*
105  * Deregister event timer hardware.
106  */
107 int
108 et_deregister(struct eventtimer *et)
109 {
110 	int err = 0;
111 
112 	if (et->et_deregister_cb != NULL) {
113 		if ((err = et->et_deregister_cb(et, et->et_arg)) != 0)
114 			return (err);
115 	}
116 
117 	ET_LOCK();
118 	SLIST_REMOVE(&eventtimers, et, eventtimer, et_all);
119 	ET_UNLOCK();
120 	sysctl_remove_oid(et->et_sysctl, 1, 1);
121 	return (0);
122 }
123 
124 /*
125  * Change the frequency of the given timer.  If it is the active timer,
126  * reconfigure it on all CPUs (reschedules all current events based on the new
127  * timer frequency).
128  */
129 void
130 et_change_frequency(struct eventtimer *et, uint64_t newfreq)
131 {
132 
133 #ifndef NO_EVENTTIMERS
134 	cpu_et_frequency(et, newfreq);
135 #endif
136 }
137 
138 /*
139  * Find free event timer hardware with specified parameters.
140  */
141 struct eventtimer *
142 et_find(const char *name, int check, int want)
143 {
144 	struct eventtimer *et = NULL;
145 
146 	SLIST_FOREACH(et, &eventtimers, et_all) {
147 		if (et->et_active)
148 			continue;
149 		if (name != NULL && strcasecmp(et->et_name, name) != 0)
150 			continue;
151 		if (name == NULL && et->et_quality < 0)
152 			continue;
153 		if ((et->et_flags & check) != want)
154 			continue;
155 		break;
156 	}
157 	return (et);
158 }
159 
160 /*
161  * Initialize event timer hardware. Set callbacks.
162  */
163 int
164 et_init(struct eventtimer *et, et_event_cb_t *event,
165     et_deregister_cb_t *deregister, void *arg)
166 {
167 
168 	if (event == NULL)
169 		return (EINVAL);
170 	if (et->et_active)
171 		return (EBUSY);
172 
173 	et->et_active = 1;
174 	et->et_event_cb = event;
175 	et->et_deregister_cb = deregister;
176 	et->et_arg = arg;
177 	return (0);
178 }
179 
180 /*
181  * Start event timer hardware.
182  * first - delay before first tick.
183  * period - period of subsequent periodic ticks.
184  */
185 int
186 et_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
187 {
188 
189 	if (!et->et_active)
190 		return (ENXIO);
191 	KASSERT(period >= 0, ("et_start: negative period"));
192 	KASSERT((et->et_flags & ET_FLAGS_PERIODIC) || period == 0,
193 		("et_start: period specified for oneshot-only timer"));
194 	KASSERT((et->et_flags & ET_FLAGS_ONESHOT) || period != 0,
195 		("et_start: period not specified for periodic-only timer"));
196 	if (period != 0) {
197 		if (period < et->et_min_period)
198 		        period = et->et_min_period;
199 		else if (period > et->et_max_period)
200 		        period = et->et_max_period;
201 	}
202 	if (period == 0 || first != 0) {
203 		if (first < et->et_min_period)
204 		        first = et->et_min_period;
205 		else if (first > et->et_max_period)
206 		        first = et->et_max_period;
207 	}
208 	return (et->et_start(et, first, period));
209 }
210 
211 /* Stop event timer hardware. */
212 int
213 et_stop(struct eventtimer *et)
214 {
215 
216 	if (!et->et_active)
217 		return (ENXIO);
218 	if (et->et_stop)
219 		return (et->et_stop(et));
220 	return (0);
221 }
222 
223 /* Mark event timer hardware as broken. */
224 int
225 et_ban(struct eventtimer *et)
226 {
227 
228 	et->et_flags &= ~(ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT);
229 	return (0);
230 }
231 
232 /* Free event timer hardware. */
233 int
234 et_free(struct eventtimer *et)
235 {
236 
237 	if (!et->et_active)
238 		return (ENXIO);
239 
240 	et->et_active = 0;
241 	return (0);
242 }
243 
244 /* Report list of supported event timer hardware via sysctl. */
245 static int
246 sysctl_kern_eventtimer_choice(SYSCTL_HANDLER_ARGS)
247 {
248 	struct sbuf sb;
249 	struct eventtimer *et;
250 	int error;
251 
252 	sbuf_new(&sb, NULL, 256, SBUF_AUTOEXTEND | SBUF_INCLUDENUL);
253 
254 	ET_LOCK();
255 	SLIST_FOREACH(et, &eventtimers, et_all) {
256 		if (et != SLIST_FIRST(&eventtimers))
257 			sbuf_putc(&sb, ' ');
258 		sbuf_printf(&sb, "%s(%d)", et->et_name, et->et_quality);
259 	}
260 	ET_UNLOCK();
261 
262 	error = sbuf_finish(&sb);
263 	if (error == 0)
264 		error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb));
265 	sbuf_delete(&sb);
266 	return (error);
267 }
268 SYSCTL_PROC(_kern_eventtimer, OID_AUTO, choice,
269     CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
270     0, 0, sysctl_kern_eventtimer_choice, "A", "Present event timers");
271 
272