Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Vladimir Zapolskiy and committed by
Wim Van Sebroeck
ff84136c fc113d54

+231 -1
+13
Documentation/watchdog/watchdog-kernel-api.txt
··· 48 48 const struct attribute_group **groups; 49 49 const struct watchdog_info *info; 50 50 const struct watchdog_ops *ops; 51 + const struct watchdog_governor *gov; 51 52 unsigned int bootstatus; 52 53 unsigned int timeout; 53 54 unsigned int pretimeout; ··· 76 75 * info: a pointer to a watchdog_info structure. This structure gives some 77 76 additional information about the watchdog timer itself. (Like it's unique name) 78 77 * ops: a pointer to the list of watchdog operations that the watchdog supports. 78 + * gov: a pointer to the assigned watchdog device pretimeout governor or NULL. 79 79 * timeout: the watchdog timer's timeout value (in seconds). 80 80 This is the time after which the system will reboot if user space does 81 81 not send a heartbeat request if WDOG_ACTIVE is set. ··· 290 288 * 128: default restart handler, use if no other handler is expected to be 291 289 available, and/or if restart is sufficient to restart the entire system 292 290 * 255: highest priority, will preempt all other restart handlers 291 + 292 + To raise a pretimeout notification, the following function should be used: 293 + 294 + void watchdog_notify_pretimeout(struct watchdog_device *wdd) 295 + 296 + The function can be called in the interrupt context. If watchdog pretimeout 297 + governor framework (kbuild CONFIG_WATCHDOG_PRETIMEOUT_GOV symbol) is enabled, 298 + an action is taken by a preconfigured pretimeout governor preassigned to 299 + the watchdog device. If watchdog pretimeout governor framework is not 300 + enabled, watchdog_notify_pretimeout() prints a notification message to 301 + the kernel log buffer.
+7
drivers/watchdog/Kconfig
··· 1831 1831 1832 1832 Most people will say N. 1833 1833 1834 + comment "Watchdog Pretimeout Governors" 1835 + 1836 + config WATCHDOG_PRETIMEOUT_GOV 1837 + bool "Enable watchdog pretimeout governors" 1838 + help 1839 + The option allows to select watchdog pretimeout governors. 1840 + 1834 1841 endif # WATCHDOG
+4 -1
drivers/watchdog/Makefile
··· 3 3 # 4 4 5 5 # The WatchDog Timer Driver Core. 6 - watchdog-objs += watchdog_core.o watchdog_dev.o 7 6 obj-$(CONFIG_WATCHDOG_CORE) += watchdog.o 7 + 8 + watchdog-objs += watchdog_core.o watchdog_dev.o 9 + 10 + watchdog-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV) += watchdog_pretimeout.o 8 11 9 12 # Only one watchdog can succeed. We probe the ISA/PCI/USB based 10 13 # watchdog-cards first, then the architecture specific watchdog
+23
drivers/watchdog/watchdog_dev.c
··· 49 49 #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ 50 50 51 51 #include "watchdog_core.h" 52 + #include "watchdog_pretimeout.h" 52 53 53 54 /* 54 55 * struct watchdog_core_data - watchdog core internal data ··· 489 488 } 490 489 static DEVICE_ATTR_RO(state); 491 490 491 + static ssize_t pretimeout_governor_show(struct device *dev, 492 + struct device_attribute *attr, 493 + char *buf) 494 + { 495 + struct watchdog_device *wdd = dev_get_drvdata(dev); 496 + 497 + return watchdog_pretimeout_governor_get(wdd, buf); 498 + } 499 + static DEVICE_ATTR_RO(pretimeout_governor); 500 + 492 501 static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, 493 502 int n) 494 503 { ··· 510 499 mode = 0; 511 500 else if (attr == &dev_attr_pretimeout.attr && 512 501 !(wdd->info->options & WDIOF_PRETIMEOUT)) 502 + mode = 0; 503 + else if (attr == &dev_attr_pretimeout_governor.attr && 504 + (!(wdd->info->options & WDIOF_PRETIMEOUT) || 505 + !IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV))) 513 506 mode = 0; 514 507 515 508 return mode; ··· 527 512 &dev_attr_bootstatus.attr, 528 513 &dev_attr_status.attr, 529 514 &dev_attr_nowayout.attr, 515 + &dev_attr_pretimeout_governor.attr, 530 516 NULL, 531 517 }; 532 518 ··· 1005 989 return PTR_ERR(dev); 1006 990 } 1007 991 992 + ret = watchdog_register_pretimeout(wdd); 993 + if (ret) { 994 + device_destroy(&watchdog_class, devno); 995 + watchdog_cdev_unregister(wdd); 996 + } 997 + 1008 998 return ret; 1009 999 } 1010 1000 ··· 1024 1002 1025 1003 void watchdog_dev_unregister(struct watchdog_device *wdd) 1026 1004 { 1005 + watchdog_unregister_pretimeout(wdd); 1027 1006 device_destroy(&watchdog_class, wdd->wd_data->cdev.dev); 1028 1007 watchdog_cdev_unregister(wdd); 1029 1008 }
+131
drivers/watchdog/watchdog_pretimeout.c
··· 1 + /* 2 + * Copyright (C) 2015-2016 Mentor Graphics 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + * 9 + */ 10 + 11 + #include <linux/list.h> 12 + #include <linux/slab.h> 13 + #include <linux/spinlock.h> 14 + #include <linux/watchdog.h> 15 + 16 + #include "watchdog_pretimeout.h" 17 + 18 + /* Default watchdog pretimeout governor */ 19 + static struct watchdog_governor *default_gov; 20 + 21 + /* The spinlock protects default_gov, wdd->gov and pretimeout_list */ 22 + static DEFINE_SPINLOCK(pretimeout_lock); 23 + 24 + /* List of watchdog devices, which can generate a pretimeout event */ 25 + static LIST_HEAD(pretimeout_list); 26 + 27 + struct watchdog_pretimeout { 28 + struct watchdog_device *wdd; 29 + struct list_head entry; 30 + }; 31 + 32 + int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf) 33 + { 34 + int count = 0; 35 + 36 + spin_lock_irq(&pretimeout_lock); 37 + if (wdd->gov) 38 + count = sprintf(buf, "%s\n", wdd->gov->name); 39 + spin_unlock_irq(&pretimeout_lock); 40 + 41 + return count; 42 + } 43 + 44 + void watchdog_notify_pretimeout(struct watchdog_device *wdd) 45 + { 46 + unsigned long flags; 47 + 48 + spin_lock_irqsave(&pretimeout_lock, flags); 49 + if (!wdd->gov) { 50 + spin_unlock_irqrestore(&pretimeout_lock, flags); 51 + return; 52 + } 53 + 54 + wdd->gov->pretimeout(wdd); 55 + spin_unlock_irqrestore(&pretimeout_lock, flags); 56 + } 57 + EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout); 58 + 59 + int watchdog_register_governor(struct watchdog_governor *gov) 60 + { 61 + struct watchdog_pretimeout *p; 62 + 63 + if (!default_gov) { 64 + spin_lock_irq(&pretimeout_lock); 65 + default_gov = gov; 66 + 67 + list_for_each_entry(p, &pretimeout_list, entry) 68 + if (!p->wdd->gov) 69 + p->wdd->gov = default_gov; 70 + spin_unlock_irq(&pretimeout_lock); 71 + } 72 + 73 + return 0; 74 + } 75 + EXPORT_SYMBOL(watchdog_register_governor); 76 + 77 + void watchdog_unregister_governor(struct watchdog_governor *gov) 78 + { 79 + struct watchdog_pretimeout *p; 80 + 81 + spin_lock_irq(&pretimeout_lock); 82 + if (gov == default_gov) 83 + default_gov = NULL; 84 + 85 + list_for_each_entry(p, &pretimeout_list, entry) 86 + if (p->wdd->gov == gov) 87 + p->wdd->gov = default_gov; 88 + spin_unlock_irq(&pretimeout_lock); 89 + } 90 + EXPORT_SYMBOL(watchdog_unregister_governor); 91 + 92 + int watchdog_register_pretimeout(struct watchdog_device *wdd) 93 + { 94 + struct watchdog_pretimeout *p; 95 + 96 + if (!(wdd->info->options & WDIOF_PRETIMEOUT)) 97 + return 0; 98 + 99 + p = kzalloc(sizeof(*p), GFP_KERNEL); 100 + if (!p) 101 + return -ENOMEM; 102 + 103 + spin_lock_irq(&pretimeout_lock); 104 + list_add(&p->entry, &pretimeout_list); 105 + p->wdd = wdd; 106 + wdd->gov = default_gov; 107 + spin_unlock_irq(&pretimeout_lock); 108 + 109 + return 0; 110 + } 111 + 112 + void watchdog_unregister_pretimeout(struct watchdog_device *wdd) 113 + { 114 + struct watchdog_pretimeout *p, *t; 115 + 116 + if (!(wdd->info->options & WDIOF_PRETIMEOUT)) 117 + return; 118 + 119 + spin_lock_irq(&pretimeout_lock); 120 + wdd->gov = NULL; 121 + 122 + list_for_each_entry_safe(p, t, &pretimeout_list, entry) { 123 + if (p->wdd == wdd) { 124 + list_del(&p->entry); 125 + break; 126 + } 127 + } 128 + spin_unlock_irq(&pretimeout_lock); 129 + 130 + kfree(p); 131 + }
+40
drivers/watchdog/watchdog_pretimeout.h
··· 1 + #ifndef __WATCHDOG_PRETIMEOUT_H 2 + #define __WATCHDOG_PRETIMEOUT_H 3 + 4 + #define WATCHDOG_GOV_NAME_MAXLEN 20 5 + 6 + struct watchdog_device; 7 + 8 + struct watchdog_governor { 9 + const char name[WATCHDOG_GOV_NAME_MAXLEN]; 10 + void (*pretimeout)(struct watchdog_device *wdd); 11 + }; 12 + 13 + #if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV) 14 + /* Interfaces to watchdog pretimeout governors */ 15 + int watchdog_register_governor(struct watchdog_governor *gov); 16 + void watchdog_unregister_governor(struct watchdog_governor *gov); 17 + 18 + /* Interfaces to watchdog_dev.c */ 19 + int watchdog_register_pretimeout(struct watchdog_device *wdd); 20 + void watchdog_unregister_pretimeout(struct watchdog_device *wdd); 21 + int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf); 22 + 23 + #else 24 + static inline int watchdog_register_pretimeout(struct watchdog_device *wdd) 25 + { 26 + return 0; 27 + } 28 + 29 + static inline void watchdog_unregister_pretimeout(struct watchdog_device *wdd) 30 + { 31 + } 32 + 33 + static inline int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, 34 + char *buf) 35 + { 36 + return -EINVAL; 37 + } 38 + #endif 39 + 40 + #endif
+13
include/linux/watchdog.h
··· 19 19 struct watchdog_ops; 20 20 struct watchdog_device; 21 21 struct watchdog_core_data; 22 + struct watchdog_governor; 22 23 23 24 /** struct watchdog_ops - The watchdog-devices operations 24 25 * ··· 62 61 * watchdog device. 63 62 * @info: Pointer to a watchdog_info structure. 64 63 * @ops: Pointer to the list of watchdog operations. 64 + * @gov: Pointer to watchdog pretimeout governor. 65 65 * @bootstatus: Status of the watchdog device at boot. 66 66 * @timeout: The watchdog devices timeout value (in seconds). 67 67 * @pretimeout: The watchdog devices pre_timeout value. ··· 99 97 const struct attribute_group **groups; 100 98 const struct watchdog_info *info; 101 99 const struct watchdog_ops *ops; 100 + const struct watchdog_governor *gov; 102 101 unsigned int bootstatus; 103 102 unsigned int timeout; 104 103 unsigned int pretimeout; ··· 187 184 { 188 185 return wdd->driver_data; 189 186 } 187 + 188 + /* Use the following functions to report watchdog pretimeout event */ 189 + #if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV) 190 + void watchdog_notify_pretimeout(struct watchdog_device *wdd); 191 + #else 192 + static inline void watchdog_notify_pretimeout(struct watchdog_device *wdd) 193 + { 194 + pr_alert("watchdog%d: pretimeout event\n", wdd->id); 195 + } 196 + #endif 190 197 191 198 /* drivers/watchdog/watchdog_core.c */ 192 199 void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);