Home | History | Annotate | Line # | Download | only in sysmon
swwdog.c revision 1.14
      1 /*	$NetBSD: swwdog.c,v 1.14 2015/04/18 10:49:31 pgoyette Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2004, 2005 Steven M. Bellovin
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *      This product includes software developed by Steven M. Bellovin
     18  * 4. The name of the author contributors may not be used to endorse or
     19  *    promote products derived from this software without specific prior
     20  *    written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS
     23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
     26  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     32  * POSSIBILITY OF SUCH DAMAGE.
     33  */
     34 
     35 #include <sys/cdefs.h>
     36 __KERNEL_RCSID(0, "$NetBSD: swwdog.c,v 1.14 2015/04/18 10:49:31 pgoyette Exp $");
     37 
     38 /*
     39  *
     40  * Software watchdog timer
     41  *
     42  */
     43 #include <sys/param.h>
     44 #include <sys/device.h>
     45 #include <sys/callout.h>
     46 #include <sys/kernel.h>
     47 #include <sys/kmem.h>
     48 #include <sys/reboot.h>
     49 #include <sys/systm.h>
     50 #include <sys/sysctl.h>
     51 #include <sys/wdog.h>
     52 #include <sys/workqueue.h>
     53 #include <sys/module.h>
     54 #include <dev/sysmon/sysmonvar.h>
     55 
     56 #ifndef _MODULE
     57 #include "opt_modular.h"
     58 #endif
     59 
     60 struct swwdog_softc {
     61 	device_t		sc_dev;
     62 	struct sysmon_wdog	sc_smw;
     63 	struct callout		sc_c;
     64 	int			sc_armed;
     65 };
     66 
     67 bool	swwdog_reboot = false;	/* false --> panic , true  --> reboot */
     68 
     69 static struct workqueue *wq;
     70 static device_t		swwdog_dev;
     71 
     72 MODULE(MODULE_CLASS_DRIVER, swwdog, NULL);
     73 
     74 #ifdef _LKM
     75 CFDRIVER_DECL(swwdog, DV_DULL, NULL);
     76 #endif
     77 
     78 int swwdogattach(int);
     79 
     80 static int	swwdog_setmode(struct sysmon_wdog *);
     81 static int	swwdog_tickle(struct sysmon_wdog *);
     82 static bool	swwdog_suspend(device_t, const pmf_qual_t *);
     83 static int	swwdog_arm(struct swwdog_softc *);
     84 static int	swwdog_disarm(struct swwdog_softc *);
     85 
     86 static void	swwdog_panic(void *);
     87 
     88 static void	swwdog_sysctl_setup(void);
     89 static struct sysctllog *swwdog_sysctllog = NULL;
     90 
     91 static int	swwdog_match(device_t, cfdata_t, void *);
     92 static void	swwdog_attach(device_t, device_t, void *);
     93 static int	swwdog_detach(device_t, int);
     94 static bool	swwdog_suspend(device_t, const pmf_qual_t *);
     95 
     96 static int	swwdog_init(void *);
     97 static int	swwdog_fini(void *);
     98 static int	swwdog_modcmd(modcmd_t, void *);
     99 
    100 CFATTACH_DECL_NEW(swwdog, sizeof(struct swwdog_softc),
    101 	swwdog_match, swwdog_attach, swwdog_detach, NULL);
    102 extern struct cfdriver swwdog_cd;
    103 
    104 #define	SWDOG_DEFAULT	60		/* 60-second default period */
    105 
    106 static void
    107 doreboot(struct work *wrkwrkwrk, void *p)
    108 {
    109 
    110 	cpu_reboot(0, NULL);
    111 }
    112 
    113 int
    114 swwdogattach(int n __unused)
    115 {
    116 	int error;
    117 	static struct cfdata cf;
    118 
    119 printf("%s: entered\n", __func__); /* XXX PRG */
    120 	if (workqueue_create(&wq, "swwreboot", doreboot, NULL,
    121 	    PRI_NONE, IPL_NONE, 0) != 0) {
    122 		aprint_error("failed to create swwdog reboot wq");
    123 		return 1;
    124 	}
    125 
    126 	error = config_cfattach_attach(swwdog_cd.cd_name, &swwdog_ca);
    127 	if (error) {
    128 		aprint_error("%s: unable to attach cfattach\n",
    129 		    swwdog_cd.cd_name);
    130 		workqueue_destroy(wq);
    131 		return error;
    132 	}
    133 
    134 	cf.cf_name = swwdog_cd.cd_name;
    135 	cf.cf_atname = swwdog_cd.cd_name;
    136 	cf.cf_unit = 0;
    137 	cf.cf_fstate = FSTATE_STAR;
    138 	cf.cf_pspec = NULL;
    139 	cf.cf_loc = NULL;
    140 	cf.cf_flags = 0;
    141 
    142 	swwdog_dev = config_attach_pseudo(&cf);
    143 
    144 printf("%s: swwdog_dev = 0x%p\n", __func__, swwdog_dev); /* XXX PRG */
    145 	if (swwdog_dev == NULL) {
    146 		config_cfattach_detach(swwdog_cd.cd_name, &swwdog_ca);
    147 		workqueue_destroy(wq);
    148 		return 1;
    149 	}
    150 	return 0;
    151 }
    152 
    153 static int
    154 swwdog_match(device_t parent, cfdata_t data, void *aux)
    155 {
    156 
    157 printf("%s: entered\n", __func__); /* XXX PRG */
    158 	return 1;
    159 }
    160 
    161 static void
    162 swwdog_attach(device_t parent, device_t self, void *aux)
    163 {
    164 	struct swwdog_softc *sc = device_private(self);
    165 
    166 printf("%s: entered\n", __func__); /* XXX PRG */
    167 	if (workqueue_create(&wq, "swwreboot", doreboot, NULL,
    168 	    PRI_NONE, IPL_NONE, 0) != 0) {
    169 		aprint_error_dev(self, "failed to create reboot workqueue");
    170 	}
    171 
    172 	sc->sc_dev = self;
    173 	sc->sc_smw.smw_name = device_xname(self);
    174 	sc->sc_smw.smw_cookie = sc;
    175 	sc->sc_smw.smw_setmode = swwdog_setmode;
    176 	sc->sc_smw.smw_tickle = swwdog_tickle;
    177 	sc->sc_smw.smw_period = SWDOG_DEFAULT;
    178 
    179 	callout_init(&sc->sc_c, 0);
    180 	callout_setfunc(&sc->sc_c, swwdog_panic, sc);
    181 
    182 	if (sysmon_wdog_register(&sc->sc_smw) == 0)
    183 		aprint_normal_dev(self, "software watchdog initialized\n");
    184 	else {
    185 		aprint_error_dev(self, "unable to register software "
    186 		    "watchdog with sysmon\n");
    187 		callout_destroy(&sc->sc_c);
    188 		workqueue_destroy(wq);
    189 		return;
    190 	}
    191 
    192 	if (!pmf_device_register(self, swwdog_suspend, NULL))
    193 		aprint_error_dev(self, "couldn't establish power handler\n");
    194 
    195 	swwdog_sysctl_setup();
    196 }
    197 
    198 static int
    199 swwdog_detach(device_t self, int flags)
    200 {
    201 	struct swwdog_softc *sc = device_private(self);
    202 
    203 printf("%s: entered\n", __func__); /* XXX PRG */
    204 	pmf_device_deregister(self);
    205 	swwdog_disarm(sc);
    206 	sysctl_teardown(&swwdog_sysctllog);
    207 	sysmon_wdog_unregister(&sc->sc_smw);
    208 	callout_destroy(&sc->sc_c);
    209 	workqueue_destroy(wq);
    210 
    211 	return 0;
    212 }
    213 
    214 static bool
    215 swwdog_suspend(device_t dev, const pmf_qual_t *qual)
    216 {
    217 	struct swwdog_softc *sc = device_private(dev);
    218 
    219 	/* Don't allow suspend if watchdog is armed */
    220 	if ((sc->sc_smw.smw_mode & WDOG_MODE_MASK) != WDOG_MODE_DISARMED)
    221 		return false;
    222 	return true;
    223 }
    224 
    225 static int
    226 swwdog_setmode(struct sysmon_wdog *smw)
    227 {
    228 	struct swwdog_softc *sc = smw->smw_cookie;
    229 	int error = 0;
    230 
    231 	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
    232 		error = swwdog_disarm(sc);
    233 	} else {
    234 		if (smw->smw_period == 0)
    235 			return EINVAL;
    236 		else if (smw->smw_period == WDOG_PERIOD_DEFAULT)
    237 			sc->sc_smw.smw_period = SWDOG_DEFAULT;
    238 		else
    239 			sc->sc_smw.smw_period = smw->smw_period;
    240 		error = swwdog_arm(sc);
    241 	}
    242 	return error;
    243 }
    244 
    245 static int
    246 swwdog_tickle(struct sysmon_wdog *smw)
    247 {
    248 
    249 	return swwdog_arm(smw->smw_cookie);
    250 }
    251 
    252 static int
    253 swwdog_arm(struct swwdog_softc *sc)
    254 {
    255 
    256 	callout_schedule(&sc->sc_c, sc->sc_smw.smw_period * hz);
    257 	return 0;
    258 }
    259 
    260 static int
    261 swwdog_disarm(struct swwdog_softc *sc)
    262 {
    263 
    264 	callout_stop(&sc->sc_c);
    265 	return 0;
    266 }
    267 
    268 static void
    269 swwdog_panic(void *vsc)
    270 {
    271 	struct swwdog_softc *sc = vsc;
    272 	static struct work wk; /* we'll need it max once */
    273 	bool do_panic;
    274 
    275 	do_panic = !swwdog_reboot;
    276 	swwdog_reboot = false;
    277 	callout_schedule(&sc->sc_c, 60 * hz);	/* deliberate double-panic */
    278 
    279 	printf("%s: %d second timer expired\n", "swwdog",
    280 	    sc->sc_smw.smw_period);
    281 
    282 	if (do_panic)
    283 		panic("watchdog timer expired");
    284 	else
    285 		workqueue_enqueue(wq, &wk, NULL);
    286 }
    287 
    288 static void
    289 swwdog_sysctl_setup(void)
    290 {
    291 	const struct sysctlnode *me;
    292 
    293 	KASSERT(swwdog_sysctllog == NULL);
    294 
    295 	sysctl_createv(&swwdog_sysctllog, 0, NULL, &me, CTLFLAG_READWRITE,
    296 	    CTLTYPE_NODE, "swwdog", NULL,
    297 	    NULL, 0, NULL, 0,
    298 	    CTL_HW, CTL_CREATE, CTL_EOL);
    299 	sysctl_createv(&swwdog_sysctllog, 0, NULL, NULL, CTLFLAG_READWRITE,
    300 	    CTLTYPE_BOOL, "reboot", "reboot if timer expires",
    301 	    NULL, 0, &swwdog_reboot, sizeof(bool),
    302 	    CTL_HW, me->sysctl_num, CTL_CREATE, CTL_EOL);
    303 }
    304 
    305 /*
    306  * Module management
    307  */
    308 
    309 static
    310 int
    311 swwdog_init(void *arg)
    312 {
    313 #ifdef _LKM
    314 	/*
    315 	 * Merge the driver info into the kernel tables and attach the
    316 	 * pseudo-device
    317 	 */
    318 	int error;
    319 
    320 	error = config_cfdriver_attach(&swwdog_cd);
    321 	if (error) {
    322 		aprint_error("%s: unable to attach cfdriver\n",
    323 		    swwdog_cd.cd_name);
    324 		return error;
    325 	}
    326 	error = swwdogattach(1);
    327 	if (error) {
    328 		aprint_error("%s: device attach failed\n", swwdog_cd.cd_name);
    329 		config_cfdriver_detach(&swwdog_cd);
    330 	}
    331 
    332 	return error;
    333 #else
    334 
    335 	return 0;
    336 #endif
    337 }
    338 
    339 static
    340 int
    341 swwdog_fini(void *arg)
    342 {
    343 	int error;
    344 
    345 	error = config_detach(swwdog_dev, 0);
    346 
    347 	error = config_cfattach_detach(swwdog_cd.cd_name, &swwdog_ca);
    348 	if (error)
    349 		aprint_error("%s: error detaching cfattach: %d\n",
    350 		    swwdog_cd.cd_name, error);
    351 
    352 #ifdef _LKM
    353 	error = config_cfdriver_detach(&swwdog_cd);
    354 	if (error)
    355 		aprint_error("%s: error detaching cfdriver: %d\n",
    356 		    swwdog_cd.cd_name, error);
    357 #endif
    358 
    359         return error;
    360 }
    361 
    362 static
    363 int
    364 swwdog_modcmd(modcmd_t cmd, void *arg)
    365 {
    366 	int ret;
    367 
    368 printf("%s: cmd %d\n", __func__, cmd); /* XXX PRG */
    369 	switch (cmd) {
    370 	case MODULE_CMD_INIT:
    371 		ret = swwdog_init(arg);
    372 		break;
    373 
    374 	case MODULE_CMD_FINI:
    375 		ret = swwdog_fini(arg);
    376 		break;
    377 
    378 	case MODULE_CMD_STAT:
    379 	default:
    380 		ret = ENOTTY;
    381 	}
    382 
    383 	return ret;
    384 }
    385 
    386