File Index
Files and directories
Sign in
FILE

watchdog_pretimeout.c

File preview with syntax highlighting.

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