Merge tag '6.10-rc1-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6
[sfrench/cifs-2.6.git] / drivers / watchdog / watchdog_pretimeout.c
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 }