Commit ef90174f authored by Jean-Baptiste Theou's avatar Jean-Baptiste Theou Committed by Wim Van Sebroeck

watchdog: watchdog_core: Add watchdog registration deferral mechanism

Currently, watchdog subsystem require the misc subsystem to
register a watchdog. This may not be the case in case of an
early registration of a watchdog, which can be required when
the watchdog cannot be disabled.

This patch introduces a deferral mechanism to remove this requirement.
Signed-off-by: default avatarJean-Baptiste Theou <jtheou@adeneo-embedded.us>
Reviewed-by: default avatarGuenter Roeck <linux@roeck-us.net>
Signed-off-by: default avatarWim Van Sebroeck <wim@iguana.be>
parent b9be9660
...@@ -36,6 +36,10 @@ The watchdog_unregister_device routine deregisters a registered watchdog timer ...@@ -36,6 +36,10 @@ The watchdog_unregister_device routine deregisters a registered watchdog timer
device. The parameter of this routine is the pointer to the registered device. The parameter of this routine is the pointer to the registered
watchdog_device structure. watchdog_device structure.
The watchdog subsystem includes an registration deferral mechanism,
which allows you to register an watchdog as early as you wish during
the boot process.
The watchdog device structure looks like this: The watchdog device structure looks like this:
struct watchdog_device { struct watchdog_device {
...@@ -52,6 +56,7 @@ struct watchdog_device { ...@@ -52,6 +56,7 @@ struct watchdog_device {
void *driver_data; void *driver_data;
struct mutex lock; struct mutex lock;
unsigned long status; unsigned long status;
struct list_head deferred;
}; };
It contains following fields: It contains following fields:
...@@ -80,6 +85,8 @@ It contains following fields: ...@@ -80,6 +85,8 @@ It contains following fields:
information about the status of the device (Like: is the watchdog timer information about the status of the device (Like: is the watchdog timer
running/active, is the nowayout bit set, is the device opened via running/active, is the nowayout bit set, is the device opened via
the /dev/watchdog interface or not, ...). the /dev/watchdog interface or not, ...).
* deferred: entry in wtd_deferred_reg_list which is used to
register early initialized watchdogs.
The list of watchdog operations is defined as: The list of watchdog operations is defined as:
......
...@@ -43,6 +43,45 @@ ...@@ -43,6 +43,45 @@
static DEFINE_IDA(watchdog_ida); static DEFINE_IDA(watchdog_ida);
static struct class *watchdog_class; static struct class *watchdog_class;
/*
* Deferred Registration infrastructure.
*
* Sometimes watchdog drivers needs to be loaded as soon as possible,
* for example when it's impossible to disable it. To do so,
* raising the initcall level of the watchdog driver is a solution.
* But in such case, the miscdev is maybe not ready (subsys_initcall), and
* watchdog_core need miscdev to register the watchdog as a char device.
*
* The deferred registration infrastructure offer a way for the watchdog
* subsystem to register a watchdog properly, even before miscdev is ready.
*/
static DEFINE_MUTEX(wtd_deferred_reg_mutex);
static LIST_HEAD(wtd_deferred_reg_list);
static bool wtd_deferred_reg_done;
static int watchdog_deferred_registration_add(struct watchdog_device *wdd)
{
list_add_tail(&wdd->deferred,
&wtd_deferred_reg_list);
return 0;
}
static void watchdog_deferred_registration_del(struct watchdog_device *wdd)
{
struct list_head *p, *n;
struct watchdog_device *wdd_tmp;
list_for_each_safe(p, n, &wtd_deferred_reg_list) {
wdd_tmp = list_entry(p, struct watchdog_device,
deferred);
if (wdd_tmp == wdd) {
list_del(&wdd_tmp->deferred);
break;
}
}
}
static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) static void watchdog_check_min_max_timeout(struct watchdog_device *wdd)
{ {
/* /*
...@@ -98,17 +137,7 @@ int watchdog_init_timeout(struct watchdog_device *wdd, ...@@ -98,17 +137,7 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
} }
EXPORT_SYMBOL_GPL(watchdog_init_timeout); EXPORT_SYMBOL_GPL(watchdog_init_timeout);
/** static int __watchdog_register_device(struct watchdog_device *wdd)
* watchdog_register_device() - register a watchdog device
* @wdd: watchdog device
*
* Register a watchdog device with the kernel so that the
* watchdog timer can be accessed from userspace.
*
* A zero is returned on success and a negative errno code for
* failure.
*/
int watchdog_register_device(struct watchdog_device *wdd)
{ {
int ret, id, devno; int ret, id, devno;
...@@ -164,16 +193,33 @@ int watchdog_register_device(struct watchdog_device *wdd) ...@@ -164,16 +193,33 @@ int watchdog_register_device(struct watchdog_device *wdd)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(watchdog_register_device);
/** /**
* watchdog_unregister_device() - unregister a watchdog device * watchdog_register_device() - register a watchdog device
* @wdd: watchdog device to unregister * @wdd: watchdog device
* *
* Unregister a watchdog device that was previously successfully * Register a watchdog device with the kernel so that the
* registered with watchdog_register_device(). * watchdog timer can be accessed from userspace.
*
* A zero is returned on success and a negative errno code for
* failure.
*/ */
void watchdog_unregister_device(struct watchdog_device *wdd)
int watchdog_register_device(struct watchdog_device *wdd)
{
int ret;
mutex_lock(&wtd_deferred_reg_mutex);
if (wtd_deferred_reg_done)
ret = __watchdog_register_device(wdd);
else
ret = watchdog_deferred_registration_add(wdd);
mutex_unlock(&wtd_deferred_reg_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(watchdog_register_device);
static void __watchdog_unregister_device(struct watchdog_device *wdd)
{ {
int ret; int ret;
int devno; int devno;
...@@ -189,8 +235,43 @@ void watchdog_unregister_device(struct watchdog_device *wdd) ...@@ -189,8 +235,43 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
ida_simple_remove(&watchdog_ida, wdd->id); ida_simple_remove(&watchdog_ida, wdd->id);
wdd->dev = NULL; wdd->dev = NULL;
} }
/**
* watchdog_unregister_device() - unregister a watchdog device
* @wdd: watchdog device to unregister
*
* Unregister a watchdog device that was previously successfully
* registered with watchdog_register_device().
*/
void watchdog_unregister_device(struct watchdog_device *wdd)
{
mutex_lock(&wtd_deferred_reg_mutex);
if (wtd_deferred_reg_done)
__watchdog_unregister_device(wdd);
else
watchdog_deferred_registration_del(wdd);
mutex_unlock(&wtd_deferred_reg_mutex);
}
EXPORT_SYMBOL_GPL(watchdog_unregister_device); EXPORT_SYMBOL_GPL(watchdog_unregister_device);
static int __init watchdog_deferred_registration(void)
{
mutex_lock(&wtd_deferred_reg_mutex);
wtd_deferred_reg_done = true;
while (!list_empty(&wtd_deferred_reg_list)) {
struct watchdog_device *wdd;
wdd = list_first_entry(&wtd_deferred_reg_list,
struct watchdog_device, deferred);
list_del(&wdd->deferred);
__watchdog_register_device(wdd);
}
mutex_unlock(&wtd_deferred_reg_mutex);
return 0;
}
static int __init watchdog_init(void) static int __init watchdog_init(void)
{ {
int err; int err;
...@@ -207,6 +288,7 @@ static int __init watchdog_init(void) ...@@ -207,6 +288,7 @@ static int __init watchdog_init(void)
return err; return err;
} }
watchdog_deferred_registration();
return 0; return 0;
} }
...@@ -217,7 +299,7 @@ static void __exit watchdog_exit(void) ...@@ -217,7 +299,7 @@ static void __exit watchdog_exit(void)
ida_destroy(&watchdog_ida); ida_destroy(&watchdog_ida);
} }
subsys_initcall(watchdog_init); subsys_initcall_sync(watchdog_init);
module_exit(watchdog_exit); module_exit(watchdog_exit);
MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
......
...@@ -65,6 +65,8 @@ struct watchdog_ops { ...@@ -65,6 +65,8 @@ struct watchdog_ops {
* @driver-data:Pointer to the drivers private data. * @driver-data:Pointer to the drivers private data.
* @lock: Lock for watchdog core internal use only. * @lock: Lock for watchdog core internal use only.
* @status: Field that contains the devices internal status bits. * @status: Field that contains the devices internal status bits.
* @deferred: entry in wtd_deferred_reg_list which is used to
* register early initialized watchdogs.
* *
* The watchdog_device structure contains all information about a * The watchdog_device structure contains all information about a
* watchdog timer device. * watchdog timer device.
...@@ -95,6 +97,7 @@ struct watchdog_device { ...@@ -95,6 +97,7 @@ struct watchdog_device {
#define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */
#define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ #define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */
#define WDOG_UNREGISTERED 4 /* Has the device been unregistered */ #define WDOG_UNREGISTERED 4 /* Has the device been unregistered */
struct list_head deferred;
}; };
#define WATCHDOG_NOWAYOUT IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT) #define WATCHDOG_NOWAYOUT IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment