1
0
Fork 0

watchdog: introduce watchdog_dev_suspend/resume

The watchdog drivers often disable wdog clock during suspend and then
enable it again during resume. Nevertheless the ping worker is still
running and can issue low-level ping while the wdog clock is disabled
causing the system hang. To prevent such condition introduce
watchdog_dev_suspend/resume which can be used by any wdog driver and
actually cancel ping worker during suspend and restore it back, if
needed, during resume.

Signed-off-by: Grzegorz Jaszczyk <grzegorz.jaszczyk@linaro.org>
zero-colors
Grzegorz Jaszczyk 2021-06-14 15:36:41 +02:00 committed by Grzegorz-J
parent 7b1bbbed90
commit 8b9e62cf99
2 changed files with 51 additions and 0 deletions

View File

@ -1167,6 +1167,55 @@ void __exit watchdog_dev_exit(void)
kthread_destroy_worker(watchdog_kworker);
}
int watchdog_dev_suspend(struct watchdog_device *wdd)
{
struct watchdog_core_data *wd_data = wdd->wd_data;
int ret;
if (!wdd->wd_data)
return -ENODEV;
/* ping for the last time before suspend */
mutex_lock(&wd_data->lock);
if (watchdog_worker_should_ping(wd_data))
ret = __watchdog_ping(wd_data->wdd);
mutex_unlock(&wd_data->lock);
if (ret)
return ret;
/*
* make sure that watchdog worker will not kick in when the wdog is
* suspended
*/
hrtimer_cancel(&wd_data->timer);
kthread_cancel_work_sync(&wd_data->work);
return 0;
}
EXPORT_SYMBOL_GPL(watchdog_dev_suspend);
int watchdog_dev_resume(struct watchdog_device *wdd)
{
struct watchdog_core_data *wd_data = wdd->wd_data;
int ret;
if (!wdd->wd_data)
return -ENODEV;
/*
* __watchdog_ping will also retrigger hrtimer and therefore restore the
* ping worker if needed.
*/
mutex_lock(&wd_data->lock);
if (watchdog_worker_should_ping(wd_data))
ret = __watchdog_ping(wd_data->wdd);
mutex_unlock(&wd_data->lock);
return ret;
}
EXPORT_SYMBOL_GPL(watchdog_dev_resume);
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="

View File

@ -209,6 +209,8 @@ extern int watchdog_init_timeout(struct watchdog_device *wdd,
unsigned int timeout_parm, struct device *dev);
extern int watchdog_register_device(struct watchdog_device *);
extern void watchdog_unregister_device(struct watchdog_device *);
int watchdog_dev_suspend(struct watchdog_device *wdd);
int watchdog_dev_resume(struct watchdog_device *wdd);
/* devres register variant */
int devm_watchdog_register_device(struct device *dev, struct watchdog_device *);