diff options
author | Hans de Goede <hdegoede@redhat.com> | 2012-05-22 05:40:26 -0400 |
---|---|---|
committer | Wim Van Sebroeck <wim@iguana.be> | 2012-05-30 01:55:31 -0400 |
commit | e907df32725204d6d2cb79b872529911c8eadcdf (patch) | |
tree | 3e90c58ea0ee9c2d77c1c4b0854dc046f6efb6a0 | |
parent | f4e9c82f64b524314a390b13d3ba7d483f09258f (diff) |
watchdog: Add support for dynamically allocated watchdog_device structs
If a driver's watchdog_device struct is part of a dynamically allocated
struct (which it often will be), merely locking the module is not enough,
even with a drivers module locked, the driver can be unbound from the device,
examples:
1) The root user can unbind it through sysfd
2) The i2c bus master driver being unloaded for an i2c watchdog
I will gladly admit that these are corner cases, but we still need to handle
them correctly.
The fix for this consists of 2 parts:
1) Add ref / unref operations, so that the driver can refcount the struct
holding the watchdog_device struct and delay freeing it until any
open filehandles referring to it are closed
2) Most driver operations will do IO on the device and the driver should not
do any IO on the device after it has been unbound. Rather then letting each
driver deal with this internally, it is better to ensure at the watchdog
core level that no operations (other then unref) will get called after
the driver has called watchdog_unregister_device(). This actually is the
bulk of this patch.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
-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 |