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