Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 45f5fed3 authored by Alan Cox's avatar Alan Cox Committed by Wim Van Sebroeck
Browse files

watchdog: Add multiple device support



We keep the old /dev/watchdog interface file for the first watchdog via
miscdev. This is basically a cut and paste of the relevant interface code
from the rtc driver layer tweaked for watchdog.

Revised to fix problems noted by Hans de Goede

Signed-off-by: default avatarAlan Cox <alan@linux.intel.com>
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarTomas Winkler <tomas.winkler@intel.com>
Signed-off-by: default avatarWim Van Sebroeck <wim@iguana.be>
parent fb5f6658
Loading
Loading
Loading
Loading
+9 −1
Original line number Original line Diff line number Diff line
The Linux WatchDog Timer Driver Core kernel API.
The Linux WatchDog Timer Driver Core kernel API.
===============================================
===============================================
Last reviewed: 16-Mar-2012
Last reviewed: 21-May-2012


Wim Van Sebroeck <wim@iguana.be>
Wim Van Sebroeck <wim@iguana.be>


@@ -39,6 +39,8 @@ watchdog_device structure.
The watchdog device structure looks like this:
The watchdog device structure looks like this:


struct watchdog_device {
struct watchdog_device {
	int id;
	struct cdev cdev;
	const struct watchdog_info *info;
	const struct watchdog_info *info;
	const struct watchdog_ops *ops;
	const struct watchdog_ops *ops;
	unsigned int bootstatus;
	unsigned int bootstatus;
@@ -50,6 +52,12 @@ struct watchdog_device {
};
};


It contains following fields:
It contains following fields:
* id: set by watchdog_register_device, id 0 is special. It has both a
  /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.
* info: a pointer to a watchdog_info structure. This structure gives some
* info: a pointer to a watchdog_info structure. This structure gives some
  additional information about the watchdog timer itself. (Like it's unique name)
  additional information about the watchdog timer itself. (Like it's unique name)
* ops: a pointer to the list of watchdog operations that the watchdog supports.
* ops: a pointer to the list of watchdog operations that the watchdog supports.
+39 −4
Original line number Original line Diff line number Diff line
@@ -34,9 +34,12 @@
#include <linux/kernel.h>	/* For printk/panic/... */
#include <linux/kernel.h>	/* For printk/panic/... */
#include <linux/watchdog.h>	/* For watchdog specific items */
#include <linux/watchdog.h>	/* For watchdog specific items */
#include <linux/init.h>		/* For __init/__exit/... */
#include <linux/init.h>		/* For __init/__exit/... */
#include <linux/idr.h>		/* For ida_* macros */


#include "watchdog_core.h"	/* For watchdog_dev_register/... */
#include "watchdog_core.h"	/* For watchdog_dev_register/... */


static DEFINE_IDA(watchdog_ida);

/**
/**
 * watchdog_register_device() - register a watchdog device
 * watchdog_register_device() - register a watchdog device
 * @wdd: watchdog device
 * @wdd: watchdog device
@@ -49,7 +52,7 @@
 */
 */
int watchdog_register_device(struct watchdog_device *wdd)
int watchdog_register_device(struct watchdog_device *wdd)
{
{
	int ret;
	int ret, id;


	if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL)
	if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL)
		return -EINVAL;
		return -EINVAL;
@@ -74,12 +77,29 @@ 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!
	 */
	 */


	/* We only support 1 watchdog device via the /dev/watchdog interface */
	id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL);
	if (id < 0)
		return id;
	wdd->id = id;

	ret = watchdog_dev_register(wdd);
	if (ret) {
		ida_simple_remove(&watchdog_ida, id);
		if (!(id == 0 && ret == -EBUSY))
			return ret;

		/* Retry in case a legacy watchdog module exists */
		id = ida_simple_get(&watchdog_ida, 1, MAX_DOGS, GFP_KERNEL);
		if (id < 0)
			return id;
		wdd->id = id;

		ret = watchdog_dev_register(wdd);
		ret = watchdog_dev_register(wdd);
		if (ret) {
		if (ret) {
		pr_err("error registering /dev/watchdog (err=%d)\n", ret);
			ida_simple_remove(&watchdog_ida, id);
			return ret;
			return ret;
		}
		}
	}


	return 0;
	return 0;
}
}
@@ -102,9 +122,24 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
	ret = watchdog_dev_unregister(wdd);
	ret = watchdog_dev_unregister(wdd);
	if (ret)
	if (ret)
		pr_err("error unregistering /dev/watchdog (err=%d)\n", ret);
		pr_err("error unregistering /dev/watchdog (err=%d)\n", ret);
	ida_simple_remove(&watchdog_ida, wdd->id);
}
}
EXPORT_SYMBOL_GPL(watchdog_unregister_device);
EXPORT_SYMBOL_GPL(watchdog_unregister_device);


static int __init watchdog_init(void)
{
	return watchdog_dev_init();
}

static void __exit watchdog_exit(void)
{
	watchdog_dev_exit();
	ida_destroy(&watchdog_ida);
}

subsys_initcall(watchdog_init);
module_exit(watchdog_exit);

MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
MODULE_DESCRIPTION("WatchDog Timer Driver Core");
MODULE_DESCRIPTION("WatchDog Timer Driver Core");
+4 −0
Original line number Original line Diff line number Diff line
@@ -26,8 +26,12 @@
 *	This material is provided "AS-IS" and at no charge.
 *	This material is provided "AS-IS" and at no charge.
 */
 */


#define MAX_DOGS	32	/* Maximum number of watchdog devices */

/*
/*
 *	Functions/procedures to be called by the core
 *	Functions/procedures to be called by the core
 */
 */
extern int watchdog_dev_register(struct watchdog_device *);
extern int watchdog_dev_register(struct watchdog_device *);
extern int watchdog_dev_unregister(struct watchdog_device *);
extern int watchdog_dev_unregister(struct watchdog_device *);
extern int __init watchdog_dev_init(void);
extern void __exit watchdog_dev_exit(void);
+82 −45
Original line number Original line Diff line number Diff line
@@ -44,10 +44,10 @@


#include "watchdog_core.h"
#include "watchdog_core.h"


/* make sure we only register one /dev/watchdog device */
/* the dev_t structure to store the dynamically allocated watchdog devices */
static unsigned long watchdog_dev_busy;
static dev_t watchdog_devt;
/* the watchdog device behind /dev/watchdog */
/* the watchdog device behind /dev/watchdog */
static struct watchdog_device *wdd;
static struct watchdog_device *old_wdd;


/*
/*
 *	watchdog_ping: ping the watchdog.
 *	watchdog_ping: ping the watchdog.
@@ -138,6 +138,7 @@ static int watchdog_stop(struct watchdog_device *wddev)
static ssize_t watchdog_write(struct file *file, const char __user *data,
static ssize_t watchdog_write(struct file *file, const char __user *data,
						size_t len, loff_t *ppos)
						size_t len, loff_t *ppos)
{
{
	struct watchdog_device *wdd = file->private_data;
	size_t i;
	size_t i;
	char c;
	char c;


@@ -177,6 +178,7 @@ static ssize_t watchdog_write(struct file *file, const char __user *data,
static long watchdog_ioctl(struct file *file, unsigned int cmd,
static long watchdog_ioctl(struct file *file, unsigned int cmd,
							unsigned long arg)
							unsigned long arg)
{
{
	struct watchdog_device *wdd = file->private_data;
	void __user *argp = (void __user *)arg;
	void __user *argp = (void __user *)arg;
	int __user *p = argp;
	int __user *p = argp;
	unsigned int val;
	unsigned int val;
@@ -249,11 +251,11 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
}
}


/*
/*
 *	watchdog_open: open the /dev/watchdog device.
 *	watchdog_open: open the /dev/watchdog* devices.
 *	@inode: inode of device
 *	@inode: inode of device
 *	@file: file handle to device
 *	@file: file handle to device
 *
 *
 *	When the /dev/watchdog device gets opened, we start the watchdog.
 *	When the /dev/watchdog* device gets opened, we start the watchdog.
 *	Watch out: the /dev/watchdog device is single open, so we make sure
 *	Watch out: the /dev/watchdog device is single open, so we make sure
 *	it can only be opened once.
 *	it can only be opened once.
 */
 */
@@ -261,6 +263,13 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
static int watchdog_open(struct inode *inode, struct file *file)
static int watchdog_open(struct inode *inode, struct file *file)
{
{
	int err = -EBUSY;
	int err = -EBUSY;
	struct watchdog_device *wdd;

	/* Get the corresponding watchdog device */
	if (imajor(inode) == MISC_MAJOR)
		wdd = old_wdd;
	else
		wdd = container_of(inode->i_cdev, struct watchdog_device, cdev);


	/* the watchdog is single open! */
	/* the watchdog is single open! */
	if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status))
	if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status))
@@ -277,6 +286,8 @@ static int watchdog_open(struct inode *inode, struct file *file)
	if (err < 0)
	if (err < 0)
		goto out_mod;
		goto out_mod;


	file->private_data = wdd;

	/* dev/watchdog is a virtual (and thus non-seekable) filesystem */
	/* dev/watchdog is a virtual (and thus non-seekable) filesystem */
	return nonseekable_open(inode, file);
	return nonseekable_open(inode, file);


@@ -288,7 +299,7 @@ static int watchdog_open(struct inode *inode, struct file *file)
}
}


/*
/*
 *      watchdog_release: release the /dev/watchdog device.
 *	watchdog_release: release the watchdog device.
 *	@inode: inode of device
 *	@inode: inode of device
 *	@file: file handle to device
 *	@file: file handle to device
 *
 *
@@ -299,6 +310,7 @@ static int watchdog_open(struct inode *inode, struct file *file)


static int watchdog_release(struct inode *inode, struct file *file)
static int watchdog_release(struct inode *inode, struct file *file)
{
{
	struct watchdog_device *wdd = file->private_data;
	int err = -EBUSY;
	int err = -EBUSY;


	/*
	/*
@@ -340,62 +352,87 @@ static struct miscdevice watchdog_miscdev = {
};
};


/*
/*
 *	watchdog_dev_register:
 *	watchdog_dev_register: register a watchdog device
 *	@watchdog: watchdog device
 *	@watchdog: watchdog device
 *
 *
 *	Register a watchdog device as /dev/watchdog. /dev/watchdog
 *	Register a watchdog device including handling the legacy
 *	is actually a miscdevice and thus we set it up like that.
 *	/dev/watchdog node. /dev/watchdog is actually a miscdevice and
 *	thus we set it up like that.
 */
 */


int watchdog_dev_register(struct watchdog_device *watchdog)
int watchdog_dev_register(struct watchdog_device *watchdog)
{
{
	int err;
	int err, devno;

	/* Only one device can register for /dev/watchdog */
	if (test_and_set_bit(0, &watchdog_dev_busy)) {
		pr_err("only one watchdog can use /dev/watchdog\n");
		return -EBUSY;
	}

	wdd = watchdog;


	if (watchdog->id == 0) {
		err = misc_register(&watchdog_miscdev);
		err = misc_register(&watchdog_miscdev);
		if (err != 0) {
		if (err != 0) {
		pr_err("%s: cannot register miscdev on minor=%d (err=%d)\n",
			pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n",
				watchdog->info->identity, WATCHDOG_MINOR, err);
				watchdog->info->identity, WATCHDOG_MINOR, err);
		goto out;
			if (err == -EBUSY)
				pr_err("%s: a legacy watchdog module is probably present.\n",
					watchdog->info->identity);
			return err;
		}
		old_wdd = watchdog;
	}
	}


	return 0;
	/* Fill in the data structures */

	devno = MKDEV(MAJOR(watchdog_devt), watchdog->id);
out:
	cdev_init(&watchdog->cdev, &watchdog_fops);
	wdd = NULL;
	watchdog->cdev.owner = watchdog->ops->owner;
	clear_bit(0, &watchdog_dev_busy);

	/* Add the device */
	err  = cdev_add(&watchdog->cdev, devno, 1);
	if (err) {
		pr_err("watchdog%d unable to add device %d:%d\n",
			watchdog->id,  MAJOR(watchdog_devt), watchdog->id);
		if (watchdog->id == 0) {
			misc_deregister(&watchdog_miscdev);
			old_wdd = NULL;
		}
	}
	return err;
	return err;
}
}


/*
/*
 *	watchdog_dev_unregister:
 *	watchdog_dev_unregister: unregister a watchdog device
 *	@watchdog: watchdog device
 *	@watchdog: watchdog device
 *
 *
 *	Deregister the /dev/watchdog device.
 *	Unregister the watchdog and if needed the legacy /dev/watchdog device.
 */
 */


int watchdog_dev_unregister(struct watchdog_device *watchdog)
int watchdog_dev_unregister(struct watchdog_device *watchdog)
{
{
	/* Check that a watchdog device was registered in the past */
	cdev_del(&watchdog->cdev);
	if (!test_bit(0, &watchdog_dev_busy) || !wdd)
	if (watchdog->id == 0) {
		return -ENODEV;
		misc_deregister(&watchdog_miscdev);
		old_wdd = NULL;
	}
	return 0;
}


	/* We can only unregister the watchdog device that was registered */
/*
	if (watchdog != wdd) {
 *	watchdog_dev_init: init dev part of watchdog core
		pr_err("%s: watchdog was not registered as /dev/watchdog\n",
 *
		       watchdog->info->identity);
 *	Allocate a range of chardev nodes to use for watchdog devices
		return -ENODEV;
 */

int __init watchdog_dev_init(void)
{
	int err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog");
	if (err < 0)
		pr_err("watchdog: unable to allocate char dev region\n");
	return err;
}
}


	misc_deregister(&watchdog_miscdev);
/*
	wdd = NULL;
 *	watchdog_dev_exit: exit dev part of watchdog core
	clear_bit(0, &watchdog_dev_busy);
 *
	return 0;
 *	Release the range of chardev nodes used for watchdog devices
 */

void __exit watchdog_dev_exit(void)
{
	unregister_chrdev_region(watchdog_devt, MAX_DOGS);
}
}
+6 −0
Original line number Original line Diff line number Diff line
@@ -54,6 +54,8 @@ struct watchdog_info {
#ifdef __KERNEL__
#ifdef __KERNEL__


#include <linux/bitops.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/cdev.h>


struct watchdog_ops;
struct watchdog_ops;
struct watchdog_device;
struct watchdog_device;
@@ -89,6 +91,8 @@ struct watchdog_ops {


/** 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)
 * @cdev:	The watchdog's Character device.
 * @info:	Pointer to a watchdog_info structure.
 * @info:	Pointer to a watchdog_info structure.
 * @ops:	Pointer to the list of watchdog operations.
 * @ops:	Pointer to the list of watchdog operations.
 * @bootstatus:	Status of the watchdog device at boot.
 * @bootstatus:	Status of the watchdog device at boot.
@@ -105,6 +109,8 @@ struct watchdog_ops {
 * via the watchdog_set_drvdata and watchdog_get_drvdata helpers.
 * via the watchdog_set_drvdata and watchdog_get_drvdata helpers.
 */
 */
struct watchdog_device {
struct watchdog_device {
	int id;
	struct cdev cdev;
	const struct watchdog_info *info;
	const struct watchdog_info *info;
	const struct watchdog_ops *ops;
	const struct watchdog_ops *ops;
	unsigned int bootstatus;
	unsigned int bootstatus;