@@ -80,12 +80,6 @@
#define WDOG_PRETIMEOUT_NMI 2
#define WDOG_PRETIMEOUT_MSG_INT 3
-/* Operations that can be performed on a pretimout. */
-#define WDOG_PREOP_NONE 0
-#define WDOG_PREOP_PANIC 1
-/* Cause data to be available to read. Doesn't work in NMI mode. */
-#define WDOG_PREOP_GIVE_DATA 2
-
/* Actions to perform on a full timeout. */
#define WDOG_SET_TIMEOUT_ACT(byte, use) \
byte = ((byte) & 0xf8) | ((use) & 0x7)
@@ -144,6 +138,7 @@ struct ipmi_wdt {
struct ipmi_recv_msg recv_msg;
int state; /* One of WDOG_TIMEOUT_xxx, NONE if disabled. */
+ int prestate; /* One of WDOG_PRETIMEOUT_xxx. */
bool nmi_works;
@@ -164,6 +159,8 @@ static int ipmi_set_timeout(struct ipmi_wdt *iwd, int timeout,
int do_heartbeat);
static int ipmi_set_pretimeout(struct ipmi_wdt *iwd, int timeout,
int do_heartbeat);
+static int ipmi_set_action(struct ipmi_wdt *iwd, unsigned int action);
+static int ipmi_set_preaction(struct ipmi_wdt *iwd, unsigned int action);
static void ipmi_register_watchdog(int ipmi_intf, struct device *dev);
static void _ipmi_unregister_watchdog(struct ipmi_wdt *iwd);
static void ipmi_unregister_watchdog(int ipmi_intf);
@@ -189,18 +186,14 @@ static int timeout = 10; /* Default timeout. */
static int pretimeout; /* Default pretimeout. */
static int panic_wdt_timeout = 255; /* Default timeout to set on panic */
-/* Default action is to reset the board on a timeout. */
-static unsigned char action_val = WDOG_TIMEOUT_RESET;
-
-static char action[16] = "reset";
-
-static unsigned char preaction_val = WDOG_PRETIMEOUT_NONE;
-
-static char preaction[16] = "pre_none";
-
-static unsigned char preop_val = WDOG_PREOP_NONE;
+#define WDOG_PREOP_NONE 0
+#define WDOG_PREOP_PANIC 1
+#define WDOG_PREOP_GIVE_DATA 2
-static char preop[16] = "preop_none";
+/* Default action is to reset the board on a timeout. */
+static unsigned char def_action_val = WDIOA_RESET;
+static unsigned char def_preaction_val = WDIOP_NONE;
+static unsigned char def_preop_val = WDOG_PREOP_NONE;
/*
* If true, the driver will start running as soon as it is configured
@@ -211,33 +204,41 @@ static int start_now;
static int action_op(const char *inval, char *outval)
{
int rv = 0;
+ unsigned int new_val;
mutex_lock(&ipmi_wdt_data_mutex);
- if (outval)
- strcpy(outval, action);
+ if (outval) {
+ switch (def_action_val) {
+ case WDIOA_RESET:
+ strcpy(outval, "reset");
+ break;
+ case WDIOA_POWER_OFF:
+ strcpy(outval, "power_off");
+ break;
+ case WDIOA_POWER_CYCLE:
+ strcpy(outval, "power_cycle");
+ break;
+ default:
+ strcpy(outval, "?");
+ break;
+ }
+ }
if (!inval)
goto out_unlock;
if (strcmp(inval, "reset") == 0)
- action_val = WDOG_TIMEOUT_RESET;
- else if (strcmp(inval, "none") == 0)
- action_val = WDOG_TIMEOUT_NONE;
+ new_val = WDIOA_RESET;
else if (strcmp(inval, "power_cycle") == 0)
- action_val = WDOG_TIMEOUT_POWER_CYCLE;
+ new_val = WDIOA_POWER_CYCLE;
else if (strcmp(inval, "power_off") == 0)
- action_val = WDOG_TIMEOUT_POWER_DOWN;
+ new_val = WDIOA_POWER_OFF;
else
rv = -EINVAL;
- if (!rv) {
- strcpy(action, inval);
- if (ipmi_wdt && ipmi_wdt->state != WDOG_TIMEOUT_NONE) {
- ipmi_wdt->state = action_val;
- rv = _ipmi_update_timeout(ipmi_wdt,
- IPMI_SET_TIMEOUT_HB_IF_NECESSARY,
- false);
- }
- }
+ if (!rv && ipmi_wdt)
+ rv = ipmi_set_action(ipmi_wdt, new_val);
+ if (!rv)
+ def_action_val = new_val;
out_unlock:
mutex_unlock(&ipmi_wdt_data_mutex);
@@ -247,84 +248,115 @@ static int action_op(const char *inval, char *outval)
static int preaction_op(const char *inval, char *outval)
{
int rv = 0;
+ unsigned int new_val;
mutex_lock(&ipmi_wdt_data_mutex);
- if (outval)
- strcpy(outval, preaction);
+ if (outval) {
+ switch (def_preaction_val) {
+ case WDIOP_NONE:
+ strcpy(outval, "pre_none");
+ break;
+ case WDIOP_NMI:
+ strcpy(outval, "pre_nmi");
+ break;
+ case WDIOP_SMI:
+ strcpy(outval, "pre_smi");
+ break;
+ case WDIOP_INTERRUPT:
+ strcpy(outval, "pre_int");
+ break;
+ default:
+ strcpy(outval, "?");
+ break;
+ }
+ }
if (!inval)
goto out_unlock;
- if (strcmp(inval, "pre_none") == 0)
- preaction_val = WDOG_PRETIMEOUT_NONE;
- else if (strcmp(inval, "pre_smi") == 0)
- preaction_val = WDOG_PRETIMEOUT_SMI;
+ if (strcmp(inval, "pre_none") == 0) {
+ new_val = WDIOP_NONE;
+ } else if (strcmp(inval, "pre_smi") == 0) {
+ new_val = WDIOP_SMI;
#ifdef HAVE_DIE_NMI
- else if (strcmp(inval, "pre_nmi") == 0) {
- if (preop_val == WDOG_PREOP_GIVE_DATA)
- rv = -EINVAL;
- else if (ipmi_wdt && !ipmi_wdt->nmi_works)
- rv = -EINVAL;
- else
- preaction_val = WDOG_PRETIMEOUT_NMI;
- }
+ } else if (strcmp(inval, "pre_nmi") == 0) {
+ new_val = WDIOP_NMI;
#endif
- else if (strcmp(inval, "pre_int") == 0)
- preaction_val = WDOG_PRETIMEOUT_MSG_INT;
- else
+ } else if (strcmp(inval, "pre_int") == 0) {
+ new_val = WDIOP_INTERRUPT;
+ } else {
rv = -EINVAL;
- if (!rv) {
- strcpy(preaction, inval);
- if (ipmi_wdt && ipmi_wdt->state != WDOG_TIMEOUT_NONE &&
- ipmi_wdt->wdd.pretimeout) {
- rv = _ipmi_update_timeout(ipmi_wdt,
- IPMI_SET_TIMEOUT_HB_IF_NECESSARY,
- false);
- }
}
+ if (!rv && ipmi_wdt)
+ rv = ipmi_set_preaction(ipmi_wdt, new_val);
+ if (!rv)
+ def_preaction_val = new_val;
out_unlock:
mutex_unlock(&ipmi_wdt_data_mutex);
return rv;
}
+static const char *preop_to_governor(unsigned int preop)
+{
+ switch (preop) {
+ case WDOG_PREOP_NONE:
+ return "noop";
+ case WDOG_PREOP_PANIC:
+ return "panic";
+ case WDOG_PREOP_GIVE_DATA:
+ return "read_data";
+ default:
+ return NULL;
+ }
+}
static int preop_op(const char *inval, char *outval)
{
int rv = 0;
const char *gov = NULL;
- unsigned int orig_val;
+ unsigned int new_val;
mutex_lock(&ipmi_wdt_data_mutex);
- if (outval)
- strcpy(outval, preop);
+ if (outval) {
+ switch (def_preop_val) {
+ case WDOG_PREOP_NONE:
+ strcpy(outval, "preop_none");
+ break;
+ case WDOG_PREOP_PANIC:
+ strcpy(outval, "preop_panic");
+ break;
+ case WDOG_PREOP_GIVE_DATA:
+ strcpy(outval, "preop_give_data");
+ break;
+ default:
+ strcpy(outval, "?");
+ break;
+ }
+ }
if (!inval)
goto out_unlock;
- orig_val = preop_val;
if (strcmp(inval, "preop_none") == 0) {
- preop_val = WDOG_PREOP_NONE;
- gov = "noop";
+ new_val = WDOG_PREOP_NONE;
} else if (strcmp(inval, "preop_panic") == 0) {
- preop_val = WDOG_PREOP_PANIC;
- gov = "panic";
+ new_val = WDOG_PREOP_PANIC;
} else if (strcmp(inval, "preop_give_data") == 0) {
- if (preaction_val == WDOG_PRETIMEOUT_NMI)
- rv = -EINVAL;
- else {
- preop_val = WDOG_PREOP_GIVE_DATA;
- gov = "read_data";
- }
+ new_val = WDOG_PREOP_GIVE_DATA;
} else {
rv = -EINVAL;
}
- if (!rv) {
- rv = watchdog_pretimeout_governor_set(&ipmi_wdt->wdd, gov);
- if (rv)
- preaction_val = orig_val;
- else
- strcpy(preop, inval);
+ if (!rv && ipmi_wdt) {
+ gov = preop_to_governor(new_val);
+ if (!gov)
+ rv = -EINVAL;
+ if (!rv) {
+ rv = watchdog_pretimeout_governor_set(&ipmi_wdt->wdd,
+ gov);
+ }
}
+ if (!rv)
+ def_preop_val = new_val;
out_unlock:
mutex_unlock(&ipmi_wdt_data_mutex);
@@ -551,7 +583,7 @@ static int __ipmi_update_timeout(struct ipmi_wdt *iwd, int *send_heartbeat_now)
WDOG_SET_TIMEOUT_ACT(data[1], iwd->state);
if ((iwd->wdd.pretimeout > 0) &&
(iwd->state != WDOG_TIMEOUT_NONE)) {
- WDOG_SET_PRETIMEOUT_ACT(data[1], preaction_val);
+ WDOG_SET_PRETIMEOUT_ACT(data[1], iwd->prestate);
data[2] = iwd->wdd.pretimeout;
} else {
WDOG_SET_PRETIMEOUT_ACT(data[1], WDOG_PRETIMEOUT_NONE);
@@ -863,19 +895,36 @@ static void ipmi_wdog_panic_handler(void *user_data)
}
}
-static int ipmi_wdt_start(struct watchdog_device *wdd)
+static int _ipmi_wdt_start(struct ipmi_wdt *iwd)
{
- struct ipmi_wdt *iwd = wdd_to_ipmi_wdt(wdd);
- int rv;
+ int rv = 0;
- mutex_lock(&iwd->lock);
- iwd->state = action_val;
- rv = _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_FORCE_HB, false);
- mutex_unlock(&iwd->lock);
+ switch (iwd->wdd.action) {
+ case WDIOA_RESET:
+ iwd->state = WDOG_TIMEOUT_RESET;
+ break;
+ case WDIOA_POWER_OFF:
+ iwd->state = WDOG_TIMEOUT_RESET;
+ break;
+ case WDIOA_POWER_CYCLE:
+ iwd->state = WDOG_TIMEOUT_RESET;
+ break;
+ default:
+ rv = -EINVAL;
+ break;
+ }
+ if (!rv)
+ rv = _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_FORCE_HB,
+ false);
return rv;
}
+static int ipmi_wdt_start(struct watchdog_device *wdd)
+{
+ return _ipmi_wdt_start(wdd_to_ipmi_wdt(wdd));
+}
+
static int ipmi_wdt_stop(struct watchdog_device *wdd)
{
struct ipmi_wdt *iwd = wdd_to_ipmi_wdt(wdd);
@@ -903,25 +952,101 @@ static int ipmi_wdt_ping(struct watchdog_device *wdd)
static int ipmi_wdt_set_timeout(struct watchdog_device *wdd, unsigned int val)
{
- struct ipmi_wdt *iwd = wdd_to_ipmi_wdt(wdd);
-
- return ipmi_set_timeout(iwd, val, IPMI_SET_TIMEOUT_HB_IF_NECESSARY);
+ return ipmi_set_timeout(wdd_to_ipmi_wdt(wdd), val,
+ IPMI_SET_TIMEOUT_HB_IF_NECESSARY);
}
static int ipmi_wdt_set_pretimeout(struct watchdog_device *wdd,
unsigned int val)
{
struct ipmi_wdt *iwd = wdd_to_ipmi_wdt(wdd);
- int rv;
+
+ return ipmi_set_pretimeout(iwd, val, IPMI_SET_TIMEOUT_HB_IF_NECESSARY);
+}
+
+static int ipmi_set_action(struct ipmi_wdt *iwd, unsigned int val)
+{
+ int rv = 0;
mutex_lock(&iwd->lock);
- iwd->wdd.pretimeout = val;
- rv = _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_NO_HB, false);
+ if (val == iwd->wdd.action)
+ goto out_unlock;
+ switch (val) {
+ case WDIOA_RESET:
+ case WDIOA_POWER_OFF:
+ case WDIOA_POWER_CYCLE:
+ iwd->wdd.action = val;
+ break;
+ default:
+ rv = -EINVAL;
+ }
+ if (!rv)
+ rv = _ipmi_wdt_start(iwd);
+out_unlock:
mutex_unlock(&iwd->lock);
return rv;
}
+static int ipmi_wdt_set_action(struct watchdog_device *wdd,
+ unsigned int val)
+{
+ return ipmi_set_action(wdd_to_ipmi_wdt(wdd), val);
+}
+
+static int ipmi_set_preaction(struct ipmi_wdt *iwd, unsigned int val)
+{
+ int rv = 0;
+ int new_state = 0;
+
+ mutex_lock(&iwd->lock);
+ if (val == iwd->wdd.preaction)
+ goto out_unlock;
+ switch (val) {
+ case WDIOP_NONE:
+ new_state = WDOG_PRETIMEOUT_NONE;
+ break;
+ case WDIOP_SMI:
+ new_state = WDOG_PRETIMEOUT_SMI;
+ break;
+ case WDIOP_INTERRUPT:
+ new_state = WDOG_PRETIMEOUT_MSG_INT;
+ break;
+#ifdef HAVE_DIE_NMI
+ case WDIOP_NMI:
+ if (!iwd->nmi_works)
+ rv = -EINVAL;
+ else
+ new_state = WDOG_PRETIMEOUT_NMI;
+ break;
+#endif
+ default:
+ rv = -EINVAL;
+ }
+ if (!rv) {
+ int old_preaction = iwd->wdd.preaction;
+ int old_prestate = iwd->prestate;
+
+ iwd->wdd.preaction = val;
+ iwd->prestate = new_state;
+ rv = _ipmi_wdt_start(iwd);
+ if (rv) {
+ iwd->wdd.preaction = old_preaction;
+ iwd->prestate = old_prestate;
+ }
+ }
+out_unlock:
+ mutex_unlock(&iwd->lock);
+
+ return rv;
+}
+
+static int ipmi_wdt_set_preaction(struct watchdog_device *wdd,
+ unsigned int val)
+{
+ return ipmi_set_preaction(wdd_to_ipmi_wdt(wdd), val);
+}
+
static const struct watchdog_info ipmi_wdt_ident = {
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
WDIOF_PRETIMEOUT | WDIOF_MAGICCLOSE,
@@ -936,7 +1061,9 @@ static const struct watchdog_ops ipmi_wdt_ops = {
.ping = ipmi_wdt_ping,
.set_timeout = ipmi_wdt_set_timeout,
.set_pretimeout = ipmi_wdt_set_pretimeout,
- .get_timeleft = ipmi_wdt_get_timeleft,
+ .get_timeleft = ipmi_wdt_get_timeleft,
+ .set_action = ipmi_wdt_set_action,
+ .set_preaction = ipmi_wdt_set_preaction,
};
static const struct ipmi_user_hndl ipmi_hndlrs = {
@@ -976,14 +1103,14 @@ ipmi_nmi(unsigned int val, struct pt_regs *regs)
if (iwd->state == WDOG_TIMEOUT_NONE)
return NMI_DONE;
- if (preaction_val != WDOG_PRETIMEOUT_NMI)
+ if (iwd->wdd.preaction != WDIOP_NMI)
return NMI_DONE;
/*
* If no one else handled the NMI, we assume it was the IPMI
* watchdog.
*/
- if (preop_val == WDOG_PREOP_PANIC) {
+ if (iwd->wdd.gov && strcmp(iwd->wdd.gov->name, "panic") == 0) {
/*
* On some machines, the heartbeat will give an error
* and not work unless we re-enable the timer. So
@@ -1000,6 +1127,7 @@ ipmi_nmi(unsigned int val, struct pt_regs *regs)
static void nmi_check(struct ipmi_wdt *iwd)
{
int old_state = iwd->state;
+ int old_prestate = iwd->prestate;
int old_pretimeout = iwd->wdd.pretimeout;
int old_timeout = iwd->wdd.timeout;
int rv;
@@ -1026,6 +1154,7 @@ static void nmi_check(struct ipmi_wdt *iwd)
* ourselves 99 seconds to stop the timer.
*/
iwd->state = WDOG_TIMEOUT_RESET;
+ iwd->prestate = WDOG_PRETIMEOUT_NMI;
iwd->wdd.pretimeout = 99;
iwd->wdd.timeout = 100;
@@ -1063,6 +1192,7 @@ static void nmi_check(struct ipmi_wdt *iwd)
mutex_unlock(&ipmi_nmi_test_mutex);
iwd->wdd.pretimeout = old_pretimeout;
iwd->wdd.timeout = old_timeout;
+ iwd->prestate = old_prestate;
iwd->state = old_state;
rv = _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_FORCE_HB, false);
if (rv)
@@ -1079,6 +1209,7 @@ static void ipmi_register_watchdog(int ipmi_intf, struct device *dev)
{
struct ipmi_wdt *iwd = NULL;
int rv = -EBUSY;
+ const char *gov;
mutex_lock(&ipmi_wdt_data_mutex);
if ((ifnum_to_use != -1) && (ifnum_to_use != ipmi_intf))
@@ -1126,6 +1257,8 @@ static void ipmi_register_watchdog(int ipmi_intf, struct device *dev)
watchdog_init_timeout(&iwd->wdd, timeout, NULL);
iwd->wdd.pretimeout = pretimeout;
iwd->wdd.parent = dev;
+ iwd->wdd.action = def_action_val;
+ iwd->wdd.preaction = def_preaction_val;
rv = watchdog_register_device(&iwd->wdd);
if (rv) {
@@ -1136,24 +1269,34 @@ static void ipmi_register_watchdog(int ipmi_intf, struct device *dev)
nmi_check(iwd);
- if (preaction_val == WDOG_PRETIMEOUT_NMI && !iwd->nmi_works) {
+ if (iwd->wdd.preaction == WDIOP_NMI && !iwd->nmi_works) {
pr_warn("NMI pretimeout test failed, disabling pretimeout.\n");
- preaction_val = WDOG_PRETIMEOUT_NONE;
+ iwd->wdd.preaction = WDIOP_NONE;
+ }
+
+ gov = preop_to_governor(def_preop_val);
+ if (!gov) {
+ pr_warn("Invalid preop value: %d\n", def_preop_val);
+ } else {
+ rv = watchdog_pretimeout_governor_set(&iwd->wdd, gov);
+ if (rv)
+ pr_warn("Setting governor to %s failed: %d\n",
+ gov, rv);
}
ipmi_wdt = iwd;
+ rv = 0;
out:
mutex_unlock(&ipmi_wdt_data_mutex);
if (start_now && rv == 0) {
/* Run from startup, so start the timer now. */
start_now = 0; /* Disable this function after first startup. */
- iwd->state = action_val;
- mutex_lock(&iwd->lock);
- _ipmi_update_timeout(iwd, IPMI_SET_TIMEOUT_FORCE_HB, false);
- mutex_unlock(&iwd->lock);
- pr_info("Starting now!\n");
- } else if (iwd && rv != 0) {
+ if (_ipmi_wdt_start(iwd))
+ pr_warn("Starting now failed!\n");
+ else
+ pr_info("Starting now!\n");
+ } else if (iwd && rv) {
kfree(iwd);
}
}
@@ -1236,22 +1379,6 @@ static int __init ipmi_wdog_init(void)
{
int rv;
- if (action_op(action, NULL)) {
- pr_info("Unknown action '%s', defaulting to reset\n", action);
- action_op("reset", NULL);
- }
-
- if (preaction_op(preaction, NULL)) {
- pr_info("Unknown preaction '%s', defaulting to none\n",
- preaction);
- preaction_op("pre_none", NULL);
- }
-
- if (preop_op(preop, NULL)) {
- pr_info("Unknown preop '%s', defaulting to none\n", preop);
- preop_op("preop_none", NULL);
- }
-
register_reboot_notifier(&wdog_reboot_notifier);
rv = ipmi_smi_watcher_register(&smi_watcher);