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