Commit ff84136c authored by Vladimir Zapolskiy's avatar Vladimir Zapolskiy Committed by Wim Van Sebroeck

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: default avatarVladimir Zapolskiy <vladimir_zapolskiy@mentor.com>
Reviewed-by: default avatarGuenter Roeck <linux@roeck-us.net>
Reviewed-by: default avatarWolfram Sang <wsa+renesas@sang-engineering.com>
Tested-by: default avatarWolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
Signed-off-by: default avatarWim Van Sebroeck <wim@iguana.be>
parent fc113d54
...@@ -48,6 +48,7 @@ struct watchdog_device { ...@@ -48,6 +48,7 @@ struct watchdog_device {
const struct attribute_group **groups; const struct attribute_group **groups;
const struct watchdog_info *info; const struct watchdog_info *info;
const struct watchdog_ops *ops; const struct watchdog_ops *ops;
const struct watchdog_governor *gov;
unsigned int bootstatus; unsigned int bootstatus;
unsigned int timeout; unsigned int timeout;
unsigned int pretimeout; unsigned int pretimeout;
...@@ -75,6 +76,7 @@ It contains following fields: ...@@ -75,6 +76,7 @@ It contains following fields:
* 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.
* gov: a pointer to the assigned watchdog device pretimeout governor or NULL.
* timeout: the watchdog timer's timeout value (in seconds). * timeout: the watchdog timer's timeout value (in seconds).
This is the time after which the system will reboot if user space does This is the time after which the system will reboot if user space does
not send a heartbeat request if WDOG_ACTIVE is set. not send a heartbeat request if WDOG_ACTIVE is set.
...@@ -288,3 +290,14 @@ User should follow the following guidelines for setting the priority: ...@@ -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 * 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 available, and/or if restart is sufficient to restart the entire system
* 255: highest priority, will preempt all other restart handlers * 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 ...@@ -1831,4 +1831,11 @@ config USBPCWATCHDOG
Most people will say N. 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 endif # WATCHDOG
...@@ -3,9 +3,12 @@ ...@@ -3,9 +3,12 @@
# #
# The WatchDog Timer Driver Core. # The WatchDog Timer Driver Core.
watchdog-objs += watchdog_core.o watchdog_dev.o
obj-$(CONFIG_WATCHDOG_CORE) += watchdog.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 # Only one watchdog can succeed. We probe the ISA/PCI/USB based
# watchdog-cards first, then the architecture specific watchdog # watchdog-cards first, then the architecture specific watchdog
# drivers and then the architecture independent "softdog" driver. # drivers and then the architecture independent "softdog" driver.
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */ #include <linux/uaccess.h> /* For copy_to_user/put_user/... */
#include "watchdog_core.h" #include "watchdog_core.h"
#include "watchdog_pretimeout.h"
/* /*
* struct watchdog_core_data - watchdog core internal data * struct watchdog_core_data - watchdog core internal data
...@@ -488,6 +489,16 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, ...@@ -488,6 +489,16 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
} }
static DEVICE_ATTR_RO(state); 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, static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
int n) int n)
{ {
...@@ -500,6 +511,10 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, ...@@ -500,6 +511,10 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
else if (attr == &dev_attr_pretimeout.attr && else if (attr == &dev_attr_pretimeout.attr &&
!(wdd->info->options & WDIOF_PRETIMEOUT)) !(wdd->info->options & WDIOF_PRETIMEOUT))
mode = 0; 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; return mode;
} }
...@@ -512,6 +527,7 @@ static struct attribute *wdt_attrs[] = { ...@@ -512,6 +527,7 @@ static struct attribute *wdt_attrs[] = {
&dev_attr_bootstatus.attr, &dev_attr_bootstatus.attr,
&dev_attr_status.attr, &dev_attr_status.attr,
&dev_attr_nowayout.attr, &dev_attr_nowayout.attr,
&dev_attr_pretimeout_governor.attr,
NULL, NULL,
}; };
...@@ -989,6 +1005,12 @@ int watchdog_dev_register(struct watchdog_device *wdd) ...@@ -989,6 +1005,12 @@ int watchdog_dev_register(struct watchdog_device *wdd)
return PTR_ERR(dev); return PTR_ERR(dev);
} }
ret = watchdog_register_pretimeout(wdd);
if (ret) {
device_destroy(&watchdog_class, devno);
watchdog_cdev_unregister(wdd);
}
return ret; return ret;
} }
...@@ -1002,6 +1024,7 @@ int watchdog_dev_register(struct watchdog_device *wdd) ...@@ -1002,6 +1024,7 @@ int watchdog_dev_register(struct watchdog_device *wdd)
void watchdog_dev_unregister(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); device_destroy(&watchdog_class, wdd->wd_data->cdev.dev);
watchdog_cdev_unregister(wdd); watchdog_cdev_unregister(wdd);
} }
......
/*
* 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);
}
#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 @@ ...@@ -19,6 +19,7 @@
struct watchdog_ops; struct watchdog_ops;
struct watchdog_device; struct watchdog_device;
struct watchdog_core_data; struct watchdog_core_data;
struct watchdog_governor;
/** struct watchdog_ops - The watchdog-devices operations /** struct watchdog_ops - The watchdog-devices operations
* *
...@@ -61,6 +62,7 @@ struct watchdog_ops { ...@@ -61,6 +62,7 @@ struct watchdog_ops {
* watchdog device. * watchdog 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.
* @gov: Pointer to watchdog pretimeout governor.
* @bootstatus: Status of the watchdog device at boot. * @bootstatus: Status of the watchdog device at boot.
* @timeout: The watchdog devices timeout value (in seconds). * @timeout: The watchdog devices timeout value (in seconds).
* @pretimeout: The watchdog devices pre_timeout value. * @pretimeout: The watchdog devices pre_timeout value.
...@@ -97,6 +99,7 @@ struct watchdog_device { ...@@ -97,6 +99,7 @@ struct watchdog_device {
const struct attribute_group **groups; const struct attribute_group **groups;
const struct watchdog_info *info; const struct watchdog_info *info;
const struct watchdog_ops *ops; const struct watchdog_ops *ops;
const struct watchdog_governor *gov;
unsigned int bootstatus; unsigned int bootstatus;
unsigned int timeout; unsigned int timeout;
unsigned int pretimeout; unsigned int pretimeout;
...@@ -185,6 +188,16 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd) ...@@ -185,6 +188,16 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
return wdd->driver_data; 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 */ /* drivers/watchdog/watchdog_core.c */
void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority); void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);
extern int watchdog_init_timeout(struct watchdog_device *wdd, extern int watchdog_init_timeout(struct watchdog_device *wdd,
......
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