Home | History | Annotate | Line # | Download | only in sysmon
sysmon.c revision 1.3
      1 /*	$NetBSD: sysmon.c,v 1.3 2000/11/04 18:37:19 thorpej 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.  This pseudo-device
     38  * is a place where hardware monitors such as the LM78 and VIA 82C686A
     39  * (or even ACPI, eventually) can register themselves to provide
     40  * backplane fan and temperature information, etc.  It also provides
     41  * a place to register watchdog timers, and provides an abstract
     42  * interface to them.
     43  */
     44 
     45 #include <sys/param.h>
     46 #include <sys/conf.h>
     47 #include <sys/errno.h>
     48 #include <sys/fcntl.h>
     49 #include <sys/lock.h>
     50 #include <sys/callout.h>
     51 #include <sys/kernel.h>
     52 #include <sys/systm.h>
     53 #include <sys/proc.h>
     54 
     55 #include <dev/sysmon/sysmonvar.h>
     56 
     57 /*
     58  * We run at ENVSYS version 1.
     59  */
     60 #define	SYSMON_ENVSYS_VERSION	(1 * 1000)
     61 
     62 struct lock sysmon_envsys_lock;
     63 
     64 LIST_HEAD(, sysmon_envsys) sysmon_envsys_list =
     65     LIST_HEAD_INITIALIZER(&sysmon_envsys_list);
     66 struct simplelock sysmon_envsys_list_slock = SIMPLELOCK_INITIALIZER;
     67 u_int	sysmon_envsys_next_sensor_index;
     68 
     69 int	sysmon_envsys_initialized;
     70 struct simplelock sysmon_envsys_initialized_slock = SIMPLELOCK_INITIALIZER;
     71 
     72 cdev_decl(sysmon);
     73 
     74 void	sysmon_envsys_init(void);
     75 
     76 int	sysmonioctl_envsys(dev_t, u_long, caddr_t, int, struct proc *);
     77 int	sysmonioctl_wdog(dev_t, u_long, caddr_t, int, struct proc *);
     78 
     79 struct sysmon_envsys *sysmon_envsys_find(u_int);
     80 void	sysmon_envsys_release(struct sysmon_envsys *);
     81 
     82 LIST_HEAD(, sysmon_wdog) sysmon_wdog_list =
     83     LIST_HEAD_INITIALIZER(&sysmon_wdog_list);
     84 int sysmon_wdog_count;
     85 struct simplelock sysmon_wdog_list_slock = SIMPLELOCK_INITIALIZER;
     86 
     87 struct simplelock sysmon_wdog_slock = SIMPLELOCK_INITIALIZER;
     88 struct sysmon_wdog *sysmon_armed_wdog;
     89 struct callout sysmon_wdog_callout = CALLOUT_INITIALIZER;
     90 void *sysmon_wdog_sdhook;
     91 
     92 #define	SYSMON_WDOG_LOCK(s)						\
     93 do {									\
     94 	s = splsoftclock();						\
     95 	simple_lock(&sysmon_wdog_slock);				\
     96 } while (0)
     97 
     98 #define	SYSMON_WDOG_UNLOCK(s)						\
     99 do {									\
    100 	simple_unlock(&sysmon_wdog_slock);				\
    101 	splx(s);							\
    102 } while (0)
    103 
    104 struct sysmon_wdog *sysmon_wdog_find(const char *);
    105 void	sysmon_wdog_release(struct sysmon_wdog *);
    106 int	sysmon_wdog_setmode(struct sysmon_wdog *, int, u_int);
    107 void	sysmon_wdog_ktickle(void *);
    108 void	sysmon_wdog_shutdown(void *);
    109 
    110 #define	SYSMON_MINOR_ENVSYS	0
    111 #define	SYSMON_MINOR_WDOG	1
    112 
    113 /*
    114  * sysmon_envsys_init:
    115  *
    116  *	Initialize the system monitor.
    117  */
    118 void
    119 sysmon_envsys_init(void)
    120 {
    121 
    122 	lockinit(&sysmon_envsys_lock, PWAIT|PCATCH, "smenv", 0, 0);
    123 	sysmon_envsys_initialized = 1;
    124 }
    125 
    126 /*
    127  * sysmonopen:
    128  *
    129  *	Open the system monitor device.
    130  */
    131 int
    132 sysmonopen(dev_t dev, int flag, int mode, struct proc *p)
    133 {
    134 	int error = 0;
    135 
    136 	switch (minor(dev)) {
    137 	case SYSMON_MINOR_ENVSYS:
    138 		simple_lock(&sysmon_envsys_initialized_slock);
    139 		if (sysmon_envsys_initialized == 0)
    140 			sysmon_envsys_init();
    141 
    142 		error = lockmgr(&sysmon_envsys_lock,
    143 		    LK_EXCLUSIVE | LK_INTERLOCK |
    144 		    ((flag & O_NONBLOCK) ? LK_NOWAIT : 0),
    145 		    &sysmon_envsys_initialized_slock);
    146 		break;
    147 
    148 	case SYSMON_MINOR_WDOG:
    149 		simple_lock(&sysmon_wdog_list_slock);
    150 		if (sysmon_wdog_sdhook == NULL) {
    151 			sysmon_wdog_sdhook =
    152 			    shutdownhook_establish(sysmon_wdog_shutdown, NULL);
    153 			if (sysmon_wdog_sdhook == NULL)
    154 				printf("WARNING: unable to register watchdog "
    155 				    "shutdown hook\n");
    156 		}
    157 		simple_unlock(&sysmon_wdog_list_slock);
    158 		break;
    159 
    160 	default:
    161 		error = ENODEV;
    162 	}
    163 
    164 	return (error);
    165 }
    166 
    167 /*
    168  * sysmonclose:
    169  *
    170  *	Close the system monitor device.
    171  */
    172 int
    173 sysmonclose(dev_t dev, int flag, int mode, struct proc *p)
    174 {
    175 	int error = 0;
    176 
    177 	switch (minor(dev)) {
    178 	case SYSMON_MINOR_ENVSYS:
    179 		(void) lockmgr(&sysmon_envsys_lock, LK_RELEASE, NULL);
    180 		break;
    181 
    182 	case SYSMON_MINOR_WDOG:
    183 	    {
    184 		struct sysmon_wdog *smw;
    185 		int omode, s;
    186 
    187 		/*
    188 		 * If this is the last close, and there is a watchdog
    189 		 * running in UTICKLE mode, we need to disable it,
    190 		 * otherwise the system will reset in short order.
    191 		 *
    192 		 * XXX Maybe we should just go into KTICKLE mode?
    193 		 */
    194 		SYSMON_WDOG_LOCK(s);
    195 		if ((smw = sysmon_armed_wdog) != NULL) {
    196 			if ((omode = smw->smw_mode) == WDOG_MODE_UTICKLE) {
    197 				error = sysmon_wdog_setmode(smw,
    198 				    WDOG_MODE_DISARMED, smw->smw_period);
    199 				if (error) {
    200 					printf("WARNING: UNABLE TO DISARM "
    201 					    "WATCHDOG %s ON CLOSE!\n",
    202 					    smw->smw_name);
    203 					/*
    204 					 * ...we will probably reboot soon.
    205 					 */
    206 				}
    207 			}
    208 		}
    209 		SYSMON_WDOG_UNLOCK(s);
    210 		break;
    211 	    }
    212 
    213 	default:
    214 		error = ENODEV;
    215 	}
    216 
    217 	return (error);
    218 }
    219 
    220 /*
    221  * sysmonioctl:
    222  *
    223  *	Perform a control request.
    224  */
    225 int
    226 sysmonioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
    227 {
    228 	int error;
    229 
    230 	switch (minor(dev)) {
    231 	case SYSMON_MINOR_ENVSYS:
    232 		error = sysmonioctl_envsys(dev, cmd, data, flag, p);
    233 		break;
    234 
    235 	case SYSMON_MINOR_WDOG:
    236 		error = sysmonioctl_wdog(dev, cmd, data, flag, p);
    237 		break;
    238 
    239 	default:
    240 		error = ENODEV;
    241 	}
    242 
    243 	return (error);
    244 }
    245 
    246 /*
    247  * sysmonioctl_envsys:
    248  *
    249  *	Perform an envsys control request.
    250  */
    251 int
    252 sysmonioctl_envsys(dev_t dev, u_long cmd, caddr_t data, int flag,
    253     struct proc *p)
    254 {
    255 	struct sysmon_envsys *sme;
    256 	int error = 0;
    257 	u_int oidx;
    258 
    259 	switch (cmd) {
    260 	/*
    261 	 * For ENVSYS commands, we translate the absolute sensor index
    262 	 * to a device-relative sensor index.
    263 	 */
    264 	case ENVSYS_VERSION:
    265 		*(int32_t *)data = SYSMON_ENVSYS_VERSION;
    266 		break;
    267 
    268 	case ENVSYS_GRANGE:
    269 	    {
    270 		struct envsys_range *rng = (void *) data;
    271 
    272 		sme = sysmon_envsys_find(0);	/* XXX */
    273 		if (sme == NULL) {
    274 			/* Return empty range for `no sensors'. */
    275 			rng->low = 1;
    276 			rng->high = 0;
    277 			break;
    278 		}
    279 
    280 		if (rng->units < ENVSYS_NSENSORS)
    281 			*rng = sme->sme_ranges[rng->units];
    282 		else {
    283 			/* Return empty range for unsupported sensor types. */
    284 			rng->low = 1;
    285 			rng->high = 0;
    286 		}
    287 		sysmon_envsys_release(sme);
    288 		break;
    289 	    }
    290 
    291 	case ENVSYS_GTREDATA:
    292 	    {
    293 		struct envsys_tre_data *tred = (void *) data;
    294 
    295 		tred->validflags = 0;
    296 
    297 		sme = sysmon_envsys_find(tred->sensor);
    298 		if (sme == NULL)
    299 			break;
    300 		oidx = tred->sensor;
    301 		tred->sensor = SME_SENSOR_IDX(sme, tred->sensor);
    302 		if (tred->sensor < sme->sme_nsensors)
    303 			error = (*sme->sme_gtredata)(sme, tred);
    304 		tred->sensor = oidx;
    305 		sysmon_envsys_release(sme);
    306 		break;
    307 	    }
    308 
    309 	case ENVSYS_STREINFO:
    310 	    {
    311 		struct envsys_basic_info *binfo = (void *) data;
    312 
    313 		sme = sysmon_envsys_find(binfo->sensor);
    314 		if (sme == NULL) {
    315 			binfo->validflags = 0;
    316 			break;
    317 		}
    318 		oidx = binfo->sensor;
    319 		binfo->sensor = SME_SENSOR_IDX(sme, binfo->sensor);
    320 		if (binfo->sensor < sme->sme_nsensors)
    321 			error = (*sme->sme_streinfo)(sme, binfo);
    322 		else
    323 			binfo->validflags = 0;
    324 		binfo->sensor = oidx;
    325 		sysmon_envsys_release(sme);
    326 		break;
    327 	    }
    328 
    329 	case ENVSYS_GTREINFO:
    330 	    {
    331 		struct envsys_basic_info *binfo = (void *) data;
    332 
    333 		binfo->validflags = 0;
    334 
    335 		sme = sysmon_envsys_find(binfo->sensor);
    336 		if (sme == NULL)
    337 			break;
    338 		oidx = binfo->sensor;
    339 		binfo->sensor = SME_SENSOR_IDX(sme, binfo->sensor);
    340 		if (binfo->sensor < sme->sme_nsensors)
    341 			*binfo = sme->sme_sensor_info[binfo->sensor];
    342 		binfo->sensor = oidx;
    343 		sysmon_envsys_release(sme);
    344 		break;
    345 	    }
    346 
    347 	default:
    348 		error = ENOTTY;
    349 	}
    350 
    351 	return (error);
    352 }
    353 
    354 /*
    355  * sysmonioctl_wdog:
    356  *
    357  *	Perform a watchdog control request.
    358  */
    359 int
    360 sysmonioctl_wdog(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
    361 {
    362 	struct sysmon_wdog *smw;
    363 	int s, error = 0;
    364 
    365 	switch (cmd) {
    366 	case WDOGIOC_GMODE:
    367 	    {
    368 		struct wdog_mode *wm = (void *) data;
    369 
    370 		wm->wm_name[sizeof(wm->wm_name) - 1] = '\0';
    371 		smw = sysmon_wdog_find(wm->wm_name);
    372 		if (smw == NULL) {
    373 			error = ESRCH;
    374 			break;
    375 		}
    376 
    377 		wm->wm_mode = smw->smw_mode;
    378 		wm->wm_period = smw->smw_period;
    379 		sysmon_wdog_release(smw);
    380 		break;
    381 	    }
    382 
    383 	case WDOGIOC_SMODE:
    384 	    {
    385 		struct wdog_mode *wm = (void *) data;
    386 
    387 		if ((flag & FWRITE) == 0) {
    388 			error = EPERM;
    389 			break;
    390 		}
    391 
    392 		wm->wm_name[sizeof(wm->wm_name) - 1] = '\0';
    393 		smw = sysmon_wdog_find(wm->wm_name);
    394 		if (smw == NULL) {
    395 			error = ESRCH;
    396 			break;
    397 		}
    398 
    399 		if (wm->wm_mode & ~(WDOG_MODE_MASK|WDOG_FEATURE_MASK))
    400 			error = EINVAL;
    401 		else {
    402 			SYSMON_WDOG_LOCK(s);
    403 			error = sysmon_wdog_setmode(smw, wm->wm_mode,
    404 			    wm->wm_period);
    405 			SYSMON_WDOG_UNLOCK(s);
    406 		}
    407 
    408 		sysmon_wdog_release(smw);
    409 		break;
    410 	    }
    411 
    412 	case WDOGIOC_WHICH:
    413 	    {
    414 		struct wdog_mode *wm = (void *) data;
    415 
    416 		SYSMON_WDOG_LOCK(s);
    417 		if ((smw = sysmon_armed_wdog) != NULL) {
    418 			strcpy(wm->wm_name, smw->smw_name);
    419 			wm->wm_mode = smw->smw_mode;
    420 			wm->wm_period = smw->smw_period;
    421 		} else
    422 			error = ESRCH;
    423 		SYSMON_WDOG_UNLOCK(s);
    424 		break;
    425 	    }
    426 
    427 	case WDOGIOC_TICKLE:
    428 		if ((flag & FWRITE) == 0) {
    429 			error = EPERM;
    430 			break;
    431 		}
    432 
    433 		SYSMON_WDOG_LOCK(s);
    434 		if ((smw = sysmon_armed_wdog) != NULL) {
    435 			error = (*smw->smw_tickle)(smw);
    436 			if (error == 0)
    437 				smw->smw_tickler = p->p_pid;
    438 		} else
    439 			error = ESRCH;
    440 		SYSMON_WDOG_UNLOCK(s);
    441 		break;
    442 
    443 	case WDOGIOC_GTICKLER:
    444 		*(pid_t *)data = smw->smw_tickler;
    445 		break;
    446 
    447 	case WDOGIOC_GWDOGS:
    448 	    {
    449 		struct wdog_conf *wc = (void *) data;
    450 		char *cp;
    451 		int i;
    452 
    453 		simple_lock(&sysmon_wdog_list_slock);
    454 		if (wc->wc_names == NULL)
    455 			wc->wc_count = sysmon_wdog_count;
    456 		else {
    457 			for (i = 0, cp = wc->wc_names,
    458 			       smw = LIST_FIRST(&sysmon_wdog_list);
    459 			     i < sysmon_wdog_count && smw != NULL && error == 0;
    460 			     i++, cp += WDOG_NAMESIZE,
    461 			       smw = LIST_NEXT(smw, smw_list))
    462 				error = copyout(smw->smw_name, cp,
    463 				    strlen(smw->smw_name) + 1);
    464 			wc->wc_count = i;
    465 		}
    466 		simple_unlock(&sysmon_wdog_list_slock);
    467 		break;
    468 	    }
    469 
    470 	default:
    471 		error = ENOTTY;
    472 	}
    473 
    474 	return (error);
    475 }
    476 
    477 /*
    478  * sysmon_envsys_register:
    479  *
    480  *	Register an ENVSYS device.
    481  */
    482 int
    483 sysmon_envsys_register(struct sysmon_envsys *sme)
    484 {
    485 	int error = 0;
    486 
    487 	simple_lock(&sysmon_envsys_list_slock);
    488 
    489 	/* XXX Only get to register one, for now. */
    490 	if (LIST_FIRST(&sysmon_envsys_list) != NULL) {
    491 		error = EEXIST;
    492 		goto out;
    493 	}
    494 
    495 	if (sme->sme_envsys_version != SYSMON_ENVSYS_VERSION) {
    496 		error = EINVAL;
    497 		goto out;
    498 	}
    499 
    500 	sme->sme_fsensor = sysmon_envsys_next_sensor_index;
    501 	sysmon_envsys_next_sensor_index += sme->sme_nsensors;
    502 	LIST_INSERT_HEAD(&sysmon_envsys_list, sme, sme_list);
    503 
    504  out:
    505 	simple_unlock(&sysmon_envsys_list_slock);
    506 	return (error);
    507 }
    508 
    509 /*
    510  * sysmon_envsys_unregister:
    511  *
    512  *	Unregister an ENVSYS device.
    513  */
    514 void
    515 sysmon_envsys_unregister(struct sysmon_envsys *sme)
    516 {
    517 
    518 	simple_lock(&sysmon_envsys_list_slock);
    519 	LIST_REMOVE(sme, sme_list);
    520 	simple_unlock(&sysmon_envsys_list_slock);
    521 }
    522 
    523 /*
    524  * sysmon_envsys_find:
    525  *
    526  *	Find an ENVSYS device.  The list remains locked upon
    527  *	a match.
    528  */
    529 struct sysmon_envsys *
    530 sysmon_envsys_find(u_int idx)
    531 {
    532 	struct sysmon_envsys *sme;
    533 
    534 	simple_lock(&sysmon_envsys_list_slock);
    535 
    536 	for (sme = LIST_FIRST(&sysmon_envsys_list); sme != NULL;
    537 	     sme = LIST_NEXT(sme, sme_list)) {
    538 		if (idx >= sme->sme_fsensor &&
    539 		    idx < (sme->sme_fsensor + sme->sme_nsensors))
    540 			return (sme);
    541 	}
    542 
    543 	simple_unlock(&sysmon_envsys_list_slock);
    544 	return (NULL);
    545 }
    546 
    547 /*
    548  * sysmon_envsys_release:
    549  *
    550  *	Release an ENVSYS device.
    551  */
    552 /* ARGSUSED */
    553 void
    554 sysmon_envsys_release(struct sysmon_envsys *sme)
    555 {
    556 
    557 	simple_unlock(&sysmon_envsys_list_slock);
    558 }
    559 
    560 /*
    561  * sysmon_wdog_register:
    562  *
    563  *	Register a watchdog device.
    564  */
    565 int
    566 sysmon_wdog_register(struct sysmon_wdog *smw)
    567 {
    568 	struct sysmon_wdog *lsmw;
    569 	int error = 0;
    570 
    571 	simple_lock(&sysmon_wdog_list_slock);
    572 
    573 	for (lsmw = LIST_FIRST(&sysmon_wdog_list); lsmw != NULL;
    574 	     lsmw = LIST_NEXT(lsmw, smw_list)) {
    575 		if (strcmp(lsmw->smw_name, smw->smw_name) == 0) {
    576 			error = EEXIST;
    577 			goto out;
    578 		}
    579 	}
    580 
    581 	smw->smw_mode = WDOG_MODE_DISARMED;
    582 	smw->smw_tickler = (pid_t) -1;
    583 	smw->smw_refcnt = 0;
    584 	sysmon_wdog_count++;
    585 	LIST_INSERT_HEAD(&sysmon_wdog_list, smw, smw_list);
    586 
    587  out:
    588 	simple_unlock(&sysmon_wdog_list_slock);
    589 	return (error);
    590 }
    591 
    592 /*
    593  * sysmon_wdog_unregister:
    594  *
    595  *	Unregister a watchdog device.
    596  */
    597 void
    598 sysmon_wdog_unregister(struct sysmon_wdog *smw)
    599 {
    600 
    601 	simple_lock(&sysmon_wdog_list_slock);
    602 	sysmon_wdog_count--;
    603 	LIST_REMOVE(smw, smw_list);
    604 	simple_unlock(&sysmon_wdog_list_slock);
    605 }
    606 
    607 /*
    608  * sysmon_wdog_find:
    609  *
    610  *	Find a watchdog device.  We increase the reference
    611  *	count on a match.
    612  */
    613 struct sysmon_wdog *
    614 sysmon_wdog_find(const char *name)
    615 {
    616 	struct sysmon_wdog *smw;
    617 
    618 	simple_lock(&sysmon_wdog_list_slock);
    619 
    620 	for (smw = LIST_FIRST(&sysmon_wdog_list); smw != NULL;
    621 	     smw = LIST_NEXT(smw, smw_list)) {
    622 		if (strcmp(smw->smw_name, name) == 0)
    623 			break;
    624 	}
    625 
    626 	if (smw != NULL)
    627 		smw->smw_refcnt++;
    628 
    629 	simple_unlock(&sysmon_wdog_list_slock);
    630 	return (smw);
    631 }
    632 
    633 /*
    634  * sysmon_wdog_release:
    635  *
    636  *	Release a watchdog device.
    637  */
    638 void
    639 sysmon_wdog_release(struct sysmon_wdog *smw)
    640 {
    641 
    642 	simple_lock(&sysmon_wdog_list_slock);
    643 	KASSERT(smw->smw_refcnt != 0);
    644 	smw->smw_refcnt--;
    645 	simple_unlock(&sysmon_wdog_list_slock);
    646 }
    647 
    648 /*
    649  * sysmon_wdog_setmode:
    650  *
    651  *	Set the mode of a watchdog device.
    652  */
    653 int
    654 sysmon_wdog_setmode(struct sysmon_wdog *smw, int mode, u_int period)
    655 {
    656 	u_int operiod = smw->smw_period;
    657 	int omode = smw->smw_mode;
    658 	int error = 0;
    659 
    660 	smw->smw_period = period;
    661 	smw->smw_mode = mode;
    662 
    663 	switch (mode & WDOG_MODE_MASK) {
    664 	case WDOG_MODE_DISARMED:
    665 		if (smw != sysmon_armed_wdog) {
    666 			error = EINVAL;
    667 			goto out;
    668 		}
    669 		break;
    670 
    671 	case WDOG_MODE_KTICKLE:
    672 	case WDOG_MODE_UTICKLE:
    673 		if (sysmon_armed_wdog != NULL) {
    674 			error = EBUSY;
    675 			goto out;
    676 		}
    677 		break;
    678 
    679 	default:
    680 		error = EINVAL;
    681 		goto out;
    682 	}
    683 
    684 	error = (*smw->smw_setmode)(smw);
    685 
    686  out:
    687 	if (error) {
    688 		smw->smw_period = operiod;
    689 		smw->smw_mode = omode;
    690 	} else {
    691 		if ((mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
    692 			smw->smw_tickler = (pid_t) -1;
    693 			smw->smw_refcnt--;
    694 			if ((omode & WDOG_MODE_MASK) == WDOG_MODE_KTICKLE)
    695 				callout_stop(&sysmon_wdog_callout);
    696 		} else {
    697 			sysmon_armed_wdog = smw;
    698 			smw->smw_refcnt++;
    699 			if ((mode & WDOG_MODE_MASK) == WDOG_MODE_KTICKLE) {
    700 				callout_reset(&sysmon_wdog_callout,
    701 				    WDOG_PERIOD_TO_TICKS(smw->smw_period) / 2,
    702 				    sysmon_wdog_ktickle, NULL);
    703 			}
    704 		}
    705 	}
    706 	return (error);
    707 }
    708 
    709 /*
    710  * sysmon_wdog_ktickle:
    711  *
    712  *	Kernel watchdog tickle routine.
    713  */
    714 void
    715 sysmon_wdog_ktickle(void *arg)
    716 {
    717 	struct sysmon_wdog *smw;
    718 	int s;
    719 
    720 	SYSMON_WDOG_LOCK(s);
    721 	if ((smw = sysmon_armed_wdog) != NULL) {
    722 		if ((*smw->smw_tickle)(smw) != 0) {
    723 			printf("WARNING: KERNEL TICKLE OF WATCHDOG %s "
    724 			    "FAILED!\n", smw->smw_name);
    725 			/*
    726 			 * ...we will probably reboot soon.
    727 			 */
    728 		}
    729 		callout_reset(&sysmon_wdog_callout,
    730 		    WDOG_PERIOD_TO_TICKS(smw->smw_period) / 2,
    731 		    sysmon_wdog_ktickle, NULL);
    732 	}
    733 	SYSMON_WDOG_UNLOCK(s);
    734 }
    735 
    736 /*
    737  * sysmon_wdog_shutdown:
    738  *
    739  *	Perform shutdown-time operations.
    740  */
    741 void
    742 sysmon_wdog_shutdown(void *arg)
    743 {
    744 	struct sysmon_wdog *smw;
    745 
    746 	/*
    747 	 * XXX Locking here?  I don't think it's necessary.
    748 	 */
    749 
    750 	if ((smw = sysmon_armed_wdog) != NULL) {
    751 		if (sysmon_wdog_setmode(smw, WDOG_MODE_DISARMED,
    752 		    smw->smw_period))
    753 			printf("WARNING: FAILED TO SHUTDOWN WATCHDOG %s!\n",
    754 			    smw->smw_name);
    755 	}
    756 }
    757