[PATCHv5 2/4] watchdog: Allow watchdog to reset device at early boot

Yang, Wenyou wenyou.yang at atmel.com
Mon Apr 13 00:56:16 PDT 2015



On 2015/4/10 21:06, Timo Kokkonen wrote:
> Historically the watchdogs have always been stopped before user space
> opens and takes over the device. This is not good on many production
> systems where any crash, in kernel or user space, must always result
> in a device reset.
>
> Add a new early_timeout_sec parameter to the watchdog that gives user
> space certain amount of time to set up itself and take over the
> watchdog. Until this timeout has been reached the watchdog core takes
> care of petting the watchdog HW. If there is any crash in kernel or
> user space, reboot is guaranteed as watchdog hardware is never
> stopped.
>
> There is also mode of supplying zero seconds for the early_timeout_sec
> parameter. In this mode the worker is not scheduled, so the watchdog
> timer is not touched nor is the HW petted until user space takes over
> it.
>
> Signed-off-by: Timo Kokkonen <timo.kokkonen at offcode.fi>
> ---
>   drivers/watchdog/watchdog_core.c | 39 ++++++++++++++++++++++++++++++---------
>   include/linux/watchdog.h         |  1 +
>   2 files changed, 31 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
> index 8e7d08d..d6bedf7 100644
> --- a/drivers/watchdog/watchdog_core.c
> +++ b/drivers/watchdog/watchdog_core.c
> @@ -111,12 +111,18 @@ EXPORT_SYMBOL_GPL(watchdog_init_timeout);
>    */
>   int watchdog_init_params(struct watchdog_device *wdd, struct device *dev)
>   {
> +	unsigned int t = 0;
>   	int ret = 0;
>   
>   	ret = watchdog_init_timeout(wdd, wdd->timeout, dev);
>   	if (ret < 0)
>   		return ret;
>   
> +	if (!of_property_read_u32(dev->of_node, "early-timeout-sec", &t))
> +		wdd->early_timeout_sec = t;
> +	else
> +		wdd->early_timeout_sec = -1;
> +
>   	/*
>   	 * Max HW timeout needs to be set so that core knows when to
>   	 * use a kernel worker to support longer watchdog timeouts
> @@ -139,6 +145,7 @@ static void watchdog_worker(struct work_struct *work)
>   	}
>   
>   	if ((!watchdog_active(wdd) && !watchdog_is_stoppable(wdd)) ||
> +		(!watchdog_active(wdd) && wdd->early_timeout_sec >= 0) ||
>   		(watchdog_active(wdd) &&
>   			wdd->hw_max_timeout < wdd->timeout * HZ)) {
>   		if (wdd->ops->ping)
> @@ -152,17 +159,31 @@ static void watchdog_worker(struct work_struct *work)
>   
>   static int prepare_watchdog(struct watchdog_device *wdd)
>   {
> -	/* Stop the watchdog now before user space opens the device */
> -	if (wdd->hw_features & WDOG_HW_IS_STOPPABLE &&
> -		wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
> -		wdd->ops->stop(wdd);
> -
> -	} else if (!(wdd->hw_features & WDOG_HW_IS_STOPPABLE)) {
> +	if (wdd->early_timeout_sec >= 0) {
>   		/*
> -		 * Can't stop it, use a kernel timer to tick
> -		 * it until it's open by user space
> +		 * early timeout, if set, ensures that watchdog will
> +		 * reset the device unless user space opens the
> +		 * watchdog device within the given interval.
>   		 */
> -		schedule_delayed_work(&wdd->work, wdd->hw_heartbeat);
> +		if (!(wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT))
> +			wdd->ops->start(wdd);
> +
> +		if (wdd->early_timeout_sec > 0) {
> +			schedule_delayed_work(&wdd->work,  wdd->hw_heartbeat);
> +		}

This bracket is unnecessary.

> +	} else {
> +		/* Stop the watchdog now before user space opens the device */
> +		if (wdd->hw_features & WDOG_HW_IS_STOPPABLE &&
> +			wdd->hw_features & WDOG_HW_RUNNING_AT_BOOT) {
> +			wdd->ops->stop(wdd);
> +
> +		} else if (!(wdd->hw_features & WDOG_HW_IS_STOPPABLE)) {
> +			/*
> +			 * Can't stop it, use a kernel timer to tick
> +			 * it until it's open by user space
> +			 */
> +			schedule_delayed_work(&wdd->work, wdd->hw_heartbeat);
> +		}
>   	}
>   	return 0;
>   }
> diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h
> index c16c921..6f868b6 100644
> --- a/include/linux/watchdog.h
> +++ b/include/linux/watchdog.h
> @@ -94,6 +94,7 @@ struct watchdog_device {
>   	unsigned int hw_max_timeout; /* in jiffies */
>   	unsigned int hw_heartbeat; /* in jiffies */
>   	unsigned long int expires; /* for soft timer */
> +	unsigned int early_timeout_sec;
>   	void *driver_data;
>   	struct mutex lock;
>   	struct delayed_work work;
Best Regards,
Wenyou Yang




More information about the linux-arm-kernel mailing list