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 |