1ff84136cSVladimir Zapolskiy /* 2ff84136cSVladimir Zapolskiy * Copyright (C) 2015-2016 Mentor Graphics 3ff84136cSVladimir Zapolskiy * 4ff84136cSVladimir Zapolskiy * This program is free software; you can redistribute it and/or modify 5ff84136cSVladimir Zapolskiy * it under the terms of the GNU General Public License as published by 6ff84136cSVladimir Zapolskiy * the Free Software Foundation; either version 2 of the License, or 7ff84136cSVladimir Zapolskiy * (at your option) any later version. 8ff84136cSVladimir Zapolskiy * 9ff84136cSVladimir Zapolskiy */ 10ff84136cSVladimir Zapolskiy 11ff84136cSVladimir Zapolskiy #include <linux/list.h> 12ff84136cSVladimir Zapolskiy #include <linux/slab.h> 13ff84136cSVladimir Zapolskiy #include <linux/spinlock.h> 1453f96ceeSVladimir Zapolskiy #include <linux/string.h> 15ff84136cSVladimir Zapolskiy #include <linux/watchdog.h> 16ff84136cSVladimir Zapolskiy 17ff84136cSVladimir Zapolskiy #include "watchdog_pretimeout.h" 18ff84136cSVladimir Zapolskiy 19ff84136cSVladimir Zapolskiy /* Default watchdog pretimeout governor */ 20ff84136cSVladimir Zapolskiy static struct watchdog_governor *default_gov; 21ff84136cSVladimir Zapolskiy 22ff84136cSVladimir Zapolskiy /* The spinlock protects default_gov, wdd->gov and pretimeout_list */ 23ff84136cSVladimir Zapolskiy static DEFINE_SPINLOCK(pretimeout_lock); 24ff84136cSVladimir Zapolskiy 25ff84136cSVladimir Zapolskiy /* List of watchdog devices, which can generate a pretimeout event */ 26ff84136cSVladimir Zapolskiy static LIST_HEAD(pretimeout_list); 27ff84136cSVladimir Zapolskiy 28ff84136cSVladimir Zapolskiy struct watchdog_pretimeout { 29ff84136cSVladimir Zapolskiy struct watchdog_device *wdd; 30ff84136cSVladimir Zapolskiy struct list_head entry; 31ff84136cSVladimir Zapolskiy }; 32ff84136cSVladimir Zapolskiy 3353f96ceeSVladimir Zapolskiy /* The mutex protects governor list and serializes external interfaces */ 3453f96ceeSVladimir Zapolskiy static DEFINE_MUTEX(governor_lock); 3553f96ceeSVladimir Zapolskiy 3653f96ceeSVladimir Zapolskiy /* List of the registered watchdog pretimeout governors */ 3753f96ceeSVladimir Zapolskiy static LIST_HEAD(governor_list); 3853f96ceeSVladimir Zapolskiy 3953f96ceeSVladimir Zapolskiy struct governor_priv { 4053f96ceeSVladimir Zapolskiy struct watchdog_governor *gov; 4153f96ceeSVladimir Zapolskiy struct list_head entry; 4253f96ceeSVladimir Zapolskiy }; 4353f96ceeSVladimir Zapolskiy 4453f96ceeSVladimir Zapolskiy static struct governor_priv *find_governor_by_name(const char *gov_name) 4553f96ceeSVladimir Zapolskiy { 4653f96ceeSVladimir Zapolskiy struct governor_priv *priv; 4753f96ceeSVladimir Zapolskiy 4853f96ceeSVladimir Zapolskiy list_for_each_entry(priv, &governor_list, entry) 4953f96ceeSVladimir Zapolskiy if (sysfs_streq(gov_name, priv->gov->name)) 5053f96ceeSVladimir Zapolskiy return priv; 5153f96ceeSVladimir Zapolskiy 5253f96ceeSVladimir Zapolskiy return NULL; 5353f96ceeSVladimir Zapolskiy } 5453f96ceeSVladimir Zapolskiy 55*89873a71SVladimir Zapolskiy int watchdog_pretimeout_available_governors_get(char *buf) 56*89873a71SVladimir Zapolskiy { 57*89873a71SVladimir Zapolskiy struct governor_priv *priv; 58*89873a71SVladimir Zapolskiy int count = 0; 59*89873a71SVladimir Zapolskiy 60*89873a71SVladimir Zapolskiy mutex_lock(&governor_lock); 61*89873a71SVladimir Zapolskiy 62*89873a71SVladimir Zapolskiy list_for_each_entry(priv, &governor_list, entry) 63*89873a71SVladimir Zapolskiy count += sprintf(buf + count, "%s\n", priv->gov->name); 64*89873a71SVladimir Zapolskiy 65*89873a71SVladimir Zapolskiy mutex_unlock(&governor_lock); 66*89873a71SVladimir Zapolskiy 67*89873a71SVladimir Zapolskiy return count; 68*89873a71SVladimir Zapolskiy } 69*89873a71SVladimir Zapolskiy 70ff84136cSVladimir Zapolskiy int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf) 71ff84136cSVladimir Zapolskiy { 72ff84136cSVladimir Zapolskiy int count = 0; 73ff84136cSVladimir Zapolskiy 74ff84136cSVladimir Zapolskiy spin_lock_irq(&pretimeout_lock); 75ff84136cSVladimir Zapolskiy if (wdd->gov) 76ff84136cSVladimir Zapolskiy count = sprintf(buf, "%s\n", wdd->gov->name); 77ff84136cSVladimir Zapolskiy spin_unlock_irq(&pretimeout_lock); 78ff84136cSVladimir Zapolskiy 79ff84136cSVladimir Zapolskiy return count; 80ff84136cSVladimir Zapolskiy } 81ff84136cSVladimir Zapolskiy 8253f96ceeSVladimir Zapolskiy int watchdog_pretimeout_governor_set(struct watchdog_device *wdd, 8353f96ceeSVladimir Zapolskiy const char *buf) 8453f96ceeSVladimir Zapolskiy { 8553f96ceeSVladimir Zapolskiy struct governor_priv *priv; 8653f96ceeSVladimir Zapolskiy 8753f96ceeSVladimir Zapolskiy mutex_lock(&governor_lock); 8853f96ceeSVladimir Zapolskiy 8953f96ceeSVladimir Zapolskiy priv = find_governor_by_name(buf); 9053f96ceeSVladimir Zapolskiy if (!priv) { 9153f96ceeSVladimir Zapolskiy mutex_unlock(&governor_lock); 9253f96ceeSVladimir Zapolskiy return -EINVAL; 9353f96ceeSVladimir Zapolskiy } 9453f96ceeSVladimir Zapolskiy 9553f96ceeSVladimir Zapolskiy spin_lock_irq(&pretimeout_lock); 9653f96ceeSVladimir Zapolskiy wdd->gov = priv->gov; 9753f96ceeSVladimir Zapolskiy spin_unlock_irq(&pretimeout_lock); 9853f96ceeSVladimir Zapolskiy 9953f96ceeSVladimir Zapolskiy mutex_unlock(&governor_lock); 10053f96ceeSVladimir Zapolskiy 10153f96ceeSVladimir Zapolskiy return 0; 10253f96ceeSVladimir Zapolskiy } 10353f96ceeSVladimir Zapolskiy 104ff84136cSVladimir Zapolskiy void watchdog_notify_pretimeout(struct watchdog_device *wdd) 105ff84136cSVladimir Zapolskiy { 106ff84136cSVladimir Zapolskiy unsigned long flags; 107ff84136cSVladimir Zapolskiy 108ff84136cSVladimir Zapolskiy spin_lock_irqsave(&pretimeout_lock, flags); 109ff84136cSVladimir Zapolskiy if (!wdd->gov) { 110ff84136cSVladimir Zapolskiy spin_unlock_irqrestore(&pretimeout_lock, flags); 111ff84136cSVladimir Zapolskiy return; 112ff84136cSVladimir Zapolskiy } 113ff84136cSVladimir Zapolskiy 114ff84136cSVladimir Zapolskiy wdd->gov->pretimeout(wdd); 115ff84136cSVladimir Zapolskiy spin_unlock_irqrestore(&pretimeout_lock, flags); 116ff84136cSVladimir Zapolskiy } 117ff84136cSVladimir Zapolskiy EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout); 118ff84136cSVladimir Zapolskiy 119ff84136cSVladimir Zapolskiy int watchdog_register_governor(struct watchdog_governor *gov) 120ff84136cSVladimir Zapolskiy { 121ff84136cSVladimir Zapolskiy struct watchdog_pretimeout *p; 12253f96ceeSVladimir Zapolskiy struct governor_priv *priv; 12353f96ceeSVladimir Zapolskiy 12453f96ceeSVladimir Zapolskiy priv = kzalloc(sizeof(*priv), GFP_KERNEL); 12553f96ceeSVladimir Zapolskiy if (!priv) 12653f96ceeSVladimir Zapolskiy return -ENOMEM; 12753f96ceeSVladimir Zapolskiy 12853f96ceeSVladimir Zapolskiy mutex_lock(&governor_lock); 12953f96ceeSVladimir Zapolskiy 13053f96ceeSVladimir Zapolskiy if (find_governor_by_name(gov->name)) { 13153f96ceeSVladimir Zapolskiy mutex_unlock(&governor_lock); 13253f96ceeSVladimir Zapolskiy kfree(priv); 13353f96ceeSVladimir Zapolskiy return -EBUSY; 13453f96ceeSVladimir Zapolskiy } 13553f96ceeSVladimir Zapolskiy 13653f96ceeSVladimir Zapolskiy priv->gov = gov; 13753f96ceeSVladimir Zapolskiy list_add(&priv->entry, &governor_list); 138ff84136cSVladimir Zapolskiy 139da0d12ffSVladimir Zapolskiy if (!strncmp(gov->name, WATCHDOG_PRETIMEOUT_DEFAULT_GOV, 140da0d12ffSVladimir Zapolskiy WATCHDOG_GOV_NAME_MAXLEN)) { 141ff84136cSVladimir Zapolskiy spin_lock_irq(&pretimeout_lock); 142ff84136cSVladimir Zapolskiy default_gov = gov; 143ff84136cSVladimir Zapolskiy 144ff84136cSVladimir Zapolskiy list_for_each_entry(p, &pretimeout_list, entry) 145ff84136cSVladimir Zapolskiy if (!p->wdd->gov) 146ff84136cSVladimir Zapolskiy p->wdd->gov = default_gov; 147ff84136cSVladimir Zapolskiy spin_unlock_irq(&pretimeout_lock); 148ff84136cSVladimir Zapolskiy } 149ff84136cSVladimir Zapolskiy 15053f96ceeSVladimir Zapolskiy mutex_unlock(&governor_lock); 15153f96ceeSVladimir Zapolskiy 152ff84136cSVladimir Zapolskiy return 0; 153ff84136cSVladimir Zapolskiy } 154ff84136cSVladimir Zapolskiy EXPORT_SYMBOL(watchdog_register_governor); 155ff84136cSVladimir Zapolskiy 156ff84136cSVladimir Zapolskiy void watchdog_unregister_governor(struct watchdog_governor *gov) 157ff84136cSVladimir Zapolskiy { 158ff84136cSVladimir Zapolskiy struct watchdog_pretimeout *p; 15953f96ceeSVladimir Zapolskiy struct governor_priv *priv, *t; 16053f96ceeSVladimir Zapolskiy 16153f96ceeSVladimir Zapolskiy mutex_lock(&governor_lock); 16253f96ceeSVladimir Zapolskiy 16353f96ceeSVladimir Zapolskiy list_for_each_entry_safe(priv, t, &governor_list, entry) { 16453f96ceeSVladimir Zapolskiy if (priv->gov == gov) { 16553f96ceeSVladimir Zapolskiy list_del(&priv->entry); 16653f96ceeSVladimir Zapolskiy kfree(priv); 16753f96ceeSVladimir Zapolskiy break; 16853f96ceeSVladimir Zapolskiy } 16953f96ceeSVladimir Zapolskiy } 170ff84136cSVladimir Zapolskiy 171ff84136cSVladimir Zapolskiy spin_lock_irq(&pretimeout_lock); 172ff84136cSVladimir Zapolskiy list_for_each_entry(p, &pretimeout_list, entry) 173ff84136cSVladimir Zapolskiy if (p->wdd->gov == gov) 174ff84136cSVladimir Zapolskiy p->wdd->gov = default_gov; 175ff84136cSVladimir Zapolskiy spin_unlock_irq(&pretimeout_lock); 17653f96ceeSVladimir Zapolskiy 17753f96ceeSVladimir Zapolskiy mutex_unlock(&governor_lock); 178ff84136cSVladimir Zapolskiy } 179ff84136cSVladimir Zapolskiy EXPORT_SYMBOL(watchdog_unregister_governor); 180ff84136cSVladimir Zapolskiy 181ff84136cSVladimir Zapolskiy int watchdog_register_pretimeout(struct watchdog_device *wdd) 182ff84136cSVladimir Zapolskiy { 183ff84136cSVladimir Zapolskiy struct watchdog_pretimeout *p; 184ff84136cSVladimir Zapolskiy 185ff84136cSVladimir Zapolskiy if (!(wdd->info->options & WDIOF_PRETIMEOUT)) 186ff84136cSVladimir Zapolskiy return 0; 187ff84136cSVladimir Zapolskiy 188ff84136cSVladimir Zapolskiy p = kzalloc(sizeof(*p), GFP_KERNEL); 189ff84136cSVladimir Zapolskiy if (!p) 190ff84136cSVladimir Zapolskiy return -ENOMEM; 191ff84136cSVladimir Zapolskiy 192ff84136cSVladimir Zapolskiy spin_lock_irq(&pretimeout_lock); 193ff84136cSVladimir Zapolskiy list_add(&p->entry, &pretimeout_list); 194ff84136cSVladimir Zapolskiy p->wdd = wdd; 195ff84136cSVladimir Zapolskiy wdd->gov = default_gov; 196ff84136cSVladimir Zapolskiy spin_unlock_irq(&pretimeout_lock); 197ff84136cSVladimir Zapolskiy 198ff84136cSVladimir Zapolskiy return 0; 199ff84136cSVladimir Zapolskiy } 200ff84136cSVladimir Zapolskiy 201ff84136cSVladimir Zapolskiy void watchdog_unregister_pretimeout(struct watchdog_device *wdd) 202ff84136cSVladimir Zapolskiy { 203ff84136cSVladimir Zapolskiy struct watchdog_pretimeout *p, *t; 204ff84136cSVladimir Zapolskiy 205ff84136cSVladimir Zapolskiy if (!(wdd->info->options & WDIOF_PRETIMEOUT)) 206ff84136cSVladimir Zapolskiy return; 207ff84136cSVladimir Zapolskiy 208ff84136cSVladimir Zapolskiy spin_lock_irq(&pretimeout_lock); 209ff84136cSVladimir Zapolskiy wdd->gov = NULL; 210ff84136cSVladimir Zapolskiy 211ff84136cSVladimir Zapolskiy list_for_each_entry_safe(p, t, &pretimeout_list, entry) { 212ff84136cSVladimir Zapolskiy if (p->wdd == wdd) { 213ff84136cSVladimir Zapolskiy list_del(&p->entry); 214ff84136cSVladimir Zapolskiy break; 215ff84136cSVladimir Zapolskiy } 216ff84136cSVladimir Zapolskiy } 217ff84136cSVladimir Zapolskiy spin_unlock_irq(&pretimeout_lock); 218ff84136cSVladimir Zapolskiy 219ff84136cSVladimir Zapolskiy kfree(p); 220ff84136cSVladimir Zapolskiy } 221