diff options
author | Alan Cox <alan@linux.intel.com> | 2012-05-10 15:48:59 -0400 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2012-05-30 01:54:25 -0400 |
commit | 45f5fed30a6460ec58f159ff297a2974153a97de (patch) | |
tree | d173b2d22ed1187fb7ed2cb85015ddf5c545da05 | |
parent | fb5f6658163412dce22724e906e324ab7fd62c18 (diff) |
watchdog: Add multiple device support
We keep the old /dev/watchdog interface file for the first watchdog via
miscdev. This is basically a cut and paste of the relevant interface code
from the rtc driver layer tweaked for watchdog.
Revised to fix problems noted by Hans de Goede
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
-rw-r--r-- | Documentation/watchdog/watchdog-kernel-api.txt | 10 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_core.c | 43 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_core.h | 4 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_dev.c | 127 | ||||
-rw-r--r-- | include/linux/watchdog.h | 6 |
5 files changed, 140 insertions, 50 deletions
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 25fe4304f2fc..3c85fc7dc1f1 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt | |||
@@ -1,6 +1,6 @@ | |||
1 | The Linux WatchDog Timer Driver Core kernel API. | 1 | The Linux WatchDog Timer Driver Core kernel API. |
2 | =============================================== | 2 | =============================================== |
3 | Last reviewed: 16-Mar-2012 | 3 | Last reviewed: 21-May-2012 |
4 | 4 | ||
5 | Wim Van Sebroeck <wim@iguana.be> | 5 | Wim Van Sebroeck <wim@iguana.be> |
6 | 6 | ||
@@ -39,6 +39,8 @@ watchdog_device structure. | |||
39 | The watchdog device structure looks like this: | 39 | The watchdog device structure looks like this: |
40 | 40 | ||
41 | struct watchdog_device { | 41 | struct watchdog_device { |
42 | int id; | ||
43 | struct cdev cdev; | ||
42 | const struct watchdog_info *info; | 44 | const struct watchdog_info *info; |
43 | const struct watchdog_ops *ops; | 45 | const struct watchdog_ops *ops; |
44 | unsigned int bootstatus; | 46 | unsigned int bootstatus; |
@@ -50,6 +52,12 @@ struct watchdog_device { | |||
50 | }; | 52 | }; |
51 | 53 | ||
52 | It contains following fields: | 54 | It contains following fields: |
55 | * id: set by watchdog_register_device, id 0 is special. It has both a | ||
56 | /dev/watchdog0 cdev (dynamic major, minor 0) as well as the old | ||
57 | /dev/watchdog miscdev. The id is set automatically when calling | ||
58 | watchdog_register_device. | ||
59 | * cdev: cdev for the dynamic /dev/watchdog<id> device nodes. This | ||
60 | field is also populated by watchdog_register_device. | ||
53 | * info: a pointer to a watchdog_info structure. This structure gives some | 61 | * info: a pointer to a watchdog_info structure. This structure gives some |
54 | additional information about the watchdog timer itself. (Like it's unique name) | 62 | additional information about the watchdog timer itself. (Like it's unique name) |
55 | * ops: a pointer to the list of watchdog operations that the watchdog supports. | 63 | * ops: a pointer to the list of watchdog operations that the watchdog supports. |
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 8598308278d3..5f9879369003 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c | |||
@@ -34,9 +34,12 @@ | |||
34 | #include <linux/kernel.h> /* For printk/panic/... */ | 34 | #include <linux/kernel.h> /* For printk/panic/... */ |
35 | #include <linux/watchdog.h> /* For watchdog specific items */ | 35 | #include <linux/watchdog.h> /* For watchdog specific items */ |
36 | #include <linux/init.h> /* For __init/__exit/... */ | 36 | #include <linux/init.h> /* For __init/__exit/... */ |
37 | #include <linux/idr.h> /* For ida_* macros */ | ||
37 | 38 | ||
38 | #include "watchdog_core.h" /* For watchdog_dev_register/... */ | 39 | #include "watchdog_core.h" /* For watchdog_dev_register/... */ |
39 | 40 | ||
41 | static DEFINE_IDA(watchdog_ida); | ||
42 | |||
40 | /** | 43 | /** |
41 | * watchdog_register_device() - register a watchdog device | 44 | * watchdog_register_device() - register a watchdog device |
42 | * @wdd: watchdog device | 45 | * @wdd: watchdog device |
@@ -49,7 +52,7 @@ | |||
49 | */ | 52 | */ |
50 | int watchdog_register_device(struct watchdog_device *wdd) | 53 | int watchdog_register_device(struct watchdog_device *wdd) |
51 | { | 54 | { |
52 | int ret; | 55 | int ret, id; |
53 | 56 | ||
54 | if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) | 57 | if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) |
55 | return -EINVAL; | 58 | return -EINVAL; |
@@ -74,11 +77,28 @@ int watchdog_register_device(struct watchdog_device *wdd) | |||
74 | * corrupted in a later stage then we expect a kernel panic! | 77 | * corrupted in a later stage then we expect a kernel panic! |
75 | */ | 78 | */ |
76 | 79 | ||
77 | /* We only support 1 watchdog device via the /dev/watchdog interface */ | 80 | id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL); |
81 | if (id < 0) | ||
82 | return id; | ||
83 | wdd->id = id; | ||
84 | |||
78 | ret = watchdog_dev_register(wdd); | 85 | ret = watchdog_dev_register(wdd); |
79 | if (ret) { | 86 | if (ret) { |
80 | pr_err("error registering /dev/watchdog (err=%d)\n", ret); | 87 | ida_simple_remove(&watchdog_ida, id); |
81 | return ret; | 88 | if (!(id == 0 && ret == -EBUSY)) |
89 | return ret; | ||
90 | |||
91 | /* Retry in case a legacy watchdog module exists */ | ||
92 | id = ida_simple_get(&watchdog_ida, 1, MAX_DOGS, GFP_KERNEL); | ||
93 | if (id < 0) | ||
94 | return id; | ||
95 | wdd->id = id; | ||
96 | |||
97 | ret = watchdog_dev_register(wdd); | ||
98 | if (ret) { | ||
99 | ida_simple_remove(&watchdog_ida, id); | ||
100 | return ret; | ||
101 | } | ||
82 | } | 102 | } |
83 | 103 | ||
84 | return 0; | 104 | return 0; |
@@ -102,9 +122,24 @@ void watchdog_unregister_device(struct watchdog_device *wdd) | |||
102 | ret = watchdog_dev_unregister(wdd); | 122 | ret = watchdog_dev_unregister(wdd); |
103 | if (ret) | 123 | if (ret) |
104 | pr_err("error unregistering /dev/watchdog (err=%d)\n", ret); | 124 | pr_err("error unregistering /dev/watchdog (err=%d)\n", ret); |
125 | ida_simple_remove(&watchdog_ida, wdd->id); | ||
105 | } | 126 | } |
106 | EXPORT_SYMBOL_GPL(watchdog_unregister_device); | 127 | EXPORT_SYMBOL_GPL(watchdog_unregister_device); |
107 | 128 | ||
129 | static int __init watchdog_init(void) | ||
130 | { | ||
131 | return watchdog_dev_init(); | ||
132 | } | ||
133 | |||
134 | static void __exit watchdog_exit(void) | ||
135 | { | ||
136 | watchdog_dev_exit(); | ||
137 | ida_destroy(&watchdog_ida); | ||
138 | } | ||
139 | |||
140 | subsys_initcall(watchdog_init); | ||
141 | module_exit(watchdog_exit); | ||
142 | |||
108 | MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); | 143 | MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); |
109 | MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>"); | 144 | MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>"); |
110 | MODULE_DESCRIPTION("WatchDog Timer Driver Core"); | 145 | MODULE_DESCRIPTION("WatchDog Timer Driver Core"); |
diff --git a/drivers/watchdog/watchdog_core.h b/drivers/watchdog/watchdog_core.h index 80503f229385..6c951418fca7 100644 --- a/drivers/watchdog/watchdog_core.h +++ b/drivers/watchdog/watchdog_core.h | |||
@@ -26,8 +26,12 @@ | |||
26 | * This material is provided "AS-IS" and at no charge. | 26 | * This material is provided "AS-IS" and at no charge. |
27 | */ | 27 | */ |
28 | 28 | ||
29 | #define MAX_DOGS 32 /* Maximum number of watchdog devices */ | ||
30 | |||
29 | /* | 31 | /* |
30 | * Functions/procedures to be called by the core | 32 | * Functions/procedures to be called by the core |
31 | */ | 33 | */ |
32 | extern int watchdog_dev_register(struct watchdog_device *); | 34 | extern int watchdog_dev_register(struct watchdog_device *); |
33 | extern int watchdog_dev_unregister(struct watchdog_device *); | 35 | extern int watchdog_dev_unregister(struct watchdog_device *); |
36 | extern int __init watchdog_dev_init(void); | ||
37 | extern void __exit watchdog_dev_exit(void); | ||
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index beaf9cb5541a..3b22582bcb04 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c | |||
@@ -44,10 +44,10 @@ | |||
44 | 44 | ||
45 | #include "watchdog_core.h" | 45 | #include "watchdog_core.h" |
46 | 46 | ||
47 | /* make sure we only register one /dev/watchdog device */ | 47 | /* the dev_t structure to store the dynamically allocated watchdog devices */ |
48 | static unsigned long watchdog_dev_busy; | 48 | static dev_t watchdog_devt; |
49 | /* the watchdog device behind /dev/watchdog */ | 49 | /* the watchdog device behind /dev/watchdog */ |
50 | static struct watchdog_device *wdd; | 50 | static struct watchdog_device *old_wdd; |
51 | 51 | ||
52 | /* | 52 | /* |
53 | * watchdog_ping: ping the watchdog. | 53 | * watchdog_ping: ping the watchdog. |
@@ -138,6 +138,7 @@ static int watchdog_stop(struct watchdog_device *wddev) | |||
138 | static ssize_t watchdog_write(struct file *file, const char __user *data, | 138 | static ssize_t watchdog_write(struct file *file, const char __user *data, |
139 | size_t len, loff_t *ppos) | 139 | size_t len, loff_t *ppos) |
140 | { | 140 | { |
141 | struct watchdog_device *wdd = file->private_data; | ||
141 | size_t i; | 142 | size_t i; |
142 | char c; | 143 | char c; |
143 | 144 | ||
@@ -177,6 +178,7 @@ static ssize_t watchdog_write(struct file *file, const char __user *data, | |||
177 | static long watchdog_ioctl(struct file *file, unsigned int cmd, | 178 | static long watchdog_ioctl(struct file *file, unsigned int cmd, |
178 | unsigned long arg) | 179 | unsigned long arg) |
179 | { | 180 | { |
181 | struct watchdog_device *wdd = file->private_data; | ||
180 | void __user *argp = (void __user *)arg; | 182 | void __user *argp = (void __user *)arg; |
181 | int __user *p = argp; | 183 | int __user *p = argp; |
182 | unsigned int val; | 184 | unsigned int val; |
@@ -249,11 +251,11 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, | |||
249 | } | 251 | } |
250 | 252 | ||
251 | /* | 253 | /* |
252 | * watchdog_open: open the /dev/watchdog device. | 254 | * watchdog_open: open the /dev/watchdog* devices. |
253 | * @inode: inode of device | 255 | * @inode: inode of device |
254 | * @file: file handle to device | 256 | * @file: file handle to device |
255 | * | 257 | * |
256 | * When the /dev/watchdog device gets opened, we start the watchdog. | 258 | * When the /dev/watchdog* device gets opened, we start the watchdog. |
257 | * Watch out: the /dev/watchdog device is single open, so we make sure | 259 | * Watch out: the /dev/watchdog device is single open, so we make sure |
258 | * it can only be opened once. | 260 | * it can only be opened once. |
259 | */ | 261 | */ |
@@ -261,6 +263,13 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, | |||
261 | static int watchdog_open(struct inode *inode, struct file *file) | 263 | static int watchdog_open(struct inode *inode, struct file *file) |
262 | { | 264 | { |
263 | int err = -EBUSY; | 265 | int err = -EBUSY; |
266 | struct watchdog_device *wdd; | ||
267 | |||
268 | /* Get the corresponding watchdog device */ | ||
269 | if (imajor(inode) == MISC_MAJOR) | ||
270 | wdd = old_wdd; | ||
271 | else | ||
272 | wdd = container_of(inode->i_cdev, struct watchdog_device, cdev); | ||
264 | 273 | ||
265 | /* the watchdog is single open! */ | 274 | /* the watchdog is single open! */ |
266 | if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) | 275 | if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) |
@@ -277,6 +286,8 @@ static int watchdog_open(struct inode *inode, struct file *file) | |||
277 | if (err < 0) | 286 | if (err < 0) |
278 | goto out_mod; | 287 | goto out_mod; |
279 | 288 | ||
289 | file->private_data = wdd; | ||
290 | |||
280 | /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ | 291 | /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ |
281 | return nonseekable_open(inode, file); | 292 | return nonseekable_open(inode, file); |
282 | 293 | ||
@@ -288,9 +299,9 @@ out: | |||
288 | } | 299 | } |
289 | 300 | ||
290 | /* | 301 | /* |
291 | * watchdog_release: release the /dev/watchdog device. | 302 | * watchdog_release: release the watchdog device. |
292 | * @inode: inode of device | 303 | * @inode: inode of device |
293 | * @file: file handle to device | 304 | * @file: file handle to device |
294 | * | 305 | * |
295 | * This is the code for when /dev/watchdog gets closed. We will only | 306 | * This is the code for when /dev/watchdog gets closed. We will only |
296 | * stop the watchdog when we have received the magic char (and nowayout | 307 | * stop the watchdog when we have received the magic char (and nowayout |
@@ -299,6 +310,7 @@ out: | |||
299 | 310 | ||
300 | static int watchdog_release(struct inode *inode, struct file *file) | 311 | static int watchdog_release(struct inode *inode, struct file *file) |
301 | { | 312 | { |
313 | struct watchdog_device *wdd = file->private_data; | ||
302 | int err = -EBUSY; | 314 | int err = -EBUSY; |
303 | 315 | ||
304 | /* | 316 | /* |
@@ -340,62 +352,87 @@ static struct miscdevice watchdog_miscdev = { | |||
340 | }; | 352 | }; |
341 | 353 | ||
342 | /* | 354 | /* |
343 | * watchdog_dev_register: | 355 | * watchdog_dev_register: register a watchdog device |
344 | * @watchdog: watchdog device | 356 | * @watchdog: watchdog device |
345 | * | 357 | * |
346 | * Register a watchdog device as /dev/watchdog. /dev/watchdog | 358 | * Register a watchdog device including handling the legacy |
347 | * is actually a miscdevice and thus we set it up like that. | 359 | * /dev/watchdog node. /dev/watchdog is actually a miscdevice and |
360 | * thus we set it up like that. | ||
348 | */ | 361 | */ |
349 | 362 | ||
350 | int watchdog_dev_register(struct watchdog_device *watchdog) | 363 | int watchdog_dev_register(struct watchdog_device *watchdog) |
351 | { | 364 | { |
352 | int err; | 365 | int err, devno; |
353 | 366 | ||
354 | /* Only one device can register for /dev/watchdog */ | 367 | if (watchdog->id == 0) { |
355 | if (test_and_set_bit(0, &watchdog_dev_busy)) { | 368 | err = misc_register(&watchdog_miscdev); |
356 | pr_err("only one watchdog can use /dev/watchdog\n"); | 369 | if (err != 0) { |
357 | return -EBUSY; | 370 | pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n", |
371 | watchdog->info->identity, WATCHDOG_MINOR, err); | ||
372 | if (err == -EBUSY) | ||
373 | pr_err("%s: a legacy watchdog module is probably present.\n", | ||
374 | watchdog->info->identity); | ||
375 | return err; | ||
376 | } | ||
377 | old_wdd = watchdog; | ||
358 | } | 378 | } |
359 | 379 | ||
360 | wdd = watchdog; | 380 | /* Fill in the data structures */ |
361 | 381 | devno = MKDEV(MAJOR(watchdog_devt), watchdog->id); | |
362 | err = misc_register(&watchdog_miscdev); | 382 | cdev_init(&watchdog->cdev, &watchdog_fops); |
363 | if (err != 0) { | 383 | watchdog->cdev.owner = watchdog->ops->owner; |
364 | pr_err("%s: cannot register miscdev on minor=%d (err=%d)\n", | 384 | |
365 | watchdog->info->identity, WATCHDOG_MINOR, err); | 385 | /* Add the device */ |
366 | goto out; | 386 | err = cdev_add(&watchdog->cdev, devno, 1); |
387 | if (err) { | ||
388 | pr_err("watchdog%d unable to add device %d:%d\n", | ||
389 | watchdog->id, MAJOR(watchdog_devt), watchdog->id); | ||
390 | if (watchdog->id == 0) { | ||
391 | misc_deregister(&watchdog_miscdev); | ||
392 | old_wdd = NULL; | ||
393 | } | ||
367 | } | 394 | } |
368 | |||
369 | return 0; | ||
370 | |||
371 | out: | ||
372 | wdd = NULL; | ||
373 | clear_bit(0, &watchdog_dev_busy); | ||
374 | return err; | 395 | return err; |
375 | } | 396 | } |
376 | 397 | ||
377 | /* | 398 | /* |
378 | * watchdog_dev_unregister: | 399 | * watchdog_dev_unregister: unregister a watchdog device |
379 | * @watchdog: watchdog device | 400 | * @watchdog: watchdog device |
380 | * | 401 | * |
381 | * Deregister the /dev/watchdog device. | 402 | * Unregister the watchdog and if needed the legacy /dev/watchdog device. |
382 | */ | 403 | */ |
383 | 404 | ||
384 | int watchdog_dev_unregister(struct watchdog_device *watchdog) | 405 | int watchdog_dev_unregister(struct watchdog_device *watchdog) |
385 | { | 406 | { |
386 | /* Check that a watchdog device was registered in the past */ | 407 | cdev_del(&watchdog->cdev); |
387 | if (!test_bit(0, &watchdog_dev_busy) || !wdd) | 408 | if (watchdog->id == 0) { |
388 | return -ENODEV; | 409 | misc_deregister(&watchdog_miscdev); |
389 | 410 | old_wdd = NULL; | |
390 | /* We can only unregister the watchdog device that was registered */ | ||
391 | if (watchdog != wdd) { | ||
392 | pr_err("%s: watchdog was not registered as /dev/watchdog\n", | ||
393 | watchdog->info->identity); | ||
394 | return -ENODEV; | ||
395 | } | 411 | } |
396 | |||
397 | misc_deregister(&watchdog_miscdev); | ||
398 | wdd = NULL; | ||
399 | clear_bit(0, &watchdog_dev_busy); | ||
400 | return 0; | 412 | return 0; |
401 | } | 413 | } |
414 | |||
415 | /* | ||
416 | * watchdog_dev_init: init dev part of watchdog core | ||
417 | * | ||
418 | * Allocate a range of chardev nodes to use for watchdog devices | ||
419 | */ | ||
420 | |||
421 | int __init watchdog_dev_init(void) | ||
422 | { | ||
423 | int err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); | ||
424 | if (err < 0) | ||
425 | pr_err("watchdog: unable to allocate char dev region\n"); | ||
426 | return err; | ||
427 | } | ||
428 | |||
429 | /* | ||
430 | * watchdog_dev_exit: exit dev part of watchdog core | ||
431 | * | ||
432 | * Release the range of chardev nodes used for watchdog devices | ||
433 | */ | ||
434 | |||
435 | void __exit watchdog_dev_exit(void) | ||
436 | { | ||
437 | unregister_chrdev_region(watchdog_devt, MAX_DOGS); | ||
438 | } | ||
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 1984ea610577..508d56399e6d 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h | |||
@@ -54,6 +54,8 @@ struct watchdog_info { | |||
54 | #ifdef __KERNEL__ | 54 | #ifdef __KERNEL__ |
55 | 55 | ||
56 | #include <linux/bitops.h> | 56 | #include <linux/bitops.h> |
57 | #include <linux/device.h> | ||
58 | #include <linux/cdev.h> | ||
57 | 59 | ||
58 | struct watchdog_ops; | 60 | struct watchdog_ops; |
59 | struct watchdog_device; | 61 | struct watchdog_device; |
@@ -89,6 +91,8 @@ struct watchdog_ops { | |||
89 | 91 | ||
90 | /** struct watchdog_device - The structure that defines a watchdog device | 92 | /** struct watchdog_device - The structure that defines a watchdog device |
91 | * | 93 | * |
94 | * @id: The watchdog's ID. (Allocated by watchdog_register_device) | ||
95 | * @cdev: The watchdog's Character device. | ||
92 | * @info: Pointer to a watchdog_info structure. | 96 | * @info: Pointer to a watchdog_info structure. |
93 | * @ops: Pointer to the list of watchdog operations. | 97 | * @ops: Pointer to the list of watchdog operations. |
94 | * @bootstatus: Status of the watchdog device at boot. | 98 | * @bootstatus: Status of the watchdog device at boot. |
@@ -105,6 +109,8 @@ struct watchdog_ops { | |||
105 | * via the watchdog_set_drvdata and watchdog_get_drvdata helpers. | 109 | * via the watchdog_set_drvdata and watchdog_get_drvdata helpers. |
106 | */ | 110 | */ |
107 | struct watchdog_device { | 111 | struct watchdog_device { |
112 | int id; | ||
113 | struct cdev cdev; | ||
108 | const struct watchdog_info *info; | 114 | const struct watchdog_info *info; |
109 | const struct watchdog_ops *ops; | 115 | const struct watchdog_ops *ops; |
110 | unsigned int bootstatus; | 116 | unsigned int bootstatus; |