xref: /linux/drivers/watchdog/watchdog_pretimeout.c (revision 24168c5e6dfbdd5b414f048f47f75d64533296ca)
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/watchdog.h>
11 
12 #include "watchdog_core.h"
13 #include "watchdog_pretimeout.h"
14 
15 /* Default watchdog pretimeout governor */
16 static struct watchdog_governor *default_gov;
17 
18 /* The spinlock protects default_gov, wdd->gov and pretimeout_list */
19 static DEFINE_SPINLOCK(pretimeout_lock);
20 
21 /* List of watchdog devices, which can generate a pretimeout event */
22 static LIST_HEAD(pretimeout_list);
23 
24 struct watchdog_pretimeout {
25 	struct watchdog_device		*wdd;
26 	struct list_head		entry;
27 };
28 
29 /* The mutex protects governor list and serializes external interfaces */
30 static DEFINE_MUTEX(governor_lock);
31 
32 /* List of the registered watchdog pretimeout governors */
33 static LIST_HEAD(governor_list);
34 
35 struct governor_priv {
36 	struct watchdog_governor	*gov;
37 	struct list_head		entry;
38 };
39 
40 static 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 
51 int 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 
66 int 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 
78 int 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 
100 void 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 }
113 EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout);
114 
115 int 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 }
150 EXPORT_SYMBOL(watchdog_register_governor);
151 
152 void 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 }
175 EXPORT_SYMBOL(watchdog_unregister_governor);
176 
177 int 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 
197 void 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