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