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: Jean-Baptiste Theou <jtheou@adeneo-embedded.us> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
This commit is contained in:
parent
b9be9660ba
commit
ef90174f82
|
@ -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
|
||||
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:
|
||||
|
||||
struct watchdog_device {
|
||||
|
@ -52,6 +56,7 @@ struct watchdog_device {
|
|||
void *driver_data;
|
||||
struct mutex lock;
|
||||
unsigned long status;
|
||||
struct list_head deferred;
|
||||
};
|
||||
|
||||
It contains following fields:
|
||||
|
@ -80,6 +85,8 @@ It contains following fields:
|
|||
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, ...).
|
||||
* deferred: entry in wtd_deferred_reg_list which is used to
|
||||
register early initialized watchdogs.
|
||||
|
||||
The list of watchdog operations is defined as:
|
||||
|
||||
|
|
|
@ -43,6 +43,45 @@
|
|||
static DEFINE_IDA(watchdog_ida);
|
||||
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)
|
||||
{
|
||||
/*
|
||||
|
@ -98,17 +137,7 @@ int watchdog_init_timeout(struct watchdog_device *wdd,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(watchdog_init_timeout);
|
||||
|
||||
/**
|
||||
* 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)
|
||||
static int __watchdog_register_device(struct watchdog_device *wdd)
|
||||
{
|
||||
int ret, id, devno;
|
||||
|
||||
|
@ -164,16 +193,33 @@ int watchdog_register_device(struct watchdog_device *wdd)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(watchdog_register_device);
|
||||
|
||||
/**
|
||||
* watchdog_unregister_device() - unregister a watchdog device
|
||||
* @wdd: watchdog device to unregister
|
||||
* watchdog_register_device() - register a watchdog device
|
||||
* @wdd: watchdog device
|
||||
*
|
||||
* Unregister a watchdog device that was previously successfully
|
||||
* registered with watchdog_register_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.
|
||||
*/
|
||||
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 devno;
|
||||
|
@ -189,8 +235,43 @@ void watchdog_unregister_device(struct watchdog_device *wdd)
|
|||
ida_simple_remove(&watchdog_ida, wdd->id);
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
int err;
|
||||
|
@ -207,6 +288,7 @@ static int __init watchdog_init(void)
|
|||
return err;
|
||||
}
|
||||
|
||||
watchdog_deferred_registration();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -217,7 +299,7 @@ static void __exit watchdog_exit(void)
|
|||
ida_destroy(&watchdog_ida);
|
||||
}
|
||||
|
||||
subsys_initcall(watchdog_init);
|
||||
subsys_initcall_sync(watchdog_init);
|
||||
module_exit(watchdog_exit);
|
||||
|
||||
MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
|
||||
|
|
|
@ -65,6 +65,8 @@ struct watchdog_ops {
|
|||
* @driver-data:Pointer to the drivers private data.
|
||||
* @lock: Lock for watchdog core internal use only.
|
||||
* @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
|
||||
* watchdog timer device.
|
||||
|
@ -95,6 +97,7 @@ struct watchdog_device {
|
|||
#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 */
|
||||
struct list_head deferred;
|
||||
};
|
||||
|
||||
#define WATCHDOG_NOWAYOUT IS_BUILTIN(CONFIG_WATCHDOG_NOWAYOUT)
|
||||
|
|
Loading…
Reference in New Issue