Home | History | Annotate | Line # | Download | only in sysmon
sysmon.c revision 1.25
      1 /*	$NetBSD: sysmon.c,v 1.25 2015/04/29 03:27:27 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.25 2015/04/29 03:27:27 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 
     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_MISC);
    155 			mutex_enter(&sysmon_minor_mtx);
    156 			if (sysmon_opvec_table[minor(dev)] == NULL)
    157 				error = ENODEV;
    158 		}
    159 		error = (sysmon_opvec_table[minor(dev)]->so_open)(dev, flag,
    160 		    mode, l);
    161 		if (error == 0)
    162 			sysmon_refcnt[minor(dev)]++;
    163 		break;
    164 	default:
    165 		error = ENODEV;
    166 	}
    167 
    168 	mutex_exit(&sysmon_minor_mtx);
    169 	return (error);
    170 }
    171 
    172 /*
    173  * sysmonclose:
    174  *
    175  *	Close the system monitor device.
    176  */
    177 int
    178 sysmonclose(dev_t dev, int flag, int mode, struct lwp *l)
    179 {
    180 	int error;
    181 
    182 	switch (minor(dev)) {
    183 	case SYSMON_MINOR_ENVSYS:
    184 	case SYSMON_MINOR_WDOG:
    185 	case SYSMON_MINOR_POWER:
    186 		if (sysmon_opvec_table[minor(dev)] == NULL)
    187 			error = ENODEV;
    188 		else {
    189 			error = (sysmon_opvec_table[minor(dev)]->so_close)(dev,
    190 			    flag, mode, l);
    191 			if (error == 0) {
    192 				sysmon_refcnt[minor(dev)]--;
    193 				KASSERT(sysmon_refcnt[minor(dev)] >= 0);
    194 			}
    195 		}
    196 		break;
    197 	default:
    198 		error = ENODEV;
    199 	}
    200 
    201 	return (error);
    202 }
    203 
    204 /*
    205  * sysmonioctl:
    206  *
    207  *	Perform a control request.
    208  */
    209 int
    210 sysmonioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
    211 {
    212 	int error;
    213 
    214 	switch (minor(dev)) {
    215 	case SYSMON_MINOR_ENVSYS:
    216 	case SYSMON_MINOR_WDOG:
    217 	case SYSMON_MINOR_POWER:
    218 		if (sysmon_opvec_table[minor(dev)] == NULL)
    219 			error = ENODEV;
    220 		else
    221 			error = (sysmon_opvec_table[minor(dev)]->so_ioctl)(dev,
    222 			    cmd, data, flag, l);
    223 		break;
    224 	default:
    225 		error = ENODEV;
    226 	}
    227 
    228 	return (error);
    229 }
    230 
    231 /*
    232  * sysmonread:
    233  *
    234  *	Perform a read request.
    235  */
    236 int
    237 sysmonread(dev_t dev, struct uio *uio, int flags)
    238 {
    239 	int error;
    240 
    241 	switch (minor(dev)) {
    242 	case SYSMON_MINOR_POWER:
    243 		if (sysmon_opvec_table[minor(dev)] == NULL)
    244 			error = ENODEV;
    245 		else
    246 			error = (sysmon_opvec_table[minor(dev)]->so_read)(dev,
    247 			    uio, flags);
    248 		break;
    249 	default:
    250 		error = ENODEV;
    251 	}
    252 
    253 	return (error);
    254 }
    255 
    256 /*
    257  * sysmonpoll:
    258  *
    259  *	Poll the system monitor device.
    260  */
    261 int
    262 sysmonpoll(dev_t dev, int events, struct lwp *l)
    263 {
    264 	int rv;
    265 
    266 	switch (minor(dev)) {
    267 	case SYSMON_MINOR_POWER:
    268 		if (sysmon_opvec_table[minor(dev)] == NULL)
    269 			rv = events;
    270 		else
    271 			rv = (sysmon_opvec_table[minor(dev)]->so_poll)(dev,
    272 			    events, l);
    273 		break;
    274 	default:
    275 		rv = events;
    276 	}
    277 
    278 	return (rv);
    279 }
    280 
    281 /*
    282  * sysmonkqfilter:
    283  *
    284  *	Kqueue filter for the system monitor device.
    285  */
    286 int
    287 sysmonkqfilter(dev_t dev, struct knote *kn)
    288 {
    289 	int error;
    290 
    291 	switch (minor(dev)) {
    292 	case SYSMON_MINOR_POWER:
    293 		if (sysmon_opvec_table[minor(dev)] == NULL)
    294 			error = ENODEV;
    295 		else
    296 			error = (sysmon_opvec_table[minor(dev)]->so_filter)(dev,
    297 			    kn);
    298 		break;
    299 	default:
    300 		error = 1;
    301 	}
    302 
    303 	return (error);
    304 }
    305 
    306 MODULE(MODULE_CLASS_DRIVER, sysmon, "");
    307 
    308 static int
    309 sm_init_once(void)
    310 {
    311 
    312 	mutex_init(&sysmon_minor_mtx, MUTEX_DEFAULT, IPL_NONE);
    313 
    314 	return 0;
    315 }
    316 
    317 int
    318 sysmon_init(void)
    319 {
    320 	int error;
    321 #ifdef _MODULE
    322 	devmajor_t bmajor, cmajor;
    323 #endif
    324 
    325 	error = RUN_ONCE(&once_sm, sm_init_once);
    326 
    327 #ifdef _MODULE
    328 	mutex_enter(&sysmon_minor_mtx);
    329 	if (!sm_is_attached) {
    330 		bmajor = cmajor = -1;
    331 		error = devsw_attach("sysmon", NULL, &bmajor,
    332 				&sysmon_cdevsw, &cmajor);
    333 		sm_is_attached = (error != 0);
    334 	}
    335 	mutex_exit(&sysmon_minor_mtx);
    336 #endif
    337 
    338 	return error;
    339 }
    340 
    341 int
    342 sysmon_fini(void)
    343 {
    344 	int error = 0;
    345 
    346 	if ((sysmon_opvec_table[SYSMON_MINOR_ENVSYS] != NULL) ||
    347 	    (sysmon_opvec_table[SYSMON_MINOR_WDOG] != NULL) ||
    348 	    (sysmon_opvec_table[SYSMON_MINOR_POWER] != NULL))
    349 		error = EBUSY;
    350 
    351 #ifdef _MODULE
    352 	if (error == 0) {
    353 		mutex_enter(&sysmon_minor_mtx);
    354 		sm_is_attached = false;
    355 		error = devsw_detach(NULL, &sysmon_cdevsw);
    356 		mutex_exit(&sysmon_minor_mtx);
    357 	}
    358 #endif
    359 
    360 	return error;
    361 }
    362 
    363 static
    364 int
    365 sysmon_modcmd(modcmd_t cmd, void *arg)
    366 {
    367 	int ret;
    368 
    369 	switch (cmd) {
    370 	case MODULE_CMD_INIT:
    371 		ret = sysmon_init();
    372 		break;
    373 
    374 	case MODULE_CMD_FINI:
    375 		ret = sysmon_fini();
    376 		break;
    377 
    378 	case MODULE_CMD_STAT:
    379 	default:
    380 		ret = ENOTTY;
    381 	}
    382 
    383 	return ret;
    384 }
    385