diff options
Diffstat (limited to 'drivers/watchdog/watchdog_dev.c')
-rw-r--r-- | drivers/watchdog/watchdog_dev.c | 61 |
1 files changed, 46 insertions, 15 deletions
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 3595cffa24ea..040bf8382f46 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -69,6 +69,7 @@ struct watchdog_core_data { unsigned long status; /* Internal status bits */ #define _WDOG_DEV_OPEN 0 /* Opened ? */ #define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */ +#define _WDOG_KEEPALIVE 2 /* Did we receive a keepalive ? */ }; /* the dev_t structure to store the dynamically allocated watchdog devices */ @@ -92,9 +93,13 @@ static inline bool watchdog_need_worker(struct watchdog_device *wdd) * thus is aware that the framework supports generating heartbeat * requests. * - Userspace requests a longer timeout than the hardware can handle. + * + * Alternatively, if userspace has not opened the watchdog + * device, we take care of feeding the watchdog if it is + * running. */ - return hm && ((watchdog_active(wdd) && t > hm) || - (t && !watchdog_active(wdd) && watchdog_hw_running(wdd))); + return (hm && watchdog_active(wdd) && t > hm) || + (t && !watchdog_active(wdd) && watchdog_hw_running(wdd)); } static long watchdog_next_keepalive(struct watchdog_device *wdd) @@ -107,7 +112,7 @@ static long watchdog_next_keepalive(struct watchdog_device *wdd) unsigned int hw_heartbeat_ms; virt_timeout = wd_data->last_keepalive + msecs_to_jiffies(timeout_ms); - hw_heartbeat_ms = min(timeout_ms, wdd->max_hw_heartbeat_ms); + hw_heartbeat_ms = min_not_zero(timeout_ms, wdd->max_hw_heartbeat_ms); keepalive_interval = msecs_to_jiffies(hw_heartbeat_ms / 2); if (!watchdog_active(wdd)) @@ -180,6 +185,8 @@ static int watchdog_ping(struct watchdog_device *wdd) if (!watchdog_active(wdd) && !watchdog_hw_running(wdd)) return 0; + set_bit(_WDOG_KEEPALIVE, &wd_data->status); + wd_data->last_keepalive = jiffies; return __watchdog_ping(wdd); } @@ -219,6 +226,8 @@ static int watchdog_start(struct watchdog_device *wdd) if (watchdog_active(wdd)) return 0; + set_bit(_WDOG_KEEPALIVE, &wd_data->status); + started_at = jiffies; if (watchdog_hw_running(wdd) && wdd->ops->ping) err = wdd->ops->ping(wdd); @@ -258,10 +267,12 @@ static int watchdog_stop(struct watchdog_device *wdd) return -EBUSY; } - if (wdd->ops->stop) + if (wdd->ops->stop) { + clear_bit(WDOG_HW_RUNNING, &wdd->status); err = wdd->ops->stop(wdd); - else + } else { set_bit(WDOG_HW_RUNNING, &wdd->status); + } if (err == 0) { clear_bit(WDOG_ACTIVE, &wdd->status); @@ -282,10 +293,27 @@ static int watchdog_stop(struct watchdog_device *wdd) static unsigned int watchdog_get_status(struct watchdog_device *wdd) { - if (!wdd->ops->status) - return 0; + struct watchdog_core_data *wd_data = wdd->wd_data; + unsigned int status; + + if (wdd->ops->status) + status = wdd->ops->status(wdd); + else + status = wdd->bootstatus & (WDIOF_CARDRESET | + WDIOF_OVERHEAT | + WDIOF_FANFAULT | + WDIOF_EXTERN1 | + WDIOF_EXTERN2 | + WDIOF_POWERUNDER | + WDIOF_POWEROVER); - return wdd->ops->status(wdd); + if (test_bit(_WDOG_ALLOW_RELEASE, &wd_data->status)) + status |= WDIOF_MAGICCLOSE; + + if (test_and_clear_bit(_WDOG_KEEPALIVE, &wd_data->status)) + status |= WDIOF_KEEPALIVEPING; + + return status; } /* @@ -361,7 +389,7 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr, status = watchdog_get_status(wdd); mutex_unlock(&wd_data->lock); - return sprintf(buf, "%u\n", status); + return sprintf(buf, "0x%x\n", status); } static DEVICE_ATTR_RO(status); @@ -429,9 +457,7 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, struct watchdog_device *wdd = dev_get_drvdata(dev); umode_t mode = attr->mode; - if (attr == &dev_attr_status.attr && !wdd->ops->status) - mode = 0; - else if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft) + if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft) mode = 0; return mode; @@ -948,17 +974,22 @@ int __init watchdog_dev_init(void) err = class_register(&watchdog_class); if (err < 0) { pr_err("couldn't register class\n"); - return err; + goto err_register; } err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); if (err < 0) { pr_err("watchdog: unable to allocate char dev region\n"); - class_unregister(&watchdog_class); - return err; + goto err_alloc; } return 0; + +err_alloc: + class_unregister(&watchdog_class); +err_register: + destroy_workqueue(watchdog_wq); + return err; } /* |