diff options
Diffstat (limited to 'target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch')
-rw-r--r-- | target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch | 969 |
1 files changed, 0 insertions, 969 deletions
diff --git a/target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch b/target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch deleted file mode 100644 index 214dabfb9a..0000000000 --- a/target/linux/ipq806x/patches-4.4/009-6-watchdog-Separate-and-maintain-variables-based-on-variable-lifetime.patch +++ /dev/null @@ -1,969 +0,0 @@ -From b4ffb1909843b28f3b1b60197d517b123b7a9b66 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck <linux@roeck-us.net> -Date: Fri, 25 Dec 2015 16:01:42 -0800 -Subject: 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: Guenter Roeck <linux@roeck-us.net> -Signed-off-by: Wim Van Sebroeck <wim@iguana.be> ---- - Documentation/watchdog/watchdog-kernel-api.txt | 45 +-- - drivers/watchdog/watchdog_core.c | 2 - - drivers/watchdog/watchdog_dev.c | 383 +++++++++++++------------ - include/linux/watchdog.h | 22 +- - 4 files changed, 218 insertions(+), 234 deletions(-) - ---- a/Documentation/watchdog/watchdog-kernel-api.txt -+++ b/Documentation/watchdog/watchdog-kernel-api.txt -@@ -44,7 +44,6 @@ The watchdog device structure looks like - - struct watchdog_device { - int id; -- struct cdev cdev; - struct device *dev; - struct device *parent; - const struct watchdog_info *info; -@@ -56,7 +55,7 @@ struct watchdog_device { - struct notifier_block reboot_nb; - struct notifier_block restart_nb; - void *driver_data; -- struct mutex lock; -+ struct watchdog_core_data *wd_data; - unsigned long status; - struct list_head deferred; - }; -@@ -66,8 +65,6 @@ It contains following fields: - /dev/watchdog0 cdev (dynamic major, minor 0) as well as the old - /dev/watchdog miscdev. The id is set automatically when calling - 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). - * parent: set this to the parent device (or NULL) before calling - watchdog_register_device. -@@ -89,11 +86,10 @@ It contains following fields: - * 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 - 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 - information about the status of the device (Like: is the watchdog timer -- running/active, is the nowayout bit set, is the device opened via -- the /dev/watchdog interface or not, ...). -+ running/active, or is the nowayout bit set). - * deferred: entry in wtd_deferred_reg_list which is used to - register early initialized watchdogs. - -@@ -110,8 +106,8 @@ struct watchdog_ops { - int (*set_timeout)(struct watchdog_device *, unsigned int); - unsigned int (*get_timeleft)(struct watchdog_device *); - int (*restart)(struct watchdog_device *); -- void (*ref)(struct watchdog_device *); -- void (*unref)(struct watchdog_device *); -+ void (*ref)(struct watchdog_device *) __deprecated; -+ void (*unref)(struct watchdog_device *) __deprecated; - long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); - }; - -@@ -120,20 +116,6 @@ driver's operations. This module owner w - the watchdog is active. (This to avoid a system crash when you unload the - 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 - are: - * start: this is a pointer to the routine that starts the watchdog timer -@@ -176,34 +158,21 @@ they are supported. These optional routi - * 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 - 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 - 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 - 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 - bit-operations. The status bits that are defined are: - * 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 - 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) --* 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. - 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 - timer device) you can either: ---- a/drivers/watchdog/watchdog_core.c -+++ b/drivers/watchdog/watchdog_core.c -@@ -210,8 +210,6 @@ static int __watchdog_register_device(st - * corrupted in a later stage then we expect a kernel panic! - */ - -- mutex_init(&wdd->lock); -- - /* Use alias for watchdog id if possible */ - if (wdd->parent) { - ret = of_alias_get_id(wdd->parent->of_node, "watchdog"); ---- a/drivers/watchdog/watchdog_dev.c -+++ b/drivers/watchdog/watchdog_dev.c -@@ -32,27 +32,51 @@ - - #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - --#include <linux/module.h> /* For module stuff/... */ --#include <linux/types.h> /* For standard types (like size_t) */ -+#include <linux/cdev.h> /* For character device */ - #include <linux/errno.h> /* For the -ENODEV/... values */ --#include <linux/kernel.h> /* For printk/panic/... */ - #include <linux/fs.h> /* For file operations */ --#include <linux/watchdog.h> /* For watchdog specific items */ --#include <linux/miscdevice.h> /* For handling misc devices */ - #include <linux/init.h> /* For __init/__exit/... */ -+#include <linux/kernel.h> /* For printk/panic/... */ -+#include <linux/kref.h> /* For data references */ -+#include <linux/miscdevice.h> /* For handling misc devices */ -+#include <linux/module.h> /* For module stuff/... */ -+#include <linux/mutex.h> /* For mutexes */ -+#include <linux/slab.h> /* For memory functions */ -+#include <linux/types.h> /* For standard types (like size_t) */ -+#include <linux/watchdog.h> /* For watchdog specific items */ - #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ - - #include "watchdog_core.h" - -+/* -+ * struct watchdog_core_data - watchdog core internal data -+ * @kref: Reference count. -+ * @cdev: The watchdog's Character device. -+ * @wdd: Pointer to watchdog device. -+ * @lock: Lock for watchdog core. -+ * @status: Watchdog core internal status bits. -+ */ -+struct watchdog_core_data { -+ struct kref kref; -+ struct cdev cdev; -+ struct watchdog_device *wdd; -+ struct mutex lock; -+ unsigned long status; /* Internal status bits */ -+#define _WDOG_DEV_OPEN 0 /* Opened ? */ -+#define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */ -+}; -+ - /* the dev_t structure to store the dynamically allocated watchdog devices */ - static dev_t watchdog_devt; --/* the watchdog device behind /dev/watchdog */ --static struct watchdog_device *old_wdd; -+/* Reference to watchdog device behind /dev/watchdog */ -+static struct watchdog_core_data *old_wd_data; - - /* - * watchdog_ping: ping the watchdog. - * @wdd: the watchdog device to ping - * -+ * The caller must hold wd_data->lock. -+ * - * If the watchdog has no own ping operation then it needs to be - * restarted via the start operation. This wrapper function does - * exactly that. -@@ -61,25 +85,16 @@ static struct watchdog_device *old_wdd; - - static int watchdog_ping(struct watchdog_device *wdd) - { -- int err = 0; -- -- mutex_lock(&wdd->lock); -- -- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { -- err = -ENODEV; -- goto out_ping; -- } -+ int err; - - if (!watchdog_active(wdd)) -- goto out_ping; -+ return 0; - - if (wdd->ops->ping) - err = wdd->ops->ping(wdd); /* ping the watchdog */ - else - err = wdd->ops->start(wdd); /* restart watchdog */ - --out_ping: -- mutex_unlock(&wdd->lock); - return err; - } - -@@ -87,6 +102,8 @@ out_ping: - * watchdog_start: wrapper to start the watchdog. - * @wdd: the watchdog device to start - * -+ * The caller must hold wd_data->lock. -+ * - * Start the watchdog if it is not active and mark it active. - * This function returns zero on success or a negative errno code for - * failure. -@@ -94,24 +111,15 @@ out_ping: - - static int watchdog_start(struct watchdog_device *wdd) - { -- int err = 0; -- -- mutex_lock(&wdd->lock); -- -- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { -- err = -ENODEV; -- goto out_start; -- } -+ int err; - - if (watchdog_active(wdd)) -- goto out_start; -+ return 0; - - err = wdd->ops->start(wdd); - if (err == 0) - set_bit(WDOG_ACTIVE, &wdd->status); - --out_start: -- mutex_unlock(&wdd->lock); - return err; - } - -@@ -119,6 +127,8 @@ out_start: - * watchdog_stop: wrapper to stop the watchdog. - * @wdd: the watchdog device to stop - * -+ * The caller must hold wd_data->lock. -+ * - * Stop the watchdog if it is still active and unmark it active. - * This function returns zero on success or a negative errno code for - * failure. -@@ -127,93 +137,58 @@ out_start: - - static int watchdog_stop(struct watchdog_device *wdd) - { -- int err = 0; -- -- mutex_lock(&wdd->lock); -- -- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { -- err = -ENODEV; -- goto out_stop; -- } -+ int err; - - if (!watchdog_active(wdd)) -- goto out_stop; -+ return 0; - - if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) { - dev_info(wdd->dev, "nowayout prevents watchdog being stopped!\n"); -- err = -EBUSY; -- goto out_stop; -+ return -EBUSY; - } - - err = wdd->ops->stop(wdd); - if (err == 0) - clear_bit(WDOG_ACTIVE, &wdd->status); - --out_stop: -- mutex_unlock(&wdd->lock); - return err; - } - - /* - * watchdog_get_status: wrapper to get the watchdog status - * @wdd: the watchdog device to get the status from -- * @status: the status of the watchdog device -+ * -+ * The caller must hold wd_data->lock. - * - * Get the watchdog's status flags. - */ - --static int watchdog_get_status(struct watchdog_device *wdd, -- unsigned int *status) -+static unsigned int watchdog_get_status(struct watchdog_device *wdd) - { -- int err = 0; -- -- *status = 0; - if (!wdd->ops->status) -- return -EOPNOTSUPP; -- -- mutex_lock(&wdd->lock); -- -- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { -- err = -ENODEV; -- goto out_status; -- } -- -- *status = wdd->ops->status(wdd); -+ return 0; - --out_status: -- mutex_unlock(&wdd->lock); -- return err; -+ return wdd->ops->status(wdd); - } - - /* - * watchdog_set_timeout: set the watchdog timer timeout - * @wdd: the watchdog device to set the timeout for - * @timeout: timeout to set in seconds -+ * -+ * The caller must hold wd_data->lock. - */ - - static int watchdog_set_timeout(struct watchdog_device *wdd, - unsigned int timeout) - { -- int err; -- - if (!wdd->ops->set_timeout || !(wdd->info->options & WDIOF_SETTIMEOUT)) - return -EOPNOTSUPP; - - if (watchdog_timeout_invalid(wdd, timeout)) - return -EINVAL; - -- mutex_lock(&wdd->lock); -- -- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { -- err = -ENODEV; -- goto out_timeout; -- } -- -- err = wdd->ops->set_timeout(wdd, timeout); -- --out_timeout: -- mutex_unlock(&wdd->lock); -- return err; -+ return wdd->ops->set_timeout(wdd, timeout); - } - - /* -@@ -221,30 +196,22 @@ out_timeout: - * @wdd: the watchdog device to get the remaining time from - * @timeleft: the time that's left - * -+ * The caller must hold wd_data->lock. -+ * - * Get the time before a watchdog will reboot (if not pinged). - */ - - static int watchdog_get_timeleft(struct watchdog_device *wdd, - unsigned int *timeleft) - { -- int err = 0; -- - *timeleft = 0; -+ - if (!wdd->ops->get_timeleft) - return -EOPNOTSUPP; - -- mutex_lock(&wdd->lock); -- -- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { -- err = -ENODEV; -- goto out_timeleft; -- } -- - *timeleft = wdd->ops->get_timeleft(wdd); - --out_timeleft: -- mutex_unlock(&wdd->lock); -- return err; -+ return 0; - } - - #ifdef CONFIG_WATCHDOG_SYSFS -@@ -261,14 +228,14 @@ static ssize_t status_show(struct device - char *buf) - { - struct watchdog_device *wdd = dev_get_drvdata(dev); -- ssize_t status; -- unsigned int val; -+ struct watchdog_core_data *wd_data = wdd->wd_data; -+ unsigned int status; - -- status = watchdog_get_status(wdd, &val); -- if (!status) -- status = sprintf(buf, "%u\n", val); -+ mutex_lock(&wd_data->lock); -+ status = watchdog_get_status(wdd); -+ mutex_unlock(&wd_data->lock); - -- return status; -+ return sprintf(buf, "%u\n", status); - } - static DEVICE_ATTR_RO(status); - -@@ -285,10 +252,13 @@ static ssize_t timeleft_show(struct devi - char *buf) - { - struct watchdog_device *wdd = dev_get_drvdata(dev); -+ struct watchdog_core_data *wd_data = wdd->wd_data; - ssize_t status; - unsigned int val; - -+ mutex_lock(&wd_data->lock); - status = watchdog_get_timeleft(wdd, &val); -+ mutex_unlock(&wd_data->lock); - if (!status) - status = sprintf(buf, "%u\n", val); - -@@ -365,28 +335,17 @@ __ATTRIBUTE_GROUPS(wdt); - * @wdd: the watchdog device to do the ioctl on - * @cmd: watchdog command - * @arg: argument pointer -+ * -+ * The caller must hold wd_data->lock. - */ - - static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd, - unsigned long arg) - { -- int err; -- - if (!wdd->ops->ioctl) - return -ENOIOCTLCMD; - -- mutex_lock(&wdd->lock); -- -- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) { -- err = -ENODEV; -- goto out_ioctl; -- } -- -- err = wdd->ops->ioctl(wdd, cmd, arg); -- --out_ioctl: -- mutex_unlock(&wdd->lock); -- return err; -+ return wdd->ops->ioctl(wdd, cmd, arg); - } - - /* -@@ -404,10 +363,11 @@ out_ioctl: - static ssize_t watchdog_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) - { -- struct watchdog_device *wdd = file->private_data; -+ struct watchdog_core_data *wd_data = file->private_data; -+ struct watchdog_device *wdd; -+ int err; - size_t i; - char c; -- int err; - - if (len == 0) - return 0; -@@ -416,18 +376,25 @@ static ssize_t watchdog_write(struct fil - * Note: just in case someone wrote the magic character - * five months ago... - */ -- clear_bit(WDOG_ALLOW_RELEASE, &wdd->status); -+ clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status); - - /* scan to see whether or not we got the magic character */ - for (i = 0; i != len; i++) { - if (get_user(c, data + i)) - return -EFAULT; - if (c == 'V') -- set_bit(WDOG_ALLOW_RELEASE, &wdd->status); -+ set_bit(_WDOG_ALLOW_RELEASE, &wd_data->status); - } - - /* someone wrote to us, so we send the watchdog a keepalive ping */ -- err = watchdog_ping(wdd); -+ -+ err = -ENODEV; -+ mutex_lock(&wd_data->lock); -+ wdd = wd_data->wdd; -+ if (wdd) -+ err = watchdog_ping(wdd); -+ mutex_unlock(&wd_data->lock); -+ - if (err < 0) - return err; - -@@ -447,71 +414,94 @@ static ssize_t watchdog_write(struct fil - static long watchdog_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) - { -- struct watchdog_device *wdd = file->private_data; -+ struct watchdog_core_data *wd_data = file->private_data; - void __user *argp = (void __user *)arg; -+ struct watchdog_device *wdd; - int __user *p = argp; - unsigned int val; - int err; - -+ mutex_lock(&wd_data->lock); -+ -+ wdd = wd_data->wdd; -+ if (!wdd) { -+ err = -ENODEV; -+ goto out_ioctl; -+ } -+ - err = watchdog_ioctl_op(wdd, cmd, arg); - if (err != -ENOIOCTLCMD) -- return err; -+ goto out_ioctl; - - switch (cmd) { - case WDIOC_GETSUPPORT: -- return copy_to_user(argp, wdd->info, -+ err = copy_to_user(argp, wdd->info, - sizeof(struct watchdog_info)) ? -EFAULT : 0; -+ break; - case WDIOC_GETSTATUS: -- err = watchdog_get_status(wdd, &val); -- if (err == -ENODEV) -- return err; -- return put_user(val, p); -+ val = watchdog_get_status(wdd); -+ err = put_user(val, p); -+ break; - case WDIOC_GETBOOTSTATUS: -- return put_user(wdd->bootstatus, p); -+ err = put_user(wdd->bootstatus, p); -+ break; - case WDIOC_SETOPTIONS: -- if (get_user(val, p)) -- return -EFAULT; -+ if (get_user(val, p)) { -+ err = -EFAULT; -+ break; -+ } - if (val & WDIOS_DISABLECARD) { - err = watchdog_stop(wdd); - if (err < 0) -- return err; -+ break; - } -- if (val & WDIOS_ENABLECARD) { -+ if (val & WDIOS_ENABLECARD) - err = watchdog_start(wdd); -- if (err < 0) -- return err; -- } -- return 0; -+ break; - case WDIOC_KEEPALIVE: -- if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) -- return -EOPNOTSUPP; -- return watchdog_ping(wdd); -+ if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) { -+ err = -EOPNOTSUPP; -+ break; -+ } -+ err = watchdog_ping(wdd); -+ break; - case WDIOC_SETTIMEOUT: -- if (get_user(val, p)) -- return -EFAULT; -+ if (get_user(val, p)) { -+ err = -EFAULT; -+ break; -+ } - err = watchdog_set_timeout(wdd, val); - if (err < 0) -- return err; -+ break; - /* If the watchdog is active then we send a keepalive ping - * to make sure that the watchdog keep's running (and if - * possible that it takes the new timeout) */ - err = watchdog_ping(wdd); - if (err < 0) -- return err; -+ break; - /* Fall */ - case WDIOC_GETTIMEOUT: - /* timeout == 0 means that we don't know the timeout */ -- if (wdd->timeout == 0) -- return -EOPNOTSUPP; -- return put_user(wdd->timeout, p); -+ if (wdd->timeout == 0) { -+ err = -EOPNOTSUPP; -+ break; -+ } -+ err = put_user(wdd->timeout, p); -+ break; - case WDIOC_GETTIMELEFT: - err = watchdog_get_timeleft(wdd, &val); -- if (err) -- return err; -- return put_user(val, p); -+ if (err < 0) -+ break; -+ err = put_user(val, p); -+ break; - default: -- return -ENOTTY; -+ err = -ENOTTY; -+ break; - } -+ -+out_ioctl: -+ mutex_unlock(&wd_data->lock); -+ return err; - } - - /* -@@ -526,45 +516,59 @@ static long watchdog_ioctl(struct file * - - static int watchdog_open(struct inode *inode, struct file *file) - { -- int err = -EBUSY; -+ struct watchdog_core_data *wd_data; - struct watchdog_device *wdd; -+ int err; - - /* Get the corresponding watchdog device */ - if (imajor(inode) == MISC_MAJOR) -- wdd = old_wdd; -+ wd_data = old_wd_data; - else -- wdd = container_of(inode->i_cdev, struct watchdog_device, cdev); -+ wd_data = container_of(inode->i_cdev, struct watchdog_core_data, -+ cdev); - - /* the watchdog is single open! */ -- if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) -+ if (test_and_set_bit(_WDOG_DEV_OPEN, &wd_data->status)) - return -EBUSY; - -+ wdd = wd_data->wdd; -+ - /* - * If the /dev/watchdog device is open, we don't want the module - * to be unloaded. - */ -- if (!try_module_get(wdd->ops->owner)) -- goto out; -+ if (!try_module_get(wdd->ops->owner)) { -+ err = -EBUSY; -+ goto out_clear; -+ } - - err = watchdog_start(wdd); - if (err < 0) - goto out_mod; - -- file->private_data = wdd; -+ file->private_data = wd_data; - -- if (wdd->ops->ref) -- wdd->ops->ref(wdd); -+ kref_get(&wd_data->kref); - - /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ - return nonseekable_open(inode, file); - - out_mod: -- module_put(wdd->ops->owner); --out: -- clear_bit(WDOG_DEV_OPEN, &wdd->status); -+ module_put(wd_data->wdd->ops->owner); -+out_clear: -+ clear_bit(_WDOG_DEV_OPEN, &wd_data->status); - return err; - } - -+static void watchdog_core_data_release(struct kref *kref) -+{ -+ struct watchdog_core_data *wd_data; -+ -+ wd_data = container_of(kref, struct watchdog_core_data, kref); -+ -+ kfree(wd_data); -+} -+ - /* - * watchdog_release: release the watchdog device. - * @inode: inode of device -@@ -577,9 +581,16 @@ out: - - static int watchdog_release(struct inode *inode, struct file *file) - { -- struct watchdog_device *wdd = file->private_data; -+ struct watchdog_core_data *wd_data = file->private_data; -+ struct watchdog_device *wdd; - int err = -EBUSY; - -+ mutex_lock(&wd_data->lock); -+ -+ wdd = wd_data->wdd; -+ if (!wdd) -+ goto done; -+ - /* - * We only stop the watchdog if we received the magic character - * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then -@@ -587,29 +598,24 @@ static int watchdog_release(struct inode - */ - if (!test_bit(WDOG_ACTIVE, &wdd->status)) - err = 0; -- else if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) || -+ else if (test_and_clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status) || - !(wdd->info->options & WDIOF_MAGICCLOSE)) - err = watchdog_stop(wdd); - - /* If the watchdog was not stopped, send a keepalive ping */ - if (err < 0) { -- mutex_lock(&wdd->lock); -- if (!test_bit(WDOG_UNREGISTERED, &wdd->status)) -- dev_crit(wdd->dev, "watchdog did not stop!\n"); -- mutex_unlock(&wdd->lock); -+ dev_crit(wdd->dev, "watchdog did not stop!\n"); - watchdog_ping(wdd); - } - -- /* Allow the owner module to be unloaded again */ -- module_put(wdd->ops->owner); -- - /* make sure that /dev/watchdog can be re-opened */ -- clear_bit(WDOG_DEV_OPEN, &wdd->status); -- -- /* Note wdd may be gone after this, do not use after this! */ -- if (wdd->ops->unref) -- wdd->ops->unref(wdd); -+ clear_bit(_WDOG_DEV_OPEN, &wd_data->status); - -+done: -+ mutex_unlock(&wd_data->lock); -+ /* Allow the owner module to be unloaded again */ -+ module_put(wd_data->cdev.owner); -+ kref_put(&wd_data->kref, watchdog_core_data_release); - return 0; - } - -@@ -639,10 +645,20 @@ static struct miscdevice watchdog_miscde - - static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) - { -+ struct watchdog_core_data *wd_data; - int err; - -+ wd_data = kzalloc(sizeof(struct watchdog_core_data), GFP_KERNEL); -+ if (!wd_data) -+ return -ENOMEM; -+ kref_init(&wd_data->kref); -+ mutex_init(&wd_data->lock); -+ -+ wd_data->wdd = wdd; -+ wdd->wd_data = wd_data; -+ - if (wdd->id == 0) { -- old_wdd = wdd; -+ old_wd_data = wd_data; - watchdog_miscdev.parent = wdd->parent; - err = misc_register(&watchdog_miscdev); - if (err != 0) { -@@ -651,23 +667,25 @@ static int watchdog_cdev_register(struct - if (err == -EBUSY) - pr_err("%s: a legacy watchdog module is probably present.\n", - wdd->info->identity); -- old_wdd = NULL; -+ old_wd_data = NULL; -+ kfree(wd_data); - return err; - } - } - - /* Fill in the data structures */ -- cdev_init(&wdd->cdev, &watchdog_fops); -- wdd->cdev.owner = wdd->ops->owner; -+ cdev_init(&wd_data->cdev, &watchdog_fops); -+ wd_data->cdev.owner = wdd->ops->owner; - - /* Add the device */ -- err = cdev_add(&wdd->cdev, devno, 1); -+ err = cdev_add(&wd_data->cdev, devno, 1); - if (err) { - pr_err("watchdog%d unable to add device %d:%d\n", - wdd->id, MAJOR(watchdog_devt), wdd->id); - if (wdd->id == 0) { - misc_deregister(&watchdog_miscdev); -- old_wdd = NULL; -+ old_wd_data = NULL; -+ kref_put(&wd_data->kref, watchdog_core_data_release); - } - } - return err; -@@ -683,15 +701,20 @@ static int watchdog_cdev_register(struct - - static void watchdog_cdev_unregister(struct watchdog_device *wdd) - { -- mutex_lock(&wdd->lock); -- set_bit(WDOG_UNREGISTERED, &wdd->status); -- mutex_unlock(&wdd->lock); -+ struct watchdog_core_data *wd_data = wdd->wd_data; - -- cdev_del(&wdd->cdev); -+ cdev_del(&wd_data->cdev); - if (wdd->id == 0) { - misc_deregister(&watchdog_miscdev); -- old_wdd = NULL; -+ old_wd_data = NULL; - } -+ -+ mutex_lock(&wd_data->lock); -+ wd_data->wdd = NULL; -+ wdd->wd_data = NULL; -+ mutex_unlock(&wd_data->lock); -+ -+ kref_put(&wd_data->kref, watchdog_core_data_release); - } - - static struct class watchdog_class = { -@@ -742,9 +765,9 @@ int watchdog_dev_register(struct watchdo - - void watchdog_dev_unregister(struct watchdog_device *wdd) - { -- watchdog_cdev_unregister(wdd); - device_destroy(&watchdog_class, wdd->dev->devt); - wdd->dev = NULL; -+ watchdog_cdev_unregister(wdd); - } - - /* ---- a/include/linux/watchdog.h -+++ b/include/linux/watchdog.h -@@ -17,6 +17,7 @@ - - struct watchdog_ops; - struct watchdog_device; -+struct watchdog_core_data; - - /** struct watchdog_ops - The watchdog-devices operations - * -@@ -28,8 +29,6 @@ struct watchdog_device; - * @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). - * @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. - * - * The watchdog_ops structure contains a list of low-level operations -@@ -48,15 +47,14 @@ struct watchdog_ops { - int (*set_timeout)(struct watchdog_device *, unsigned int); - unsigned int (*get_timeleft)(struct watchdog_device *); - int (*restart)(struct watchdog_device *); -- void (*ref)(struct watchdog_device *); -- void (*unref)(struct watchdog_device *); -+ void (*ref)(struct watchdog_device *) __deprecated; -+ void (*unref)(struct watchdog_device *) __deprecated; - long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); - }; - - /** struct watchdog_device - The structure that defines a watchdog device - * - * @id: The watchdog's ID. (Allocated by watchdog_register_device) -- * @cdev: The watchdog's Character device. - * @dev: The device for our watchdog - * @parent: The parent bus device - * @info: Pointer to a watchdog_info structure. -@@ -67,8 +65,8 @@ struct watchdog_ops { - * @max_timeout:The watchdog devices maximum timeout value (in seconds). - * @reboot_nb: The notifier block to stop watchdog on reboot. - * @restart_nb: The notifier block to register a restart function. -- * @driver-data:Pointer to the drivers private data. -- * @lock: Lock for watchdog core internal use only. -+ * @driver_data:Pointer to the drivers private data. -+ * @wd_data: Pointer to watchdog core internal data. - * @status: Field that contains the devices internal status bits. - * @deferred: entry in wtd_deferred_reg_list which is used to - * register early initialized watchdogs. -@@ -84,7 +82,6 @@ struct watchdog_ops { - */ - struct watchdog_device { - int id; -- struct cdev cdev; - struct device *dev; - struct device *parent; - const struct watchdog_info *info; -@@ -96,15 +93,12 @@ struct watchdog_device { - struct notifier_block reboot_nb; - struct notifier_block restart_nb; - void *driver_data; -- struct mutex lock; -+ struct watchdog_core_data *wd_data; - unsigned long status; - /* Bit numbers for status flags */ - #define WDOG_ACTIVE 0 /* Is the watchdog running/active */ --#define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ --#define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ --#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 */ -+#define WDOG_NO_WAY_OUT 1 /* Is 'nowayout' feature set ? */ -+#define WDOG_STOP_ON_REBOOT 2 /* Should be stopped on reboot */ - struct list_head deferred; - }; - |