brintos

brintos / linux-shallow public Read only

0
0
Text · 4.5 KiB · e5295c9 Raw
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