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