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