12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2ff84136cSVladimir Zapolskiy /* 3ff84136cSVladimir Zapolskiy * Copyright (C) 2015-2016 Mentor Graphics 4ff84136cSVladimir Zapolskiy */ 5ff84136cSVladimir Zapolskiy 6ff84136cSVladimir Zapolskiy #include <linux/list.h> 7ff84136cSVladimir Zapolskiy #include <linux/slab.h> 8ff84136cSVladimir Zapolskiy #include <linux/spinlock.h> 953f96ceeSVladimir Zapolskiy #include <linux/string.h> 10ff84136cSVladimir Zapolskiy #include <linux/watchdog.h> 11ff84136cSVladimir Zapolskiy 12*7b7d2fdcSCurtis Klein #include "watchdog_core.h" 13ff84136cSVladimir Zapolskiy #include "watchdog_pretimeout.h" 14ff84136cSVladimir Zapolskiy 15ff84136cSVladimir Zapolskiy /* Default watchdog pretimeout governor */ 16ff84136cSVladimir Zapolskiy static struct watchdog_governor *default_gov; 17ff84136cSVladimir Zapolskiy 18ff84136cSVladimir Zapolskiy /* The spinlock protects default_gov, wdd->gov and pretimeout_list */ 19ff84136cSVladimir Zapolskiy static DEFINE_SPINLOCK(pretimeout_lock); 20ff84136cSVladimir Zapolskiy 21ff84136cSVladimir Zapolskiy /* List of watchdog devices, which can generate a pretimeout event */ 22ff84136cSVladimir Zapolskiy static LIST_HEAD(pretimeout_list); 23ff84136cSVladimir Zapolskiy 24ff84136cSVladimir Zapolskiy struct watchdog_pretimeout { 25ff84136cSVladimir Zapolskiy struct watchdog_device *wdd; 26ff84136cSVladimir Zapolskiy struct list_head entry; 27ff84136cSVladimir Zapolskiy }; 28ff84136cSVladimir Zapolskiy 2953f96ceeSVladimir Zapolskiy /* The mutex protects governor list and serializes external interfaces */ 3053f96ceeSVladimir Zapolskiy static DEFINE_MUTEX(governor_lock); 3153f96ceeSVladimir Zapolskiy 3253f96ceeSVladimir Zapolskiy /* List of the registered watchdog pretimeout governors */ 3353f96ceeSVladimir Zapolskiy static LIST_HEAD(governor_list); 3453f96ceeSVladimir Zapolskiy 3553f96ceeSVladimir Zapolskiy struct governor_priv { 3653f96ceeSVladimir Zapolskiy struct watchdog_governor *gov; 3753f96ceeSVladimir Zapolskiy struct list_head entry; 3853f96ceeSVladimir Zapolskiy }; 3953f96ceeSVladimir Zapolskiy 4053f96ceeSVladimir Zapolskiy static struct governor_priv *find_governor_by_name(const char *gov_name) 4153f96ceeSVladimir Zapolskiy { 4253f96ceeSVladimir Zapolskiy struct governor_priv *priv; 4353f96ceeSVladimir Zapolskiy 4453f96ceeSVladimir Zapolskiy list_for_each_entry(priv, &governor_list, entry) 4553f96ceeSVladimir Zapolskiy if (sysfs_streq(gov_name, priv->gov->name)) 4653f96ceeSVladimir Zapolskiy return priv; 4753f96ceeSVladimir Zapolskiy 4853f96ceeSVladimir Zapolskiy return NULL; 4953f96ceeSVladimir Zapolskiy } 5053f96ceeSVladimir Zapolskiy 5189873a71SVladimir Zapolskiy int watchdog_pretimeout_available_governors_get(char *buf) 5289873a71SVladimir Zapolskiy { 5389873a71SVladimir Zapolskiy struct governor_priv *priv; 5489873a71SVladimir Zapolskiy int count = 0; 5589873a71SVladimir Zapolskiy 5689873a71SVladimir Zapolskiy mutex_lock(&governor_lock); 5789873a71SVladimir Zapolskiy 5889873a71SVladimir Zapolskiy list_for_each_entry(priv, &governor_list, entry) 593bb21781SJuerg Haefliger count += sysfs_emit_at(buf, count, "%s\n", priv->gov->name); 6089873a71SVladimir Zapolskiy 6189873a71SVladimir Zapolskiy mutex_unlock(&governor_lock); 6289873a71SVladimir Zapolskiy 6389873a71SVladimir Zapolskiy return count; 6489873a71SVladimir Zapolskiy } 6589873a71SVladimir Zapolskiy 66ff84136cSVladimir Zapolskiy int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf) 67ff84136cSVladimir Zapolskiy { 68ff84136cSVladimir Zapolskiy int count = 0; 69ff84136cSVladimir Zapolskiy 70ff84136cSVladimir Zapolskiy spin_lock_irq(&pretimeout_lock); 71ff84136cSVladimir Zapolskiy if (wdd->gov) 723bb21781SJuerg Haefliger count = sysfs_emit(buf, "%s\n", wdd->gov->name); 73ff84136cSVladimir Zapolskiy spin_unlock_irq(&pretimeout_lock); 74ff84136cSVladimir Zapolskiy 75ff84136cSVladimir Zapolskiy return count; 76ff84136cSVladimir Zapolskiy } 77ff84136cSVladimir Zapolskiy 7853f96ceeSVladimir Zapolskiy int watchdog_pretimeout_governor_set(struct watchdog_device *wdd, 7953f96ceeSVladimir Zapolskiy const char *buf) 8053f96ceeSVladimir Zapolskiy { 8153f96ceeSVladimir Zapolskiy struct governor_priv *priv; 8253f96ceeSVladimir Zapolskiy 8353f96ceeSVladimir Zapolskiy mutex_lock(&governor_lock); 8453f96ceeSVladimir Zapolskiy 8553f96ceeSVladimir Zapolskiy priv = find_governor_by_name(buf); 8653f96ceeSVladimir Zapolskiy if (!priv) { 8753f96ceeSVladimir Zapolskiy mutex_unlock(&governor_lock); 8853f96ceeSVladimir Zapolskiy return -EINVAL; 8953f96ceeSVladimir Zapolskiy } 9053f96ceeSVladimir Zapolskiy 9153f96ceeSVladimir Zapolskiy spin_lock_irq(&pretimeout_lock); 9253f96ceeSVladimir Zapolskiy wdd->gov = priv->gov; 9353f96ceeSVladimir Zapolskiy spin_unlock_irq(&pretimeout_lock); 9453f96ceeSVladimir Zapolskiy 9553f96ceeSVladimir Zapolskiy mutex_unlock(&governor_lock); 9653f96ceeSVladimir Zapolskiy 9753f96ceeSVladimir Zapolskiy return 0; 9853f96ceeSVladimir Zapolskiy } 9953f96ceeSVladimir Zapolskiy 100ff84136cSVladimir Zapolskiy void watchdog_notify_pretimeout(struct watchdog_device *wdd) 101ff84136cSVladimir Zapolskiy { 102ff84136cSVladimir Zapolskiy unsigned long flags; 103ff84136cSVladimir Zapolskiy 104ff84136cSVladimir Zapolskiy spin_lock_irqsave(&pretimeout_lock, flags); 105ff84136cSVladimir Zapolskiy if (!wdd->gov) { 106ff84136cSVladimir Zapolskiy spin_unlock_irqrestore(&pretimeout_lock, flags); 107ff84136cSVladimir Zapolskiy return; 108ff84136cSVladimir Zapolskiy } 109ff84136cSVladimir Zapolskiy 110ff84136cSVladimir Zapolskiy wdd->gov->pretimeout(wdd); 111ff84136cSVladimir Zapolskiy spin_unlock_irqrestore(&pretimeout_lock, flags); 112ff84136cSVladimir Zapolskiy } 113ff84136cSVladimir Zapolskiy EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout); 114ff84136cSVladimir Zapolskiy 115ff84136cSVladimir Zapolskiy int watchdog_register_governor(struct watchdog_governor *gov) 116ff84136cSVladimir Zapolskiy { 117ff84136cSVladimir Zapolskiy struct watchdog_pretimeout *p; 11853f96ceeSVladimir Zapolskiy struct governor_priv *priv; 11953f96ceeSVladimir Zapolskiy 12053f96ceeSVladimir Zapolskiy priv = kzalloc(sizeof(*priv), GFP_KERNEL); 12153f96ceeSVladimir Zapolskiy if (!priv) 12253f96ceeSVladimir Zapolskiy return -ENOMEM; 12353f96ceeSVladimir Zapolskiy 12453f96ceeSVladimir Zapolskiy mutex_lock(&governor_lock); 12553f96ceeSVladimir Zapolskiy 12653f96ceeSVladimir Zapolskiy if (find_governor_by_name(gov->name)) { 12753f96ceeSVladimir Zapolskiy mutex_unlock(&governor_lock); 12853f96ceeSVladimir Zapolskiy kfree(priv); 12953f96ceeSVladimir Zapolskiy return -EBUSY; 13053f96ceeSVladimir Zapolskiy } 13153f96ceeSVladimir Zapolskiy 13253f96ceeSVladimir Zapolskiy priv->gov = gov; 13353f96ceeSVladimir Zapolskiy list_add(&priv->entry, &governor_list); 134ff84136cSVladimir Zapolskiy 135da0d12ffSVladimir Zapolskiy if (!strncmp(gov->name, WATCHDOG_PRETIMEOUT_DEFAULT_GOV, 136da0d12ffSVladimir Zapolskiy WATCHDOG_GOV_NAME_MAXLEN)) { 137ff84136cSVladimir Zapolskiy spin_lock_irq(&pretimeout_lock); 138ff84136cSVladimir Zapolskiy default_gov = gov; 139ff84136cSVladimir Zapolskiy 140ff84136cSVladimir Zapolskiy list_for_each_entry(p, &pretimeout_list, entry) 141ff84136cSVladimir Zapolskiy if (!p->wdd->gov) 142ff84136cSVladimir Zapolskiy p->wdd->gov = default_gov; 143ff84136cSVladimir Zapolskiy spin_unlock_irq(&pretimeout_lock); 144ff84136cSVladimir Zapolskiy } 145ff84136cSVladimir Zapolskiy 14653f96ceeSVladimir Zapolskiy mutex_unlock(&governor_lock); 14753f96ceeSVladimir Zapolskiy 148ff84136cSVladimir Zapolskiy return 0; 149ff84136cSVladimir Zapolskiy } 150ff84136cSVladimir Zapolskiy EXPORT_SYMBOL(watchdog_register_governor); 151ff84136cSVladimir Zapolskiy 152ff84136cSVladimir Zapolskiy void watchdog_unregister_governor(struct watchdog_governor *gov) 153ff84136cSVladimir Zapolskiy { 154ff84136cSVladimir Zapolskiy struct watchdog_pretimeout *p; 15553f96ceeSVladimir Zapolskiy struct governor_priv *priv, *t; 15653f96ceeSVladimir Zapolskiy 15753f96ceeSVladimir Zapolskiy mutex_lock(&governor_lock); 15853f96ceeSVladimir Zapolskiy 15953f96ceeSVladimir Zapolskiy list_for_each_entry_safe(priv, t, &governor_list, entry) { 16053f96ceeSVladimir Zapolskiy if (priv->gov == gov) { 16153f96ceeSVladimir Zapolskiy list_del(&priv->entry); 16253f96ceeSVladimir Zapolskiy kfree(priv); 16353f96ceeSVladimir Zapolskiy break; 16453f96ceeSVladimir Zapolskiy } 16553f96ceeSVladimir Zapolskiy } 166ff84136cSVladimir Zapolskiy 167ff84136cSVladimir Zapolskiy spin_lock_irq(&pretimeout_lock); 168ff84136cSVladimir Zapolskiy list_for_each_entry(p, &pretimeout_list, entry) 169ff84136cSVladimir Zapolskiy if (p->wdd->gov == gov) 170ff84136cSVladimir Zapolskiy p->wdd->gov = default_gov; 171ff84136cSVladimir Zapolskiy spin_unlock_irq(&pretimeout_lock); 17253f96ceeSVladimir Zapolskiy 17353f96ceeSVladimir Zapolskiy mutex_unlock(&governor_lock); 174ff84136cSVladimir Zapolskiy } 175ff84136cSVladimir Zapolskiy EXPORT_SYMBOL(watchdog_unregister_governor); 176ff84136cSVladimir Zapolskiy 177ff84136cSVladimir Zapolskiy int watchdog_register_pretimeout(struct watchdog_device *wdd) 178ff84136cSVladimir Zapolskiy { 179ff84136cSVladimir Zapolskiy struct watchdog_pretimeout *p; 180ff84136cSVladimir Zapolskiy 181*7b7d2fdcSCurtis Klein if (!watchdog_have_pretimeout(wdd)) 182ff84136cSVladimir Zapolskiy return 0; 183ff84136cSVladimir Zapolskiy 184ff84136cSVladimir Zapolskiy p = kzalloc(sizeof(*p), GFP_KERNEL); 185ff84136cSVladimir Zapolskiy if (!p) 186ff84136cSVladimir Zapolskiy return -ENOMEM; 187ff84136cSVladimir Zapolskiy 188ff84136cSVladimir Zapolskiy spin_lock_irq(&pretimeout_lock); 189ff84136cSVladimir Zapolskiy list_add(&p->entry, &pretimeout_list); 190ff84136cSVladimir Zapolskiy p->wdd = wdd; 191ff84136cSVladimir Zapolskiy wdd->gov = default_gov; 192ff84136cSVladimir Zapolskiy spin_unlock_irq(&pretimeout_lock); 193ff84136cSVladimir Zapolskiy 194ff84136cSVladimir Zapolskiy return 0; 195ff84136cSVladimir Zapolskiy } 196ff84136cSVladimir Zapolskiy 197ff84136cSVladimir Zapolskiy void watchdog_unregister_pretimeout(struct watchdog_device *wdd) 198ff84136cSVladimir Zapolskiy { 199ff84136cSVladimir Zapolskiy struct watchdog_pretimeout *p, *t; 200ff84136cSVladimir Zapolskiy 201*7b7d2fdcSCurtis Klein if (!watchdog_have_pretimeout(wdd)) 202ff84136cSVladimir Zapolskiy return; 203ff84136cSVladimir Zapolskiy 204ff84136cSVladimir Zapolskiy spin_lock_irq(&pretimeout_lock); 205ff84136cSVladimir Zapolskiy wdd->gov = NULL; 206ff84136cSVladimir Zapolskiy 207ff84136cSVladimir Zapolskiy list_for_each_entry_safe(p, t, &pretimeout_list, entry) { 208ff84136cSVladimir Zapolskiy if (p->wdd == wdd) { 209ff84136cSVladimir Zapolskiy list_del(&p->entry); 210ff84136cSVladimir Zapolskiy break; 211ff84136cSVladimir Zapolskiy } 212ff84136cSVladimir Zapolskiy } 213ff84136cSVladimir Zapolskiy spin_unlock_irq(&pretimeout_lock); 214ff84136cSVladimir Zapolskiy 215ff84136cSVladimir Zapolskiy kfree(p); 216ff84136cSVladimir Zapolskiy } 217