216 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2015-2016 Mentor Graphics4 */5 6#include <linux/list.h>7#include <linux/slab.h>8#include <linux/spinlock.h>9#include <linux/string.h>10#include <linux/watchdog.h>11 12#include "watchdog_core.h"13#include "watchdog_pretimeout.h"14 15/* Default watchdog pretimeout governor */16static struct watchdog_governor *default_gov;17 18/* The spinlock protects default_gov, wdd->gov and pretimeout_list */19static DEFINE_SPINLOCK(pretimeout_lock);20 21/* List of watchdog devices, which can generate a pretimeout event */22static LIST_HEAD(pretimeout_list);23 24struct watchdog_pretimeout {25 struct watchdog_device *wdd;26 struct list_head entry;27};28 29/* The mutex protects governor list and serializes external interfaces */30static DEFINE_MUTEX(governor_lock);31 32/* List of the registered watchdog pretimeout governors */33static LIST_HEAD(governor_list);34 35struct governor_priv {36 struct watchdog_governor *gov;37 struct list_head entry;38};39 40static struct governor_priv *find_governor_by_name(const char *gov_name)41{42 struct governor_priv *priv;43 44 list_for_each_entry(priv, &governor_list, entry)45 if (sysfs_streq(gov_name, priv->gov->name))46 return priv;47 48 return NULL;49}50 51int watchdog_pretimeout_available_governors_get(char *buf)52{53 struct governor_priv *priv;54 int count = 0;55 56 mutex_lock(&governor_lock);57 58 list_for_each_entry(priv, &governor_list, entry)59 count += sysfs_emit_at(buf, count, "%s\n", priv->gov->name);60 61 mutex_unlock(&governor_lock);62 63 return count;64}65 66int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf)67{68 int count = 0;69 70 spin_lock_irq(&pretimeout_lock);71 if (wdd->gov)72 count = sysfs_emit(buf, "%s\n", wdd->gov->name);73 spin_unlock_irq(&pretimeout_lock);74 75 return count;76}77 78int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,79 const char *buf)80{81 struct governor_priv *priv;82 83 mutex_lock(&governor_lock);84 85 priv = find_governor_by_name(buf);86 if (!priv) {87 mutex_unlock(&governor_lock);88 return -EINVAL;89 }90 91 spin_lock_irq(&pretimeout_lock);92 wdd->gov = priv->gov;93 spin_unlock_irq(&pretimeout_lock);94 95 mutex_unlock(&governor_lock);96 97 return 0;98}99 100void watchdog_notify_pretimeout(struct watchdog_device *wdd)101{102 unsigned long flags;103 104 spin_lock_irqsave(&pretimeout_lock, flags);105 if (!wdd->gov) {106 spin_unlock_irqrestore(&pretimeout_lock, flags);107 return;108 }109 110 wdd->gov->pretimeout(wdd);111 spin_unlock_irqrestore(&pretimeout_lock, flags);112}113EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout);114 115int watchdog_register_governor(struct watchdog_governor *gov)116{117 struct watchdog_pretimeout *p;118 struct governor_priv *priv;119 120 priv = kzalloc(sizeof(*priv), GFP_KERNEL);121 if (!priv)122 return -ENOMEM;123 124 mutex_lock(&governor_lock);125 126 if (find_governor_by_name(gov->name)) {127 mutex_unlock(&governor_lock);128 kfree(priv);129 return -EBUSY;130 }131 132 priv->gov = gov;133 list_add(&priv->entry, &governor_list);134 135 if (!strncmp(gov->name, WATCHDOG_PRETIMEOUT_DEFAULT_GOV,136 WATCHDOG_GOV_NAME_MAXLEN)) {137 spin_lock_irq(&pretimeout_lock);138 default_gov = gov;139 140 list_for_each_entry(p, &pretimeout_list, entry)141 if (!p->wdd->gov)142 p->wdd->gov = default_gov;143 spin_unlock_irq(&pretimeout_lock);144 }145 146 mutex_unlock(&governor_lock);147 148 return 0;149}150EXPORT_SYMBOL(watchdog_register_governor);151 152void watchdog_unregister_governor(struct watchdog_governor *gov)153{154 struct watchdog_pretimeout *p;155 struct governor_priv *priv, *t;156 157 mutex_lock(&governor_lock);158 159 list_for_each_entry_safe(priv, t, &governor_list, entry) {160 if (priv->gov == gov) {161 list_del(&priv->entry);162 kfree(priv);163 break;164 }165 }166 167 spin_lock_irq(&pretimeout_lock);168 list_for_each_entry(p, &pretimeout_list, entry)169 if (p->wdd->gov == gov)170 p->wdd->gov = default_gov;171 spin_unlock_irq(&pretimeout_lock);172 173 mutex_unlock(&governor_lock);174}175EXPORT_SYMBOL(watchdog_unregister_governor);176 177int watchdog_register_pretimeout(struct watchdog_device *wdd)178{179 struct watchdog_pretimeout *p;180 181 if (!watchdog_have_pretimeout(wdd))182 return 0;183 184 p = kzalloc(sizeof(*p), GFP_KERNEL);185 if (!p)186 return -ENOMEM;187 188 spin_lock_irq(&pretimeout_lock);189 list_add(&p->entry, &pretimeout_list);190 p->wdd = wdd;191 wdd->gov = default_gov;192 spin_unlock_irq(&pretimeout_lock);193 194 return 0;195}196 197void watchdog_unregister_pretimeout(struct watchdog_device *wdd)198{199 struct watchdog_pretimeout *p, *t;200 201 if (!watchdog_have_pretimeout(wdd))202 return;203 204 spin_lock_irq(&pretimeout_lock);205 wdd->gov = NULL;206 207 list_for_each_entry_safe(p, t, &pretimeout_list, entry) {208 if (p->wdd == wdd) {209 list_del(&p->entry);210 kfree(p);211 break;212 }213 }214 spin_unlock_irq(&pretimeout_lock);215}216