From patchwork Wed May 25 13:32:25 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wolfram Sang X-Patchwork-Id: 9135355 X-Patchwork-Delegate: geert@linux-m68k.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 35DCD607D9 for ; Wed, 25 May 2016 13:33:10 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 27BD627DB3 for ; Wed, 25 May 2016 13:33:10 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 1C734282D7; Wed, 25 May 2016 13:33:10 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D252E27DB3 for ; Wed, 25 May 2016 13:33:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752546AbcEYNcx (ORCPT ); Wed, 25 May 2016 09:32:53 -0400 Received: from sauhun.de ([89.238.76.85]:36368 "EHLO pokefinder.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751965AbcEYNct (ORCPT ); Wed, 25 May 2016 09:32:49 -0400 Received: from dslb-178-008-084-178.178.008.pools.vodafone-ip.de ([178.8.84.178]:46604 helo=localhost) by pokefinder.org with esmtpsa (TLS1.2:RSA_AES_128_CBC_SHA1:128) (Exim 4.80) (envelope-from ) id 1b5YvV-0007eF-G9; Wed, 25 May 2016 15:32:45 +0200 From: Wolfram Sang To: linux-watchdog@vger.kernel.org Cc: Wolfram Sang , linux-renesas-soc@vger.kernel.org, Guenter Roeck , Vladimir Zapolskiy , Robin Gong Subject: [PATCH 1/7] watchdog: add watchdog pretimeout framework Date: Wed, 25 May 2016 15:32:25 +0200 Message-Id: <1464183151-4912-2-git-send-email-wsa@the-dreams.de> X-Mailer: git-send-email 2.8.1 In-Reply-To: <1464183151-4912-1-git-send-email-wsa@the-dreams.de> References: <1464183151-4912-1-git-send-email-wsa@the-dreams.de> Sender: linux-renesas-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-renesas-soc@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Wolfram Sang 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 a watchdog driver. By design every watchdog pretimeout governor may be compiled as a kernel module, a user selects a default watchdog pretimeout governor during compilation stage and can select another governor in runtime. Watchdogs with WDIOF_PRETIMEOUT capability now have two device attributes in sysfs: read/write pretimeout_governor attribute and read only pretimeout_available_governors attribute. Watchdogs with no WDIOF_PRETIMEOUT capability has no changes in sysfs. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Wolfram Sang --- Changes since Vladimir's last version: * rebased adapt to the internal data reorganization, especially the now private struct device *dev * dropped can_sleep support! The additional lock, list, and workqueue made the code quite complex. The only user was the userspace governor which can be reworked to let the watchdog device code do the bottom half. In addition, I am not fully convinced sending a uevent is the proper thing to do, but this needs to be discussed in another thread. Removing this support makes the code much easier to follow (locking!), saves 30% of LoC + a list + a workqueue. * moved pretimeout registration from watchdog_core to watchdog_dev Let's handle it exactly where the device is created, so we have access to the now private device pointer for adding the sysfs files. * don't export watchdog_(un)register_pretimeout since they are linked to the core anyhow * whitespace cleanups drivers/watchdog/Kconfig | 8 + drivers/watchdog/Makefile | 6 +- drivers/watchdog/watchdog_dev.c | 8 + drivers/watchdog/watchdog_pretimeout.c | 269 +++++++++++++++++++++++++++++++++ drivers/watchdog/watchdog_pretimeout.h | 35 +++++ include/linux/watchdog.h | 10 ++ 6 files changed, 334 insertions(+), 2 deletions(-) create mode 100644 drivers/watchdog/watchdog_pretimeout.c create mode 100644 drivers/watchdog/watchdog_pretimeout.h diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 3902c9ca7f099d..909d1021de5cbc 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1774,4 +1774,12 @@ config USBPCWATCHDOG Most people will say N. +comment "Watchdog Pretimeout Governors" + +config WATCHDOG_PRETIMEOUT_GOV + bool "Enable watchdog pretimeout governors" + default n + help + The option allows to select watchdog pretimeout governors. + endif # WATCHDOG diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 2cbc9709852d0e..820860cf3e8d62 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -3,8 +3,10 @@ # # 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-y += 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 diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 3595cffa24ea49..5d028f94a90743 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -49,6 +49,7 @@ #include /* For copy_to_user/put_user/... */ #include "watchdog_core.h" +#include "watchdog_pretimeout.h" /* * struct watchdog_core_data - watchdog core internal data @@ -911,6 +912,12 @@ int watchdog_dev_register(struct watchdog_device *wdd) return PTR_ERR(dev); } + ret = watchdog_register_pretimeout(wdd, dev); + if (ret) { + device_destroy(&watchdog_class, devno); + watchdog_cdev_unregister(wdd); + } + return ret; } @@ -924,6 +931,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); } diff --git a/drivers/watchdog/watchdog_pretimeout.c b/drivers/watchdog/watchdog_pretimeout.c new file mode 100644 index 00000000000000..87a10ebeaacc7e --- /dev/null +++ b/drivers/watchdog/watchdog_pretimeout.c @@ -0,0 +1,269 @@ +/* + * Watchdog pretimout governor framework + * + * Copyright (C) 2015 Mentor Graphics + * Copyright (C) 2016 Renesas Electronics Corporation + * Copyright (C) 2016 Sang Engineering, Wolfram Sang + * + * 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 +#include +#include + +#include "watchdog_pretimeout.h" + +/* The mutex protects governor list and serializes external interfaces */ +static DEFINE_MUTEX(governor_lock); + +/* List of registered watchdog pretimeout governors */ +static LIST_HEAD(governor_list); + +/* The spinlock protects wdd->gov */ +static DEFINE_SPINLOCK(pretimeout_lock); + +struct governor_priv { + struct watchdog_governor *gov; + bool is_default; + struct list_head entry; +}; + +static struct governor_priv *find_governor_by_name(const char *gov_name) +{ + struct governor_priv *priv; + + list_for_each_entry(priv, &governor_list, entry) + if (!strncmp(gov_name, priv->gov->name, WATCHDOG_GOV_NAME_LEN)) + return priv; + + return NULL; +} + +static struct watchdog_governor *find_default_governor(void) +{ + struct governor_priv *priv; + + list_for_each_entry(priv, &governor_list, entry) + if (priv->is_default) + return priv->gov; + + return NULL; +} + +void watchdog_notify_pretimeout(struct watchdog_device *wdd) +{ + unsigned long flags; + + if (!wdd) + return; + + spin_lock_irqsave(&pretimeout_lock, flags); + + if (wdd->gov) + 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 governor_priv *priv; + + if (!gov || !gov->pretimeout || strlen(gov->name) >= WATCHDOG_GOV_NAME_LEN) + return -EINVAL; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_lock(&governor_lock); + + if (find_governor_by_name(gov->name)) { + kfree(priv); + mutex_unlock(&governor_lock); + return -EBUSY; + } + + priv->gov = gov; + + if (!strncmp(WATCHDOG_PRETIMEOUT_DEFAULT_GOV, + gov->name, WATCHDOG_GOV_NAME_LEN)) { + priv->is_default = true; + } + + list_add(&priv->entry, &governor_list); + + mutex_unlock(&governor_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(watchdog_register_governor); + +void watchdog_unregister_governor(struct watchdog_governor *gov) +{ + struct governor_priv *priv; + + if (!gov) + return; + + mutex_lock(&governor_lock); + + list_for_each_entry(priv, &governor_list, entry) { + if (priv->gov == gov) { + list_del(&priv->entry); + kfree(priv); + break; + } + } + + mutex_unlock(&governor_lock); +} +EXPORT_SYMBOL_GPL(watchdog_unregister_governor); + +static ssize_t pretimeout_governor_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + struct governor_priv *priv; + + mutex_lock(&governor_lock); + + priv = find_governor_by_name(buf); + if (!priv) { + mutex_unlock(&governor_lock); + return -EINVAL; + } + + if (!try_module_get(priv->gov->owner)) { + mutex_unlock(&governor_lock); + return -ENODEV; + } + + spin_lock_irq(&pretimeout_lock); + if (wdd->gov) + module_put(wdd->gov->owner); + wdd->gov = priv->gov; + spin_unlock_irq(&pretimeout_lock); + + mutex_unlock(&governor_lock); + + return count; +} + +static ssize_t pretimeout_governor_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + char gov_name[WATCHDOG_GOV_NAME_LEN] = { 0 }; + int count; + + mutex_lock(&governor_lock); + + spin_lock_irq(&pretimeout_lock); + if (wdd->gov) + strncpy(gov_name, wdd->gov->name, WATCHDOG_GOV_NAME_LEN); + spin_unlock_irq(&pretimeout_lock); + + count = sprintf(buf, "%s\n", gov_name); + + mutex_unlock(&governor_lock); + + return count; +} +static DEVICE_ATTR_RW(pretimeout_governor); + +static ssize_t pretimeout_available_governors_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct governor_priv *priv; + int count = 0; + + mutex_lock(&governor_lock); + + list_for_each_entry(priv, &governor_list, entry) + count += sprintf(buf + count, "%s\n", priv->gov->name); + + mutex_unlock(&governor_lock); + + return count; +} +static DEVICE_ATTR_RO(pretimeout_available_governors); + +static struct attribute *wdd_pretimeout_attrs[] = { + &dev_attr_pretimeout_governor.attr, + &dev_attr_pretimeout_available_governors.attr, + NULL, +}; + +static umode_t wdd_pretimeout_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct watchdog_device *wdd = dev_get_drvdata(dev); + + if (wdd->info->options & WDIOF_PRETIMEOUT) + return attr->mode; + + return 0; +} + +static const struct attribute_group wdd_pretimeout_group = { + .is_visible = wdd_pretimeout_attr_is_visible, + .attrs = wdd_pretimeout_attrs, +}; + +static const struct attribute_group *wdd_pretimeout_groups[] = { + &wdd_pretimeout_group, + NULL, +}; + +int watchdog_register_pretimeout(struct watchdog_device *wdd, struct device *dev) +{ + struct watchdog_governor *gov; + int ret; + + if (!(wdd->info->options & WDIOF_PRETIMEOUT)) + return 0; + + ret = sysfs_create_groups(&dev->kobj, wdd_pretimeout_groups); + if (ret) + return ret; + + mutex_lock(&governor_lock); + gov = find_default_governor(); + if (gov && !try_module_get(gov->owner)) { + mutex_unlock(&governor_lock); + return -ENODEV; + } + + spin_lock_irq(&pretimeout_lock); + wdd->gov = gov; + spin_unlock_irq(&pretimeout_lock); + + mutex_unlock(&governor_lock); + + return 0; +} + +void watchdog_unregister_pretimeout(struct watchdog_device *wdd) +{ + if (!(wdd->info->options & WDIOF_PRETIMEOUT)) + return; + + spin_lock_irq(&pretimeout_lock); + if (wdd->gov) + module_put(wdd->gov->owner); + wdd->gov = NULL; + spin_unlock_irq(&pretimeout_lock); +} + +MODULE_AUTHOR("Vladimir Zapolskiy "); +MODULE_DESCRIPTION("Watchdog pretimeout governor framework"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/watchdog_pretimeout.h b/drivers/watchdog/watchdog_pretimeout.h new file mode 100644 index 00000000000000..38965cdd23bebe --- /dev/null +++ b/drivers/watchdog/watchdog_pretimeout.h @@ -0,0 +1,35 @@ +#ifndef __WATCHDOG_PRETIMEOUT_H +#define __WATCHDOG_PRETIMEOUT_H + +#define WATCHDOG_GOV_NAME_LEN 20 + +struct module; +struct watchdog_device; + +struct watchdog_governor { + const char name[WATCHDOG_GOV_NAME_LEN]; + void (*pretimeout)(struct watchdog_device *wdd); + struct module *owner; +}; + +/* Interfaces to watchdog pretimeout governors */ +int watchdog_register_governor(struct watchdog_governor *gov); +void watchdog_unregister_governor(struct watchdog_governor *gov); + +/* Interfaces to watchdog_core.c */ +#ifdef CONFIG_WATCHDOG_PRETIMEOUT_GOV + +#define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "none" + +int watchdog_register_pretimeout(struct watchdog_device *wdd, struct device *dev); +void watchdog_unregister_pretimeout(struct watchdog_device *wdd); +#else +static inline int watchdog_register_pretimeout(struct watchdog_device *wdd, + struct device *dev) +{ + return 0; +} +static inline void watchdog_unregister_pretimeout(struct watchdog_device *wdd) {} +#endif + +#endif diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 51732d6c9555de..4b2fa45370fceb 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -19,6 +19,7 @@ struct watchdog_ops; struct watchdog_device; struct watchdog_core_data; +struct watchdog_governor; /** struct watchdog_ops - The watchdog-devices operations * @@ -59,6 +60,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). * @min_timeout:The watchdog devices minimum timeout value (in seconds). @@ -93,6 +95,7 @@ struct watchdog_device { const struct attribute_group **groups; const struct watchdog_info *info; const struct watchdog_ops *ops; + struct watchdog_governor *gov; unsigned int bootstatus; unsigned int timeout; unsigned int min_timeout; @@ -180,4 +183,11 @@ extern int watchdog_init_timeout(struct watchdog_device *wdd, extern int watchdog_register_device(struct watchdog_device *); extern void watchdog_unregister_device(struct watchdog_device *); +/* drivers/watchdog/watchdog_pretimeout.c */ +#ifdef CONFIG_WATCHDOG_PRETIMEOUT_GOV +void watchdog_notify_pretimeout(struct watchdog_device *wdd); +#else +static inline void watchdog_notify_pretimeout(struct watchdog_device *wdd) {} +#endif + #endif /* ifndef _LINUX_WATCHDOG_H */