diff options
author | Jean-Baptiste Theou <jtheou@adeneo-embedded.us> | 2015-06-09 12:55:02 -0400 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2015-06-29 05:51:06 -0400 |
commit | ef90174f821041313d42d99c1c8b35a3af64a910 (patch) | |
tree | 0265d89b6ee866b7e20a8925fae03e290e078e00 | |
parent | b9be9660ba2d23259e4a430a44167ef441dc5fe6 (diff) |
watchdog: watchdog_core: Add watchdog registration deferral mechanism
Currently, watchdog subsystem require the misc subsystem to
register a watchdog. This may not be the case in case of an
early registration of a watchdog, which can be required when
the watchdog cannot be disabled.
This patch introduces a deferral mechanism to remove this requirement.
Signed-off-by: Jean-Baptiste Theou <jtheou@adeneo-embedded.us>
Reviewed-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 | 7 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_core.c | 118 | ||||
-rw-r--r-- | include/linux/watchdog.h | 3 |
3 files changed, 110 insertions, 18 deletions
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index a0438f3957ca..d8b0d3367706 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt | |||
@@ -36,6 +36,10 @@ The watchdog_unregister_device routine deregisters a registered watchdog timer | |||
36 | device. The parameter of this routine is the pointer to the registered | 36 | device. The parameter of this routine is the pointer to the registered |
37 | watchdog_device structure. | 37 | watchdog_device structure. |
38 | 38 | ||
39 | The watchdog subsystem includes an registration deferral mechanism, | ||
40 | which allows you to register an watchdog as early as you wish during | ||
41 | the boot process. | ||
42 | |||
39 | The watchdog device structure looks like this: | 43 | The watchdog device structure looks like this: |
40 | 44 | ||
41 | struct watchdog_device { | 45 | struct watchdog_device { |
@@ -52,6 +56,7 @@ struct watchdog_device { | |||
52 | void *driver_data; | 56 | void *driver_data; |
53 | struct mutex lock; | 57 | struct mutex lock; |
54 | unsigned long status; | 58 | unsigned long status; |
59 | struct list_head deferred; | ||
55 | }; | 60 | }; |
56 | 61 | ||
57 | It contains following fields: | 62 | It contains following fields: |
@@ -80,6 +85,8 @@ It contains following fields: | |||
80 | information about the status of the device (Like: is the watchdog timer | 85 | information about the status of the device (Like: is the watchdog timer |
81 | running/active, is the nowayout bit set, is the device opened via | 86 | running/active, is the nowayout bit set, is the device opened via |
82 | the /dev/watchdog interface or not, ...). | 87 | the /dev/watchdog interface or not, ...). |
88 | * deferred: entry in wtd_deferred_reg_list which is used to | ||
89 | register early initialized watchdogs. | ||
83 | 90 | ||
84 | The list of watchdog operations is defined as: | 91 | The list of watchdog operations is defined as: |
85 | 92 | ||
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index cec9b559647d..1a8059455413 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c | |||
@@ -43,6 +43,45 @@ | |||
43 | static DEFINE_IDA(watchdog_ida); | 43 | static DEFINE_IDA(watchdog_ida); |
44 | static struct class *watchdog_class; | 44 | static struct class *watchdog_class; |
45 | 45 | ||
46 | /* | ||
47 | * Deferred Registration infrastructure. | ||
48 | * | ||
49 | * Sometimes watchdog drivers needs to be loaded as soon as possible, | ||
50 | * for example when it's impossible to disable it. To do so, | ||
51 | * raising the initcall level of the watchdog driver is a solution. | ||
52 | * But in such case, the miscdev is maybe not ready (subsys_initcall), and | ||
53 | * watchdog_core need miscdev to register the watchdog as a char device. | ||
54 | * | ||
55 | * The deferred registration infrastructure offer a way for the watchdog | ||
56 | * subsystem to register a watchdog properly, even before miscdev is ready. | ||
57 | */ | ||
58 | |||
59 | static DEFINE_MUTEX(wtd_deferred_reg_mutex); | ||
60 | static LIST_HEAD(wtd_deferred_reg_list); | ||
61 | static bool wtd_deferred_reg_done; | ||
62 | |||
63 | static int watchdog_deferred_registration_add(struct watchdog_device *wdd) | ||
64 | { | ||
65 | list_add_tail(&wdd->deferred, | ||
66 | &wtd_deferred_reg_list); | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | static void watchdog_deferred_registration_del(struct watchdog_device *wdd) | ||
71 | { | ||
72 | struct list_head *p, *n; | ||
73 | struct watchdog_device *wdd_tmp; | ||
74 | |||
75 | list_for_each_safe(p, n, &wtd_deferred_reg_list) { | ||
76 | wdd_tmp = list_entry(p, struct watchdog_device, | ||
77 | deferred); | ||
78 | if (wdd_tmp == wdd) { | ||
79 | list_del(&wdd_tmp->deferred); | ||
80 | break; | ||
81 | } | ||
82 | } | ||
83 | } | ||
84 | |||
46 | static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) | 85 | static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) |
47 | { | 86 | { |
48 | /* | 87 | /* |
@@ -98,17 +137,7 @@ int watchdog_init_timeout(struct watchdog_device *wdd, | |||
98 | } | 137 | } |
99 | EXPORT_SYMBOL_GPL(watchdog_init_timeout); | 138 | EXPORT_SYMBOL_GPL(watchdog_init_timeout); |
100 | 139 | ||
101 | /** | 140 | static int __watchdog_register_device(struct watchdog_device *wdd) |
102 | * watchdog_register_device() - register a watchdog device | ||
103 | * @wdd: watchdog device | ||
104 | * | ||
105 | * Register a watchdog device with the kernel so that the | ||
106 | * watchdog timer can be accessed from userspace. | ||
107 | * | ||
108 | * A zero is returned on success and a negative errno code for | ||
109 | * failure. | ||
110 | */ | ||
111 | int watchdog_register_device(struct watchdog_device *wdd) | ||
112 | { | 141 | { |
113 | int ret, id, devno; | 142 | int ret, id, devno; |
114 | 143 | ||
@@ -164,16 +193,33 @@ int watchdog_register_device(struct watchdog_device *wdd) | |||
164 | 193 | ||
165 | return 0; | 194 | return 0; |
166 | } | 195 | } |
167 | EXPORT_SYMBOL_GPL(watchdog_register_device); | ||
168 | 196 | ||
169 | /** | 197 | /** |
170 | * watchdog_unregister_device() - unregister a watchdog device | 198 | * watchdog_register_device() - register a watchdog device |
171 | * @wdd: watchdog device to unregister | 199 | * @wdd: watchdog device |
172 | * | 200 | * |
173 | * Unregister a watchdog device that was previously successfully | 201 | * Register a watchdog device with the kernel so that the |
174 | * registered with watchdog_register_device(). | 202 | * watchdog timer can be accessed from userspace. |
203 | * | ||
204 | * A zero is returned on success and a negative errno code for | ||
205 | * failure. | ||
175 | */ | 206 | */ |
176 | void watchdog_unregister_device(struct watchdog_device *wdd) | 207 | |
208 | int watchdog_register_device(struct watchdog_device *wdd) | ||
209 | { | ||
210 | int ret; | ||
211 | |||
212 | mutex_lock(&wtd_deferred_reg_mutex); | ||
213 | if (wtd_deferred_reg_done) | ||
214 | ret = __watchdog_register_device(wdd); | ||
215 | else | ||
216 | ret = watchdog_deferred_registration_add(wdd); | ||
217 | mutex_unlock(&wtd_deferred_reg_mutex); | ||
218 | return ret; | ||
219 | } | ||
220 | EXPORT_SYMBOL_GPL(watchdog_register_device); | ||
221 | |||
222 | static void __watchdog_unregister_device(struct watchdog_device *wdd) | ||
177 | { | 223 | { |
178 | int ret; | 224 | int ret; |
179 | int devno; | 225 | int devno; |
@@ -189,8 +235,43 @@ void watchdog_unregister_device(struct watchdog_device *wdd) | |||
189 | ida_simple_remove(&watchdog_ida, wdd->id); | 235 | ida_simple_remove(&watchdog_ida, wdd->id); |
190 | wdd->dev = NULL; | 236 | wdd->dev = NULL; |
191 | } | 237 | } |
238 | |||
239 | /** | ||
240 | * watchdog_unregister_device() - unregister a watchdog device | ||
241 | * @wdd: watchdog device to unregister | ||
242 | * | ||
243 | * Unregister a watchdog device that was previously successfully | ||
244 | * registered with watchdog_register_device(). | ||
245 | */ | ||
246 | |||
247 | void watchdog_unregister_device(struct watchdog_device *wdd) | ||
248 | { | ||
249 | mutex_lock(&wtd_deferred_reg_mutex); | ||
250 | if (wtd_deferred_reg_done) | ||
251 | __watchdog_unregister_device(wdd); | ||
252 | else | ||
253 | watchdog_deferred_registration_del(wdd); | ||
254 | mutex_unlock(&wtd_deferred_reg_mutex); | ||
255 | } | ||
256 | |||
192 | EXPORT_SYMBOL_GPL(watchdog_unregister_device); | 257 | EXPORT_SYMBOL_GPL(watchdog_unregister_device); |
193 | 258 | ||
259 | static int __init watchdog_deferred_registration(void) | ||
260 | { | ||
261 | mutex_lock(&wtd_deferred_reg_mutex); | ||
262 | wtd_deferred_reg_done = true; | ||
263 | while (!list_empty(&wtd_deferred_reg_list)) { | ||
264 | struct watchdog_device *wdd; | ||
265 | |||
266 | wdd = list_first_entry(&wtd_deferred_reg_list, | ||
267 | struct watchdog_device, deferred); | ||
268 | list_del(&wdd->deferred); | ||
269 | __watchdog_register_device(wdd); | ||
270 | } | ||
271 | mutex_unlock(&wtd_deferred_reg_mutex); | ||
272 | return 0; | ||
273 | } | ||
274 | |||
194 | static int __init watchdog_init(void) | 275 | static int __init watchdog_init(void) |
195 | { | 276 | { |
196 | int err; | 277 | int err; |
@@ -207,6 +288,7 @@ static int __init watchdog_init(void) | |||
207 | return err; | 288 | return err; |
208 | } | 289 | } |
209 | 290 | ||
291 | watchdog_deferred_registration(); | ||
210 | return 0; | 292 | return 0; |
211 | } | 293 | } |
212 | 294 | ||
@@ -217,7 +299,7 @@ static void __exit watchdog_exit(void) | |||
217 | ida_destroy(&watchdog_ida); | 299 | ida_destroy(&watchdog_ida); |
218 | } | 300 | } |
219 | 301 | ||
220 | subsys_initcall(watchdog_init); | 302 | subsys_initcall_sync(watchdog_init); |
221 | module_exit(watchdog_exit); | 303 | module_exit(watchdog_exit); |
222 | 304 | ||
223 | MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); | 305 | MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); |
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index a746bf5216f8..f47feada5b42 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h | |||
@@ -65,6 +65,8 @@ struct watchdog_ops { | |||
65 | * @driver-data:Pointer to the drivers private data. | 65 | * @driver-data:Pointer to the drivers private data. |
66 | * @lock: Lock for watchdog core internal use only. | 66 | * @lock: Lock for watchdog core internal use only. |
67 | * @status: Field that contains the devices internal status bits. | 67 | * @status: Field that contains the devices internal status bits. |
68 | * @deferred: entry in wtd_deferred_reg_list which is used to | ||
69 | * register early initialized watchdogs. | ||
68 | * | 70 | * |
69 | * The watchdog_device structure contains all information about a | 71 | * The watchdog_device structure contains all information about a |
70 | * watchdog timer device. | 72 | * watchdog timer device. |
@@ -95,6 +97,7 @@ struct watchdog_device { | |||
95 | #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ | 97 | #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ |
96 | #define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ | 98 | #define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ |
97 | #define WDOG_UNREGISTERED 4 /* Has the device been unregistered */ | 99 | #define WDOG_UNREGISTERED 4 /* Has the device been unregistered */ |
100 | struct list_head deferred; | ||
98 | }; | 101 | }; |
99 | 102 | ||
100 | #define WATCHDOG_NOWAYOUT IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT) | 103 | #define WATCHDOG_NOWAYOUT IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT) |