Commit 2165bf52 authored by Damien Riegel's avatar Damien Riegel Committed by Wim Van Sebroeck

watchdog: core: add restart handler support

Many watchdog drivers implement the same code to register a restart
handler. This patch provides a generic way to set such a function.

The patch adds a new restart watchdog operation. If a restart priority
greater than 0 is needed, the driver can call
watchdog_set_restart_priority to set it.
Suggested-by: default avatarVivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: default avatarDamien Riegel <damien.riegel@savoirfairelinux.com>
Reviewed-by: default avatarGuenter Roeck <linux@roeck-us.net>
Reviewed-by: default avatarVivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
Signed-off-by: default avatarWim Van Sebroeck <wim@iguana.be>
parent 1f32f83e
...@@ -53,6 +53,7 @@ struct watchdog_device { ...@@ -53,6 +53,7 @@ struct watchdog_device {
unsigned int timeout; unsigned int timeout;
unsigned int min_timeout; unsigned int min_timeout;
unsigned int max_timeout; unsigned int max_timeout;
struct notifier_block restart_nb;
void *driver_data; void *driver_data;
struct mutex lock; struct mutex lock;
unsigned long status; unsigned long status;
...@@ -75,6 +76,10 @@ It contains following fields: ...@@ -75,6 +76,10 @@ It contains following fields:
* timeout: the watchdog timer's timeout value (in seconds). * timeout: the watchdog timer's timeout value (in seconds).
* min_timeout: the watchdog timer's minimum timeout value (in seconds). * min_timeout: the watchdog timer's minimum timeout value (in seconds).
* max_timeout: the watchdog timer's maximum timeout value (in seconds). * max_timeout: the watchdog timer's maximum timeout value (in seconds).
* restart_nb: notifier block that is registered for machine restart, for
internal use only. If a watchdog is capable of restarting the machine, it
should define ops->restart. Priority can be changed through
watchdog_set_restart_priority.
* bootstatus: status of the device after booting (reported with watchdog * bootstatus: status of the device after booting (reported with watchdog
WDIOF_* status bits). WDIOF_* status bits).
* driver_data: a pointer to the drivers private data of a watchdog device. * driver_data: a pointer to the drivers private data of a watchdog device.
...@@ -100,6 +105,7 @@ struct watchdog_ops { ...@@ -100,6 +105,7 @@ struct watchdog_ops {
unsigned int (*status)(struct watchdog_device *); unsigned int (*status)(struct watchdog_device *);
int (*set_timeout)(struct watchdog_device *, unsigned int); int (*set_timeout)(struct watchdog_device *, unsigned int);
unsigned int (*get_timeleft)(struct watchdog_device *); unsigned int (*get_timeleft)(struct watchdog_device *);
int (*restart)(struct watchdog_device *);
void (*ref)(struct watchdog_device *); void (*ref)(struct watchdog_device *);
void (*unref)(struct watchdog_device *); void (*unref)(struct watchdog_device *);
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
...@@ -164,6 +170,8 @@ they are supported. These optional routines/operations are: ...@@ -164,6 +170,8 @@ they are supported. These optional routines/operations are:
(Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the
watchdog's info structure). watchdog's info structure).
* get_timeleft: this routines returns the time that's left before a reset. * get_timeleft: this routines returns the time that's left before a reset.
* restart: this routine restarts the machine. It returns 0 on success or a
negative errno code for failure.
* ref: the operation that calls kref_get on the kref of a dynamically * ref: the operation that calls kref_get on the kref of a dynamically
allocated watchdog_device struct. allocated watchdog_device struct.
* unref: the operation that calls kref_put on the kref of a dynamically * unref: the operation that calls kref_put on the kref of a dynamically
...@@ -231,3 +239,14 @@ the device tree (if the module timeout parameter is invalid). Best practice is ...@@ -231,3 +239,14 @@ the device tree (if the module timeout parameter is invalid). Best practice is
to set the default timeout value as timeout value in the watchdog_device and to set the default timeout value as timeout value in the watchdog_device and
then use this function to set the user "preferred" timeout value. then use this function to set the user "preferred" timeout value.
This routine returns zero on success and a negative errno code for failure. This routine returns zero on success and a negative errno code for failure.
To change the priority of the restart handler the following helper should be
used:
void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);
User should follow the following guidelines for setting the priority:
* 0: should be called in last resort, has limited restart capabilities
* 128: default restart handler, use if no other handler is expected to be
available, and/or if restart is sufficient to restart the entire system
* 255: highest priority, will preempt all other restart handlers
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <linux/types.h> /* For standard types */ #include <linux/types.h> /* For standard types */
#include <linux/errno.h> /* For the -ENODEV/... values */ #include <linux/errno.h> /* For the -ENODEV/... values */
#include <linux/kernel.h> /* For printk/panic/... */ #include <linux/kernel.h> /* For printk/panic/... */
#include <linux/reboot.h> /* For restart handler */
#include <linux/watchdog.h> /* For watchdog specific items */ #include <linux/watchdog.h> /* For watchdog specific items */
#include <linux/init.h> /* For __init/__exit/... */ #include <linux/init.h> /* For __init/__exit/... */
#include <linux/idr.h> /* For ida_* macros */ #include <linux/idr.h> /* For ida_* macros */
...@@ -137,6 +138,41 @@ int watchdog_init_timeout(struct watchdog_device *wdd, ...@@ -137,6 +138,41 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
} }
EXPORT_SYMBOL_GPL(watchdog_init_timeout); EXPORT_SYMBOL_GPL(watchdog_init_timeout);
static int watchdog_restart_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
struct watchdog_device *wdd = container_of(nb, struct watchdog_device,
restart_nb);
int ret;
ret = wdd->ops->restart(wdd);
if (ret)
return NOTIFY_BAD;
return NOTIFY_DONE;
}
/**
* watchdog_set_restart_priority - Change priority of restart handler
* @wdd: watchdog device
* @priority: priority of the restart handler, should follow these guidelines:
* 0: use watchdog's restart function as last resort, has limited restart
* capabilies
* 128: default restart handler, use if no other handler is expected to be
* available and/or if restart is sufficient to restart the entire system
* 255: preempt all other handlers
*
* If a wdd->ops->restart function is provided when watchdog_register_device is
* called, it will be registered as a restart handler with the priority given
* here.
*/
void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority)
{
wdd->restart_nb.priority = priority;
}
EXPORT_SYMBOL_GPL(watchdog_set_restart_priority);
static int __watchdog_register_device(struct watchdog_device *wdd) static int __watchdog_register_device(struct watchdog_device *wdd)
{ {
int ret, id = -1, devno; int ret, id = -1, devno;
...@@ -202,6 +238,15 @@ static int __watchdog_register_device(struct watchdog_device *wdd) ...@@ -202,6 +238,15 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
return ret; return ret;
} }
if (wdd->ops->restart) {
wdd->restart_nb.notifier_call = watchdog_restart_notifier;
ret = register_restart_handler(&wdd->restart_nb);
if (ret)
dev_warn(wdd->dev, "Cannot register restart handler (%d)\n",
ret);
}
return 0; return 0;
} }
...@@ -238,6 +283,9 @@ static void __watchdog_unregister_device(struct watchdog_device *wdd) ...@@ -238,6 +283,9 @@ static void __watchdog_unregister_device(struct watchdog_device *wdd)
if (wdd == NULL) if (wdd == NULL)
return; return;
if (wdd->ops->restart)
unregister_restart_handler(&wdd->restart_nb);
devno = wdd->cdev.dev; devno = wdd->cdev.dev;
ret = watchdog_dev_unregister(wdd); ret = watchdog_dev_unregister(wdd);
if (ret) if (ret)
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/cdev.h> #include <linux/cdev.h>
#include <linux/notifier.h>
#include <uapi/linux/watchdog.h> #include <uapi/linux/watchdog.h>
struct watchdog_ops; struct watchdog_ops;
...@@ -26,6 +27,7 @@ struct watchdog_device; ...@@ -26,6 +27,7 @@ struct watchdog_device;
* @status: The routine that shows the status of the watchdog device. * @status: The routine that shows the status of the watchdog device.
* @set_timeout:The routine for setting the watchdog devices timeout value (in seconds). * @set_timeout:The routine for setting the watchdog devices timeout value (in seconds).
* @get_timeleft:The routine that gets the time left before a reset (in seconds). * @get_timeleft:The routine that gets the time left before a reset (in seconds).
* @restart: The routine for restarting the machine.
* @ref: The ref operation for dyn. allocated watchdog_device structs * @ref: The ref operation for dyn. allocated watchdog_device structs
* @unref: The unref operation for dyn. allocated watchdog_device structs * @unref: The unref operation for dyn. allocated watchdog_device structs
* @ioctl: The routines that handles extra ioctl calls. * @ioctl: The routines that handles extra ioctl calls.
...@@ -45,6 +47,7 @@ struct watchdog_ops { ...@@ -45,6 +47,7 @@ struct watchdog_ops {
unsigned int (*status)(struct watchdog_device *); unsigned int (*status)(struct watchdog_device *);
int (*set_timeout)(struct watchdog_device *, unsigned int); int (*set_timeout)(struct watchdog_device *, unsigned int);
unsigned int (*get_timeleft)(struct watchdog_device *); unsigned int (*get_timeleft)(struct watchdog_device *);
int (*restart)(struct watchdog_device *);
void (*ref)(struct watchdog_device *); void (*ref)(struct watchdog_device *);
void (*unref)(struct watchdog_device *); void (*unref)(struct watchdog_device *);
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
...@@ -62,6 +65,7 @@ struct watchdog_ops { ...@@ -62,6 +65,7 @@ struct watchdog_ops {
* @timeout: The watchdog devices timeout value (in seconds). * @timeout: The watchdog devices timeout value (in seconds).
* @min_timeout:The watchdog devices minimum timeout value (in seconds). * @min_timeout:The watchdog devices minimum timeout value (in seconds).
* @max_timeout:The watchdog devices maximum timeout value (in seconds). * @max_timeout:The watchdog devices maximum timeout value (in seconds).
* @restart_nb: The notifier block to register a restart function.
* @driver-data:Pointer to the drivers private data. * @driver-data:Pointer to the drivers private data.
* @lock: Lock for watchdog core internal use only. * @lock: Lock for watchdog core internal use only.
* @status: Field that contains the devices internal status bits. * @status: Field that contains the devices internal status bits.
...@@ -88,6 +92,7 @@ struct watchdog_device { ...@@ -88,6 +92,7 @@ struct watchdog_device {
unsigned int timeout; unsigned int timeout;
unsigned int min_timeout; unsigned int min_timeout;
unsigned int max_timeout; unsigned int max_timeout;
struct notifier_block restart_nb;
void *driver_data; void *driver_data;
struct mutex lock; struct mutex lock;
unsigned long status; unsigned long status;
...@@ -142,6 +147,7 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd) ...@@ -142,6 +147,7 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
} }
/* drivers/watchdog/watchdog_core.c */ /* drivers/watchdog/watchdog_core.c */
void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);
extern int watchdog_init_timeout(struct watchdog_device *wdd, extern int watchdog_init_timeout(struct watchdog_device *wdd,
unsigned int timeout_parm, struct device *dev); unsigned int timeout_parm, struct device *dev);
extern int watchdog_register_device(struct watchdog_device *); extern int watchdog_register_device(struct watchdog_device *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment