Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Alan Cox and committed by
Wim Van Sebroeck
45f5fed3 fb5f6658

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