123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969 |
- 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;
- };
-
|