aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean-Baptiste Theou <jtheou@adeneo-embedded.us>2015-06-09 12:55:02 -0400
committerWim Van Sebroeck <wim@iguana.be>2015-06-29 05:51:06 -0400
commitef90174f821041313d42d99c1c8b35a3af64a910 (patch)
tree0265d89b6ee866b7e20a8925fae03e290e078e00
parentb9be9660ba2d23259e4a430a44167ef441dc5fe6 (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.txt7
-rw-r--r--drivers/watchdog/watchdog_core.c118
-rw-r--r--include/linux/watchdog.h3
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
36device. The parameter of this routine is the pointer to the registered 36device. The parameter of this routine is the pointer to the registered
37watchdog_device structure. 37watchdog_device structure.
38 38
39The watchdog subsystem includes an registration deferral mechanism,
40which allows you to register an watchdog as early as you wish during
41the boot process.
42
39The watchdog device structure looks like this: 43The watchdog device structure looks like this:
40 44
41struct watchdog_device { 45struct 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
57It contains following fields: 62It 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
84The list of watchdog operations is defined as: 91The 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 @@
43static DEFINE_IDA(watchdog_ida); 43static DEFINE_IDA(watchdog_ida);
44static struct class *watchdog_class; 44static 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
59static DEFINE_MUTEX(wtd_deferred_reg_mutex);
60static LIST_HEAD(wtd_deferred_reg_list);
61static bool wtd_deferred_reg_done;
62
63static 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
70static 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
46static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) 85static 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}
99EXPORT_SYMBOL_GPL(watchdog_init_timeout); 138EXPORT_SYMBOL_GPL(watchdog_init_timeout);
100 139
101/** 140static 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 */
111int 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}
167EXPORT_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 */
176void watchdog_unregister_device(struct watchdog_device *wdd) 207
208int 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}
220EXPORT_SYMBOL_GPL(watchdog_register_device);
221
222static 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
247void 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
192EXPORT_SYMBOL_GPL(watchdog_unregister_device); 257EXPORT_SYMBOL_GPL(watchdog_unregister_device);
193 258
259static 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
194static int __init watchdog_init(void) 275static 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
220subsys_initcall(watchdog_init); 302subsys_initcall_sync(watchdog_init);
221module_exit(watchdog_exit); 303module_exit(watchdog_exit);
222 304
223MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); 305MODULE_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)