watchdog: add watchdog pretimeout governor framework
The change adds a simple watchdog pretimeout framework infrastructure, its purpose is to allow users to select a desired handling of watchdog pretimeout events, which may be generated by some watchdog devices. A user selects a default watchdog pretimeout governor during compilation stage. Watchdogs with WDIOF_PRETIMEOUT capability now have one more device attribute in sysfs, pretimeout_governor attribute is intended to display the selected watchdog pretimeout governor. The framework has no impact at runtime on watchdog devices with no WDIOF_PRETIMEOUT capability set. Signed-off-by: Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
This commit is contained in:
parent
fc113d54e9
commit
ff84136cb6
7 changed files with 231 additions and 1 deletions
|
@ -48,6 +48,7 @@ struct watchdog_device {
|
|||
const struct attribute_group **groups;
|
||||
const struct watchdog_info *info;
|
||||
const struct watchdog_ops *ops;
|
||||
const struct watchdog_governor *gov;
|
||||
unsigned int bootstatus;
|
||||
unsigned int timeout;
|
||||
unsigned int pretimeout;
|
||||
|
@ -75,6 +76,7 @@ It contains following fields:
|
|||
* info: a pointer to a watchdog_info structure. This structure gives some
|
||||
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.
|
||||
* gov: a pointer to the assigned watchdog device pretimeout governor or NULL.
|
||||
* timeout: the watchdog timer's timeout value (in seconds).
|
||||
This is the time after which the system will reboot if user space does
|
||||
not send a heartbeat request if WDOG_ACTIVE is set.
|
||||
|
@ -288,3 +290,14 @@ User should follow the following guidelines for setting the priority:
|
|||
* 128: default restart handler, use if no other handler is expected to be
|
||||
available, and/or if restart is sufficient to restart the entire system
|
||||
* 255: highest priority, will preempt all other restart handlers
|
||||
|
||||
To raise a pretimeout notification, the following function should be used:
|
||||
|
||||
void watchdog_notify_pretimeout(struct watchdog_device *wdd)
|
||||
|
||||
The function can be called in the interrupt context. If watchdog pretimeout
|
||||
governor framework (kbuild CONFIG_WATCHDOG_PRETIMEOUT_GOV symbol) is enabled,
|
||||
an action is taken by a preconfigured pretimeout governor preassigned to
|
||||
the watchdog device. If watchdog pretimeout governor framework is not
|
||||
enabled, watchdog_notify_pretimeout() prints a notification message to
|
||||
the kernel log buffer.
|
||||
|
|
|
@ -1831,4 +1831,11 @@ config USBPCWATCHDOG
|
|||
|
||||
Most people will say N.
|
||||
|
||||
comment "Watchdog Pretimeout Governors"
|
||||
|
||||
config WATCHDOG_PRETIMEOUT_GOV
|
||||
bool "Enable watchdog pretimeout governors"
|
||||
help
|
||||
The option allows to select watchdog pretimeout governors.
|
||||
|
||||
endif # WATCHDOG
|
||||
|
|
|
@ -3,9 +3,12 @@
|
|||
#
|
||||
|
||||
# The WatchDog Timer Driver Core.
|
||||
watchdog-objs += watchdog_core.o watchdog_dev.o
|
||||
obj-$(CONFIG_WATCHDOG_CORE) += watchdog.o
|
||||
|
||||
watchdog-objs += watchdog_core.o watchdog_dev.o
|
||||
|
||||
watchdog-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV) += watchdog_pretimeout.o
|
||||
|
||||
# Only one watchdog can succeed. We probe the ISA/PCI/USB based
|
||||
# watchdog-cards first, then the architecture specific watchdog
|
||||
# drivers and then the architecture independent "softdog" driver.
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
|
||||
|
||||
#include "watchdog_core.h"
|
||||
#include "watchdog_pretimeout.h"
|
||||
|
||||
/*
|
||||
* struct watchdog_core_data - watchdog core internal data
|
||||
|
@ -488,6 +489,16 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
|
|||
}
|
||||
static DEVICE_ATTR_RO(state);
|
||||
|
||||
static ssize_t pretimeout_governor_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct watchdog_device *wdd = dev_get_drvdata(dev);
|
||||
|
||||
return watchdog_pretimeout_governor_get(wdd, buf);
|
||||
}
|
||||
static DEVICE_ATTR_RO(pretimeout_governor);
|
||||
|
||||
static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
|
||||
int n)
|
||||
{
|
||||
|
@ -500,6 +511,10 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
|
|||
else if (attr == &dev_attr_pretimeout.attr &&
|
||||
!(wdd->info->options & WDIOF_PRETIMEOUT))
|
||||
mode = 0;
|
||||
else if (attr == &dev_attr_pretimeout_governor.attr &&
|
||||
(!(wdd->info->options & WDIOF_PRETIMEOUT) ||
|
||||
!IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)))
|
||||
mode = 0;
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
@ -512,6 +527,7 @@ static struct attribute *wdt_attrs[] = {
|
|||
&dev_attr_bootstatus.attr,
|
||||
&dev_attr_status.attr,
|
||||
&dev_attr_nowayout.attr,
|
||||
&dev_attr_pretimeout_governor.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -989,6 +1005,12 @@ int watchdog_dev_register(struct watchdog_device *wdd)
|
|||
return PTR_ERR(dev);
|
||||
}
|
||||
|
||||
ret = watchdog_register_pretimeout(wdd);
|
||||
if (ret) {
|
||||
device_destroy(&watchdog_class, devno);
|
||||
watchdog_cdev_unregister(wdd);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1002,6 +1024,7 @@ int watchdog_dev_register(struct watchdog_device *wdd)
|
|||
|
||||
void watchdog_dev_unregister(struct watchdog_device *wdd)
|
||||
{
|
||||
watchdog_unregister_pretimeout(wdd);
|
||||
device_destroy(&watchdog_class, wdd->wd_data->cdev.dev);
|
||||
watchdog_cdev_unregister(wdd);
|
||||
}
|
||||
|
|
131
drivers/watchdog/watchdog_pretimeout.c
Normal file
131
drivers/watchdog/watchdog_pretimeout.c
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright (C) 2015-2016 Mentor Graphics
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#include "watchdog_pretimeout.h"
|
||||
|
||||
/* Default watchdog pretimeout governor */
|
||||
static struct watchdog_governor *default_gov;
|
||||
|
||||
/* The spinlock protects default_gov, wdd->gov and pretimeout_list */
|
||||
static DEFINE_SPINLOCK(pretimeout_lock);
|
||||
|
||||
/* List of watchdog devices, which can generate a pretimeout event */
|
||||
static LIST_HEAD(pretimeout_list);
|
||||
|
||||
struct watchdog_pretimeout {
|
||||
struct watchdog_device *wdd;
|
||||
struct list_head entry;
|
||||
};
|
||||
|
||||
int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
spin_lock_irq(&pretimeout_lock);
|
||||
if (wdd->gov)
|
||||
count = sprintf(buf, "%s\n", wdd->gov->name);
|
||||
spin_unlock_irq(&pretimeout_lock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void watchdog_notify_pretimeout(struct watchdog_device *wdd)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&pretimeout_lock, flags);
|
||||
if (!wdd->gov) {
|
||||
spin_unlock_irqrestore(&pretimeout_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
wdd->gov->pretimeout(wdd);
|
||||
spin_unlock_irqrestore(&pretimeout_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout);
|
||||
|
||||
int watchdog_register_governor(struct watchdog_governor *gov)
|
||||
{
|
||||
struct watchdog_pretimeout *p;
|
||||
|
||||
if (!default_gov) {
|
||||
spin_lock_irq(&pretimeout_lock);
|
||||
default_gov = gov;
|
||||
|
||||
list_for_each_entry(p, &pretimeout_list, entry)
|
||||
if (!p->wdd->gov)
|
||||
p->wdd->gov = default_gov;
|
||||
spin_unlock_irq(&pretimeout_lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(watchdog_register_governor);
|
||||
|
||||
void watchdog_unregister_governor(struct watchdog_governor *gov)
|
||||
{
|
||||
struct watchdog_pretimeout *p;
|
||||
|
||||
spin_lock_irq(&pretimeout_lock);
|
||||
if (gov == default_gov)
|
||||
default_gov = NULL;
|
||||
|
||||
list_for_each_entry(p, &pretimeout_list, entry)
|
||||
if (p->wdd->gov == gov)
|
||||
p->wdd->gov = default_gov;
|
||||
spin_unlock_irq(&pretimeout_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(watchdog_unregister_governor);
|
||||
|
||||
int watchdog_register_pretimeout(struct watchdog_device *wdd)
|
||||
{
|
||||
struct watchdog_pretimeout *p;
|
||||
|
||||
if (!(wdd->info->options & WDIOF_PRETIMEOUT))
|
||||
return 0;
|
||||
|
||||
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_irq(&pretimeout_lock);
|
||||
list_add(&p->entry, &pretimeout_list);
|
||||
p->wdd = wdd;
|
||||
wdd->gov = default_gov;
|
||||
spin_unlock_irq(&pretimeout_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void watchdog_unregister_pretimeout(struct watchdog_device *wdd)
|
||||
{
|
||||
struct watchdog_pretimeout *p, *t;
|
||||
|
||||
if (!(wdd->info->options & WDIOF_PRETIMEOUT))
|
||||
return;
|
||||
|
||||
spin_lock_irq(&pretimeout_lock);
|
||||
wdd->gov = NULL;
|
||||
|
||||
list_for_each_entry_safe(p, t, &pretimeout_list, entry) {
|
||||
if (p->wdd == wdd) {
|
||||
list_del(&p->entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&pretimeout_lock);
|
||||
|
||||
kfree(p);
|
||||
}
|
40
drivers/watchdog/watchdog_pretimeout.h
Normal file
40
drivers/watchdog/watchdog_pretimeout.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef __WATCHDOG_PRETIMEOUT_H
|
||||
#define __WATCHDOG_PRETIMEOUT_H
|
||||
|
||||
#define WATCHDOG_GOV_NAME_MAXLEN 20
|
||||
|
||||
struct watchdog_device;
|
||||
|
||||
struct watchdog_governor {
|
||||
const char name[WATCHDOG_GOV_NAME_MAXLEN];
|
||||
void (*pretimeout)(struct watchdog_device *wdd);
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)
|
||||
/* Interfaces to watchdog pretimeout governors */
|
||||
int watchdog_register_governor(struct watchdog_governor *gov);
|
||||
void watchdog_unregister_governor(struct watchdog_governor *gov);
|
||||
|
||||
/* Interfaces to watchdog_dev.c */
|
||||
int watchdog_register_pretimeout(struct watchdog_device *wdd);
|
||||
void watchdog_unregister_pretimeout(struct watchdog_device *wdd);
|
||||
int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf);
|
||||
|
||||
#else
|
||||
static inline int watchdog_register_pretimeout(struct watchdog_device *wdd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void watchdog_unregister_pretimeout(struct watchdog_device *wdd)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int watchdog_pretimeout_governor_get(struct watchdog_device *wdd,
|
||||
char *buf)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -19,6 +19,7 @@
|
|||
struct watchdog_ops;
|
||||
struct watchdog_device;
|
||||
struct watchdog_core_data;
|
||||
struct watchdog_governor;
|
||||
|
||||
/** struct watchdog_ops - The watchdog-devices operations
|
||||
*
|
||||
|
@ -61,6 +62,7 @@ struct watchdog_ops {
|
|||
* watchdog device.
|
||||
* @info: Pointer to a watchdog_info structure.
|
||||
* @ops: Pointer to the list of watchdog operations.
|
||||
* @gov: Pointer to watchdog pretimeout governor.
|
||||
* @bootstatus: Status of the watchdog device at boot.
|
||||
* @timeout: The watchdog devices timeout value (in seconds).
|
||||
* @pretimeout: The watchdog devices pre_timeout value.
|
||||
|
@ -97,6 +99,7 @@ struct watchdog_device {
|
|||
const struct attribute_group **groups;
|
||||
const struct watchdog_info *info;
|
||||
const struct watchdog_ops *ops;
|
||||
const struct watchdog_governor *gov;
|
||||
unsigned int bootstatus;
|
||||
unsigned int timeout;
|
||||
unsigned int pretimeout;
|
||||
|
@ -185,6 +188,16 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
|
|||
return wdd->driver_data;
|
||||
}
|
||||
|
||||
/* Use the following functions to report watchdog pretimeout event */
|
||||
#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)
|
||||
void watchdog_notify_pretimeout(struct watchdog_device *wdd);
|
||||
#else
|
||||
static inline void watchdog_notify_pretimeout(struct watchdog_device *wdd)
|
||||
{
|
||||
pr_alert("watchdog%d: pretimeout event\n", wdd->id);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* drivers/watchdog/watchdog_core.c */
|
||||
void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);
|
||||
extern int watchdog_init_timeout(struct watchdog_device *wdd,
|
||||
|
|
Loading…
Reference in a new issue