sysmon.c revision 1.28.2.1 1 /* $NetBSD: sysmon.c,v 1.28.2.1 2016/07/18 03:49:59 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.2.1 2016/07/18 03:49:59 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 #ifdef _MODULE
68 struct localcount sysmon_localcount;
69 #endif
70
71 const struct cdevsw sysmon_cdevsw = {
72 .d_open = sysmonopen,
73 .d_close = sysmonclose,
74 .d_read = sysmonread,
75 .d_write = nowrite,
76 .d_ioctl = sysmonioctl,
77 .d_stop = nostop,
78 .d_tty = notty,
79 .d_poll = sysmonpoll,
80 .d_mmap = nommap,
81 .d_kqfilter = sysmonkqfilter,
82 .d_discard = nodiscard,
83 #ifdef _MODULE
84 .d_localcount = &sysmon_localcount,
85 #endif
86 .d_flag = D_OTHER | D_MPSAFE
87 };
88
89 static int sysmon_modcmd(modcmd_t, void *);
90 static int sm_init_once(void);
91
92 /*
93 * Info about our minor "devices"
94 */
95 static struct sysmon_opvec *sysmon_opvec_table[] = { NULL, NULL, NULL };
96 static int sysmon_refcnt[] = { 0, 0, 0 };
97 static const char *sysmon_mod[] = { "sysmon_envsys",
98 "sysmon_wdog",
99 "sysmon_power" };
100 static kmutex_t sysmon_minor_mtx;
101
102 #ifdef _MODULE
103 static bool sm_is_attached;
104 #endif
105
106 ONCE_DECL(once_sm);
107
108 /*
109 * sysmon_attach_minor
110 *
111 * Attach a minor device for wdog, power, or envsys. Manage a
112 * reference count so we can prevent the device from being
113 * detached if there are still users with the minor device opened.
114 *
115 * If the opvec argument is NULL, this is a request to detach the
116 * minor device - make sure the refcnt is zero!
117 */
118 int
119 sysmon_attach_minor(int minor, struct sysmon_opvec *opvec)
120 {
121 int ret;
122
123 mutex_enter(&sysmon_minor_mtx);
124 if (opvec) {
125 if (sysmon_opvec_table[minor] == NULL) {
126 sysmon_refcnt[minor] = 0;
127 sysmon_opvec_table[minor] = opvec;
128 ret = 0;
129 } else
130 ret = EEXIST;
131 } else {
132 if (sysmon_refcnt[minor] == 0) {
133 sysmon_opvec_table[minor] = NULL;
134 ret = 0;
135 } else
136 ret = EBUSY;
137 }
138
139 mutex_exit(&sysmon_minor_mtx);
140 return ret;
141 }
142
143 /*
144 * sysmonopen:
145 *
146 * Open the system monitor device.
147 */
148 int
149 sysmonopen(dev_t dev, int flag, int mode, struct lwp *l)
150 {
151 int error;
152
153 mutex_enter(&sysmon_minor_mtx);
154
155 switch (minor(dev)) {
156 case SYSMON_MINOR_ENVSYS:
157 case SYSMON_MINOR_WDOG:
158 case SYSMON_MINOR_POWER:
159 if (sysmon_opvec_table[minor(dev)] == NULL) {
160 mutex_exit(&sysmon_minor_mtx);
161 error = module_autoload(sysmon_mod[minor(dev)],
162 MODULE_CLASS_MISC);
163 if (error)
164 return error;
165 mutex_enter(&sysmon_minor_mtx);
166 if (sysmon_opvec_table[minor(dev)] == NULL) {
167 error = ENODEV;
168 break;
169 }
170 }
171 error = (sysmon_opvec_table[minor(dev)]->so_open)(dev, flag,
172 mode, l);
173 if (error == 0)
174 sysmon_refcnt[minor(dev)]++;
175 break;
176 default:
177 error = ENODEV;
178 }
179
180 mutex_exit(&sysmon_minor_mtx);
181 return error;
182 }
183
184 /*
185 * sysmonclose:
186 *
187 * Close the system monitor device.
188 */
189 int
190 sysmonclose(dev_t dev, int flag, int mode, struct lwp *l)
191 {
192 int error;
193
194 switch (minor(dev)) {
195 case SYSMON_MINOR_ENVSYS:
196 case SYSMON_MINOR_WDOG:
197 case SYSMON_MINOR_POWER:
198 if (sysmon_opvec_table[minor(dev)] == NULL)
199 error = ENODEV;
200 else {
201 error = (sysmon_opvec_table[minor(dev)]->so_close)(dev,
202 flag, mode, l);
203 if (error == 0) {
204 sysmon_refcnt[minor(dev)]--;
205 KASSERT(sysmon_refcnt[minor(dev)] >= 0);
206 }
207 }
208 break;
209 default:
210 error = ENODEV;
211 }
212
213 return (error);
214 }
215
216 /*
217 * sysmonioctl:
218 *
219 * Perform a control request.
220 */
221 int
222 sysmonioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
223 {
224 int error;
225
226 switch (minor(dev)) {
227 case SYSMON_MINOR_ENVSYS:
228 case SYSMON_MINOR_WDOG:
229 case SYSMON_MINOR_POWER:
230 if (sysmon_opvec_table[minor(dev)] == NULL)
231 error = ENODEV;
232 else
233 error = (sysmon_opvec_table[minor(dev)]->so_ioctl)(dev,
234 cmd, data, flag, l);
235 break;
236 default:
237 error = ENODEV;
238 }
239
240 return (error);
241 }
242
243 /*
244 * sysmonread:
245 *
246 * Perform a read request.
247 */
248 int
249 sysmonread(dev_t dev, struct uio *uio, int flags)
250 {
251 int error;
252
253 switch (minor(dev)) {
254 case SYSMON_MINOR_POWER:
255 if (sysmon_opvec_table[minor(dev)] == NULL)
256 error = ENODEV;
257 else
258 error = (sysmon_opvec_table[minor(dev)]->so_read)(dev,
259 uio, flags);
260 break;
261 default:
262 error = ENODEV;
263 }
264
265 return (error);
266 }
267
268 /*
269 * sysmonpoll:
270 *
271 * Poll the system monitor device.
272 */
273 int
274 sysmonpoll(dev_t dev, int events, struct lwp *l)
275 {
276 int rv;
277
278 switch (minor(dev)) {
279 case SYSMON_MINOR_POWER:
280 if (sysmon_opvec_table[minor(dev)] == NULL)
281 rv = events;
282 else
283 rv = (sysmon_opvec_table[minor(dev)]->so_poll)(dev,
284 events, l);
285 break;
286 default:
287 rv = events;
288 }
289
290 return (rv);
291 }
292
293 /*
294 * sysmonkqfilter:
295 *
296 * Kqueue filter for the system monitor device.
297 */
298 int
299 sysmonkqfilter(dev_t dev, struct knote *kn)
300 {
301 int error;
302
303 switch (minor(dev)) {
304 case SYSMON_MINOR_POWER:
305 if (sysmon_opvec_table[minor(dev)] == NULL)
306 error = ENODEV;
307 else
308 error = (sysmon_opvec_table[minor(dev)]->so_filter)(dev,
309 kn);
310 break;
311 default:
312 error = 1;
313 }
314
315 return (error);
316 }
317
318 MODULE(MODULE_CLASS_DRIVER, sysmon, "");
319
320 static int
321 sm_init_once(void)
322 {
323
324 mutex_init(&sysmon_minor_mtx, MUTEX_DEFAULT, IPL_NONE);
325
326 return 0;
327 }
328
329 int
330 sysmon_init(void)
331 {
332 int error;
333 #ifdef _MODULE
334 devmajor_t bmajor, cmajor;
335 #endif
336
337 error = RUN_ONCE(&once_sm, sm_init_once);
338
339 #ifdef _MODULE
340 mutex_enter(&sysmon_minor_mtx);
341 if (!sm_is_attached) {
342 bmajor = cmajor = -1;
343 error = devsw_attach("sysmon", NULL, &bmajor,
344 &sysmon_cdevsw, &cmajor);
345 sm_is_attached = (error != 0);
346 }
347 mutex_exit(&sysmon_minor_mtx);
348 #endif
349
350 return error;
351 }
352
353 int
354 sysmon_fini(void)
355 {
356 int error = 0;
357
358 if ((sysmon_opvec_table[SYSMON_MINOR_ENVSYS] != NULL) ||
359 (sysmon_opvec_table[SYSMON_MINOR_WDOG] != NULL) ||
360 (sysmon_opvec_table[SYSMON_MINOR_POWER] != NULL))
361 error = EBUSY;
362
363 #ifdef _MODULE
364 if (error == 0) {
365 mutex_enter(&sysmon_minor_mtx);
366 sm_is_attached = false;
367 error = devsw_detach(NULL, &sysmon_cdevsw);
368 mutex_exit(&sysmon_minor_mtx);
369 }
370 #endif
371
372 return error;
373 }
374
375 static
376 int
377 sysmon_modcmd(modcmd_t cmd, void *arg)
378 {
379 int ret;
380
381 switch (cmd) {
382 case MODULE_CMD_INIT:
383 ret = sysmon_init();
384 break;
385
386 case MODULE_CMD_FINI:
387 ret = sysmon_fini();
388 break;
389
390 case MODULE_CMD_STAT:
391 default:
392 ret = ENOTTY;
393 }
394
395 return ret;
396 }
397