Loading...
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 */
16static struct watchdog_governor *default_gov;
17
18/* The spinlock protects default_gov, wdd->gov and pretimeout_list */
19static DEFINE_SPINLOCK(pretimeout_lock);
20
21/* List of watchdog devices, which can generate a pretimeout event */
22static LIST_HEAD(pretimeout_list);
23
24struct watchdog_pretimeout {
25 struct watchdog_device *wdd;
26 struct list_head entry;
27};
28
29/* The mutex protects governor list and serializes external interfaces */
30static DEFINE_MUTEX(governor_lock);
31
32/* List of the registered watchdog pretimeout governors */
33static LIST_HEAD(governor_list);
34
35struct governor_priv {
36 struct watchdog_governor *gov;
37 struct list_head entry;
38};
39
40static 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
51int 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
66int 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
78int 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
100void 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}
113EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout);
114
115int 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}
150EXPORT_SYMBOL(watchdog_register_governor);
151
152void 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}
175EXPORT_SYMBOL(watchdog_unregister_governor);
176
177int 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
197void 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}
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 */
20static struct watchdog_governor *default_gov;
21
22/* The spinlock protects default_gov, wdd->gov and pretimeout_list */
23static DEFINE_SPINLOCK(pretimeout_lock);
24
25/* List of watchdog devices, which can generate a pretimeout event */
26static LIST_HEAD(pretimeout_list);
27
28struct watchdog_pretimeout {
29 struct watchdog_device *wdd;
30 struct list_head entry;
31};
32
33/* The mutex protects governor list and serializes external interfaces */
34static DEFINE_MUTEX(governor_lock);
35
36/* List of the registered watchdog pretimeout governors */
37static LIST_HEAD(governor_list);
38
39struct governor_priv {
40 struct watchdog_governor *gov;
41 struct list_head entry;
42};
43
44static 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
55int 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
70int 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
82int 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
104void 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}
117EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout);
118
119int 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}
154EXPORT_SYMBOL(watchdog_register_governor);
155
156void 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}
179EXPORT_SYMBOL(watchdog_unregister_governor);
180
181int 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
201void 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}