Home | History | Annotate | Line # | Download | only in sysmon
      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 (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.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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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