Commit b4ffb190 authored by Guenter Roeck's avatar Guenter Roeck Committed by Wim Van Sebroeck

watchdog: Separate and maintain variables based on variable lifetime

All variables required by the watchdog core to manage a watchdog are
currently stored in struct watchdog_device. The lifetime of those
variables is determined by the watchdog driver. However, the lifetime
of variables used by the watchdog core differs from the lifetime of
struct watchdog_device. To remedy this situation, watchdog drivers
can implement ref and unref callbacks, to be used by the watchdog
core to lock struct watchdog_device in memory.

While this solves the immediate problem, it depends on watchdog drivers
to actually implement the ref/unref callbacks. This is error prone,
often not implemented in the first place, or not implemented correctly.

To solve the problem without requiring driver support, split the variables
in struct watchdog_device into two data structures - one for variables
associated with the watchdog driver, one for variables associated with
the watchdog core. With this approach, the watchdog core can keep track
of its variable lifetime and no longer depends on ref/unref callbacks
in the driver. As a side effect, some of the variables originally in
struct watchdog_driver are now private to the watchdog core and no longer
visible in watchdog drivers.

As a side effect of the changes made, an ioctl will now always fail
with -ENODEV after a watchdog device was unregistered with the character
device still open. Previously, it would only fail with -ENODEV in some
situations. Also, ioctl operations are now atomic from driver perspective.
With this change, it is now guaranteed that the driver will not unregister
a watchdog between a timeout change and the subsequent ping.

The 'ref' and 'unref' callbacks in struct watchdog_driver are no longer
used and marked as deprecated.
Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
Signed-off-by: default avatarWim Van Sebroeck <wim@iguana.be>
parent e7d162fa
...@@ -44,7 +44,6 @@ The watchdog device structure looks like this: ...@@ -44,7 +44,6 @@ The watchdog device structure looks like this:
struct watchdog_device { struct watchdog_device {
int id; int id;
struct cdev cdev;
struct device *dev; struct device *dev;
struct device *parent; struct device *parent;
const struct watchdog_info *info; const struct watchdog_info *info;
...@@ -56,7 +55,7 @@ struct watchdog_device { ...@@ -56,7 +55,7 @@ struct watchdog_device {
struct notifier_block reboot_nb; struct notifier_block reboot_nb;
struct notifier_block restart_nb; struct notifier_block restart_nb;
void *driver_data; void *driver_data;
struct mutex lock; struct watchdog_core_data *wd_data;
unsigned long status; unsigned long status;
struct list_head deferred; struct list_head deferred;
}; };
...@@ -66,8 +65,6 @@ It contains following fields: ...@@ -66,8 +65,6 @@ It contains following fields:
/dev/watchdog0 cdev (dynamic major, minor 0) as well as the old /dev/watchdog0 cdev (dynamic major, minor 0) as well as the old
/dev/watchdog miscdev. The id is set automatically when calling /dev/watchdog miscdev. The id is set automatically when calling
watchdog_register_device. watchdog_register_device.
* cdev: cdev for the dynamic /dev/watchdog<id> device nodes. This
field is also populated by watchdog_register_device.
* dev: device under the watchdog class (created by watchdog_register_device). * dev: device under the watchdog class (created by watchdog_register_device).
* parent: set this to the parent device (or NULL) before calling * parent: set this to the parent device (or NULL) before calling
watchdog_register_device. watchdog_register_device.
...@@ -89,11 +86,10 @@ It contains following fields: ...@@ -89,11 +86,10 @@ It contains following fields:
* driver_data: a pointer to the drivers private data of a watchdog device. * driver_data: a pointer to the drivers private data of a watchdog device.
This data should only be accessed via the watchdog_set_drvdata and This data should only be accessed via the watchdog_set_drvdata and
watchdog_get_drvdata routines. watchdog_get_drvdata routines.
* lock: Mutex for WatchDog Timer Driver Core internal use only. * wd_data: a pointer to watchdog core internal data.
* status: this field contains a number of status bits that give extra * status: this field contains a number of status bits that give extra
information about the status of the device (Like: is the watchdog timer information about the status of the device (Like: is the watchdog timer
running/active, is the nowayout bit set, is the device opened via running/active, or is the nowayout bit set).
the /dev/watchdog interface or not, ...).
* deferred: entry in wtd_deferred_reg_list which is used to * deferred: entry in wtd_deferred_reg_list which is used to
register early initialized watchdogs. register early initialized watchdogs.
...@@ -110,8 +106,8 @@ struct watchdog_ops { ...@@ -110,8 +106,8 @@ struct watchdog_ops {
int (*set_timeout)(struct watchdog_device *, unsigned int); int (*set_timeout)(struct watchdog_device *, unsigned int);
unsigned int (*get_timeleft)(struct watchdog_device *); unsigned int (*get_timeleft)(struct watchdog_device *);
int (*restart)(struct watchdog_device *); int (*restart)(struct watchdog_device *);
void (*ref)(struct watchdog_device *); void (*ref)(struct watchdog_device *) __deprecated;
void (*unref)(struct watchdog_device *); void (*unref)(struct watchdog_device *) __deprecated;
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
}; };
...@@ -120,20 +116,6 @@ driver's operations. This module owner will be used to lock the module when ...@@ -120,20 +116,6 @@ driver's operations. This module owner will be used to lock the module when
the watchdog is active. (This to avoid a system crash when you unload the the watchdog is active. (This to avoid a system crash when you unload the
module and /dev/watchdog is still open). module and /dev/watchdog is still open).
If the watchdog_device struct is dynamically allocated, just locking the module
is not enough and a driver also needs to define the ref and unref operations to
ensure the structure holding the watchdog_device does not go away.
The simplest (and usually sufficient) implementation of this is to:
1) Add a kref struct to the same structure which is holding the watchdog_device
2) Define a release callback for the kref which frees the struct holding both
3) Call kref_init on this kref *before* calling watchdog_register_device()
4) Define a ref operation calling kref_get on this kref
5) Define a unref operation calling kref_put on this kref
6) When it is time to cleanup:
* Do not kfree() the struct holding both, the last kref_put will do this!
* *After* calling watchdog_unregister_device() call kref_put on the kref
Some operations are mandatory and some are optional. The mandatory operations Some operations are mandatory and some are optional. The mandatory operations
are: are:
* start: this is a pointer to the routine that starts the watchdog timer * start: this is a pointer to the routine that starts the watchdog timer
...@@ -176,34 +158,21 @@ they are supported. These optional routines/operations are: ...@@ -176,34 +158,21 @@ they are supported. These optional routines/operations are:
* get_timeleft: this routines returns the time that's left before a reset. * get_timeleft: this routines returns the time that's left before a reset.
* restart: this routine restarts the machine. It returns 0 on success or a * restart: this routine restarts the machine. It returns 0 on success or a
negative errno code for failure. negative errno code for failure.
* ref: the operation that calls kref_get on the kref of a dynamically
allocated watchdog_device struct.
* unref: the operation that calls kref_put on the kref of a dynamically
allocated watchdog_device struct.
* ioctl: if this routine is present then it will be called first before we do * ioctl: if this routine is present then it will be called first before we do
our own internal ioctl call handling. This routine should return -ENOIOCTLCMD our own internal ioctl call handling. This routine should return -ENOIOCTLCMD
if a command is not supported. The parameters that are passed to the ioctl if a command is not supported. The parameters that are passed to the ioctl
call are: watchdog_device, cmd and arg. call are: watchdog_device, cmd and arg.
The 'ref' and 'unref' operations are no longer used and deprecated.
The status bits should (preferably) be set with the set_bit and clear_bit alike The status bits should (preferably) be set with the set_bit and clear_bit alike
bit-operations. The status bits that are defined are: bit-operations. The status bits that are defined are:
* WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device * WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device
is active or not. When the watchdog is active after booting, then you should is active or not. When the watchdog is active after booting, then you should
set this status bit (Note: when you register the watchdog timer device with set this status bit (Note: when you register the watchdog timer device with
this bit set, then opening /dev/watchdog will skip the start operation) this bit set, then opening /dev/watchdog will skip the start operation)
* WDOG_DEV_OPEN: this status bit shows whether or not the watchdog device
was opened via /dev/watchdog.
(This bit should only be used by the WatchDog Timer Driver Core).
* WDOG_ALLOW_RELEASE: this bit stores whether or not the magic close character
has been sent (so that we can support the magic close feature).
(This bit should only be used by the WatchDog Timer Driver Core).
* WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. * WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog.
If this bit is set then the watchdog timer will not be able to stop. If this bit is set then the watchdog timer will not be able to stop.
* WDOG_UNREGISTERED: this bit gets set by the WatchDog Timer Driver Core
after calling watchdog_unregister_device, and then checked before calling
any watchdog_ops, so that you can be sure that no operations (other then
unref) will get called after unregister, even if userspace still holds a
reference to /dev/watchdog
To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog
timer device) you can either: timer device) you can either:
......
...@@ -210,8 +210,6 @@ static int __watchdog_register_device(struct watchdog_device *wdd) ...@@ -210,8 +210,6 @@ static int __watchdog_register_device(struct watchdog_device *wdd)
* corrupted in a later stage then we expect a kernel panic! * corrupted in a later stage then we expect a kernel panic!
*/ */
mutex_init(&wdd->lock);
/* Use alias for watchdog id if possible */ /* Use alias for watchdog id if possible */
if (wdd->parent) { if (wdd->parent) {
ret = of_alias_get_id(wdd->parent->of_node, "watchdog"); ret = of_alias_get_id(wdd->parent->of_node, "watchdog");
......
This diff is collapsed.
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
struct watchdog_ops; struct watchdog_ops;
struct watchdog_device; struct watchdog_device;
struct watchdog_core_data;
/** struct watchdog_ops - The watchdog-devices operations /** struct watchdog_ops - The watchdog-devices operations
* *
...@@ -28,8 +29,6 @@ struct watchdog_device; ...@@ -28,8 +29,6 @@ struct watchdog_device;
* @set_timeout:The routine for setting the watchdog devices timeout value (in seconds). * @set_timeout:The routine for setting the watchdog devices timeout value (in seconds).
* @get_timeleft:The routine that gets the time left before a reset (in seconds). * @get_timeleft:The routine that gets the time left before a reset (in seconds).
* @restart: The routine for restarting the machine. * @restart: The routine for restarting the machine.
* @ref: The ref operation for dyn. allocated watchdog_device structs
* @unref: The unref operation for dyn. allocated watchdog_device structs
* @ioctl: The routines that handles extra ioctl calls. * @ioctl: The routines that handles extra ioctl calls.
* *
* The watchdog_ops structure contains a list of low-level operations * The watchdog_ops structure contains a list of low-level operations
...@@ -48,15 +47,14 @@ struct watchdog_ops { ...@@ -48,15 +47,14 @@ struct watchdog_ops {
int (*set_timeout)(struct watchdog_device *, unsigned int); int (*set_timeout)(struct watchdog_device *, unsigned int);
unsigned int (*get_timeleft)(struct watchdog_device *); unsigned int (*get_timeleft)(struct watchdog_device *);
int (*restart)(struct watchdog_device *); int (*restart)(struct watchdog_device *);
void (*ref)(struct watchdog_device *); void (*ref)(struct watchdog_device *) __deprecated;
void (*unref)(struct watchdog_device *); void (*unref)(struct watchdog_device *) __deprecated;
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
}; };
/** struct watchdog_device - The structure that defines a watchdog device /** struct watchdog_device - The structure that defines a watchdog device
* *
* @id: The watchdog's ID. (Allocated by watchdog_register_device) * @id: The watchdog's ID. (Allocated by watchdog_register_device)
* @cdev: The watchdog's Character device.
* @dev: The device for our watchdog * @dev: The device for our watchdog
* @parent: The parent bus device * @parent: The parent bus device
* @info: Pointer to a watchdog_info structure. * @info: Pointer to a watchdog_info structure.
...@@ -67,8 +65,8 @@ struct watchdog_ops { ...@@ -67,8 +65,8 @@ struct watchdog_ops {
* @max_timeout:The watchdog devices maximum timeout value (in seconds). * @max_timeout:The watchdog devices maximum timeout value (in seconds).
* @reboot_nb: The notifier block to stop watchdog on reboot. * @reboot_nb: The notifier block to stop watchdog on reboot.
* @restart_nb: The notifier block to register a restart function. * @restart_nb: The notifier block to register a restart function.
* @driver-data:Pointer to the drivers private data. * @driver_data:Pointer to the drivers private data.
* @lock: Lock for watchdog core internal use only. * @wd_data: Pointer to watchdog core internal data.
* @status: Field that contains the devices internal status bits. * @status: Field that contains the devices internal status bits.
* @deferred: entry in wtd_deferred_reg_list which is used to * @deferred: entry in wtd_deferred_reg_list which is used to
* register early initialized watchdogs. * register early initialized watchdogs.
...@@ -84,7 +82,6 @@ struct watchdog_ops { ...@@ -84,7 +82,6 @@ struct watchdog_ops {
*/ */
struct watchdog_device { struct watchdog_device {
int id; int id;
struct cdev cdev;
struct device *dev; struct device *dev;
struct device *parent; struct device *parent;
const struct watchdog_info *info; const struct watchdog_info *info;
...@@ -96,15 +93,12 @@ struct watchdog_device { ...@@ -96,15 +93,12 @@ struct watchdog_device {
struct notifier_block reboot_nb; struct notifier_block reboot_nb;
struct notifier_block restart_nb; struct notifier_block restart_nb;
void *driver_data; void *driver_data;
struct mutex lock; struct watchdog_core_data *wd_data;
unsigned long status; unsigned long status;
/* Bit numbers for status flags */ /* Bit numbers for status flags */
#define WDOG_ACTIVE 0 /* Is the watchdog running/active */ #define WDOG_ACTIVE 0 /* Is the watchdog running/active */
#define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ #define WDOG_NO_WAY_OUT 1 /* Is 'nowayout' feature set ? */
#define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ #define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */
#define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */
#define WDOG_UNREGISTERED 4 /* Has the device been unregistered */
#define WDOG_STOP_ON_REBOOT 5 /* Should be stopped on reboot */
struct list_head deferred; struct list_head deferred;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment