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