aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--Documentation/watchdog/watchdog-kernel-api.txt28
-rw-r--r--drivers/watchdog/watchdog_dev.c55
-rw-r--r--include/linux/watchdog.h5
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 @@
1The Linux WatchDog Timer Driver Core kernel API. 1The Linux WatchDog Timer Driver Core kernel API.
2=============================================== 2===============================================
3Last reviewed: 21-May-2012 3Last reviewed: 22-May-2012
4 4
5Wim Van Sebroeck <wim@iguana.be> 5Wim 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
100driver's operations. This module owner will be used to lock the module when 102driver's operations. This module owner will be used to lock the module when
101the watchdog is active. (This to avoid a system crash when you unload the 103the watchdog is active. (This to avoid a system crash when you unload the
102module and /dev/watchdog is still open). 104module and /dev/watchdog is still open).
105
106If the watchdog_device struct is dynamically allocated, just locking the module
107is not enough and a driver also needs to define the ref and unref operations to
108ensure the structure holding the watchdog_device does not go away.
109
110The simplest (and usually sufficient) implementation of this is to:
1111) Add a kref struct to the same structure which is holding the watchdog_device
1122) Define a release callback for the kref which frees the struct holding both
1133) Call kref_init on this kref *before* calling watchdog_register_device()
1144) Define a ref operation calling kref_get on this kref
1155) Define a unref operation calling kref_put on this kref
1166) 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
103Some operations are mandatory and some are optional. The mandatory operations 120Some operations are mandatory and some are optional. The mandatory operations
104are: 121are:
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
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);
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