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