blob: c9e4a03269382b255384dc9ec52e80182fbd309c [file] [log] [blame]
Vladimir Zapolskiyff841362016-10-07 15:39:54 +03001/*
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>
Vladimir Zapolskiy53f96ce2016-10-07 15:37:00 +030014#include <linux/string.h>
Vladimir Zapolskiyff841362016-10-07 15:39:54 +030015#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
Vladimir Zapolskiy53f96ce2016-10-07 15:37:00 +030033/* 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
Vladimir Zapolskiyff841362016-10-07 15:39:54 +030055int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf)
56{
57 int count = 0;
58
59 spin_lock_irq(&pretimeout_lock);
60 if (wdd->gov)
61 count = sprintf(buf, "%s\n", wdd->gov->name);
62 spin_unlock_irq(&pretimeout_lock);
63
64 return count;
65}
66
Vladimir Zapolskiy53f96ce2016-10-07 15:37:00 +030067int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,
68 const char *buf)
69{
70 struct governor_priv *priv;
71
72 mutex_lock(&governor_lock);
73
74 priv = find_governor_by_name(buf);
75 if (!priv) {
76 mutex_unlock(&governor_lock);
77 return -EINVAL;
78 }
79
80 spin_lock_irq(&pretimeout_lock);
81 wdd->gov = priv->gov;
82 spin_unlock_irq(&pretimeout_lock);
83
84 mutex_unlock(&governor_lock);
85
86 return 0;
87}
88
Vladimir Zapolskiyff841362016-10-07 15:39:54 +030089void watchdog_notify_pretimeout(struct watchdog_device *wdd)
90{
91 unsigned long flags;
92
93 spin_lock_irqsave(&pretimeout_lock, flags);
94 if (!wdd->gov) {
95 spin_unlock_irqrestore(&pretimeout_lock, flags);
96 return;
97 }
98
99 wdd->gov->pretimeout(wdd);
100 spin_unlock_irqrestore(&pretimeout_lock, flags);
101}
102EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout);
103
104int watchdog_register_governor(struct watchdog_governor *gov)
105{
106 struct watchdog_pretimeout *p;
Vladimir Zapolskiy53f96ce2016-10-07 15:37:00 +0300107 struct governor_priv *priv;
108
109 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
110 if (!priv)
111 return -ENOMEM;
112
113 mutex_lock(&governor_lock);
114
115 if (find_governor_by_name(gov->name)) {
116 mutex_unlock(&governor_lock);
117 kfree(priv);
118 return -EBUSY;
119 }
120
121 priv->gov = gov;
122 list_add(&priv->entry, &governor_list);
Vladimir Zapolskiyff841362016-10-07 15:39:54 +0300123
Vladimir Zapolskiyda0d12f2016-10-07 15:39:56 +0300124 if (!strncmp(gov->name, WATCHDOG_PRETIMEOUT_DEFAULT_GOV,
125 WATCHDOG_GOV_NAME_MAXLEN)) {
Vladimir Zapolskiyff841362016-10-07 15:39:54 +0300126 spin_lock_irq(&pretimeout_lock);
127 default_gov = gov;
128
129 list_for_each_entry(p, &pretimeout_list, entry)
130 if (!p->wdd->gov)
131 p->wdd->gov = default_gov;
132 spin_unlock_irq(&pretimeout_lock);
133 }
134
Vladimir Zapolskiy53f96ce2016-10-07 15:37:00 +0300135 mutex_unlock(&governor_lock);
136
Vladimir Zapolskiyff841362016-10-07 15:39:54 +0300137 return 0;
138}
139EXPORT_SYMBOL(watchdog_register_governor);
140
141void watchdog_unregister_governor(struct watchdog_governor *gov)
142{
143 struct watchdog_pretimeout *p;
Vladimir Zapolskiy53f96ce2016-10-07 15:37:00 +0300144 struct governor_priv *priv, *t;
145
146 mutex_lock(&governor_lock);
147
148 list_for_each_entry_safe(priv, t, &governor_list, entry) {
149 if (priv->gov == gov) {
150 list_del(&priv->entry);
151 kfree(priv);
152 break;
153 }
154 }
Vladimir Zapolskiyff841362016-10-07 15:39:54 +0300155
156 spin_lock_irq(&pretimeout_lock);
Vladimir Zapolskiyff841362016-10-07 15:39:54 +0300157 list_for_each_entry(p, &pretimeout_list, entry)
158 if (p->wdd->gov == gov)
159 p->wdd->gov = default_gov;
160 spin_unlock_irq(&pretimeout_lock);
Vladimir Zapolskiy53f96ce2016-10-07 15:37:00 +0300161
162 mutex_unlock(&governor_lock);
Vladimir Zapolskiyff841362016-10-07 15:39:54 +0300163}
164EXPORT_SYMBOL(watchdog_unregister_governor);
165
166int watchdog_register_pretimeout(struct watchdog_device *wdd)
167{
168 struct watchdog_pretimeout *p;
169
170 if (!(wdd->info->options & WDIOF_PRETIMEOUT))
171 return 0;
172
173 p = kzalloc(sizeof(*p), GFP_KERNEL);
174 if (!p)
175 return -ENOMEM;
176
177 spin_lock_irq(&pretimeout_lock);
178 list_add(&p->entry, &pretimeout_list);
179 p->wdd = wdd;
180 wdd->gov = default_gov;
181 spin_unlock_irq(&pretimeout_lock);
182
183 return 0;
184}
185
186void watchdog_unregister_pretimeout(struct watchdog_device *wdd)
187{
188 struct watchdog_pretimeout *p, *t;
189
190 if (!(wdd->info->options & WDIOF_PRETIMEOUT))
191 return;
192
193 spin_lock_irq(&pretimeout_lock);
194 wdd->gov = NULL;
195
196 list_for_each_entry_safe(p, t, &pretimeout_list, entry) {
197 if (p->wdd == wdd) {
198 list_del(&p->entry);
199 break;
200 }
201 }
202 spin_unlock_irq(&pretimeout_lock);
203
204 kfree(p);
205}