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

source code of linux/drivers/watchdog/watchdog_pretimeout.c