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(s1: gov_name, s2: 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, at: count, fmt: "%s\n" , priv->gov->name); |
60 | |
61 | mutex_unlock(lock: &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(lock: &pretimeout_lock); |
71 | if (wdd->gov) |
72 | count = sysfs_emit(buf, fmt: "%s\n" , wdd->gov->name); |
73 | spin_unlock_irq(lock: &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(gov_name: buf); |
86 | if (!priv) { |
87 | mutex_unlock(lock: &governor_lock); |
88 | return -EINVAL; |
89 | } |
90 | |
91 | spin_lock_irq(lock: &pretimeout_lock); |
92 | wdd->gov = priv->gov; |
93 | spin_unlock_irq(lock: &pretimeout_lock); |
94 | |
95 | mutex_unlock(lock: &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(lock: &pretimeout_lock, flags); |
107 | return; |
108 | } |
109 | |
110 | wdd->gov->pretimeout(wdd); |
111 | spin_unlock_irqrestore(lock: &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(size: 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: gov->name)) { |
127 | mutex_unlock(lock: &governor_lock); |
128 | kfree(objp: priv); |
129 | return -EBUSY; |
130 | } |
131 | |
132 | priv->gov = gov; |
133 | list_add(new: &priv->entry, head: &governor_list); |
134 | |
135 | if (!strncmp(gov->name, WATCHDOG_PRETIMEOUT_DEFAULT_GOV, |
136 | WATCHDOG_GOV_NAME_MAXLEN)) { |
137 | spin_lock_irq(lock: &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(lock: &pretimeout_lock); |
144 | } |
145 | |
146 | mutex_unlock(lock: &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(entry: &priv->entry); |
162 | kfree(objp: priv); |
163 | break; |
164 | } |
165 | } |
166 | |
167 | spin_lock_irq(lock: &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(lock: &pretimeout_lock); |
172 | |
173 | mutex_unlock(lock: &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(size: sizeof(*p), GFP_KERNEL); |
185 | if (!p) |
186 | return -ENOMEM; |
187 | |
188 | spin_lock_irq(lock: &pretimeout_lock); |
189 | list_add(new: &p->entry, head: &pretimeout_list); |
190 | p->wdd = wdd; |
191 | wdd->gov = default_gov; |
192 | spin_unlock_irq(lock: &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(lock: &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(entry: &p->entry); |
210 | kfree(objp: p); |
211 | break; |
212 | } |
213 | } |
214 | spin_unlock_irq(lock: &pretimeout_lock); |
215 | } |
216 | |