sysmon.c revision 1.28.8.1 1 /* $NetBSD: sysmon.c,v 1.28.8.1 2017/04/27 05:36:36 pgoyette Exp $ */
2
3 /*-
4 * Copyright (c) 2000 Zembu Labs, Inc.
5 * All rights reserved.
6 *
7 * Author: Jason R. Thorpe <thorpej (at) zembu.com>
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Zembu Labs, Inc.
20 * 4. Neither the name of Zembu Labs nor the names of its employees may
21 * be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY ZEMBU LABS, INC. ``AS IS'' AND ANY EXPRESS
25 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAR-
26 * RANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DIS-
27 * CLAIMED. IN NO EVENT SHALL ZEMBU LABS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 /*
37 * Clearing house for system monitoring hardware. We currently
38 * handle environmental sensors, watchdog timers, and power management.
39 */
40
41 #include <sys/cdefs.h>
42 __KERNEL_RCSID(0, "$NetBSD: sysmon.c,v 1.28.8.1 2017/04/27 05:36:36 pgoyette Exp $");
43
44 #include <sys/param.h>
45 #include <sys/conf.h>
46 #include <sys/errno.h>
47 #include <sys/fcntl.h>
48 #include <sys/callout.h>
49 #include <sys/kernel.h>
50 #include <sys/systm.h>
51 #include <sys/proc.h>
52 #include <sys/module.h>
53 #include <sys/mutex.h>
54 #include <sys/device.h>
55 #include <sys/once.h>
56 #include <sys/localcount.h>
57
58 #include <dev/sysmon/sysmonvar.h>
59
60 dev_type_open(sysmonopen);
61 dev_type_close(sysmonclose);
62 dev_type_ioctl(sysmonioctl);
63 dev_type_read(sysmonread);
64 dev_type_poll(sysmonpoll);
65 dev_type_kqfilter(sysmonkqfilter);
66
67 const struct cdevsw sysmon_cdevsw = {
68 DEVSW_MODULE_INIT
69 .d_open = sysmonopen,
70 .d_close = sysmonclose,
71 .d_read = sysmonread,
72 .d_write = nowrite,
73 .d_ioctl = sysmonioctl,
74 .d_stop = nostop,
75 .d_tty = notty,
76 .d_poll = sysmonpoll,
77 .d_mmap = nommap,
78 .d_kqfilter = sysmonkqfilter,
79 .d_discard = nodiscard,
80 .d_flag = D_OTHER | D_MPSAFE
81 };
82
83 static int sysmon_modcmd(modcmd_t, void *);
84 static int sm_init_once(void);
85
86 /*
87 * Info about our minor "devices"
88 */
89 static struct sysmon_opvec *sysmon_opvec_table[] = { NULL, NULL, NULL };
90 static int sysmon_refcnt[] = { 0, 0, 0 };
91 static const char *sysmon_mod[] = { "sysmon_envsys",
92 "sysmon_wdog",
93 "sysmon_power" };
94 static kmutex_t sysmon_minor_mtx;
95
96 #ifdef _MODULE
97 static bool sm_is_attached;
98 #endif
99
100 ONCE_DECL(once_sm);
101
102 /*
103 * sysmon_attach_minor
104 *
105 * Attach a minor device for wdog, power, or envsys. Manage a
106 * reference count so we can prevent the device from being
107 * detached if there are still users with the minor device opened.
108 *
109 * If the opvec argument is NULL, this is a request to detach the
110 * minor device - make sure the refcnt is zero!
111 */
112 int
113 sysmon_attach_minor(int minor, struct sysmon_opvec *opvec)
114 {
115 int ret;
116
117 mutex_enter(&sysmon_minor_mtx);
118 if (opvec) {
119 if (sysmon_opvec_table[minor] == NULL) {
120 sysmon_refcnt[minor] = 0;
121 sysmon_opvec_table[minor] = opvec;
122 ret = 0;
123 } else
124 ret = EEXIST;
125 } else {
126 if (sysmon_refcnt[minor] == 0) {
127 sysmon_opvec_table[minor] = NULL;
128 ret = 0;
129 } else
130 ret = EBUSY;
131 }
132
133 mutex_exit(&sysmon_minor_mtx);
134 return ret;
135 }
136
137 /*
138 * sysmonopen:
139 *
140 * Open the system monitor device.
141 */
142 int
143 sysmonopen(dev_t dev, int flag, int mode, struct lwp *l)
144 {
145 int error;
146
147 mutex_enter(&sysmon_minor_mtx);
148
149 switch (minor(dev)) {
150 case SYSMON_MINOR_ENVSYS:
151 case SYSMON_MINOR_WDOG:
152 case SYSMON_MINOR_POWER:
153 if (sysmon_opvec_table[minor(dev)] == NULL) {
154 mutex_exit(&sysmon_minor_mtx);
155 error = module_autoload(sysmon_mod[minor(dev)],
156 MODULE_CLASS_MISC);
157 if (error)
158 return error;
159 mutex_enter(&sysmon_minor_mtx);
160 if (sysmon_opvec_table[minor(dev)] == NULL) {
161 error = ENODEV;
162 break;
163 }
164 }
165 error = (sysmon_opvec_table[minor(dev)]->so_open)(dev, flag,
166 mode, l);
167 if (error == 0)
168 sysmon_refcnt[minor(dev)]++;
169 break;
170 default:
171 error = ENODEV;
172 }
173
174 mutex_exit(&sysmon_minor_mtx);
175 return error;
176 }
177
178 /*
179 * sysmonclose:
180 *
181 * Close the system monitor device.
182 */
183 int
184 sysmonclose(dev_t dev, int flag, int mode, struct lwp *l)
185 {
186 int error;
187
188 switch (minor(dev)) {
189 case SYSMON_MINOR_ENVSYS:
190 case SYSMON_MINOR_WDOG:
191 case SYSMON_MINOR_POWER:
192 if (sysmon_opvec_table[minor(dev)] == NULL)
193 error = ENODEV;
194 else {
195 error = (sysmon_opvec_table[minor(dev)]->so_close)(dev,
196 flag, mode, l);
197 if (error == 0) {
198 sysmon_refcnt[minor(dev)]--;
199 KASSERT(sysmon_refcnt[minor(dev)] >= 0);
200 }
201 }
202 break;
203 default:
204 error = ENODEV;
205 }
206
207 return (error);
208 }
209
210 /*
211 * sysmonioctl:
212 *
213 * Perform a control request.
214 */
215 int
216 sysmonioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
217 {
218 int error;
219
220 switch (minor(dev)) {
221 case SYSMON_MINOR_ENVSYS:
222 case SYSMON_MINOR_WDOG:
223 case SYSMON_MINOR_POWER:
224 if (sysmon_opvec_table[minor(dev)] == NULL)
225 error = ENODEV;
226 else
227 error = (sysmon_opvec_table[minor(dev)]->so_ioctl)(dev,
228 cmd, data, flag, l);
229 break;
230 default:
231 error = ENODEV;
232 }
233
234 return (error);
235 }
236
237 /*
238 * sysmonread:
239 *
240 * Perform a read request.
241 */
242 int
243 sysmonread(dev_t dev, struct uio *uio, int flags)
244 {
245 int error;
246
247 switch (minor(dev)) {
248 case SYSMON_MINOR_POWER:
249 if (sysmon_opvec_table[minor(dev)] == NULL)
250 error = ENODEV;
251 else
252 error = (sysmon_opvec_table[minor(dev)]->so_read)(dev,
253 uio, flags);
254 break;
255 default:
256 error = ENODEV;
257 }
258
259 return (error);
260 }
261
262 /*
263 * sysmonpoll:
264 *
265 * Poll the system monitor device.
266 */
267 int
268 sysmonpoll(dev_t dev, int events, struct lwp *l)
269 {
270 int rv;
271
272 switch (minor(dev)) {
273 case SYSMON_MINOR_POWER:
274 if (sysmon_opvec_table[minor(dev)] == NULL)
275 rv = events;
276 else
277 rv = (sysmon_opvec_table[minor(dev)]->so_poll)(dev,
278 events, l);
279 break;
280 default:
281 rv = events;
282 }
283
284 return (rv);
285 }
286
287 /*
288 * sysmonkqfilter:
289 *
290 * Kqueue filter for the system monitor device.
291 */
292 int
293 sysmonkqfilter(dev_t dev, struct knote *kn)
294 {
295 int error;
296
297 switch (minor(dev)) {
298 case SYSMON_MINOR_POWER:
299 if (sysmon_opvec_table[minor(dev)] == NULL)
300 error = ENODEV;
301 else
302 error = (sysmon_opvec_table[minor(dev)]->so_filter)(dev,
303 kn);
304 break;
305 default:
306 error = 1;
307 }
308
309 return (error);
310 }
311
312 MODULE(MODULE_CLASS_DRIVER, sysmon, "");
313
314 static int
315 sm_init_once(void)
316 {
317
318 mutex_init(&sysmon_minor_mtx, MUTEX_DEFAULT, IPL_NONE);
319
320 return 0;
321 }
322
323 int
324 sysmon_init(void)
325 {
326 int error;
327 #ifdef _MODULE
328 devmajor_t bmajor, cmajor;
329 #endif
330
331 error = RUN_ONCE(&once_sm, sm_init_once);
332
333 #ifdef _MODULE
334 mutex_enter(&sysmon_minor_mtx);
335 if (!sm_is_attached) {
336 bmajor = cmajor = -1;
337 error = devsw_attach("sysmon", NULL, &bmajor,
338 &sysmon_cdevsw, &cmajor);
339 sm_is_attached = (error != 0);
340 }
341 mutex_exit(&sysmon_minor_mtx);
342 #endif
343
344 return error;
345 }
346
347 int
348 sysmon_fini(void)
349 {
350 int error = 0;
351
352 if ((sysmon_opvec_table[SYSMON_MINOR_ENVSYS] != NULL) ||
353 (sysmon_opvec_table[SYSMON_MINOR_WDOG] != NULL) ||
354 (sysmon_opvec_table[SYSMON_MINOR_POWER] != NULL))
355 error = EBUSY;
356
357 #ifdef _MODULE
358 if (error == 0) {
359 mutex_enter(&sysmon_minor_mtx);
360 sm_is_attached = false;
361 error = devsw_detach(NULL, &sysmon_cdevsw);
362 mutex_exit(&sysmon_minor_mtx);
363 }
364 #endif
365
366 return error;
367 }
368
369 static
370 int
371 sysmon_modcmd(modcmd_t cmd, void *arg)
372 {
373 int ret;
374
375 switch (cmd) {
376 case MODULE_CMD_INIT:
377 ret = sysmon_init();
378 break;
379
380 case MODULE_CMD_FINI:
381 ret = sysmon_fini();
382 break;
383
384 case MODULE_CMD_STAT:
385 default:
386 ret = ENOTTY;
387 }
388
389 return ret;
390 }
391