diff options
| -rw-r--r-- | Documentation/watchdog/watchdog-kernel-api.txt | 28 | ||||
| -rw-r--r-- | drivers/watchdog/watchdog_dev.c | 55 | ||||
| -rw-r--r-- | include/linux/watchdog.h | 5 |
3 files changed, 86 insertions, 2 deletions
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 08d34e11bc54..086638f6c82d 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: 21-May-2012 | 3 | Last reviewed: 22-May-2012 |
| 4 | 4 | ||
| 5 | Wim Van Sebroeck <wim@iguana.be> | 5 | Wim Van Sebroeck <wim@iguana.be> |
| 6 | 6 | ||
| @@ -93,6 +93,8 @@ struct watchdog_ops { | |||
| 93 | unsigned int (*status)(struct watchdog_device *); | 93 | unsigned int (*status)(struct watchdog_device *); |
| 94 | int (*set_timeout)(struct watchdog_device *, unsigned int); | 94 | int (*set_timeout)(struct watchdog_device *, unsigned int); |
| 95 | unsigned int (*get_timeleft)(struct watchdog_device *); | 95 | unsigned int (*get_timeleft)(struct watchdog_device *); |
| 96 | void (*ref)(struct watchdog_device *); | ||
| 97 | void (*unref)(struct watchdog_device *); | ||
| 96 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); | 98 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); |
| 97 | }; | 99 | }; |
| 98 | 100 | ||
| @@ -100,6 +102,21 @@ It is important that you first define the module owner of the watchdog timer | |||
| 100 | driver's operations. This module owner will be used to lock the module when | 102 | driver's operations. This module owner will be used to lock the module when |
| 101 | the watchdog is active. (This to avoid a system crash when you unload the | 103 | the watchdog is active. (This to avoid a system crash when you unload the |
| 102 | module and /dev/watchdog is still open). | 104 | module and /dev/watchdog is still open). |
| 105 | |||
| 106 | If the watchdog_device struct is dynamically allocated, just locking the module | ||
| 107 | is not enough and a driver also needs to define the ref and unref operations to | ||
| 108 | ensure the structure holding the watchdog_device does not go away. | ||
| 109 | |||
| 110 | The simplest (and usually sufficient) implementation of this is to: | ||
| 111 | 1) Add a kref struct to the same structure which is holding the watchdog_device | ||
| 112 | 2) Define a release callback for the kref which frees the struct holding both | ||
| 113 | 3) Call kref_init on this kref *before* calling watchdog_register_device() | ||
| 114 | 4) Define a ref operation calling kref_get on this kref | ||
| 115 | 5) Define a unref operation calling kref_put on this kref | ||
| 116 | 6) When it is time to cleanup: | ||
| 117 | * Do not kfree() the struct holding both, the last kref_put will do this! | ||
| 118 | * *After* calling watchdog_unregister_device() call kref_put on the kref | ||
| 119 | |||
| 103 | Some operations are mandatory and some are optional. The mandatory operations | 120 | Some operations are mandatory and some are optional. The mandatory operations |
| 104 | are: | 121 | are: |
| 105 | * start: this is a pointer to the routine that starts the watchdog timer | 122 | * start: this is a pointer to the routine that starts the watchdog timer |
| @@ -140,6 +157,10 @@ they are supported. These optional routines/operations are: | |||
| 140 | (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the | 157 | (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the |
| 141 | watchdog's info structure). | 158 | watchdog's info structure). |
| 142 | * get_timeleft: this routines returns the time that's left before a reset. | 159 | * get_timeleft: this routines returns the time that's left before a reset. |
| 160 | * ref: the operation that calls kref_get on the kref of a dynamically | ||
| 161 | allocated watchdog_device struct. | ||
| 162 | * unref: the operation that calls kref_put on the kref of a dynamically | ||
| 163 | allocated watchdog_device struct. | ||
| 143 | * ioctl: if this routine is present then it will be called first before we do | 164 | * ioctl: if this routine is present then it will be called first before we do |
| 144 | our own internal ioctl call handling. This routine should return -ENOIOCTLCMD | 165 | our own internal ioctl call handling. This routine should return -ENOIOCTLCMD |
| 145 | if a command is not supported. The parameters that are passed to the ioctl | 166 | if a command is not supported. The parameters that are passed to the ioctl |
| @@ -159,6 +180,11 @@ bit-operations. The status bits that are defined are: | |||
| 159 | (This bit should only be used by the WatchDog Timer Driver Core). | 180 | (This bit should only be used by the WatchDog Timer Driver Core). |
| 160 | * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. | 181 | * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. |
| 161 | If this bit is set then the watchdog timer will not be able to stop. | 182 | If this bit is set then the watchdog timer will not be able to stop. |
| 183 | * WDOG_UNREGISTERED: this bit gets set by the WatchDog Timer Driver Core | ||
| 184 | after calling watchdog_unregister_device, and then checked before calling | ||
| 185 | any watchdog_ops, so that you can be sure that no operations (other then | ||
| 186 | unref) will get called after unregister, even if userspace still holds a | ||
| 187 | reference to /dev/watchdog | ||
| 162 | 188 | ||
| 163 | To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog | 189 | To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog |
| 164 | timer device) you can either: | 190 | timer device) you can either: |
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 4d295d229a07..672d169bf1da 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c | |||
| @@ -65,6 +65,11 @@ static int watchdog_ping(struct watchdog_device *wddev) | |||
| 65 | 65 | ||
| 66 | mutex_lock(&wddev->lock); | 66 | mutex_lock(&wddev->lock); |
| 67 | 67 | ||
| 68 | if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { | ||
| 69 | err = -ENODEV; | ||
| 70 | goto out_ping; | ||
| 71 | } | ||
| 72 | |||
| 68 | if (!watchdog_active(wddev)) | 73 | if (!watchdog_active(wddev)) |
| 69 | goto out_ping; | 74 | goto out_ping; |
| 70 | 75 | ||
| @@ -93,6 +98,11 @@ static int watchdog_start(struct watchdog_device *wddev) | |||
| 93 | 98 | ||
| 94 | mutex_lock(&wddev->lock); | 99 | mutex_lock(&wddev->lock); |
| 95 | 100 | ||
| 101 | if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { | ||
| 102 | err = -ENODEV; | ||
| 103 | goto out_start; | ||
| 104 | } | ||
| 105 | |||
| 96 | if (watchdog_active(wddev)) | 106 | if (watchdog_active(wddev)) |
| 97 | goto out_start; | 107 | goto out_start; |
| 98 | 108 | ||
| @@ -121,6 +131,11 @@ static int watchdog_stop(struct watchdog_device *wddev) | |||
| 121 | 131 | ||
| 122 | mutex_lock(&wddev->lock); | 132 | mutex_lock(&wddev->lock); |
| 123 | 133 | ||
| 134 | if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { | ||
| 135 | err = -ENODEV; | ||
| 136 | goto out_stop; | ||
| 137 | } | ||
| 138 | |||
| 124 | if (!watchdog_active(wddev)) | 139 | if (!watchdog_active(wddev)) |
| 125 | goto out_stop; | 140 | goto out_stop; |
| 126 | 141 | ||
| @@ -158,8 +173,14 @@ static int watchdog_get_status(struct watchdog_device *wddev, | |||
| 158 | 173 | ||
| 159 | mutex_lock(&wddev->lock); | 174 | mutex_lock(&wddev->lock); |
| 160 | 175 | ||
| 176 | if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { | ||
| 177 | err = -ENODEV; | ||
| 178 | goto out_status; | ||
| 179 | } | ||
| 180 | |||
| 161 | *status = wddev->ops->status(wddev); | 181 | *status = wddev->ops->status(wddev); |
| 162 | 182 | ||
| 183 | out_status: | ||
| 163 | mutex_unlock(&wddev->lock); | 184 | mutex_unlock(&wddev->lock); |
| 164 | return err; | 185 | return err; |
| 165 | } | 186 | } |
| @@ -185,8 +206,14 @@ static int watchdog_set_timeout(struct watchdog_device *wddev, | |||
| 185 | 206 | ||
| 186 | mutex_lock(&wddev->lock); | 207 | mutex_lock(&wddev->lock); |
| 187 | 208 | ||
| 209 | if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { | ||
| 210 | err = -ENODEV; | ||
| 211 | goto out_timeout; | ||
| 212 | } | ||
| 213 | |||
| 188 | err = wddev->ops->set_timeout(wddev, timeout); | 214 | err = wddev->ops->set_timeout(wddev, timeout); |
| 189 | 215 | ||
| 216 | out_timeout: | ||
| 190 | mutex_unlock(&wddev->lock); | 217 | mutex_unlock(&wddev->lock); |
| 191 | return err; | 218 | return err; |
| 192 | } | 219 | } |
| @@ -210,8 +237,14 @@ static int watchdog_get_timeleft(struct watchdog_device *wddev, | |||
| 210 | 237 | ||
| 211 | mutex_lock(&wddev->lock); | 238 | mutex_lock(&wddev->lock); |
| 212 | 239 | ||
| 240 | if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { | ||
| 241 | err = -ENODEV; | ||
| 242 | goto out_timeleft; | ||
| 243 | } | ||
| 244 | |||
| 213 | *timeleft = wddev->ops->get_timeleft(wddev); | 245 | *timeleft = wddev->ops->get_timeleft(wddev); |
| 214 | 246 | ||
| 247 | out_timeleft: | ||
| 215 | mutex_unlock(&wddev->lock); | 248 | mutex_unlock(&wddev->lock); |
| 216 | return err; | 249 | return err; |
| 217 | } | 250 | } |
| @@ -233,8 +266,14 @@ static int watchdog_ioctl_op(struct watchdog_device *wddev, unsigned int cmd, | |||
| 233 | 266 | ||
| 234 | mutex_lock(&wddev->lock); | 267 | mutex_lock(&wddev->lock); |
| 235 | 268 | ||
| 269 | if (test_bit(WDOG_UNREGISTERED, &wddev->status)) { | ||
| 270 | err = -ENODEV; | ||
| 271 | goto out_ioctl; | ||
| 272 | } | ||
| 273 | |||
| 236 | err = wddev->ops->ioctl(wddev, cmd, arg); | 274 | err = wddev->ops->ioctl(wddev, cmd, arg); |
| 237 | 275 | ||
| 276 | out_ioctl: | ||
| 238 | mutex_unlock(&wddev->lock); | 277 | mutex_unlock(&wddev->lock); |
| 239 | return err; | 278 | return err; |
| 240 | } | 279 | } |
| @@ -398,6 +437,9 @@ static int watchdog_open(struct inode *inode, struct file *file) | |||
| 398 | 437 | ||
| 399 | file->private_data = wdd; | 438 | file->private_data = wdd; |
| 400 | 439 | ||
| 440 | if (wdd->ops->ref) | ||
| 441 | wdd->ops->ref(wdd); | ||
| 442 | |||
| 401 | /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ | 443 | /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ |
| 402 | return nonseekable_open(inode, file); | 444 | return nonseekable_open(inode, file); |
| 403 | 445 | ||
| @@ -434,7 +476,10 @@ static int watchdog_release(struct inode *inode, struct file *file) | |||
| 434 | 476 | ||
| 435 | /* If the watchdog was not stopped, send a keepalive ping */ | 477 | /* If the watchdog was not stopped, send a keepalive ping */ |
| 436 | if (err < 0) { | 478 | if (err < 0) { |
| 437 | dev_crit(wdd->dev, "watchdog did not stop!\n"); | 479 | mutex_lock(&wdd->lock); |
| 480 | if (!test_bit(WDOG_UNREGISTERED, &wdd->status)) | ||
| 481 | dev_crit(wdd->dev, "watchdog did not stop!\n"); | ||
| 482 | mutex_unlock(&wdd->lock); | ||
| 438 | watchdog_ping(wdd); | 483 | watchdog_ping(wdd); |
| 439 | } | 484 | } |
| 440 | 485 | ||
| @@ -444,6 +489,10 @@ static int watchdog_release(struct inode *inode, struct file *file) | |||
| 444 | /* make sure that /dev/watchdog can be re-opened */ | 489 | /* make sure that /dev/watchdog can be re-opened */ |
| 445 | clear_bit(WDOG_DEV_OPEN, &wdd->status); | 490 | clear_bit(WDOG_DEV_OPEN, &wdd->status); |
| 446 | 491 | ||
| 492 | /* Note wdd may be gone after this, do not use after this! */ | ||
| 493 | if (wdd->ops->unref) | ||
| 494 | wdd->ops->unref(wdd); | ||
| 495 | |||
| 447 | return 0; | 496 | return 0; |
| 448 | } | 497 | } |
| 449 | 498 | ||
| @@ -515,6 +564,10 @@ int watchdog_dev_register(struct watchdog_device *watchdog) | |||
| 515 | 564 | ||
| 516 | int watchdog_dev_unregister(struct watchdog_device *watchdog) | 565 | int watchdog_dev_unregister(struct watchdog_device *watchdog) |
| 517 | { | 566 | { |
| 567 | mutex_lock(&watchdog->lock); | ||
| 568 | set_bit(WDOG_UNREGISTERED, &watchdog->status); | ||
| 569 | mutex_unlock(&watchdog->lock); | ||
| 570 | |||
| 518 | cdev_del(&watchdog->cdev); | 571 | cdev_del(&watchdog->cdev); |
| 519 | if (watchdog->id == 0) { | 572 | if (watchdog->id == 0) { |
| 520 | misc_deregister(&watchdog_miscdev); | 573 | misc_deregister(&watchdog_miscdev); |
diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index da1dc1b52744..da70f0facd2b 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h | |||
| @@ -71,6 +71,8 @@ struct watchdog_device; | |||
| 71 | * @status: The routine that shows the status of the watchdog device. | 71 | * @status: The routine that shows the status of the watchdog device. |
| 72 | * @set_timeout:The routine for setting the watchdog devices timeout value. | 72 | * @set_timeout:The routine for setting the watchdog devices timeout value. |
| 73 | * @get_timeleft:The routine that get's the time that's left before a reset. | 73 | * @get_timeleft:The routine that get's the time that's left before a reset. |
| 74 | * @ref: The ref operation for dyn. allocated watchdog_device structs | ||
| 75 | * @unref: The unref operation for dyn. allocated watchdog_device structs | ||
| 74 | * @ioctl: The routines that handles extra ioctl calls. | 76 | * @ioctl: The routines that handles extra ioctl calls. |
| 75 | * | 77 | * |
| 76 | * The watchdog_ops structure contains a list of low-level operations | 78 | * The watchdog_ops structure contains a list of low-level operations |
| @@ -88,6 +90,8 @@ struct watchdog_ops { | |||
| 88 | unsigned int (*status)(struct watchdog_device *); | 90 | unsigned int (*status)(struct watchdog_device *); |
| 89 | int (*set_timeout)(struct watchdog_device *, unsigned int); | 91 | int (*set_timeout)(struct watchdog_device *, unsigned int); |
| 90 | unsigned int (*get_timeleft)(struct watchdog_device *); | 92 | unsigned int (*get_timeleft)(struct watchdog_device *); |
| 93 | void (*ref)(struct watchdog_device *); | ||
| 94 | void (*unref)(struct watchdog_device *); | ||
| 91 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); | 95 | long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); |
| 92 | }; | 96 | }; |
| 93 | 97 | ||
| @@ -135,6 +139,7 @@ struct watchdog_device { | |||
| 135 | #define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ | 139 | #define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ |
| 136 | #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ | 140 | #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ |
| 137 | #define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ | 141 | #define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ |
| 142 | #define WDOG_UNREGISTERED 4 /* Has the device been unregistered */ | ||
| 138 | }; | 143 | }; |
| 139 | 144 | ||
| 140 | #ifdef CONFIG_WATCHDOG_NOWAYOUT | 145 | #ifdef CONFIG_WATCHDOG_NOWAYOUT |
