Blame view
drivers/watchdog/watchdog_dev.c
27.1 KB
43316044d watchdog: WatchDo... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
/* * 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> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * 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... |
34 |
#include <linux/cdev.h> /* For character device */ |
43316044d watchdog: WatchDo... |
35 |
#include <linux/errno.h> /* For the -ENODEV/... values */ |
43316044d watchdog: WatchDo... |
36 |
#include <linux/fs.h> /* For file operations */ |
43316044d watchdog: WatchDo... |
37 |
#include <linux/init.h> /* For __init/__exit/... */ |
664a39236 watchdog: Introdu... |
38 |
#include <linux/jiffies.h> /* For timeout functions */ |
b4ffb1909 watchdog: Separat... |
39 40 41 42 43 44 45 46 |
#include <linux/kernel.h> /* For printk/panic/... */ #include <linux/kref.h> /* For data references */ #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 */ |
664a39236 watchdog: Introdu... |
47 |
#include <linux/workqueue.h> /* For workqueue */ |
43316044d watchdog: WatchDo... |
48 |
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */ |
6cfb5aa83 watchdog: correct... |
49 |
#include "watchdog_core.h" |
ff84136cb watchdog: add wat... |
50 |
#include "watchdog_pretimeout.h" |
09a46e739 watchdog: watchdo... |
51 |
|
b4ffb1909 watchdog: Separat... |
52 53 54 55 56 57 58 59 60 61 62 63 64 |
/* * struct watchdog_core_data - watchdog core internal data * @kref: Reference count. * @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 { struct kref kref; struct cdev cdev; struct watchdog_device *wdd; struct mutex lock; |
664a39236 watchdog: Introdu... |
65 |
unsigned long last_keepalive; |
15013ad81 watchdog: Add sup... |
66 |
unsigned long last_hw_keepalive; |
664a39236 watchdog: Introdu... |
67 |
struct delayed_work work; |
b4ffb1909 watchdog: Separat... |
68 69 70 |
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... |
71 |
#define _WDOG_KEEPALIVE 2 /* Did we receive a keepalive ? */ |
b4ffb1909 watchdog: Separat... |
72 |
}; |
45f5fed30 watchdog: Add mul... |
73 74 |
/* the dev_t structure to store the dynamically allocated watchdog devices */ static dev_t watchdog_devt; |
b4ffb1909 watchdog: Separat... |
75 76 |
/* Reference to watchdog device behind /dev/watchdog */ static struct watchdog_core_data *old_wd_data; |
43316044d watchdog: WatchDo... |
77 |
|
664a39236 watchdog: Introdu... |
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
static struct workqueue_struct *watchdog_wq; 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 ... |
94 95 96 97 |
* * Alternatively, if userspace has not opened the watchdog * device, we take care of feeding the watchdog if it is * running. |
664a39236 watchdog: Introdu... |
98 |
*/ |
3fbfe9264 watchdog: change ... |
99 100 |
return (hm && watchdog_active(wdd) && t > hm) || (t && !watchdog_active(wdd) && watchdog_hw_running(wdd)); |
664a39236 watchdog: Introdu... |
101 102 103 104 105 106 107 108 109 110 111 112 |
} static long watchdog_next_keepalive(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; unsigned int timeout_ms = wdd->timeout * 1000; unsigned long keepalive_interval; unsigned long last_heartbeat; unsigned long virt_timeout; unsigned int hw_heartbeat_ms; virt_timeout = wd_data->last_keepalive + msecs_to_jiffies(timeout_ms); |
3fbfe9264 watchdog: change ... |
113 |
hw_heartbeat_ms = min_not_zero(timeout_ms, wdd->max_hw_heartbeat_ms); |
664a39236 watchdog: Introdu... |
114 |
keepalive_interval = msecs_to_jiffies(hw_heartbeat_ms / 2); |
ee142889e watchdog: Introdu... |
115 116 |
if (!watchdog_active(wdd)) return keepalive_interval; |
664a39236 watchdog: Introdu... |
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
/* * 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. */ last_heartbeat = virt_timeout - msecs_to_jiffies(hw_heartbeat_ms); return min_t(long, last_heartbeat - jiffies, keepalive_interval); } static inline void watchdog_update_worker(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; if (watchdog_need_worker(wdd)) { long t = watchdog_next_keepalive(wdd); if (t > 0) mod_delayed_work(watchdog_wq, &wd_data->work, t); } else { cancel_delayed_work(&wd_data->work); } } static int __watchdog_ping(struct watchdog_device *wdd) { |
15013ad81 watchdog: Add sup... |
142 143 144 |
struct watchdog_core_data *wd_data = wdd->wd_data; unsigned long earliest_keepalive = wd_data->last_hw_keepalive + msecs_to_jiffies(wdd->min_hw_heartbeat_ms); |
664a39236 watchdog: Introdu... |
145 |
int err; |
15013ad81 watchdog: Add sup... |
146 147 148 149 150 151 152 |
if (time_is_after_jiffies(earliest_keepalive)) { mod_delayed_work(watchdog_wq, &wd_data->work, earliest_keepalive - jiffies); return 0; } wd_data->last_hw_keepalive = jiffies; |
664a39236 watchdog: Introdu... |
153 154 155 156 157 158 159 160 161 |
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... |
162 163 |
/* * watchdog_ping: ping the watchdog. |
bc794ac3b watchdog: watchdo... |
164 |
* @wdd: the watchdog device to ping |
43316044d watchdog: WatchDo... |
165 |
* |
b4ffb1909 watchdog: Separat... |
166 167 |
* The caller must hold wd_data->lock. * |
43316044d watchdog: WatchDo... |
168 169 170 |
* 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... |
171 |
* We only ping when the watchdog device is running. |
43316044d watchdog: WatchDo... |
172 |
*/ |
bc794ac3b watchdog: watchdo... |
173 |
static int watchdog_ping(struct watchdog_device *wdd) |
43316044d watchdog: WatchDo... |
174 |
{ |
664a39236 watchdog: Introdu... |
175 |
struct watchdog_core_data *wd_data = wdd->wd_data; |
e907df327 watchdog: Add sup... |
176 |
|
ee142889e watchdog: Introdu... |
177 |
if (!watchdog_active(wdd) && !watchdog_hw_running(wdd)) |
b4ffb1909 watchdog: Separat... |
178 |
return 0; |
7a8798242 watchdog: watchdo... |
179 |
|
90b826f17 watchdog: Impleme... |
180 |
set_bit(_WDOG_KEEPALIVE, &wd_data->status); |
664a39236 watchdog: Introdu... |
181 182 183 |
wd_data->last_keepalive = jiffies; return __watchdog_ping(wdd); } |
7a8798242 watchdog: watchdo... |
184 |
|
664a39236 watchdog: Introdu... |
185 186 187 188 189 190 191 192 193 194 |
static void watchdog_ping_work(struct work_struct *work) { struct watchdog_core_data *wd_data; struct watchdog_device *wdd; wd_data = container_of(to_delayed_work(work), struct watchdog_core_data, work); mutex_lock(&wd_data->lock); wdd = wd_data->wdd; |
ee142889e watchdog: Introdu... |
195 |
if (wdd && (watchdog_active(wdd) || watchdog_hw_running(wdd))) |
664a39236 watchdog: Introdu... |
196 197 |
__watchdog_ping(wdd); mutex_unlock(&wd_data->lock); |
234445b4e watchdog: WatchDo... |
198 199 200 201 |
} /* * watchdog_start: wrapper to start the watchdog. |
bc794ac3b watchdog: watchdo... |
202 |
* @wdd: the watchdog device to start |
234445b4e watchdog: WatchDo... |
203 |
* |
b4ffb1909 watchdog: Separat... |
204 205 |
* The caller must hold wd_data->lock. * |
234445b4e watchdog: WatchDo... |
206 207 208 209 |
* 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... |
210 |
static int watchdog_start(struct watchdog_device *wdd) |
234445b4e watchdog: WatchDo... |
211 |
{ |
664a39236 watchdog: Introdu... |
212 213 |
struct watchdog_core_data *wd_data = wdd->wd_data; unsigned long started_at; |
b4ffb1909 watchdog: Separat... |
214 |
int err; |
e907df327 watchdog: Add sup... |
215 |
|
bc794ac3b watchdog: watchdo... |
216 |
if (watchdog_active(wdd)) |
b4ffb1909 watchdog: Separat... |
217 |
return 0; |
234445b4e watchdog: WatchDo... |
218 |
|
90b826f17 watchdog: Impleme... |
219 |
set_bit(_WDOG_KEEPALIVE, &wd_data->status); |
664a39236 watchdog: Introdu... |
220 |
started_at = jiffies; |
ee142889e watchdog: Introdu... |
221 222 223 224 |
if (watchdog_hw_running(wdd) && wdd->ops->ping) err = wdd->ops->ping(wdd); else err = wdd->ops->start(wdd); |
664a39236 watchdog: Introdu... |
225 |
if (err == 0) { |
bc794ac3b watchdog: watchdo... |
226 |
set_bit(WDOG_ACTIVE, &wdd->status); |
664a39236 watchdog: Introdu... |
227 228 229 |
wd_data->last_keepalive = started_at; watchdog_update_worker(wdd); } |
7a8798242 watchdog: watchdo... |
230 |
|
7a8798242 watchdog: watchdo... |
231 |
return err; |
234445b4e watchdog: WatchDo... |
232 233 234 235 |
} /* * watchdog_stop: wrapper to stop the watchdog. |
bc794ac3b watchdog: watchdo... |
236 |
* @wdd: the watchdog device to stop |
234445b4e watchdog: WatchDo... |
237 |
* |
b4ffb1909 watchdog: Separat... |
238 239 |
* The caller must hold wd_data->lock. * |
234445b4e watchdog: WatchDo... |
240 241 242 |
* 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... |
243 |
* If the 'nowayout' feature was set, the watchdog cannot be stopped. |
234445b4e watchdog: WatchDo... |
244 |
*/ |
bc794ac3b watchdog: watchdo... |
245 |
static int watchdog_stop(struct watchdog_device *wdd) |
234445b4e watchdog: WatchDo... |
246 |
{ |
ee142889e watchdog: Introdu... |
247 |
int err = 0; |
e907df327 watchdog: Add sup... |
248 |
|
bc794ac3b watchdog: watchdo... |
249 |
if (!watchdog_active(wdd)) |
b4ffb1909 watchdog: Separat... |
250 |
return 0; |
7e192b9c4 watchdog: WatchDo... |
251 |
|
bc794ac3b watchdog: watchdo... |
252 |
if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) { |
0254e9535 watchdog: Drop po... |
253 254 255 |
pr_info("watchdog%d: nowayout prevents watchdog being stopped! ", wdd->id); |
b4ffb1909 watchdog: Separat... |
256 |
return -EBUSY; |
7e192b9c4 watchdog: WatchDo... |
257 |
} |
234445b4e watchdog: WatchDo... |
258 |
|
3c10bbde1 watchdog: core: C... |
259 260 |
if (wdd->ops->stop) { clear_bit(WDOG_HW_RUNNING, &wdd->status); |
d0684c8a9 watchdog: Make st... |
261 |
err = wdd->ops->stop(wdd); |
3c10bbde1 watchdog: core: C... |
262 |
} else { |
d0684c8a9 watchdog: Make st... |
263 |
set_bit(WDOG_HW_RUNNING, &wdd->status); |
3c10bbde1 watchdog: core: C... |
264 |
} |
d0684c8a9 watchdog: Make st... |
265 |
|
664a39236 watchdog: Introdu... |
266 |
if (err == 0) { |
bc794ac3b watchdog: watchdo... |
267 |
clear_bit(WDOG_ACTIVE, &wdd->status); |
ee142889e watchdog: Introdu... |
268 |
watchdog_update_worker(wdd); |
664a39236 watchdog: Introdu... |
269 |
} |
7a8798242 watchdog: watchdo... |
270 |
|
7a8798242 watchdog: watchdo... |
271 272 273 274 275 |
return err; } /* * watchdog_get_status: wrapper to get the watchdog status |
bc794ac3b watchdog: watchdo... |
276 |
* @wdd: the watchdog device to get the status from |
b4ffb1909 watchdog: Separat... |
277 278 |
* * The caller must hold wd_data->lock. |
7a8798242 watchdog: watchdo... |
279 280 281 |
* * Get the watchdog's status flags. */ |
b4ffb1909 watchdog: Separat... |
282 |
static unsigned int watchdog_get_status(struct watchdog_device *wdd) |
7a8798242 watchdog: watchdo... |
283 |
{ |
90b826f17 watchdog: Impleme... |
284 285 |
struct watchdog_core_data *wd_data = wdd->wd_data; unsigned int status; |
7a8798242 watchdog: watchdo... |
286 |
|
90b826f17 watchdog: Impleme... |
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
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... |
305 306 307 308 |
} /* * watchdog_set_timeout: set the watchdog timer timeout |
bc794ac3b watchdog: watchdo... |
309 |
* @wdd: the watchdog device to set the timeout for |
7a8798242 watchdog: watchdo... |
310 |
* @timeout: timeout to set in seconds |
b4ffb1909 watchdog: Separat... |
311 312 |
* * The caller must hold wd_data->lock. |
7a8798242 watchdog: watchdo... |
313 |
*/ |
bc794ac3b watchdog: watchdo... |
314 |
static int watchdog_set_timeout(struct watchdog_device *wdd, |
7a8798242 watchdog: watchdo... |
315 316 |
unsigned int timeout) { |
fb32e9b9d watchdog: Make se... |
317 318 319 |
int err = 0; if (!(wdd->info->options & WDIOF_SETTIMEOUT)) |
7a8798242 watchdog: watchdo... |
320 |
return -EOPNOTSUPP; |
bc794ac3b watchdog: watchdo... |
321 |
if (watchdog_timeout_invalid(wdd, timeout)) |
7a8798242 watchdog: watchdo... |
322 |
return -EINVAL; |
df044e022 watchdog: add pre... |
323 |
if (wdd->ops->set_timeout) { |
fb32e9b9d watchdog: Make se... |
324 |
err = wdd->ops->set_timeout(wdd, timeout); |
df044e022 watchdog: add pre... |
325 |
} else { |
fb32e9b9d watchdog: Make se... |
326 |
wdd->timeout = timeout; |
df044e022 watchdog: add pre... |
327 328 329 330 |
/* Disable pretimeout if it doesn't fit the new timeout */ if (wdd->pretimeout >= wdd->timeout) wdd->pretimeout = 0; } |
fb32e9b9d watchdog: Make se... |
331 |
|
664a39236 watchdog: Introdu... |
332 |
watchdog_update_worker(wdd); |
fb32e9b9d watchdog: Make se... |
333 |
return err; |
7a8798242 watchdog: watchdo... |
334 335 336 |
} /* |
df044e022 watchdog: add pre... |
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
* 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... |
362 |
* watchdog_get_timeleft: wrapper to get the time left before a reboot |
bc794ac3b watchdog: watchdo... |
363 |
* @wdd: the watchdog device to get the remaining time from |
7a8798242 watchdog: watchdo... |
364 365 |
* @timeleft: the time that's left * |
b4ffb1909 watchdog: Separat... |
366 367 |
* The caller must hold wd_data->lock. * |
7a8798242 watchdog: watchdo... |
368 369 |
* Get the time before a watchdog will reboot (if not pinged). */ |
bc794ac3b watchdog: watchdo... |
370 |
static int watchdog_get_timeleft(struct watchdog_device *wdd, |
7a8798242 watchdog: watchdo... |
371 372 |
unsigned int *timeleft) { |
7a8798242 watchdog: watchdo... |
373 |
*timeleft = 0; |
b4ffb1909 watchdog: Separat... |
374 |
|
bc794ac3b watchdog: watchdo... |
375 |
if (!wdd->ops->get_timeleft) |
7a8798242 watchdog: watchdo... |
376 |
return -EOPNOTSUPP; |
bc794ac3b watchdog: watchdo... |
377 |
*timeleft = wdd->ops->get_timeleft(wdd); |
7a8798242 watchdog: watchdo... |
378 |
|
b4ffb1909 watchdog: Separat... |
379 |
return 0; |
7a8798242 watchdog: watchdo... |
380 |
} |
33b711269 watchdog: Read de... |
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
#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... |
396 397 |
struct watchdog_core_data *wd_data = wdd->wd_data; unsigned int status; |
33b711269 watchdog: Read de... |
398 |
|
b4ffb1909 watchdog: Separat... |
399 400 401 |
mutex_lock(&wd_data->lock); status = watchdog_get_status(wdd); mutex_unlock(&wd_data->lock); |
33b711269 watchdog: Read de... |
402 |
|
90b826f17 watchdog: Impleme... |
403 404 |
return sprintf(buf, "0x%x ", status); |
33b711269 watchdog: Read de... |
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 |
} 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... |
422 |
struct watchdog_core_data *wd_data = wdd->wd_data; |
33b711269 watchdog: Read de... |
423 424 |
ssize_t status; unsigned int val; |
b4ffb1909 watchdog: Separat... |
425 |
mutex_lock(&wd_data->lock); |
33b711269 watchdog: Read de... |
426 |
status = watchdog_get_timeleft(wdd, &val); |
b4ffb1909 watchdog: Separat... |
427 |
mutex_unlock(&wd_data->lock); |
33b711269 watchdog: Read de... |
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
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... |
445 446 447 448 449 450 451 452 453 |
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... |
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 |
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... |
477 478 479 480 481 482 |
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... |
483 484 485 486 487 488 489 490 |
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... |
491 492 493 494 495 496 497 498 499 500 501 502 503 504 |
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... |
505 |
|
33b711269 watchdog: Read de... |
506 507 508 509 510 511 |
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... |
512 |
if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft) |
33b711269 watchdog: Read de... |
513 |
mode = 0; |
df044e022 watchdog: add pre... |
514 515 516 |
else if (attr == &dev_attr_pretimeout.attr && !(wdd->info->options & WDIOF_PRETIMEOUT)) mode = 0; |
89873a711 watchdog: pretime... |
517 518 |
else if ((attr == &dev_attr_pretimeout_governor.attr || attr == &dev_attr_pretimeout_available_governors.attr) && |
ff84136cb watchdog: add wat... |
519 520 521 |
(!(wdd->info->options & WDIOF_PRETIMEOUT) || !IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV))) mode = 0; |
33b711269 watchdog: Read de... |
522 523 524 525 526 527 528 |
return mode; } static struct attribute *wdt_attrs[] = { &dev_attr_state.attr, &dev_attr_identity.attr, &dev_attr_timeout.attr, |
df044e022 watchdog: add pre... |
529 |
&dev_attr_pretimeout.attr, |
33b711269 watchdog: Read de... |
530 531 532 533 |
&dev_attr_timeleft.attr, &dev_attr_bootstatus.attr, &dev_attr_status.attr, &dev_attr_nowayout.attr, |
ff84136cb watchdog: add wat... |
534 |
&dev_attr_pretimeout_governor.attr, |
89873a711 watchdog: pretime... |
535 |
&dev_attr_pretimeout_available_governors.attr, |
33b711269 watchdog: Read de... |
536 537 538 539 540 541 542 543 544 545 546 |
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... |
547 548 |
/* * watchdog_ioctl_op: call the watchdog drivers ioctl op if defined |
bc794ac3b watchdog: watchdo... |
549 |
* @wdd: the watchdog device to do the ioctl on |
7a8798242 watchdog: watchdo... |
550 551 |
* @cmd: watchdog command * @arg: argument pointer |
b4ffb1909 watchdog: Separat... |
552 553 |
* * The caller must hold wd_data->lock. |
7a8798242 watchdog: watchdo... |
554 |
*/ |
bc794ac3b watchdog: watchdo... |
555 |
static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd, |
7a8798242 watchdog: watchdo... |
556 557 |
unsigned long arg) { |
bc794ac3b watchdog: watchdo... |
558 |
if (!wdd->ops->ioctl) |
7a8798242 watchdog: watchdo... |
559 |
return -ENOIOCTLCMD; |
b4ffb1909 watchdog: Separat... |
560 |
return wdd->ops->ioctl(wdd, cmd, arg); |
43316044d watchdog: WatchDo... |
561 562 563 564 565 566 567 568 569 570 |
} /* * 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... |
571 |
* Writing the magic 'V' sequence allows the next close to turn |
7e192b9c4 watchdog: WatchDo... |
572 |
* off the watchdog (if 'nowayout' is not set). |
43316044d watchdog: WatchDo... |
573 574 575 576 577 |
*/ static ssize_t watchdog_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) { |
b4ffb1909 watchdog: Separat... |
578 579 580 |
struct watchdog_core_data *wd_data = file->private_data; struct watchdog_device *wdd; int err; |
43316044d watchdog: WatchDo... |
581 582 583 584 585 |
size_t i; char c; if (len == 0) return 0; |
017cf0805 watchdog: WatchDo... |
586 587 588 589 |
/* * Note: just in case someone wrote the magic character * five months ago... */ |
b4ffb1909 watchdog: Separat... |
590 |
clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status); |
017cf0805 watchdog: WatchDo... |
591 592 |
/* scan to see whether or not we got the magic character */ |
43316044d watchdog: WatchDo... |
593 594 595 |
for (i = 0; i != len; i++) { if (get_user(c, data + i)) return -EFAULT; |
017cf0805 watchdog: WatchDo... |
596 |
if (c == 'V') |
b4ffb1909 watchdog: Separat... |
597 |
set_bit(_WDOG_ALLOW_RELEASE, &wd_data->status); |
43316044d watchdog: WatchDo... |
598 599 600 |
} /* someone wrote to us, so we send the watchdog a keepalive ping */ |
b4ffb1909 watchdog: Separat... |
601 602 603 604 605 606 607 |
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... |
608 609 |
if (err < 0) return err; |
43316044d watchdog: WatchDo... |
610 611 612 613 614 |
return len; } /* |
2fa03560a watchdog: WatchDo... |
615 616 617 618 619 620 621 622 623 624 625 626 |
* 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... |
627 |
struct watchdog_core_data *wd_data = file->private_data; |
2fa03560a watchdog: WatchDo... |
628 |
void __user *argp = (void __user *)arg; |
b4ffb1909 watchdog: Separat... |
629 |
struct watchdog_device *wdd; |
2fa03560a watchdog: WatchDo... |
630 631 |
int __user *p = argp; unsigned int val; |
234445b4e watchdog: WatchDo... |
632 |
int err; |
2fa03560a watchdog: WatchDo... |
633 |
|
b4ffb1909 watchdog: Separat... |
634 635 636 637 638 639 640 |
mutex_lock(&wd_data->lock); wdd = wd_data->wdd; if (!wdd) { err = -ENODEV; goto out_ioctl; } |
7a8798242 watchdog: watchdo... |
641 642 |
err = watchdog_ioctl_op(wdd, cmd, arg); if (err != -ENOIOCTLCMD) |
b4ffb1909 watchdog: Separat... |
643 |
goto out_ioctl; |
78d88fc01 watchdog: WatchDo... |
644 |
|
2fa03560a watchdog: WatchDo... |
645 646 |
switch (cmd) { case WDIOC_GETSUPPORT: |
b4ffb1909 watchdog: Separat... |
647 |
err = copy_to_user(argp, wdd->info, |
2fa03560a watchdog: WatchDo... |
648 |
sizeof(struct watchdog_info)) ? -EFAULT : 0; |
b4ffb1909 watchdog: Separat... |
649 |
break; |
2fa03560a watchdog: WatchDo... |
650 |
case WDIOC_GETSTATUS: |
b4ffb1909 watchdog: Separat... |
651 652 653 |
val = watchdog_get_status(wdd); err = put_user(val, p); break; |
2fa03560a watchdog: WatchDo... |
654 |
case WDIOC_GETBOOTSTATUS: |
b4ffb1909 watchdog: Separat... |
655 656 |
err = put_user(wdd->bootstatus, p); break; |
234445b4e watchdog: WatchDo... |
657 |
case WDIOC_SETOPTIONS: |
b4ffb1909 watchdog: Separat... |
658 659 660 661 |
if (get_user(val, p)) { err = -EFAULT; break; } |
234445b4e watchdog: WatchDo... |
662 663 664 |
if (val & WDIOS_DISABLECARD) { err = watchdog_stop(wdd); if (err < 0) |
b4ffb1909 watchdog: Separat... |
665 |
break; |
234445b4e watchdog: WatchDo... |
666 |
} |
b4ffb1909 watchdog: Separat... |
667 |
if (val & WDIOS_ENABLECARD) |
234445b4e watchdog: WatchDo... |
668 |
err = watchdog_start(wdd); |
b4ffb1909 watchdog: Separat... |
669 |
break; |
c2dc00e49 watchdog: WatchDo... |
670 |
case WDIOC_KEEPALIVE: |
b4ffb1909 watchdog: Separat... |
671 672 673 674 675 676 |
if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) { err = -EOPNOTSUPP; break; } err = watchdog_ping(wdd); break; |
014d694e5 watchdog: WatchDo... |
677 |
case WDIOC_SETTIMEOUT: |
b4ffb1909 watchdog: Separat... |
678 679 680 681 |
if (get_user(val, p)) { err = -EFAULT; break; } |
7a8798242 watchdog: watchdo... |
682 |
err = watchdog_set_timeout(wdd, val); |
014d694e5 watchdog: WatchDo... |
683 |
if (err < 0) |
b4ffb1909 watchdog: Separat... |
684 |
break; |
014d694e5 watchdog: WatchDo... |
685 686 687 |
/* 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... |
688 689 |
err = watchdog_ping(wdd); if (err < 0) |
b4ffb1909 watchdog: Separat... |
690 |
break; |
014d694e5 watchdog: WatchDo... |
691 692 693 |
/* Fall */ case WDIOC_GETTIMEOUT: /* timeout == 0 means that we don't know the timeout */ |
b4ffb1909 watchdog: Separat... |
694 695 696 697 698 699 |
if (wdd->timeout == 0) { err = -EOPNOTSUPP; break; } err = put_user(wdd->timeout, p); break; |
fd7b673c9 watchdog: Add sup... |
700 |
case WDIOC_GETTIMELEFT: |
7a8798242 watchdog: watchdo... |
701 |
err = watchdog_get_timeleft(wdd, &val); |
b4ffb1909 watchdog: Separat... |
702 703 704 705 |
if (err < 0) break; err = put_user(val, p); break; |
df044e022 watchdog: add pre... |
706 707 708 709 710 711 712 713 714 715 |
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... |
716 |
default: |
b4ffb1909 watchdog: Separat... |
717 718 |
err = -ENOTTY; break; |
2fa03560a watchdog: WatchDo... |
719 |
} |
b4ffb1909 watchdog: Separat... |
720 721 722 723 |
out_ioctl: mutex_unlock(&wd_data->lock); return err; |
2fa03560a watchdog: WatchDo... |
724 725 726 |
} /* |
45f5fed30 watchdog: Add mul... |
727 |
* watchdog_open: open the /dev/watchdog* devices. |
43316044d watchdog: WatchDo... |
728 729 730 |
* @inode: inode of device * @file: file handle to device * |
45f5fed30 watchdog: Add mul... |
731 |
* When the /dev/watchdog* device gets opened, we start the watchdog. |
43316044d watchdog: WatchDo... |
732 733 734 735 736 737 |
* 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... |
738 |
struct watchdog_core_data *wd_data; |
45f5fed30 watchdog: Add mul... |
739 |
struct watchdog_device *wdd; |
b4ffb1909 watchdog: Separat... |
740 |
int err; |
45f5fed30 watchdog: Add mul... |
741 742 743 |
/* Get the corresponding watchdog device */ if (imajor(inode) == MISC_MAJOR) |
b4ffb1909 watchdog: Separat... |
744 |
wd_data = old_wd_data; |
45f5fed30 watchdog: Add mul... |
745 |
else |
b4ffb1909 watchdog: Separat... |
746 747 |
wd_data = container_of(inode->i_cdev, struct watchdog_core_data, cdev); |
43316044d watchdog: WatchDo... |
748 749 |
/* the watchdog is single open! */ |
b4ffb1909 watchdog: Separat... |
750 |
if (test_and_set_bit(_WDOG_DEV_OPEN, &wd_data->status)) |
43316044d watchdog: WatchDo... |
751 |
return -EBUSY; |
b4ffb1909 watchdog: Separat... |
752 |
wdd = wd_data->wdd; |
43316044d watchdog: WatchDo... |
753 754 755 756 |
/* * If the /dev/watchdog device is open, we don't want the module * to be unloaded. */ |
ee142889e watchdog: Introdu... |
757 |
if (!watchdog_hw_running(wdd) && !try_module_get(wdd->ops->owner)) { |
b4ffb1909 watchdog: Separat... |
758 759 760 |
err = -EBUSY; goto out_clear; } |
43316044d watchdog: WatchDo... |
761 |
|
234445b4e watchdog: WatchDo... |
762 |
err = watchdog_start(wdd); |
43316044d watchdog: WatchDo... |
763 764 |
if (err < 0) goto out_mod; |
b4ffb1909 watchdog: Separat... |
765 |
file->private_data = wd_data; |
45f5fed30 watchdog: Add mul... |
766 |
|
ee142889e watchdog: Introdu... |
767 768 |
if (!watchdog_hw_running(wdd)) kref_get(&wd_data->kref); |
e907df327 watchdog: Add sup... |
769 |
|
43316044d watchdog: WatchDo... |
770 771 772 773 |
/* dev/watchdog is a virtual (and thus non-seekable) filesystem */ return nonseekable_open(inode, file); out_mod: |
b4ffb1909 watchdog: Separat... |
774 775 776 |
module_put(wd_data->wdd->ops->owner); out_clear: clear_bit(_WDOG_DEV_OPEN, &wd_data->status); |
43316044d watchdog: WatchDo... |
777 778 |
return err; } |
b4ffb1909 watchdog: Separat... |
779 780 781 782 783 784 785 786 |
static void watchdog_core_data_release(struct kref *kref) { struct watchdog_core_data *wd_data; wd_data = container_of(kref, struct watchdog_core_data, kref); kfree(wd_data); } |
43316044d watchdog: WatchDo... |
787 |
/* |
45f5fed30 watchdog: Add mul... |
788 789 790 |
* watchdog_release: release the watchdog device. * @inode: inode of device * @file: file handle to device |
43316044d watchdog: WatchDo... |
791 |
* |
017cf0805 watchdog: WatchDo... |
792 |
* This is the code for when /dev/watchdog gets closed. We will only |
7e192b9c4 watchdog: WatchDo... |
793 794 |
* stop the watchdog when we have received the magic char (and nowayout * was not set), else the watchdog will keep running. |
43316044d watchdog: WatchDo... |
795 796 797 798 |
*/ static int watchdog_release(struct inode *inode, struct file *file) { |
b4ffb1909 watchdog: Separat... |
799 800 |
struct watchdog_core_data *wd_data = file->private_data; struct watchdog_device *wdd; |
017cf0805 watchdog: WatchDo... |
801 |
int err = -EBUSY; |
d1ed3ba4e watchdog: Ensure ... |
802 |
bool running; |
017cf0805 watchdog: WatchDo... |
803 |
|
b4ffb1909 watchdog: Separat... |
804 805 806 807 808 |
mutex_lock(&wd_data->lock); wdd = wd_data->wdd; if (!wdd) goto done; |
017cf0805 watchdog: WatchDo... |
809 810 |
/* * We only stop the watchdog if we received the magic character |
7e192b9c4 watchdog: WatchDo... |
811 812 |
* or if WDIOF_MAGICCLOSE is not set. If nowayout was set then * watchdog_stop will fail. |
017cf0805 watchdog: WatchDo... |
813 |
*/ |
fcf95670f watchdog: core: d... |
814 815 |
if (!test_bit(WDOG_ACTIVE, &wdd->status)) err = 0; |
b4ffb1909 watchdog: Separat... |
816 |
else if (test_and_clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status) || |
fcf95670f watchdog: core: d... |
817 |
!(wdd->info->options & WDIOF_MAGICCLOSE)) |
017cf0805 watchdog: WatchDo... |
818 |
err = watchdog_stop(wdd); |
43316044d watchdog: WatchDo... |
819 |
|
017cf0805 watchdog: WatchDo... |
820 |
/* If the watchdog was not stopped, send a keepalive ping */ |
234445b4e watchdog: WatchDo... |
821 |
if (err < 0) { |
0254e9535 watchdog: Drop po... |
822 823 |
pr_crit("watchdog%d: watchdog did not stop! ", wdd->id); |
43316044d watchdog: WatchDo... |
824 825 |
watchdog_ping(wdd); } |
ee142889e watchdog: Introdu... |
826 |
watchdog_update_worker(wdd); |
664a39236 watchdog: Introdu... |
827 |
|
43316044d watchdog: WatchDo... |
828 |
/* make sure that /dev/watchdog can be re-opened */ |
b4ffb1909 watchdog: Separat... |
829 |
clear_bit(_WDOG_DEV_OPEN, &wd_data->status); |
e907df327 watchdog: Add sup... |
830 |
|
b4ffb1909 watchdog: Separat... |
831 |
done: |
d1ed3ba4e watchdog: Ensure ... |
832 |
running = wdd && watchdog_hw_running(wdd); |
b4ffb1909 watchdog: Separat... |
833 |
mutex_unlock(&wd_data->lock); |
ee142889e watchdog: Introdu... |
834 835 836 837 838 |
/* * 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 ... |
839 840 |
if (!running) { module_put(wd_data->cdev.owner); |
ee142889e watchdog: Introdu... |
841 842 |
kref_put(&wd_data->kref, watchdog_core_data_release); } |
43316044d watchdog: WatchDo... |
843 844 845 846 847 848 |
return 0; } static const struct file_operations watchdog_fops = { .owner = THIS_MODULE, .write = watchdog_write, |
2fa03560a watchdog: WatchDo... |
849 |
.unlocked_ioctl = watchdog_ioctl, |
43316044d watchdog: WatchDo... |
850 851 852 853 854 855 856 857 858 859 860 |
.open = watchdog_open, .release = watchdog_release, }; static struct miscdevice watchdog_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &watchdog_fops, }; /* |
32ecc6392 watchdog: Create ... |
861 |
* watchdog_cdev_register: register watchdog character device |
bc794ac3b watchdog: watchdo... |
862 |
* @wdd: watchdog device |
32ecc6392 watchdog: Create ... |
863 |
* @devno: character device number |
43316044d watchdog: WatchDo... |
864 |
* |
32ecc6392 watchdog: Create ... |
865 |
* Register a watchdog character device including handling the legacy |
45f5fed30 watchdog: Add mul... |
866 867 |
* /dev/watchdog node. /dev/watchdog is actually a miscdevice and * thus we set it up like that. |
43316044d watchdog: WatchDo... |
868 |
*/ |
32ecc6392 watchdog: Create ... |
869 |
static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) |
43316044d watchdog: WatchDo... |
870 |
{ |
b4ffb1909 watchdog: Separat... |
871 |
struct watchdog_core_data *wd_data; |
32ecc6392 watchdog: Create ... |
872 |
int err; |
45f5fed30 watchdog: Add mul... |
873 |
|
b4ffb1909 watchdog: Separat... |
874 875 876 877 878 879 880 881 |
wd_data = kzalloc(sizeof(struct watchdog_core_data), GFP_KERNEL); if (!wd_data) return -ENOMEM; kref_init(&wd_data->kref); mutex_init(&wd_data->lock); wd_data->wdd = wdd; wdd->wd_data = wd_data; |
664a39236 watchdog: Introdu... |
882 883 884 885 |
if (!watchdog_wq) return -ENODEV; INIT_DELAYED_WORK(&wd_data->work, watchdog_ping_work); |
bc794ac3b watchdog: watchdo... |
886 |
if (wdd->id == 0) { |
b4ffb1909 watchdog: Separat... |
887 |
old_wd_data = wd_data; |
bc794ac3b watchdog: watchdo... |
888 |
watchdog_miscdev.parent = wdd->parent; |
45f5fed30 watchdog: Add mul... |
889 890 891 892 |
err = misc_register(&watchdog_miscdev); if (err != 0) { pr_err("%s: cannot register miscdev on minor=%d (err=%d). ", |
bc794ac3b watchdog: watchdo... |
893 |
wdd->info->identity, WATCHDOG_MINOR, err); |
45f5fed30 watchdog: Add mul... |
894 895 896 |
if (err == -EBUSY) pr_err("%s: a legacy watchdog module is probably present. ", |
bc794ac3b watchdog: watchdo... |
897 |
wdd->info->identity); |
b4ffb1909 watchdog: Separat... |
898 899 |
old_wd_data = NULL; kfree(wd_data); |
45f5fed30 watchdog: Add mul... |
900 901 |
return err; } |
43316044d watchdog: WatchDo... |
902 |
} |
45f5fed30 watchdog: Add mul... |
903 |
/* Fill in the data structures */ |
b4ffb1909 watchdog: Separat... |
904 905 |
cdev_init(&wd_data->cdev, &watchdog_fops); wd_data->cdev.owner = wdd->ops->owner; |
45f5fed30 watchdog: Add mul... |
906 907 |
/* Add the device */ |
b4ffb1909 watchdog: Separat... |
908 |
err = cdev_add(&wd_data->cdev, devno, 1); |
45f5fed30 watchdog: Add mul... |
909 910 911 |
if (err) { pr_err("watchdog%d unable to add device %d:%d ", |
bc794ac3b watchdog: watchdo... |
912 913 |
wdd->id, MAJOR(watchdog_devt), wdd->id); if (wdd->id == 0) { |
45f5fed30 watchdog: Add mul... |
914 |
misc_deregister(&watchdog_miscdev); |
b4ffb1909 watchdog: Separat... |
915 916 |
old_wd_data = NULL; kref_put(&wd_data->kref, watchdog_core_data_release); |
45f5fed30 watchdog: Add mul... |
917 |
} |
ee142889e watchdog: Introdu... |
918 |
return err; |
43316044d watchdog: WatchDo... |
919 |
} |
ee142889e watchdog: Introdu... |
920 |
|
15013ad81 watchdog: Add sup... |
921 922 |
/* Record time of most recent heartbeat as 'just before now'. */ wd_data->last_hw_keepalive = jiffies - 1; |
ee142889e watchdog: Introdu... |
923 924 925 926 927 928 929 930 931 932 933 |
/* * If the watchdog is running, prevent its driver from being unloaded, * and schedule an immediate ping. */ if (watchdog_hw_running(wdd)) { __module_get(wdd->ops->owner); kref_get(&wd_data->kref); queue_delayed_work(watchdog_wq, &wd_data->work, 0); } return 0; |
43316044d watchdog: WatchDo... |
934 935 936 |
} /* |
32ecc6392 watchdog: Create ... |
937 |
* watchdog_cdev_unregister: unregister watchdog character device |
43316044d watchdog: WatchDo... |
938 939 |
* @watchdog: watchdog device * |
32ecc6392 watchdog: Create ... |
940 941 |
* Unregister watchdog character device and if needed the legacy * /dev/watchdog device. |
43316044d watchdog: WatchDo... |
942 |
*/ |
32ecc6392 watchdog: Create ... |
943 |
static void watchdog_cdev_unregister(struct watchdog_device *wdd) |
43316044d watchdog: WatchDo... |
944 |
{ |
b4ffb1909 watchdog: Separat... |
945 |
struct watchdog_core_data *wd_data = wdd->wd_data; |
e907df327 watchdog: Add sup... |
946 |
|
b4ffb1909 watchdog: Separat... |
947 |
cdev_del(&wd_data->cdev); |
bc794ac3b watchdog: watchdo... |
948 |
if (wdd->id == 0) { |
45f5fed30 watchdog: Add mul... |
949 |
misc_deregister(&watchdog_miscdev); |
b4ffb1909 watchdog: Separat... |
950 |
old_wd_data = NULL; |
43316044d watchdog: WatchDo... |
951 |
} |
b4ffb1909 watchdog: Separat... |
952 953 954 955 956 |
mutex_lock(&wd_data->lock); wd_data->wdd = NULL; wdd->wd_data = NULL; mutex_unlock(&wd_data->lock); |
664a39236 watchdog: Introdu... |
957 |
cancel_delayed_work_sync(&wd_data->work); |
b4ffb1909 watchdog: Separat... |
958 |
kref_put(&wd_data->kref, watchdog_core_data_release); |
43316044d watchdog: WatchDo... |
959 |
} |
45f5fed30 watchdog: Add mul... |
960 |
|
906d7a5cf watchdog: Use sta... |
961 962 963 |
static struct class watchdog_class = { .name = "watchdog", .owner = THIS_MODULE, |
33b711269 watchdog: Read de... |
964 |
.dev_groups = wdt_groups, |
906d7a5cf watchdog: Use sta... |
965 |
}; |
45f5fed30 watchdog: Add mul... |
966 |
/* |
32ecc6392 watchdog: Create ... |
967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 |
* 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) { struct device *dev; dev_t devno; int ret; devno = MKDEV(MAJOR(watchdog_devt), wdd->id); ret = watchdog_cdev_register(wdd, devno); if (ret) return ret; |
faa584757 watchdog: Add sup... |
986 987 988 |
dev = device_create_with_groups(&watchdog_class, wdd->parent, devno, wdd, wdd->groups, "watchdog%d", wdd->id); |
32ecc6392 watchdog: Create ... |
989 990 991 992 |
if (IS_ERR(dev)) { watchdog_cdev_unregister(wdd); return PTR_ERR(dev); } |
32ecc6392 watchdog: Create ... |
993 |
|
ff84136cb watchdog: add wat... |
994 995 996 997 998 |
ret = watchdog_register_pretimeout(wdd); if (ret) { device_destroy(&watchdog_class, devno); watchdog_cdev_unregister(wdd); } |
32ecc6392 watchdog: Create ... |
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 |
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... |
1012 |
watchdog_unregister_pretimeout(wdd); |
0254e9535 watchdog: Drop po... |
1013 |
device_destroy(&watchdog_class, wdd->wd_data->cdev.dev); |
b4ffb1909 watchdog: Separat... |
1014 |
watchdog_cdev_unregister(wdd); |
32ecc6392 watchdog: Create ... |
1015 1016 1017 |
} /* |
45f5fed30 watchdog: Add mul... |
1018 1019 1020 1021 |
* watchdog_dev_init: init dev part of watchdog core * * Allocate a range of chardev nodes to use for watchdog devices */ |
32ecc6392 watchdog: Create ... |
1022 |
int __init watchdog_dev_init(void) |
45f5fed30 watchdog: Add mul... |
1023 |
{ |
906d7a5cf watchdog: Use sta... |
1024 |
int err; |
664a39236 watchdog: Introdu... |
1025 1026 1027 1028 1029 1030 1031 |
watchdog_wq = alloc_workqueue("watchdogd", WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); if (!watchdog_wq) { pr_err("Failed to create watchdog workqueue "); return -ENOMEM; } |
906d7a5cf watchdog: Use sta... |
1032 1033 1034 1035 |
err = class_register(&watchdog_class); if (err < 0) { pr_err("couldn't register class "); |
138913cb6 watchdog: core: F... |
1036 |
goto err_register; |
906d7a5cf watchdog: Use sta... |
1037 1038 1039 1040 |
} err = alloc_chrdev_region(&watchdog_devt, 0, MAX_DOGS, "watchdog"); if (err < 0) { |
45f5fed30 watchdog: Add mul... |
1041 1042 |
pr_err("watchdog: unable to allocate char dev region "); |
138913cb6 watchdog: core: F... |
1043 |
goto err_alloc; |
906d7a5cf watchdog: Use sta... |
1044 |
} |
32ecc6392 watchdog: Create ... |
1045 |
return 0; |
138913cb6 watchdog: core: F... |
1046 1047 1048 1049 1050 1051 |
err_alloc: class_unregister(&watchdog_class); err_register: destroy_workqueue(watchdog_wq); return err; |
45f5fed30 watchdog: Add mul... |
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 |
} /* * 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... |
1063 |
class_unregister(&watchdog_class); |
664a39236 watchdog: Introdu... |
1064 |
destroy_workqueue(watchdog_wq); |
45f5fed30 watchdog: Add mul... |
1065 |
} |