diff options
author | Damien Riegel <damien.riegel@savoirfairelinux.com> | 2015-11-16 12:27:59 -0500 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2015-12-13 09:27:10 -0500 |
commit | 2165bf524da5f5e496d1cdb8c5afae1345ecce1e (patch) | |
tree | 75a24ea774d08021e1c45e201ffc9f7a164ca661 | |
parent | 1f32f83e5d81c1e99a1c16366e71d5867cd1e364 (diff) |
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: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: Damien Riegel <damien.riegel@savoirfairelinux.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
-rw-r--r-- | Documentation/watchdog/watchdog-kernel-api.txt | 19 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_core.c | 48 | ||||
-rw-r--r-- | include/linux/watchdog.h | 6 |
3 files changed, 73 insertions, 0 deletions
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index d8b0d3367706..dbc6a65f0bd1 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt | |||
@@ -53,6 +53,7 @@ struct watchdog_device { | |||
53 | unsigned int timeout; | 53 | unsigned int timeout; |
54 | unsigned int min_timeout; | 54 | unsigned int min_timeout; |
55 | unsigned int max_timeout; | 55 | unsigned int max_timeout; |
56 | struct notifier_block restart_nb; | ||
56 | void *driver_data; | 57 | void *driver_data; |
57 | struct mutex lock; | 58 | struct mutex lock; |
58 | unsigned long status; | 59 | unsigned long status; |
@@ -75,6 +76,10 @@ It contains following fields: | |||
75 | * timeout: the watchdog timer's timeout value (in seconds). | 76 | * timeout: the watchdog timer's timeout value (in seconds). |
76 | * min_timeout: the watchdog timer's minimum timeout value (in seconds). | 77 | * min_timeout: the watchdog timer's minimum timeout value (in seconds). |
77 | * max_timeout: the watchdog timer's maximum timeout value (in seconds). | 78 | * max_timeout: the watchdog timer's maximum timeout value (in seconds). |
79 | * restart_nb: notifier block that is registered for machine restart, for | ||
80 | internal use only. If a watchdog is capable of restarting the machine, it | ||
81 | should define ops->restart. Priority can be changed through | ||
82 | watchdog_set_restart_priority. | ||
78 | * bootstatus: status of the device after booting (reported with watchdog | 83 | * bootstatus: status of the device after booting (reported with watchdog |
79 | WDIOF_* status bits). | 84 | WDIOF_* status bits). |
80 | * driver_data: a pointer to the drivers private data of a watchdog device. | 85 | * driver_data: a pointer to the drivers private data of a watchdog device. |
@@ -100,6 +105,7 @@ struct watchdog_ops { | |||
100 | unsigned int (*status)(struct watchdog_device *); | 105 | unsigned int (*status)(struct watchdog_device *); |
101 | int (*set_timeout)(struct watchdog_device *, unsigned int); | 106 | int (*set_timeout)(struct watchdog_device *, unsigned int); |
102 | unsigned int (*get_timeleft)(struct watchdog_device *); | 107 | unsigned int (*get_timeleft)(struct watchdog_device *); |
108 | int (*restart)(struct watchdog_device *); | ||
103 | void (*ref)(struct watchdog_device *); | 109 | void (*ref)(struct watchdog_device *); |
104 | void (*unref)(struct watchdog_device *); | 110 | void (*unref)(struct watchdog_device *); |
105 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); | 111 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); |
@@ -164,6 +170,8 @@ they are supported. These optional routines/operations are: | |||
164 | (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the | 170 | (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the |
165 | watchdog's info structure). | 171 | watchdog's info structure). |
166 | * get_timeleft: this routines returns the time that's left before a reset. | 172 | * get_timeleft: this routines returns the time that's left before a reset. |
173 | * restart: this routine restarts the machine. It returns 0 on success or a | ||
174 | negative errno code for failure. | ||
167 | * ref: the operation that calls kref_get on the kref of a dynamically | 175 | * ref: the operation that calls kref_get on the kref of a dynamically |
168 | allocated watchdog_device struct. | 176 | allocated watchdog_device struct. |
169 | * unref: the operation that calls kref_put on the kref of a dynamically | 177 | * 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 | to set the default timeout value as timeout value in the watchdog_device and | 239 | to set the default timeout value as timeout value in the watchdog_device and |
232 | then use this function to set the user "preferred" timeout value. | 240 | then use this function to set the user "preferred" timeout value. |
233 | This routine returns zero on success and a negative errno code for failure. | 241 | This routine returns zero on success and a negative errno code for failure. |
242 | |||
243 | To change the priority of the restart handler the following helper should be | ||
244 | used: | ||
245 | |||
246 | void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority); | ||
247 | |||
248 | User should follow the following guidelines for setting the priority: | ||
249 | * 0: should be called in last resort, has limited restart capabilities | ||
250 | * 128: default restart handler, use if no other handler is expected to be | ||
251 | available, and/or if restart is sufficient to restart the entire system | ||
252 | * 255: highest priority, will preempt all other restart handlers | ||
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 873f13972cf4..88a34efac400 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/types.h> /* For standard types */ | 32 | #include <linux/types.h> /* For standard types */ |
33 | #include <linux/errno.h> /* For the -ENODEV/... values */ | 33 | #include <linux/errno.h> /* For the -ENODEV/... values */ |
34 | #include <linux/kernel.h> /* For printk/panic/... */ | 34 | #include <linux/kernel.h> /* For printk/panic/... */ |
35 | #include <linux/reboot.h> /* For restart handler */ | ||
35 | #include <linux/watchdog.h> /* For watchdog specific items */ | 36 | #include <linux/watchdog.h> /* For watchdog specific items */ |
36 | #include <linux/init.h> /* For __init/__exit/... */ | 37 | #include <linux/init.h> /* For __init/__exit/... */ |
37 | #include <linux/idr.h> /* For ida_* macros */ | 38 | #include <linux/idr.h> /* For ida_* macros */ |
@@ -137,6 +138,41 @@ int watchdog_init_timeout(struct watchdog_device *wdd, | |||
137 | } | 138 | } |
138 | EXPORT_SYMBOL_GPL(watchdog_init_timeout); | 139 | EXPORT_SYMBOL_GPL(watchdog_init_timeout); |
139 | 140 | ||
141 | static int watchdog_restart_notifier(struct notifier_block *nb, | ||
142 | unsigned long action, void *data) | ||
143 | { | ||
144 | struct watchdog_device *wdd = container_of(nb, struct watchdog_device, | ||
145 | restart_nb); | ||
146 | |||
147 | int ret; | ||
148 | |||
149 | ret = wdd->ops->restart(wdd); | ||
150 | if (ret) | ||
151 | return NOTIFY_BAD; | ||
152 | |||
153 | return NOTIFY_DONE; | ||
154 | } | ||
155 | |||
156 | /** | ||
157 | * watchdog_set_restart_priority - Change priority of restart handler | ||
158 | * @wdd: watchdog device | ||
159 | * @priority: priority of the restart handler, should follow these guidelines: | ||
160 | * 0: use watchdog's restart function as last resort, has limited restart | ||
161 | * capabilies | ||
162 | * 128: default restart handler, use if no other handler is expected to be | ||
163 | * available and/or if restart is sufficient to restart the entire system | ||
164 | * 255: preempt all other handlers | ||
165 | * | ||
166 | * If a wdd->ops->restart function is provided when watchdog_register_device is | ||
167 | * called, it will be registered as a restart handler with the priority given | ||
168 | * here. | ||
169 | */ | ||
170 | void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority) | ||
171 | { | ||
172 | wdd->restart_nb.priority = priority; | ||
173 | } | ||
174 | EXPORT_SYMBOL_GPL(watchdog_set_restart_priority); | ||
175 | |||
140 | static int __watchdog_register_device(struct watchdog_device *wdd) | 176 | static int __watchdog_register_device(struct watchdog_device *wdd) |
141 | { | 177 | { |
142 | int ret, id = -1, devno; | 178 | int ret, id = -1, devno; |
@@ -202,6 +238,15 @@ static int __watchdog_register_device(struct watchdog_device *wdd) | |||
202 | return ret; | 238 | return ret; |
203 | } | 239 | } |
204 | 240 | ||
241 | if (wdd->ops->restart) { | ||
242 | wdd->restart_nb.notifier_call = watchdog_restart_notifier; | ||
243 | |||
244 | ret = register_restart_handler(&wdd->restart_nb); | ||
245 | if (ret) | ||
246 | dev_warn(wdd->dev, "Cannot register restart handler (%d)\n", | ||
247 | ret); | ||
248 | } | ||
249 | |||
205 | return 0; | 250 | return 0; |
206 | } | 251 | } |
207 | 252 | ||
@@ -238,6 +283,9 @@ static void __watchdog_unregister_device(struct watchdog_device *wdd) | |||
238 | if (wdd == NULL) | 283 | if (wdd == NULL) |
239 | return; | 284 | return; |
240 | 285 | ||
286 | if (wdd->ops->restart) | ||
287 | unregister_restart_handler(&wdd->restart_nb); | ||
288 | |||
241 | devno = wdd->cdev.dev; | 289 | devno = wdd->cdev.dev; |
242 | ret = watchdog_dev_unregister(wdd); | 290 | ret = watchdog_dev_unregister(wdd); |
243 | if (ret) | 291 | if (ret) |
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 027b1f43f12d..5b52c834f7aa 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/bitops.h> | 12 | #include <linux/bitops.h> |
13 | #include <linux/device.h> | 13 | #include <linux/device.h> |
14 | #include <linux/cdev.h> | 14 | #include <linux/cdev.h> |
15 | #include <linux/notifier.h> | ||
15 | #include <uapi/linux/watchdog.h> | 16 | #include <uapi/linux/watchdog.h> |
16 | 17 | ||
17 | struct watchdog_ops; | 18 | struct watchdog_ops; |
@@ -26,6 +27,7 @@ struct watchdog_device; | |||
26 | * @status: The routine that shows the status of the watchdog device. | 27 | * @status: The routine that shows the status of the watchdog device. |
27 | * @set_timeout:The routine for setting the watchdog devices timeout value (in seconds). | 28 | * @set_timeout:The routine for setting the watchdog devices timeout value (in seconds). |
28 | * @get_timeleft:The routine that gets the time left before a reset (in seconds). | 29 | * @get_timeleft:The routine that gets the time left before a reset (in seconds). |
30 | * @restart: The routine for restarting the machine. | ||
29 | * @ref: The ref operation for dyn. allocated watchdog_device structs | 31 | * @ref: The ref operation for dyn. allocated watchdog_device structs |
30 | * @unref: The unref operation for dyn. allocated watchdog_device structs | 32 | * @unref: The unref operation for dyn. allocated watchdog_device structs |
31 | * @ioctl: The routines that handles extra ioctl calls. | 33 | * @ioctl: The routines that handles extra ioctl calls. |
@@ -45,6 +47,7 @@ struct watchdog_ops { | |||
45 | unsigned int (*status)(struct watchdog_device *); | 47 | unsigned int (*status)(struct watchdog_device *); |
46 | int (*set_timeout)(struct watchdog_device *, unsigned int); | 48 | int (*set_timeout)(struct watchdog_device *, unsigned int); |
47 | unsigned int (*get_timeleft)(struct watchdog_device *); | 49 | unsigned int (*get_timeleft)(struct watchdog_device *); |
50 | int (*restart)(struct watchdog_device *); | ||
48 | void (*ref)(struct watchdog_device *); | 51 | void (*ref)(struct watchdog_device *); |
49 | void (*unref)(struct watchdog_device *); | 52 | void (*unref)(struct watchdog_device *); |
50 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); | 53 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); |
@@ -62,6 +65,7 @@ struct watchdog_ops { | |||
62 | * @timeout: The watchdog devices timeout value (in seconds). | 65 | * @timeout: The watchdog devices timeout value (in seconds). |
63 | * @min_timeout:The watchdog devices minimum timeout value (in seconds). | 66 | * @min_timeout:The watchdog devices minimum timeout value (in seconds). |
64 | * @max_timeout:The watchdog devices maximum timeout value (in seconds). | 67 | * @max_timeout:The watchdog devices maximum timeout value (in seconds). |
68 | * @restart_nb: The notifier block to register a restart function. | ||
65 | * @driver-data:Pointer to the drivers private data. | 69 | * @driver-data:Pointer to the drivers private data. |
66 | * @lock: Lock for watchdog core internal use only. | 70 | * @lock: Lock for watchdog core internal use only. |
67 | * @status: Field that contains the devices internal status bits. | 71 | * @status: Field that contains the devices internal status bits. |
@@ -88,6 +92,7 @@ struct watchdog_device { | |||
88 | unsigned int timeout; | 92 | unsigned int timeout; |
89 | unsigned int min_timeout; | 93 | unsigned int min_timeout; |
90 | unsigned int max_timeout; | 94 | unsigned int max_timeout; |
95 | struct notifier_block restart_nb; | ||
91 | void *driver_data; | 96 | void *driver_data; |
92 | struct mutex lock; | 97 | struct mutex lock; |
93 | unsigned long status; | 98 | unsigned long status; |
@@ -142,6 +147,7 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd) | |||
142 | } | 147 | } |
143 | 148 | ||
144 | /* drivers/watchdog/watchdog_core.c */ | 149 | /* drivers/watchdog/watchdog_core.c */ |
150 | void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority); | ||
145 | extern int watchdog_init_timeout(struct watchdog_device *wdd, | 151 | extern int watchdog_init_timeout(struct watchdog_device *wdd, |
146 | unsigned int timeout_parm, struct device *dev); | 152 | unsigned int timeout_parm, struct device *dev); |
147 | extern int watchdog_register_device(struct watchdog_device *); | 153 | extern int watchdog_register_device(struct watchdog_device *); |