aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2012-05-22 05:40:26 -0400
committerWim Van Sebroeck <wim@iguana.be>2012-05-30 01:55:31 -0400
commite907df32725204d6d2cb79b872529911c8eadcdf (patch)
tree3e90c58ea0ee9c2d77c1c4b0854dc046f6efb6a0 /drivers/watchdog
parentf4e9c82f64b524314a390b13d3ba7d483f09258f (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>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/watchdog_dev.c55
1 files changed, 54 insertions, 1 deletions
diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
index 4d295d229a0..672d169bf1d 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
183out_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
216out_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
247out_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
276out_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
516int watchdog_dev_unregister(struct watchdog_device *watchdog) 565int 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);