Blame view
drivers/watchdog/watchdog_dev.c
29.4 KB
d01732789 watchdog: convert... |
1 |
// SPDX-License-Identifier: GPL-2.0+ |
43316044d watchdog: WatchDo... |
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/* * watchdog_dev.c * * (c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>, * All Rights Reserved. * * (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>. * * * This source code is part of the generic code that can be used * by all the watchdog timer drivers. * * This part of the generic code takes care of the following * misc device: /dev/watchdog. * * Based on source code of the following authors: * Matt Domsch <Matt_Domsch@dell.com>, * Rob Radez <rob@osinvestor.com>, * Rusty Lynch <rusty@linux.co.intel.com> * Satyam Sharma <satyam@infradead.org> * Randy Dunlap <randy.dunlap@oracle.com> * |
43316044d watchdog: WatchDo... |
24 25 26 27 28 29 |
* Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. * admit liability nor provide warranty for any of this software. * This material is provided "AS-IS" and at no charge. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
b4ffb1909 watchdog: Separat... |
30 |
#include <linux/cdev.h> /* For character device */ |
43316044d watchdog: WatchDo... |
31 |
#include <linux/errno.h> /* For the -ENODEV/... values */ |
43316044d watchdog: WatchDo... |
32 |
#include <linux/fs.h> /* For file operations */ |
43316044d watchdog: WatchDo... |
33 |
#include <linux/init.h> /* For __init/__exit/... */ |
1ff688209 watchdog: core: m... |
34 |
#include <linux/hrtimer.h> /* For hrtimers */ |
b4ffb1909 watchdog: Separat... |
35 |
#include <linux/kernel.h> /* For printk/panic/... */ |
1ff688209 watchdog: core: m... |
36 |
#include <linux/kthread.h> /* For kthread_work */ |
b4ffb1909 watchdog: Separat... |
37 38 39 40 41 42 |
#include <linux/miscdevice.h> /* For handling misc devices */ #include <linux/module.h> /* For module stuff/... */ #include <linux/mutex.h> /* For mutexes */ #include <linux/slab.h> /* For memory functions */ #include <linux/types.h> /* For standard types (like size_t) */ #include <linux/watchdog.h> /* For watchdog specific items */ |
43316044d watchdog: WatchDo... |
43 |
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */ |
38a1222ae watchdog: core: m... |
44 |
#include <uapi/linux/sched/types.h> /* For struct sched_param */ |
6cfb5aa83 watchdog: correct... |
45 |
#include "watchdog_core.h" |
ff84136cb watchdog: add wat... |
46 |
#include "watchdog_pretimeout.h" |
09a46e739 watchdog: watchdo... |
47 |
|
b4ffb1909 watchdog: Separat... |
48 49 |
/* * struct watchdog_core_data - watchdog core internal data |
ca7851d46 watchdog: Fix the... |
50 |
* @dev: The watchdog's internal device |
b4ffb1909 watchdog: Separat... |
51 52 53 54 55 56 |
* @cdev: The watchdog's Character device. * @wdd: Pointer to watchdog device. * @lock: Lock for watchdog core. * @status: Watchdog core internal status bits. */ struct watchdog_core_data { |
ca7851d46 watchdog: Fix the... |
57 |
struct device dev; |
b4ffb1909 watchdog: Separat... |
58 59 60 |
struct cdev cdev; struct watchdog_device *wdd; struct mutex lock; |
1ff688209 watchdog: core: m... |
61 62 |
ktime_t last_keepalive; ktime_t last_hw_keepalive; |
4d1c6a0ec watchdog: introdu... |
63 |
ktime_t open_deadline; |
1ff688209 watchdog: core: m... |
64 65 |
struct hrtimer timer; struct kthread_work work; |
b4ffb1909 watchdog: Separat... |
66 67 68 |
unsigned long status; /* Internal status bits */ #define _WDOG_DEV_OPEN 0 /* Opened ? */ #define _WDOG_ALLOW_RELEASE 1 /* Did we receive the magic char ? */ |
90b826f17 watchdog: Impleme... |
69 |
#define _WDOG_KEEPALIVE 2 /* Did we receive a keepalive ? */ |
b4ffb1909 watchdog: Separat... |
70 |
}; |
45f5fed30 watchdog: Add mul... |
71 72 |
/* the dev_t structure to store the dynamically allocated watchdog devices */ static dev_t watchdog_devt; |
b4ffb1909 watchdog: Separat... |
73 74 |
/* Reference to watchdog device behind /dev/watchdog */ static struct watchdog_core_data *old_wd_data; |
43316044d watchdog: WatchDo... |
75 |
|
38a1222ae watchdog: core: m... |
76 |
static struct kthread_worker *watchdog_kworker; |
664a39236 watchdog: Introdu... |
77 |
|
2501b0153 watchdog: core: a... |
78 79 |
static bool handle_boot_enabled = IS_ENABLED(CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED); |
487e4e082 watchdog: introdu... |
80 |
static unsigned open_timeout = CONFIG_WATCHDOG_OPEN_TIMEOUT; |
4d1c6a0ec watchdog: introdu... |
81 82 83 84 85 86 87 88 89 90 91 |
static bool watchdog_past_open_deadline(struct watchdog_core_data *data) { return ktime_after(ktime_get(), data->open_deadline); } static void watchdog_set_open_deadline(struct watchdog_core_data *data) { data->open_deadline = open_timeout ? ktime_get() + ktime_set(open_timeout, 0) : KTIME_MAX; } |
664a39236 watchdog: Introdu... |
92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
static inline bool watchdog_need_worker(struct watchdog_device *wdd) { /* All variables in milli-seconds */ unsigned int hm = wdd->max_hw_heartbeat_ms; unsigned int t = wdd->timeout * 1000; /* * A worker to generate heartbeat requests is needed if all of the * following conditions are true. * - Userspace activated the watchdog. * - The driver provided a value for the maximum hardware timeout, and * thus is aware that the framework supports generating heartbeat * requests. * - Userspace requests a longer timeout than the hardware can handle. |
3fbfe9264 watchdog: change ... |
106 107 108 109 |
* * Alternatively, if userspace has not opened the watchdog * device, we take care of feeding the watchdog if it is * running. |
664a39236 watchdog: Introdu... |
110 |
*/ |
3fbfe9264 watchdog: change ... |
111 112 |
return (hm && watchdog_active(wdd) && t > hm) || (t && !watchdog_active(wdd) && watchdog_hw_running(wdd)); |
664a39236 watchdog: Introdu... |
113 |
} |
1ff688209 watchdog: core: m... |
114 |
static ktime_t watchdog_next_keepalive(struct watchdog_device *wdd) |
664a39236 watchdog: Introdu... |
115 116 117 |
{ struct watchdog_core_data *wd_data = wdd->wd_data; unsigned int timeout_ms = wdd->timeout * 1000; |
1ff688209 watchdog: core: m... |
118 119 120 |
ktime_t keepalive_interval; ktime_t last_heartbeat, latest_heartbeat; ktime_t virt_timeout; |
664a39236 watchdog: Introdu... |
121 |
unsigned int hw_heartbeat_ms; |
c2eac35bc watchdog: make th... |
122 123 124 125 126 |
if (watchdog_active(wdd)) virt_timeout = ktime_add(wd_data->last_keepalive, ms_to_ktime(timeout_ms)); else virt_timeout = wd_data->open_deadline; |
3fbfe9264 watchdog: change ... |
127 |
hw_heartbeat_ms = min_not_zero(timeout_ms, wdd->max_hw_heartbeat_ms); |
1ff688209 watchdog: core: m... |
128 |
keepalive_interval = ms_to_ktime(hw_heartbeat_ms / 2); |
664a39236 watchdog: Introdu... |
129 130 131 132 133 134 |
/* * To ensure that the watchdog times out wdd->timeout seconds * after the most recent ping from userspace, the last * worker ping has to come in hw_heartbeat_ms before this timeout. */ |
1ff688209 watchdog: core: m... |
135 136 137 138 139 |
last_heartbeat = ktime_sub(virt_timeout, ms_to_ktime(hw_heartbeat_ms)); latest_heartbeat = ktime_sub(last_heartbeat, ktime_get()); if (ktime_before(latest_heartbeat, keepalive_interval)) return latest_heartbeat; return keepalive_interval; |
664a39236 watchdog: Introdu... |
140 141 142 143 144 145 146 |
} static inline void watchdog_update_worker(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; if (watchdog_need_worker(wdd)) { |
1ff688209 watchdog: core: m... |
147 |
ktime_t t = watchdog_next_keepalive(wdd); |
664a39236 watchdog: Introdu... |
148 149 |
if (t > 0) |
ce4d6fff7 watchdog: prevent... |
150 151 |
hrtimer_start(&wd_data->timer, t, HRTIMER_MODE_REL_HARD); |
664a39236 watchdog: Introdu... |
152 |
} else { |
1ff688209 watchdog: core: m... |
153 |
hrtimer_cancel(&wd_data->timer); |
664a39236 watchdog: Introdu... |
154 155 156 157 158 |
} } static int __watchdog_ping(struct watchdog_device *wdd) { |
15013ad81 watchdog: Add sup... |
159 |
struct watchdog_core_data *wd_data = wdd->wd_data; |
1ff688209 watchdog: core: m... |
160 |
ktime_t earliest_keepalive, now; |
664a39236 watchdog: Introdu... |
161 |
int err; |
1ff688209 watchdog: core: m... |
162 163 164 165 166 167 168 |
earliest_keepalive = ktime_add(wd_data->last_hw_keepalive, ms_to_ktime(wdd->min_hw_heartbeat_ms)); now = ktime_get(); if (ktime_after(earliest_keepalive, now)) { hrtimer_start(&wd_data->timer, ktime_sub(earliest_keepalive, now), |
ce4d6fff7 watchdog: prevent... |
169 |
HRTIMER_MODE_REL_HARD); |
15013ad81 watchdog: Add sup... |
170 171 |
return 0; } |
1ff688209 watchdog: core: m... |
172 |
wd_data->last_hw_keepalive = now; |
15013ad81 watchdog: Add sup... |
173 |
|
664a39236 watchdog: Introdu... |
174 175 176 177 178 179 180 181 182 |
if (wdd->ops->ping) err = wdd->ops->ping(wdd); /* ping the watchdog */ else err = wdd->ops->start(wdd); /* restart watchdog */ watchdog_update_worker(wdd); return err; } |
43316044d watchdog: WatchDo... |
183 184 |
/* * watchdog_ping: ping the watchdog. |
bc794ac3b watchdog: watchdo... |
185 |
* @wdd: the watchdog device to ping |
43316044d watchdog: WatchDo... |
186 |
* |
b4ffb1909 watchdog: Separat... |
187 188 |
* The caller must hold wd_data->lock. * |
43316044d watchdog: WatchDo... |
189 190 191 |
* If the watchdog has no own ping operation then it needs to be * restarted via the start operation. This wrapper function does * exactly that. |
234445b4e watchdog: WatchDo... |
192 |
* We only ping when the watchdog device is running. |
43316044d watchdog: WatchDo... |
193 |
*/ |
bc794ac3b watchdog: watchdo... |
194 |
static int watchdog_ping(struct watchdog_device *wdd) |
43316044d watchdog: WatchDo... |
195 |
{ |
664a39236 watchdog: Introdu... |
196 |
struct watchdog_core_data *wd_data = wdd->wd_data; |
e907df327 watchdog: Add sup... |
197 |
|
ee142889e watchdog: Introdu... |
198 |
if (!watchdog_active(wdd) && !watchdog_hw_running(wdd)) |
b4ffb1909 watchdog: Separat... |
199 |
return 0; |
7a8798242 watchdog: watchdo... |
200 |
|
90b826f17 watchdog: Impleme... |
201 |
set_bit(_WDOG_KEEPALIVE, &wd_data->status); |
1ff688209 watchdog: core: m... |
202 |
wd_data->last_keepalive = ktime_get(); |
664a39236 watchdog: Introdu... |
203 204 |
return __watchdog_ping(wdd); } |
7a8798242 watchdog: watchdo... |
205 |
|
c013b65ad watchdog: introdu... |
206 207 208 |
static bool watchdog_worker_should_ping(struct watchdog_core_data *wd_data) { struct watchdog_device *wdd = wd_data->wdd; |
4d1c6a0ec watchdog: introdu... |
209 210 211 212 213 214 215 |
if (!wdd) return false; if (watchdog_active(wdd)) return true; return watchdog_hw_running(wdd) && !watchdog_past_open_deadline(wd_data); |
c013b65ad watchdog: introdu... |
216 |
} |
38a1222ae watchdog: core: m... |
217 |
static void watchdog_ping_work(struct kthread_work *work) |
664a39236 watchdog: Introdu... |
218 219 |
{ struct watchdog_core_data *wd_data; |
664a39236 watchdog: Introdu... |
220 |
|
1ff688209 watchdog: core: m... |
221 |
wd_data = container_of(work, struct watchdog_core_data, work); |
664a39236 watchdog: Introdu... |
222 223 |
mutex_lock(&wd_data->lock); |
c013b65ad watchdog: introdu... |
224 225 |
if (watchdog_worker_should_ping(wd_data)) __watchdog_ping(wd_data->wdd); |
664a39236 watchdog: Introdu... |
226 |
mutex_unlock(&wd_data->lock); |
234445b4e watchdog: WatchDo... |
227 |
} |
1ff688209 watchdog: core: m... |
228 229 230 231 232 233 234 235 236 |
static enum hrtimer_restart watchdog_timer_expired(struct hrtimer *timer) { struct watchdog_core_data *wd_data; wd_data = container_of(timer, struct watchdog_core_data, timer); kthread_queue_work(watchdog_kworker, &wd_data->work); return HRTIMER_NORESTART; } |
234445b4e watchdog: WatchDo... |
237 238 |
/* * watchdog_start: wrapper to start the watchdog. |
bc794ac3b watchdog: watchdo... |
239 |
* @wdd: the watchdog device to start |
234445b4e watchdog: WatchDo... |
240 |
* |
b4ffb1909 watchdog: Separat... |
241 242 |
* The caller must hold wd_data->lock. * |
234445b4e watchdog: WatchDo... |
243 244 245 246 |
* Start the watchdog if it is not active and mark it active. * This function returns zero on success or a negative errno code for * failure. */ |
bc794ac3b watchdog: watchdo... |
247 |
static int watchdog_start(struct watchdog_device *wdd) |
234445b4e watchdog: WatchDo... |
248 |
{ |
664a39236 watchdog: Introdu... |
249 |
struct watchdog_core_data *wd_data = wdd->wd_data; |
1ff688209 watchdog: core: m... |
250 |
ktime_t started_at; |
b4ffb1909 watchdog: Separat... |
251 |
int err; |
e907df327 watchdog: Add sup... |
252 |
|
bc794ac3b watchdog: watchdo... |
253 |
if (watchdog_active(wdd)) |
b4ffb1909 watchdog: Separat... |
254 |
return 0; |
234445b4e watchdog: WatchDo... |
255 |
|
90b826f17 watchdog: Impleme... |
256 |
set_bit(_WDOG_KEEPALIVE, &wd_data->status); |
1ff688209 watchdog: core: m... |
257 |
started_at = ktime_get(); |
ee142889e watchdog: Introdu... |
258 259 260 261 |
if (watchdog_hw_running(wdd) && wdd->ops->ping) err = wdd->ops->ping(wdd); else err = wdd->ops->start(wdd); |
664a39236 watchdog: Introdu... |
262 |
if (err == 0) { |
bc794ac3b watchdog: watchdo... |
263 |
set_bit(WDOG_ACTIVE, &wdd->status); |
664a39236 watchdog: Introdu... |
264 |
wd_data->last_keepalive = started_at; |
66491dadd watchdog: reset l... |
265 |
wd_data->last_hw_keepalive = started_at; |
664a39236 watchdog: Introdu... |
266 267 |
watchdog_update_worker(wdd); } |
7a8798242 watchdog: watchdo... |
268 |
|
7a8798242 watchdog: watchdo... |
269 |
return err; |
234445b4e watchdog: WatchDo... |
270 271 272 273 |
} /* * watchdog_stop: wrapper to stop the watchdog. |
bc794ac3b watchdog: watchdo... |
274 |
* @wdd: the watchdog device to stop |
234445b4e watchdog: WatchDo... |
275 |
* |
b4ffb1909 watchdog: Separat... |
276 277 |
* The caller must hold wd_data->lock. * |
234445b4e watchdog: WatchDo... |
278 279 280 |
* Stop the watchdog if it is still active and unmark it active. * This function returns zero on success or a negative errno code for * failure. |
7e192b9c4 watchdog: WatchDo... |
281 |
* If the 'nowayout' feature was set, the watchdog cannot be stopped. |
234445b4e watchdog: WatchDo... |
282 |
*/ |
bc794ac3b watchdog: watchdo... |
283 |
static int watchdog_stop(struct watchdog_device *wdd) |
234445b4e watchdog: WatchDo... |
284 |
{ |
ee142889e watchdog: Introdu... |
285 |
int err = 0; |
e907df327 watchdog: Add sup... |
286 |
|
bc794ac3b watchdog: watchdo... |
287 |
if (!watchdog_active(wdd)) |
b4ffb1909 watchdog: Separat... |
288 |
return 0; |
7e192b9c4 watchdog: WatchDo... |
289 |
|
bc794ac3b watchdog: watchdo... |
290 |
if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) { |
0254e9535 watchdog: Drop po... |
291 292 293 |
pr_info("watchdog%d: nowayout prevents watchdog being stopped! ", wdd->id); |
b4ffb1909 watchdog: Separat... |
294 |
return -EBUSY; |
7e192b9c4 watchdog: WatchDo... |
295 |
} |
234445b4e watchdog: WatchDo... |
296 |
|
3c10bbde1 watchdog: core: C... |
297 298 |
if (wdd->ops->stop) { clear_bit(WDOG_HW_RUNNING, &wdd->status); |
d0684c8a9 watchdog: Make st... |
299 |
err = wdd->ops->stop(wdd); |
3c10bbde1 watchdog: core: C... |
300 |
} else { |
d0684c8a9 watchdog: Make st... |
301 |
set_bit(WDOG_HW_RUNNING, &wdd->status); |
3c10bbde1 watchdog: core: C... |
302 |
} |
d0684c8a9 watchdog: Make st... |
303 |
|
664a39236 watchdog: Introdu... |
304 |
if (err == 0) { |
bc794ac3b watchdog: watchdo... |
305 |
clear_bit(WDOG_ACTIVE, &wdd->status); |
ee142889e watchdog: Introdu... |
306 |
watchdog_update_worker(wdd); |
664a39236 watchdog: Introdu... |
307 |
} |
7a8798242 watchdog: watchdo... |
308 |
|
7a8798242 watchdog: watchdo... |
309 310 311 312 313 |
return err; } /* * watchdog_get_status: wrapper to get the watchdog status |
bc794ac3b watchdog: watchdo... |
314 |
* @wdd: the watchdog device to get the status from |
b4ffb1909 watchdog: Separat... |
315 316 |
* * The caller must hold wd_data->lock. |
7a8798242 watchdog: watchdo... |
317 318 319 |
* * Get the watchdog's status flags. */ |
b4ffb1909 watchdog: Separat... |
320 |
static unsigned int watchdog_get_status(struct watchdog_device *wdd) |
7a8798242 watchdog: watchdo... |
321 |
{ |
90b826f17 watchdog: Impleme... |
322 323 |
struct watchdog_core_data *wd_data = wdd->wd_data; unsigned int status; |
7a8798242 watchdog: watchdo... |
324 |
|
90b826f17 watchdog: Impleme... |
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 |
if (wdd->ops->status) status = wdd->ops->status(wdd); else status = wdd->bootstatus & (WDIOF_CARDRESET | WDIOF_OVERHEAT | WDIOF_FANFAULT | WDIOF_EXTERN1 | WDIOF_EXTERN2 | WDIOF_POWERUNDER | WDIOF_POWEROVER); if (test_bit(_WDOG_ALLOW_RELEASE, &wd_data->status)) status |= WDIOF_MAGICCLOSE; if (test_and_clear_bit(_WDOG_KEEPALIVE, &wd_data->status)) status |= WDIOF_KEEPALIVEPING; return status; |
7a8798242 watchdog: watchdo... |
343 344 345 346 |
} /* * watchdog_set_timeout: set the watchdog timer timeout |
bc794ac3b watchdog: watchdo... |
347 |
* @wdd: the watchdog device to set the timeout for |
7a8798242 watchdog: watchdo... |
348 |
* @timeout: timeout to set in seconds |
b4ffb1909 watchdog: Separat... |
349 350 |
* * The caller must hold wd_data->lock. |
7a8798242 watchdog: watchdo... |
351 |
*/ |
bc794ac3b watchdog: watchdo... |
352 |
static int watchdog_set_timeout(struct watchdog_device *wdd, |
7a8798242 watchdog: watchdo... |
353 354 |
unsigned int timeout) { |
fb32e9b9d watchdog: Make se... |
355 356 357 |
int err = 0; if (!(wdd->info->options & WDIOF_SETTIMEOUT)) |
7a8798242 watchdog: watchdo... |
358 |
return -EOPNOTSUPP; |
bc794ac3b watchdog: watchdo... |
359 |
if (watchdog_timeout_invalid(wdd, timeout)) |
7a8798242 watchdog: watchdo... |
360 |
return -EINVAL; |
df044e022 watchdog: add pre... |
361 |
if (wdd->ops->set_timeout) { |
fb32e9b9d watchdog: Make se... |
362 |
err = wdd->ops->set_timeout(wdd, timeout); |
df044e022 watchdog: add pre... |
363 |
} else { |
fb32e9b9d watchdog: Make se... |
364 |
wdd->timeout = timeout; |
df044e022 watchdog: add pre... |
365 366 367 368 |
/* Disable pretimeout if it doesn't fit the new timeout */ if (wdd->pretimeout >= wdd->timeout) wdd->pretimeout = 0; } |
fb32e9b9d watchdog: Make se... |
369 |
|
664a39236 watchdog: Introdu... |
370 |
watchdog_update_worker(wdd); |
fb32e9b9d watchdog: Make se... |
371 |
return err; |
7a8798242 watchdog: watchdo... |
372 373 374 |
} /* |
df044e022 watchdog: add pre... |
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
* watchdog_set_pretimeout: set the watchdog timer pretimeout * @wdd: the watchdog device to set the timeout for * @timeout: pretimeout to set in seconds */ static int watchdog_set_pretimeout(struct watchdog_device *wdd, unsigned int timeout) { int err = 0; if (!(wdd->info->options & WDIOF_PRETIMEOUT)) return -EOPNOTSUPP; if (watchdog_pretimeout_invalid(wdd, timeout)) return -EINVAL; if (wdd->ops->set_pretimeout) err = wdd->ops->set_pretimeout(wdd, timeout); else wdd->pretimeout = timeout; return err; } /* |
7a8798242 watchdog: watchdo... |
400 |
* watchdog_get_timeleft: wrapper to get the time left before a reboot |
bc794ac3b watchdog: watchdo... |
401 |
* @wdd: the watchdog device to get the remaining time from |
7a8798242 watchdog: watchdo... |
402 403 |
* @timeleft: the time that's left * |
b4ffb1909 watchdog: Separat... |
404 405 |
* The caller must hold wd_data->lock. * |
7a8798242 watchdog: watchdo... |
406 407 |
* Get the time before a watchdog will reboot (if not pinged). */ |
bc794ac3b watchdog: watchdo... |
408 |
static int watchdog_get_timeleft(struct watchdog_device *wdd, |
7a8798242 watchdog: watchdo... |
409 410 |
unsigned int *timeleft) { |
7a8798242 watchdog: watchdo... |
411 |
*timeleft = 0; |
b4ffb1909 watchdog: Separat... |
412 |
|
bc794ac3b watchdog: watchdo... |
413 |
if (!wdd->ops->get_timeleft) |
7a8798242 watchdog: watchdo... |
414 |
return -EOPNOTSUPP; |
bc794ac3b watchdog: watchdo... |
415 |
*timeleft = wdd->ops->get_timeleft(wdd); |
7a8798242 watchdog: watchdo... |
416 |
|
b4ffb1909 watchdog: Separat... |
417 |
return 0; |
7a8798242 watchdog: watchdo... |
418 |
} |
33b711269 watchdog: Read de... |
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 |
#ifdef CONFIG_WATCHDOG_SYSFS static ssize_t nowayout_show(struct device *dev, struct device_attribute *attr, char *buf) { struct watchdog_device *wdd = dev_get_drvdata(dev); return sprintf(buf, "%d ", !!test_bit(WDOG_NO_WAY_OUT, &wdd->status)); } static DEVICE_ATTR_RO(nowayout); static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf) { struct watchdog_device *wdd = dev_get_drvdata(dev); |
b4ffb1909 watchdog: Separat... |
434 435 |
struct watchdog_core_data *wd_data = wdd->wd_data; unsigned int status; |
33b711269 watchdog: Read de... |
436 |
|
b4ffb1909 watchdog: Separat... |
437 438 439 |
mutex_lock(&wd_data->lock); status = watchdog_get_status(wdd); mutex_unlock(&wd_data->lock); |
33b711269 watchdog: Read de... |
440 |
|
90b826f17 watchdog: Impleme... |
441 442 |
return sprintf(buf, "0x%x ", status); |
33b711269 watchdog: Read de... |
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 |
} static DEVICE_ATTR_RO(status); static ssize_t bootstatus_show(struct device *dev, struct device_attribute *attr, char *buf) { struct watchdog_device *wdd = dev_get_drvdata(dev); return sprintf(buf, "%u ", wdd->bootstatus); } static DEVICE_ATTR_RO(bootstatus); static ssize_t timeleft_show(struct device *dev, struct device_attribute *attr, char *buf) { struct watchdog_device *wdd = dev_get_drvdata(dev); |
b4ffb1909 watchdog: Separat... |
460 |
struct watchdog_core_data *wd_data = wdd->wd_data; |
33b711269 watchdog: Read de... |
461 462 |
ssize_t status; unsigned int val; |
b4ffb1909 watchdog: Separat... |
463 |
mutex_lock(&wd_data->lock); |
33b711269 watchdog: Read de... |
464 |
status = watchdog_get_timeleft(wdd, &val); |
b4ffb1909 watchdog: Separat... |
465 |
mutex_unlock(&wd_data->lock); |
33b711269 watchdog: Read de... |
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 |
if (!status) status = sprintf(buf, "%u ", val); return status; } static DEVICE_ATTR_RO(timeleft); static ssize_t timeout_show(struct device *dev, struct device_attribute *attr, char *buf) { struct watchdog_device *wdd = dev_get_drvdata(dev); return sprintf(buf, "%u ", wdd->timeout); } static DEVICE_ATTR_RO(timeout); |
df044e022 watchdog: add pre... |
483 484 485 486 487 488 489 490 491 |
static ssize_t pretimeout_show(struct device *dev, struct device_attribute *attr, char *buf) { struct watchdog_device *wdd = dev_get_drvdata(dev); return sprintf(buf, "%u ", wdd->pretimeout); } static DEVICE_ATTR_RO(pretimeout); |
33b711269 watchdog: Read de... |
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 |
static ssize_t identity_show(struct device *dev, struct device_attribute *attr, char *buf) { struct watchdog_device *wdd = dev_get_drvdata(dev); return sprintf(buf, "%s ", wdd->info->identity); } static DEVICE_ATTR_RO(identity); static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct watchdog_device *wdd = dev_get_drvdata(dev); if (watchdog_active(wdd)) return sprintf(buf, "active "); return sprintf(buf, "inactive "); } static DEVICE_ATTR_RO(state); |
89873a711 watchdog: pretime... |
515 516 517 518 519 520 |
static ssize_t pretimeout_available_governors_show(struct device *dev, struct device_attribute *attr, char *buf) { return watchdog_pretimeout_available_governors_get(buf); } static DEVICE_ATTR_RO(pretimeout_available_governors); |
ff84136cb watchdog: add wat... |
521 522 523 524 525 526 527 528 |
static ssize_t pretimeout_governor_show(struct device *dev, struct device_attribute *attr, char *buf) { struct watchdog_device *wdd = dev_get_drvdata(dev); return watchdog_pretimeout_governor_get(wdd, buf); } |
53f96cee1 watchdog: pretime... |
529 530 531 532 533 534 535 536 537 538 539 540 541 542 |
static ssize_t pretimeout_governor_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct watchdog_device *wdd = dev_get_drvdata(dev); int ret = watchdog_pretimeout_governor_set(wdd, buf); if (!ret) ret = count; return ret; } static DEVICE_ATTR_RW(pretimeout_governor); |
ff84136cb watchdog: add wat... |
543 |
|
33b711269 watchdog: Read de... |
544 545 546 547 548 549 |
static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, int n) { struct device *dev = container_of(kobj, struct device, kobj); struct watchdog_device *wdd = dev_get_drvdata(dev); umode_t mode = attr->mode; |
90b826f17 watchdog: Impleme... |
550 |
if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft) |
33b711269 watchdog: Read de... |
551 |
mode = 0; |
df044e022 watchdog: add pre... |
552 553 554 |
else if (attr == &dev_attr_pretimeout.attr && !(wdd->info->options & WDIOF_PRETIMEOUT)) mode = 0; |
89873a711 watchdog: pretime... |
555 556 |
else if ((attr == &dev_attr_pretimeout_governor.attr || attr == &dev_attr_pretimeout_available_governors.attr) && |
ff84136cb watchdog: add wat... |
557 558 559 |
(!(wdd->info->options & WDIOF_PRETIMEOUT) || !IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV))) mode = 0; |
33b711269 watchdog: Read de... |
560 561 562 563 564 565 566 |
return mode; } static struct attribute *wdt_attrs[] = { &dev_attr_state.attr, &dev_attr_identity.attr, &dev_attr_timeout.attr, |
df044e022 watchdog: add pre... |
567 |
&dev_attr_pretimeout.attr, |
33b711269 watchdog: Read de... |
568 569 570 571 |
&dev_attr_timeleft.attr, &dev_attr_bootstatus.attr, &dev_attr_status.attr, &dev_attr_nowayout.attr, |
ff84136cb watchdog: add wat... |
572 |
&dev_attr_pretimeout_governor.attr, |
89873a711 watchdog: pretime... |
573 |
&dev_attr_pretimeout_available_governors.attr, |
33b711269 watchdog: Read de... |
574 575 576 577 578 579 580 581 582 583 584 |
NULL, }; static const struct attribute_group wdt_group = { .attrs = wdt_attrs, .is_visible = wdt_is_visible, }; __ATTRIBUTE_GROUPS(wdt); #else #define wdt_groups NULL #endif |
7a8798242 watchdog: watchdo... |
585 586 |
/* * watchdog_ioctl_op: call the watchdog drivers ioctl op if defined |
bc794ac3b watchdog: watchdo... |
587 |
* @wdd: the watchdog device to do the ioctl on |
7a8798242 watchdog: watchdo... |
588 589 |
* @cmd: watchdog command * @arg: argument pointer |
b4ffb1909 watchdog: Separat... |
590 591 |
* * The caller must hold wd_data->lock. |
7a8798242 watchdog: watchdo... |
592 |
*/ |
bc794ac3b watchdog: watchdo... |
593 |
static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd, |
7a8798242 watchdog: watchdo... |
594 595 |
unsigned long arg) { |
bc794ac3b watchdog: watchdo... |
596 |
if (!wdd->ops->ioctl) |
7a8798242 watchdog: watchdo... |
597 |
return -ENOIOCTLCMD; |
b4ffb1909 watchdog: Separat... |
598 |
return wdd->ops->ioctl(wdd, cmd, arg); |
43316044d watchdog: WatchDo... |
599 600 601 602 603 604 605 606 607 608 |
} /* * watchdog_write: writes to the watchdog. * @file: file from VFS * @data: user address of data * @len: length of data * @ppos: pointer to the file offset * * A write to a watchdog device is defined as a keepalive ping. |
017cf0805 watchdog: WatchDo... |
609 |
* Writing the magic 'V' sequence allows the next close to turn |
7e192b9c4 watchdog: WatchDo... |
610 |
* off the watchdog (if 'nowayout' is not set). |
43316044d watchdog: WatchDo... |
611 612 613 614 615 |
*/ static ssize_t watchdog_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) { |
b4ffb1909 watchdog: Separat... |
616 617 618 |
struct watchdog_core_data *wd_data = file->private_data; struct watchdog_device *wdd; int err; |
43316044d watchdog: WatchDo... |
619 620 621 622 623 |
size_t i; char c; if (len == 0) return 0; |
017cf0805 watchdog: WatchDo... |
624 625 626 627 |
/* * Note: just in case someone wrote the magic character * five months ago... */ |
b4ffb1909 watchdog: Separat... |
628 |
clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status); |
017cf0805 watchdog: WatchDo... |
629 630 |
/* scan to see whether or not we got the magic character */ |
43316044d watchdog: WatchDo... |
631 632 633 |
for (i = 0; i != len; i++) { if (get_user(c, data + i)) return -EFAULT; |
017cf0805 watchdog: WatchDo... |
634 |
if (c == 'V') |
b4ffb1909 watchdog: Separat... |
635 |
set_bit(_WDOG_ALLOW_RELEASE, &wd_data->status); |
43316044d watchdog: WatchDo... |
636 637 638 |
} /* someone wrote to us, so we send the watchdog a keepalive ping */ |
b4ffb1909 watchdog: Separat... |
639 640 641 642 643 644 645 |
err = -ENODEV; mutex_lock(&wd_data->lock); wdd = wd_data->wdd; if (wdd) err = watchdog_ping(wdd); mutex_unlock(&wd_data->lock); |
5ef796639 watchdog: core: p... |
646 647 |
if (err < 0) return err; |
43316044d watchdog: WatchDo... |
648 649 650 651 652 |
return len; } /* |
2fa03560a watchdog: WatchDo... |
653 654 655 656 657 658 659 660 661 662 663 664 |
* watchdog_ioctl: handle the different ioctl's for the watchdog device. * @file: file handle to the device * @cmd: watchdog command * @arg: argument pointer * * The watchdog API defines a common set of functions for all watchdogs * according to their available features. */ static long watchdog_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { |
b4ffb1909 watchdog: Separat... |
665 |
struct watchdog_core_data *wd_data = file->private_data; |
2fa03560a watchdog: WatchDo... |
666 |
void __user *argp = (void __user *)arg; |
b4ffb1909 watchdog: Separat... |
667 |
struct watchdog_device *wdd; |
2fa03560a watchdog: WatchDo... |
668 669 |
int __user *p = argp; unsigned int val; |
234445b4e watchdog: WatchDo... |
670 |
int err; |
2fa03560a watchdog: WatchDo... |
671 |
|
b4ffb1909 watchdog: Separat... |
672 673 674 675 676 677 678 |
mutex_lock(&wd_data->lock); wdd = wd_data->wdd; if (!wdd) { err = -ENODEV; goto out_ioctl; } |
7a8798242 watchdog: watchdo... |
679 680 |
err = watchdog_ioctl_op(wdd, cmd, arg); if (err != -ENOIOCTLCMD) |
b4ffb1909 watchdog: Separat... |
681 |
goto out_ioctl; |
78d88fc01 watchdog: WatchDo... |
682 |
|
2fa03560a watchdog: WatchDo... |
683 684 |
switch (cmd) { case WDIOC_GETSUPPORT: |
b4ffb1909 watchdog: Separat... |
685 |
err = copy_to_user(argp, wdd->info, |
2fa03560a watchdog: WatchDo... |
686 |
sizeof(struct watchdog_info)) ? -EFAULT : 0; |
b4ffb1909 watchdog: Separat... |
687 |
break; |
2fa03560a watchdog: WatchDo... |
688 |
case WDIOC_GETSTATUS: |
b4ffb1909 watchdog: Separat... |
689 690 691 |
val = watchdog_get_status(wdd); err = put_user(val, p); break; |
2fa03560a watchdog: WatchDo... |
692 |
case WDIOC_GETBOOTSTATUS: |
b4ffb1909 watchdog: Separat... |
693 694 |
err = put_user(wdd->bootstatus, p); break; |
234445b4e watchdog: WatchDo... |
695 |
case WDIOC_SETOPTIONS: |
b4ffb1909 watchdog: Separat... |
696 697 698 699 |
if (get_user(val, p)) { err = -EFAULT; break; } |
234445b4e watchdog: WatchDo... |
700 701 702 |
if (val & WDIOS_DISABLECARD) { err = watchdog_stop(wdd); if (err < 0) |
b4ffb1909 watchdog: Separat... |
703 |
break; |
234445b4e watchdog: WatchDo... |
704 |
} |
b4ffb1909 watchdog: Separat... |
705 |
if (val & WDIOS_ENABLECARD) |
234445b4e watchdog: WatchDo... |
706 |
err = watchdog_start(wdd); |
b4ffb1909 watchdog: Separat... |
707 |
break; |
c2dc00e49 watchdog: WatchDo... |
708 |
case WDIOC_KEEPALIVE: |
b4ffb1909 watchdog: Separat... |
709 710 711 712 713 714 |
if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) { err = -EOPNOTSUPP; break; } err = watchdog_ping(wdd); break; |
014d694e5 watchdog: WatchDo... |
715 |
case WDIOC_SETTIMEOUT: |
b4ffb1909 watchdog: Separat... |
716 717 718 719 |
if (get_user(val, p)) { err = -EFAULT; break; } |
7a8798242 watchdog: watchdo... |
720 |
err = watchdog_set_timeout(wdd, val); |
014d694e5 watchdog: WatchDo... |
721 |
if (err < 0) |
b4ffb1909 watchdog: Separat... |
722 |
break; |
014d694e5 watchdog: WatchDo... |
723 724 725 |
/* If the watchdog is active then we send a keepalive ping * to make sure that the watchdog keep's running (and if * possible that it takes the new timeout) */ |
5ef796639 watchdog: core: p... |
726 727 |
err = watchdog_ping(wdd); if (err < 0) |
b4ffb1909 watchdog: Separat... |
728 |
break; |
e2af3092d watchdog: watchdo... |
729 |
/* fall through */ |
014d694e5 watchdog: WatchDo... |
730 731 |
case WDIOC_GETTIMEOUT: /* timeout == 0 means that we don't know the timeout */ |
b4ffb1909 watchdog: Separat... |
732 733 734 735 736 737 |
if (wdd->timeout == 0) { err = -EOPNOTSUPP; break; } err = put_user(wdd->timeout, p); break; |
fd7b673c9 watchdog: Add sup... |
738 |
case WDIOC_GETTIMELEFT: |
7a8798242 watchdog: watchdo... |
739 |
err = watchdog_get_timeleft(wdd, &val); |
b4ffb1909 watchdog: Separat... |
740 741 742 743 |
if (err < 0) break; err = put_user(val, p); break; |
df044e022 watchdog: add pre... |
744 745 746 747 748 749 750 751 752 753 |
case WDIOC_SETPRETIMEOUT: if (get_user(val, p)) { err = -EFAULT; break; } err = watchdog_set_pretimeout(wdd, val); break; case WDIOC_GETPRETIMEOUT: err = put_user(wdd->pretimeout, p); break; |
2fa03560a watchdog: WatchDo... |
754 |
default: |
b4ffb1909 watchdog: Separat... |
755 756 |
err = -ENOTTY; break; |
2fa03560a watchdog: WatchDo... |
757 |
} |
b4ffb1909 watchdog: Separat... |
758 759 760 761 |
out_ioctl: mutex_unlock(&wd_data->lock); return err; |
2fa03560a watchdog: WatchDo... |
762 763 764 |
} /* |
45f5fed30 watchdog: Add mul... |
765 |
* watchdog_open: open the /dev/watchdog* devices. |
43316044d watchdog: WatchDo... |
766 767 768 |
* @inode: inode of device * @file: file handle to device * |
45f5fed30 watchdog: Add mul... |
769 |
* When the /dev/watchdog* device gets opened, we start the watchdog. |
43316044d watchdog: WatchDo... |
770 771 772 773 774 775 |
* Watch out: the /dev/watchdog device is single open, so we make sure * it can only be opened once. */ static int watchdog_open(struct inode *inode, struct file *file) { |
b4ffb1909 watchdog: Separat... |
776 |
struct watchdog_core_data *wd_data; |
45f5fed30 watchdog: Add mul... |
777 |
struct watchdog_device *wdd; |
4bcd615fa watchdog: Fix pot... |
778 |
bool hw_running; |
b4ffb1909 watchdog: Separat... |
779 |
int err; |
45f5fed30 watchdog: Add mul... |
780 781 782 |
/* Get the corresponding watchdog device */ if (imajor(inode) == MISC_MAJOR) |
b4ffb1909 watchdog: Separat... |
783 |
wd_data = old_wd_data; |
45f5fed30 watchdog: Add mul... |
784 |
else |
b4ffb1909 watchdog: Separat... |
785 786 |
wd_data = container_of(inode->i_cdev, struct watchdog_core_data, cdev); |
43316044d watchdog: WatchDo... |
787 788 |
/* the watchdog is single open! */ |
b4ffb1909 watchdog: Separat... |
789 |
if (test_and_set_bit(_WDOG_DEV_OPEN, &wd_data->status)) |
43316044d watchdog: WatchDo... |
790 |
return -EBUSY; |
b4ffb1909 watchdog: Separat... |
791 |
wdd = wd_data->wdd; |
43316044d watchdog: WatchDo... |
792 793 794 795 |
/* * If the /dev/watchdog device is open, we don't want the module * to be unloaded. */ |
4bcd615fa watchdog: Fix pot... |
796 797 |
hw_running = watchdog_hw_running(wdd); if (!hw_running && !try_module_get(wdd->ops->owner)) { |
b4ffb1909 watchdog: Separat... |
798 799 800 |
err = -EBUSY; goto out_clear; } |
43316044d watchdog: WatchDo... |
801 |
|
234445b4e watchdog: WatchDo... |
802 |
err = watchdog_start(wdd); |
43316044d watchdog: WatchDo... |
803 804 |
if (err < 0) goto out_mod; |
b4ffb1909 watchdog: Separat... |
805 |
file->private_data = wd_data; |
45f5fed30 watchdog: Add mul... |
806 |
|
4bcd615fa watchdog: Fix pot... |
807 |
if (!hw_running) |
ca7851d46 watchdog: Fix the... |
808 |
get_device(&wd_data->dev); |
e907df327 watchdog: Add sup... |
809 |
|
4d1c6a0ec watchdog: introdu... |
810 811 812 813 814 815 816 817 |
/* * open_timeout only applies for the first open from * userspace. Set open_deadline to infinity so that the kernel * will take care of an always-running hardware watchdog in * case the device gets magic-closed or WDIOS_DISABLECARD is * applied. */ wd_data->open_deadline = KTIME_MAX; |
43316044d watchdog: WatchDo... |
818 |
/* dev/watchdog is a virtual (and thus non-seekable) filesystem */ |
c5bf68fe0 *: convert stream... |
819 |
return stream_open(inode, file); |
43316044d watchdog: WatchDo... |
820 821 |
out_mod: |
b4ffb1909 watchdog: Separat... |
822 823 824 |
module_put(wd_data->wdd->ops->owner); out_clear: clear_bit(_WDOG_DEV_OPEN, &wd_data->status); |
43316044d watchdog: WatchDo... |
825 826 |
return err; } |
ca7851d46 watchdog: Fix the... |
827 |
static void watchdog_core_data_release(struct device *dev) |
b4ffb1909 watchdog: Separat... |
828 829 |
{ struct watchdog_core_data *wd_data; |
ca7851d46 watchdog: Fix the... |
830 |
wd_data = container_of(dev, struct watchdog_core_data, dev); |
b4ffb1909 watchdog: Separat... |
831 832 833 |
kfree(wd_data); } |
43316044d watchdog: WatchDo... |
834 |
/* |
45f5fed30 watchdog: Add mul... |
835 836 837 |
* watchdog_release: release the watchdog device. * @inode: inode of device * @file: file handle to device |
43316044d watchdog: WatchDo... |
838 |
* |
017cf0805 watchdog: WatchDo... |
839 |
* This is the code for when /dev/watchdog gets closed. We will only |
7e192b9c4 watchdog: WatchDo... |
840 841 |
* stop the watchdog when we have received the magic char (and nowayout * was not set), else the watchdog will keep running. |
43316044d watchdog: WatchDo... |
842 843 844 845 |
*/ static int watchdog_release(struct inode *inode, struct file *file) { |
b4ffb1909 watchdog: Separat... |
846 847 |
struct watchdog_core_data *wd_data = file->private_data; struct watchdog_device *wdd; |
017cf0805 watchdog: WatchDo... |
848 |
int err = -EBUSY; |
d1ed3ba4e watchdog: Ensure ... |
849 |
bool running; |
017cf0805 watchdog: WatchDo... |
850 |
|
b4ffb1909 watchdog: Separat... |
851 852 853 854 855 |
mutex_lock(&wd_data->lock); wdd = wd_data->wdd; if (!wdd) goto done; |
017cf0805 watchdog: WatchDo... |
856 857 |
/* * We only stop the watchdog if we received the magic character |
7e192b9c4 watchdog: WatchDo... |
858 859 |
* or if WDIOF_MAGICCLOSE is not set. If nowayout was set then * watchdog_stop will fail. |
017cf0805 watchdog: WatchDo... |
860 |
*/ |
fcf95670f watchdog: core: d... |
861 862 |
if (!test_bit(WDOG_ACTIVE, &wdd->status)) err = 0; |
b4ffb1909 watchdog: Separat... |
863 |
else if (test_and_clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status) || |
fcf95670f watchdog: core: d... |
864 |
!(wdd->info->options & WDIOF_MAGICCLOSE)) |
017cf0805 watchdog: WatchDo... |
865 |
err = watchdog_stop(wdd); |
43316044d watchdog: WatchDo... |
866 |
|
017cf0805 watchdog: WatchDo... |
867 |
/* If the watchdog was not stopped, send a keepalive ping */ |
234445b4e watchdog: WatchDo... |
868 |
if (err < 0) { |
0254e9535 watchdog: Drop po... |
869 870 |
pr_crit("watchdog%d: watchdog did not stop! ", wdd->id); |
43316044d watchdog: WatchDo... |
871 872 |
watchdog_ping(wdd); } |
ee142889e watchdog: Introdu... |
873 |
watchdog_update_worker(wdd); |
664a39236 watchdog: Introdu... |
874 |
|
43316044d watchdog: WatchDo... |
875 |
/* make sure that /dev/watchdog can be re-opened */ |
b4ffb1909 watchdog: Separat... |
876 |
clear_bit(_WDOG_DEV_OPEN, &wd_data->status); |
e907df327 watchdog: Add sup... |
877 |
|
b4ffb1909 watchdog: Separat... |
878 |
done: |
d1ed3ba4e watchdog: Ensure ... |
879 |
running = wdd && watchdog_hw_running(wdd); |
b4ffb1909 watchdog: Separat... |
880 |
mutex_unlock(&wd_data->lock); |
ee142889e watchdog: Introdu... |
881 882 883 884 885 |
/* * Allow the owner module to be unloaded again unless the watchdog * is still running. If the watchdog is still running, it can not * be stopped, and its driver must not be unloaded. */ |
d1ed3ba4e watchdog: Ensure ... |
886 887 |
if (!running) { module_put(wd_data->cdev.owner); |
ca7851d46 watchdog: Fix the... |
888 |
put_device(&wd_data->dev); |
ee142889e watchdog: Introdu... |
889 |
} |
43316044d watchdog: WatchDo... |
890 891 892 893 894 895 |
return 0; } static const struct file_operations watchdog_fops = { .owner = THIS_MODULE, .write = watchdog_write, |
2fa03560a watchdog: WatchDo... |
896 |
.unlocked_ioctl = watchdog_ioctl, |
43316044d watchdog: WatchDo... |
897 898 899 900 901 902 903 904 905 |
.open = watchdog_open, .release = watchdog_release, }; static struct miscdevice watchdog_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &watchdog_fops, }; |
ca7851d46 watchdog: Fix the... |
906 907 908 909 910 |
static struct class watchdog_class = { .name = "watchdog", .owner = THIS_MODULE, .dev_groups = wdt_groups, }; |
43316044d watchdog: WatchDo... |
911 |
/* |
32ecc6392 watchdog: Create ... |
912 |
* watchdog_cdev_register: register watchdog character device |
bc794ac3b watchdog: watchdo... |
913 |
* @wdd: watchdog device |
43316044d watchdog: WatchDo... |
914 |
* |
32ecc6392 watchdog: Create ... |
915 |
* Register a watchdog character device including handling the legacy |
45f5fed30 watchdog: Add mul... |
916 917 |
* /dev/watchdog node. /dev/watchdog is actually a miscdevice and * thus we set it up like that. |
43316044d watchdog: WatchDo... |
918 |
*/ |
ca7851d46 watchdog: Fix the... |
919 |
static int watchdog_cdev_register(struct watchdog_device *wdd) |
43316044d watchdog: WatchDo... |
920 |
{ |
b4ffb1909 watchdog: Separat... |
921 |
struct watchdog_core_data *wd_data; |
32ecc6392 watchdog: Create ... |
922 |
int err; |
45f5fed30 watchdog: Add mul... |
923 |
|
b4ffb1909 watchdog: Separat... |
924 925 926 |
wd_data = kzalloc(sizeof(struct watchdog_core_data), GFP_KERNEL); if (!wd_data) return -ENOMEM; |
b4ffb1909 watchdog: Separat... |
927 928 929 930 |
mutex_init(&wd_data->lock); wd_data->wdd = wdd; wdd->wd_data = wd_data; |
38a1222ae watchdog: core: m... |
931 |
if (IS_ERR_OR_NULL(watchdog_kworker)) |
664a39236 watchdog: Introdu... |
932 |
return -ENODEV; |
1ff688209 watchdog: core: m... |
933 |
kthread_init_work(&wd_data->work, watchdog_ping_work); |
ce4d6fff7 watchdog: prevent... |
934 |
hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD); |
1ff688209 watchdog: core: m... |
935 |
wd_data->timer.function = watchdog_timer_expired; |
664a39236 watchdog: Introdu... |
936 |
|
bc794ac3b watchdog: watchdo... |
937 |
if (wdd->id == 0) { |
b4ffb1909 watchdog: Separat... |
938 |
old_wd_data = wd_data; |
bc794ac3b watchdog: watchdo... |
939 |
watchdog_miscdev.parent = wdd->parent; |
45f5fed30 watchdog: Add mul... |
940 941 942 943 |
err = misc_register(&watchdog_miscdev); if (err != 0) { pr_err("%s: cannot register miscdev on minor=%d (err=%d). ", |
bc794ac3b watchdog: watchdo... |
944 |
wdd->info->identity, WATCHDOG_MINOR, err); |
45f5fed30 watchdog: Add mul... |
945 946 947 |
if (err == -EBUSY) pr_err("%s: a legacy watchdog module is probably present. ", |
bc794ac3b watchdog: watchdo... |
948 |
wdd->info->identity); |
b4ffb1909 watchdog: Separat... |
949 950 |
old_wd_data = NULL; kfree(wd_data); |
45f5fed30 watchdog: Add mul... |
951 952 |
return err; } |
43316044d watchdog: WatchDo... |
953 |
} |
ca7851d46 watchdog: Fix the... |
954 955 956 957 958 959 960 961 |
device_initialize(&wd_data->dev); wd_data->dev.devt = MKDEV(MAJOR(watchdog_devt), wdd->id); wd_data->dev.class = &watchdog_class; wd_data->dev.parent = wdd->parent; wd_data->dev.groups = wdd->groups; wd_data->dev.release = watchdog_core_data_release; dev_set_drvdata(&wd_data->dev, wdd); dev_set_name(&wd_data->dev, "watchdog%d", wdd->id); |
45f5fed30 watchdog: Add mul... |
962 |
/* Fill in the data structures */ |
b4ffb1909 watchdog: Separat... |
963 |
cdev_init(&wd_data->cdev, &watchdog_fops); |
45f5fed30 watchdog: Add mul... |
964 965 |
/* Add the device */ |
ca7851d46 watchdog: Fix the... |
966 |
err = cdev_device_add(&wd_data->cdev, &wd_data->dev); |
45f5fed30 watchdog: Add mul... |
967 968 969 |
if (err) { pr_err("watchdog%d unable to add device %d:%d ", |
bc794ac3b watchdog: watchdo... |
970 971 |
wdd->id, MAJOR(watchdog_devt), wdd->id); if (wdd->id == 0) { |
45f5fed30 watchdog: Add mul... |
972 |
misc_deregister(&watchdog_miscdev); |
b4ffb1909 watchdog: Separat... |
973 |
old_wd_data = NULL; |
ca7851d46 watchdog: Fix the... |
974 |
put_device(&wd_data->dev); |
45f5fed30 watchdog: Add mul... |
975 |
} |
ee142889e watchdog: Introdu... |
976 |
return err; |
43316044d watchdog: WatchDo... |
977 |
} |
ee142889e watchdog: Introdu... |
978 |
|
ca7851d46 watchdog: Fix the... |
979 |
wd_data->cdev.owner = wdd->ops->owner; |
15013ad81 watchdog: Add sup... |
980 |
/* Record time of most recent heartbeat as 'just before now'. */ |
1ff688209 watchdog: core: m... |
981 |
wd_data->last_hw_keepalive = ktime_sub(ktime_get(), 1); |
4d1c6a0ec watchdog: introdu... |
982 |
watchdog_set_open_deadline(wd_data); |
15013ad81 watchdog: Add sup... |
983 |
|
ee142889e watchdog: Introdu... |
984 985 986 987 988 |
/* * If the watchdog is running, prevent its driver from being unloaded, * and schedule an immediate ping. */ if (watchdog_hw_running(wdd)) { |
914d65f3f watchdog: Fix kre... |
989 |
__module_get(wdd->ops->owner); |
ca7851d46 watchdog: Fix the... |
990 |
get_device(&wd_data->dev); |
914d65f3f watchdog: Fix kre... |
991 |
if (handle_boot_enabled) |
ce4d6fff7 watchdog: prevent... |
992 993 |
hrtimer_start(&wd_data->timer, 0, HRTIMER_MODE_REL_HARD); |
914d65f3f watchdog: Fix kre... |
994 |
else |
2501b0153 watchdog: core: a... |
995 996 |
pr_info("watchdog%d running and kernel based pre-userspace handler disabled ", |
914d65f3f watchdog: Fix kre... |
997 |
wdd->id); |
ee142889e watchdog: Introdu... |
998 999 1000 |
} return 0; |
43316044d watchdog: WatchDo... |
1001 1002 1003 |
} /* |
32ecc6392 watchdog: Create ... |
1004 |
* watchdog_cdev_unregister: unregister watchdog character device |
43316044d watchdog: WatchDo... |
1005 1006 |
* @watchdog: watchdog device * |
32ecc6392 watchdog: Create ... |
1007 1008 |
* Unregister watchdog character device and if needed the legacy * /dev/watchdog device. |
43316044d watchdog: WatchDo... |
1009 |
*/ |
32ecc6392 watchdog: Create ... |
1010 |
static void watchdog_cdev_unregister(struct watchdog_device *wdd) |
43316044d watchdog: WatchDo... |
1011 |
{ |
b4ffb1909 watchdog: Separat... |
1012 |
struct watchdog_core_data *wd_data = wdd->wd_data; |
e907df327 watchdog: Add sup... |
1013 |
|
ca7851d46 watchdog: Fix the... |
1014 |
cdev_device_del(&wd_data->cdev, &wd_data->dev); |
bc794ac3b watchdog: watchdo... |
1015 |
if (wdd->id == 0) { |
45f5fed30 watchdog: Add mul... |
1016 |
misc_deregister(&watchdog_miscdev); |
b4ffb1909 watchdog: Separat... |
1017 |
old_wd_data = NULL; |
43316044d watchdog: WatchDo... |
1018 |
} |
b4ffb1909 watchdog: Separat... |
1019 |
|
bb292ac1c watchdog: Introdu... |
1020 1021 1022 1023 |
if (watchdog_active(wdd) && test_bit(WDOG_STOP_ON_UNREGISTER, &wdd->status)) { watchdog_stop(wdd); } |
953b9dd77 watchdog: core: f... |
1024 1025 1026 1027 |
mutex_lock(&wd_data->lock); wd_data->wdd = NULL; wdd->wd_data = NULL; mutex_unlock(&wd_data->lock); |
1ff688209 watchdog: core: m... |
1028 1029 |
hrtimer_cancel(&wd_data->timer); kthread_cancel_work_sync(&wd_data->work); |
664a39236 watchdog: Introdu... |
1030 |
|
ca7851d46 watchdog: Fix the... |
1031 |
put_device(&wd_data->dev); |
43316044d watchdog: WatchDo... |
1032 |
} |
45f5fed30 watchdog: Add mul... |
1033 1034 |
/* |
32ecc6392 watchdog: Create ... |
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 |
* watchdog_dev_register: register a watchdog device * @wdd: watchdog device * * Register a watchdog device including handling the legacy * /dev/watchdog node. /dev/watchdog is actually a miscdevice and * thus we set it up like that. */ int watchdog_dev_register(struct watchdog_device *wdd) { |
32ecc6392 watchdog: Create ... |
1045 |
int ret; |
ca7851d46 watchdog: Fix the... |
1046 |
ret = watchdog_cdev_register(wdd); |
32ecc6392 watchdog: Create ... |
1047 1048 |
if (ret) return ret; |
ff84136cb watchdog: add wat... |
1049 |
ret = watchdog_register_pretimeout(wdd); |
f158399c1 watchdog: fix UAF... |
1050 |
if (ret) |
ff84136cb watchdog: add wat... |
1051 |
watchdog_cdev_unregister(wdd); |
ff84136cb watchdog: add wat... |
1052 |
|
32ecc6392 watchdog: Create ... |
1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 |
return ret; } /* * watchdog_dev_unregister: unregister a watchdog device * @watchdog: watchdog device * * Unregister watchdog device and if needed the legacy * /dev/watchdog device. */ void watchdog_dev_unregister(struct watchdog_device *wdd) { |
ff84136cb watchdog: add wat... |
1066 |
watchdog_unregister_pretimeout(wdd); |
b4ffb1909 watchdog: Separat... |
1067 |
watchdog_cdev_unregister(wdd); |
32ecc6392 watchdog: Create ... |
1068 1069 1070 |
} /* |
45f5fed30 watchdog: Add mul... |
1071 1072 1073 1074 |
* watchdog_dev_init: init dev part of watchdog core * * Allocate a range of chardev nodes to use for watchdog devices */ |
32ecc6392 watchdog: Create ... |
1075 |
int __init watchdog_dev_init(void) |
45f5fed30 watchdog: Add mul... |
1076 |
{ |
906d7a5cf watchdog: Use sta... |
1077 |
int err; |
38a1222ae watchdog: core: m... |
1078 |
struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1,}; |
906d7a5cf watchdog: Use sta... |
1079 |
|
38a1222ae watchdog: core: m... |
1080 1081 1082 1083 1084 |
watchdog_kworker = kthread_create_worker(0, "watchdogd"); if (IS_ERR(watchdog_kworker)) { pr_err("Failed to create watchdog kworker "); return PTR_ERR(watchdog_kworker); |
664a39236 watchdog: Introdu... |
1085 |
} |
38a1222ae watchdog: core: m... |
1086 |
sched_setscheduler(watchdog_kworker->task, SCHED_FIFO, ¶m); |
664a39236 watchdog: Introdu... |
1087 |
|
906d7a5cf watchdog: Use sta... |
1088 1089 1090 1091 |
err = class_register(&watchdog_class); if (err < 0) { pr_err("couldn't register class "); |
138913cb6 watchdog: core: F... |
1092 |
goto err_register; |
906d7a5cf watchdog: Use sta... |
1093 1094 1095 1096 |
} err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); if (err < 0) { |
45f5fed30 watchdog: Add mul... |
1097 1098 |
pr_err("watchdog: unable to allocate char dev region "); |
138913cb6 watchdog: core: F... |
1099 |
goto err_alloc; |
906d7a5cf watchdog: Use sta... |
1100 |
} |
32ecc6392 watchdog: Create ... |
1101 |
return 0; |
138913cb6 watchdog: core: F... |
1102 1103 1104 1105 |
err_alloc: class_unregister(&watchdog_class); err_register: |
38a1222ae watchdog: core: m... |
1106 |
kthread_destroy_worker(watchdog_kworker); |
138913cb6 watchdog: core: F... |
1107 |
return err; |
45f5fed30 watchdog: Add mul... |
1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 |
} /* * watchdog_dev_exit: exit dev part of watchdog core * * Release the range of chardev nodes used for watchdog devices */ void __exit watchdog_dev_exit(void) { unregister_chrdev_region(watchdog_devt, MAX_DOGS); |
906d7a5cf watchdog: Use sta... |
1119 |
class_unregister(&watchdog_class); |
38a1222ae watchdog: core: m... |
1120 |
kthread_destroy_worker(watchdog_kworker); |
45f5fed30 watchdog: Add mul... |
1121 |
} |
2501b0153 watchdog: core: a... |
1122 1123 1124 1125 1126 |
module_param(handle_boot_enabled, bool, 0444); MODULE_PARM_DESC(handle_boot_enabled, "Watchdog core auto-updates boot enabled watchdogs before userspace takes over (default=" __MODULE_STRING(IS_ENABLED(CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED)) ")"); |
4d1c6a0ec watchdog: introdu... |
1127 1128 1129 |
module_param(open_timeout, uint, 0644); MODULE_PARM_DESC(open_timeout, |
487e4e082 watchdog: introdu... |
1130 1131 |
"Maximum time (in seconds, 0 means infinity) for userspace to take over a running watchdog (default=" __MODULE_STRING(CONFIG_WATCHDOG_OPEN_TIMEOUT) ")"); |