diff options
-rw-r--r-- | Documentation/watchdog/watchdog-parameters.txt | 8 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_dev.c | 36 |
2 files changed, 43 insertions, 1 deletions
diff --git a/Documentation/watchdog/watchdog-parameters.txt b/Documentation/watchdog/watchdog-parameters.txt index 0b88e333f9e1..32d3606caa65 100644 --- a/Documentation/watchdog/watchdog-parameters.txt +++ b/Documentation/watchdog/watchdog-parameters.txt @@ -8,6 +8,14 @@ See Documentation/admin-guide/kernel-parameters.rst for information on providing kernel parameters for builtin drivers versus loadable modules. +The watchdog core parameter watchdog.open_timeout is the maximum time, +in seconds, for which the watchdog framework will take care of pinging +a running hardware watchdog until userspace opens the corresponding +/dev/watchdogN device. A value of 0 (the default) means an infinite +timeout. Setting this to a non-zero value can be useful to ensure that +either userspace comes up properly, or the board gets reset and allows +fallback logic in the bootloader to try something else. + ------------------------------------------------- acquirewdt: diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 252a7c7b6592..e4b51db48f0e 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -69,6 +69,7 @@ struct watchdog_core_data { struct mutex lock; ktime_t last_keepalive; ktime_t last_hw_keepalive; + ktime_t open_deadline; struct hrtimer timer; struct kthread_work work; unsigned long status; /* Internal status bits */ @@ -87,6 +88,19 @@ static struct kthread_worker *watchdog_kworker; static bool handle_boot_enabled = IS_ENABLED(CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED); +static unsigned open_timeout; + +static bool watchdog_past_open_deadline(struct watchdog_core_data *data) +{ + return ktime_after(ktime_get(), data->open_deadline); +} + +static void watchdog_set_open_deadline(struct watchdog_core_data *data) +{ + data->open_deadline = open_timeout ? + ktime_get() + ktime_set(open_timeout, 0) : KTIME_MAX; +} + static inline bool watchdog_need_worker(struct watchdog_device *wdd) { /* All variables in milli-seconds */ @@ -211,7 +225,13 @@ static bool watchdog_worker_should_ping(struct watchdog_core_data *wd_data) { struct watchdog_device *wdd = wd_data->wdd; - return wdd && (watchdog_active(wdd) || watchdog_hw_running(wdd)); + if (!wdd) + return false; + + if (watchdog_active(wdd)) + return true; + + return watchdog_hw_running(wdd) && !watchdog_past_open_deadline(wd_data); } static void watchdog_ping_work(struct kthread_work *work) @@ -824,6 +844,15 @@ static int watchdog_open(struct inode *inode, struct file *file) if (!hw_running) kref_get(&wd_data->kref); + /* + * open_timeout only applies for the first open from + * userspace. Set open_deadline to infinity so that the kernel + * will take care of an always-running hardware watchdog in + * case the device gets magic-closed or WDIOS_DISABLECARD is + * applied. + */ + wd_data->open_deadline = KTIME_MAX; + /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ return stream_open(inode, file); @@ -983,6 +1012,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) /* Record time of most recent heartbeat as 'just before now'. */ wd_data->last_hw_keepalive = ktime_sub(ktime_get(), 1); + watchdog_set_open_deadline(wd_data); /* * If the watchdog is running, prevent its driver from being unloaded, @@ -1181,3 +1211,7 @@ module_param(handle_boot_enabled, bool, 0444); MODULE_PARM_DESC(handle_boot_enabled, "Watchdog core auto-updates boot enabled watchdogs before userspace takes over (default=" __MODULE_STRING(IS_ENABLED(CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED)) ")"); + +module_param(open_timeout, uint, 0644); +MODULE_PARM_DESC(open_timeout, + "Maximum time (in seconds, 0 means infinity) for userspace to take over a running watchdog (default=0)"); |