diff options
-rw-r--r-- | Documentation/watchdog/watchdog-kernel-api.txt | 45 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_core.c | 2 | ||||
-rw-r--r-- | drivers/watchdog/watchdog_dev.c | 383 | ||||
-rw-r--r-- | include/linux/watchdog.h | 22 |
4 files changed, 218 insertions, 234 deletions
diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 0a37da76acef..72a009478b15 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -44,7 +44,6 @@ The watchdog device structure looks like this: 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 will be used to lock the module when 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 routines/operations are: * 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: diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index f0293f7d2b80..ec1ab6c1a80b 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -210,8 +210,6 @@ static int __watchdog_register_device(struct watchdog_device *wdd) * 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"); diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 7ba3fc6157c7..3cab6f6e7f1c 100644 --- 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 *dev, struct device_attribute *attr, 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 device *dev, struct device_attribute *attr, 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 file *file, const char __user *data, * 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 file *file, const char __user *data, 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 *file, unsigned int cmd, 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 *inode, struct file *file) */ 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_miscdev = { 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 watchdog_device *wdd, dev_t devno) 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 watchdog_device *wdd, dev_t devno) 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 watchdog_device *wdd) 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); } /* diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index a88f955fde92..850af04fe0c7 100644 --- 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; }; |