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