xref: /linux/drivers/watchdog/watchdog_pretimeout.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
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