diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
commit | 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch) | |
tree | a94efe259b9009378be6d90eb30d2b019d95c194 /Documentation/watchdog/watchdog-kernel-api.rst | |
parent | Initial commit. (diff) | |
download | linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.tar.xz linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.zip |
Adding upstream version 5.10.209.upstream/5.10.209upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'Documentation/watchdog/watchdog-kernel-api.rst')
-rw-r--r-- | Documentation/watchdog/watchdog-kernel-api.rst | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/Documentation/watchdog/watchdog-kernel-api.rst b/Documentation/watchdog/watchdog-kernel-api.rst new file mode 100644 index 000000000..baf44e986 --- /dev/null +++ b/Documentation/watchdog/watchdog-kernel-api.rst @@ -0,0 +1,350 @@ +=============================================== +The Linux WatchDog Timer Driver Core kernel API +=============================================== + +Last reviewed: 12-Feb-2013 + +Wim Van Sebroeck <wim@iguana.be> + +Introduction +------------ +This document does not describe what a WatchDog Timer (WDT) Driver or Device is. +It also does not describe the API which can be used by user space to communicate +with a WatchDog Timer. If you want to know this then please read the following +file: Documentation/watchdog/watchdog-api.rst . + +So what does this document describe? It describes the API that can be used by +WatchDog Timer Drivers that want to use the WatchDog Timer Driver Core +Framework. This framework provides all interfacing towards user space so that +the same code does not have to be reproduced each time. This also means that +a watchdog timer driver then only needs to provide the different routines +(operations) that control the watchdog timer (WDT). + +The API +------- +Each watchdog timer driver that wants to use the WatchDog Timer Driver Core +must #include <linux/watchdog.h> (you would have to do this anyway when +writing a watchdog device driver). This include file contains following +register/unregister routines:: + + extern int watchdog_register_device(struct watchdog_device *); + extern void watchdog_unregister_device(struct watchdog_device *); + +The watchdog_register_device routine registers a watchdog timer device. +The parameter of this routine is a pointer to a watchdog_device structure. +This routine returns zero on success and a negative errno code for failure. + +The watchdog_unregister_device routine deregisters a registered watchdog timer +device. The parameter of this routine is the pointer to the registered +watchdog_device structure. + +The watchdog subsystem includes an registration deferral mechanism, +which allows you to register an watchdog as early as you wish during +the boot process. + +The watchdog device structure looks like this:: + + struct watchdog_device { + int id; + struct device *parent; + const struct attribute_group **groups; + const struct watchdog_info *info; + const struct watchdog_ops *ops; + const struct watchdog_governor *gov; + unsigned int bootstatus; + unsigned int timeout; + unsigned int pretimeout; + unsigned int min_timeout; + unsigned int max_timeout; + unsigned int min_hw_heartbeat_ms; + unsigned int max_hw_heartbeat_ms; + struct notifier_block reboot_nb; + struct notifier_block restart_nb; + void *driver_data; + struct watchdog_core_data *wd_data; + unsigned long status; + struct list_head deferred; + }; + +It contains following fields: + +* id: set by watchdog_register_device, id 0 is special. It has both a + /dev/watchdog0 cdev (dynamic major, minor 0) as well as the old + /dev/watchdog miscdev. The id is set automatically when calling + watchdog_register_device. +* parent: set this to the parent device (or NULL) before calling + watchdog_register_device. +* groups: List of sysfs attribute groups to create when creating the watchdog + device. +* info: a pointer to a watchdog_info structure. This structure gives some + additional information about the watchdog timer itself. (Like it's unique name) +* ops: a pointer to the list of watchdog operations that the watchdog supports. +* gov: a pointer to the assigned watchdog device pretimeout governor or NULL. +* timeout: the watchdog timer's timeout value (in seconds). + This is the time after which the system will reboot if user space does + not send a heartbeat request if WDOG_ACTIVE is set. +* pretimeout: the watchdog timer's pretimeout value (in seconds). +* min_timeout: the watchdog timer's minimum timeout value (in seconds). + If set, the minimum configurable value for 'timeout'. +* max_timeout: the watchdog timer's maximum timeout value (in seconds), + as seen from userspace. If set, the maximum configurable value for + 'timeout'. Not used if max_hw_heartbeat_ms is non-zero. +* min_hw_heartbeat_ms: Hardware limit for minimum time between heartbeats, + in milli-seconds. This value is normally 0; it should only be provided + if the hardware can not tolerate lower intervals between heartbeats. +* max_hw_heartbeat_ms: Maximum hardware heartbeat, in milli-seconds. + If set, the infrastructure will send heartbeats to the watchdog driver + if 'timeout' is larger than max_hw_heartbeat_ms, unless WDOG_ACTIVE + is set and userspace failed to send a heartbeat for at least 'timeout' + seconds. max_hw_heartbeat_ms must be set if a driver does not implement + the stop function. +* reboot_nb: notifier block that is registered for reboot notifications, for + internal use only. If the driver calls watchdog_stop_on_reboot, watchdog core + will stop the watchdog on such notifications. +* restart_nb: notifier block that is registered for machine restart, for + internal use only. If a watchdog is capable of restarting the machine, it + should define ops->restart. Priority can be changed through + watchdog_set_restart_priority. +* bootstatus: status of the device after booting (reported with watchdog + WDIOF_* status bits). +* driver_data: a pointer to the drivers private data of a watchdog device. + This data should only be accessed via the watchdog_set_drvdata and + watchdog_get_drvdata routines. +* wd_data: a pointer to watchdog core internal data. +* status: this field contains a number of status bits that give extra + information about the status of the device (Like: is the watchdog timer + running/active, or is the nowayout bit set). +* deferred: entry in wtd_deferred_reg_list which is used to + register early initialized watchdogs. + +The list of watchdog operations is defined as:: + + struct watchdog_ops { + struct module *owner; + /* mandatory operations */ + int (*start)(struct watchdog_device *); + /* optional operations */ + int (*stop)(struct watchdog_device *); + int (*ping)(struct watchdog_device *); + unsigned int (*status)(struct watchdog_device *); + int (*set_timeout)(struct watchdog_device *, unsigned int); + int (*set_pretimeout)(struct watchdog_device *, unsigned int); + unsigned int (*get_timeleft)(struct watchdog_device *); + int (*restart)(struct watchdog_device *); + long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); + }; + +It is important that you first define the module owner of the watchdog timer +driver's operations. This module owner will be used to lock the module when +the watchdog is active. (This to avoid a system crash when you unload the +module and /dev/watchdog is still open). + +Some operations are mandatory and some are optional. The mandatory operations +are: + +* start: this is a pointer to the routine that starts the watchdog timer + device. + The routine needs a pointer to the watchdog timer device structure as a + parameter. It returns zero on success or a negative errno code for failure. + +Not all watchdog timer hardware supports the same functionality. That's why +all other routines/operations are optional. They only need to be provided if +they are supported. These optional routines/operations are: + +* stop: with this routine the watchdog timer device is being stopped. + + The routine needs a pointer to the watchdog timer device structure as a + parameter. It returns zero on success or a negative errno code for failure. + Some watchdog timer hardware can only be started and not be stopped. A + driver supporting such hardware does not have to implement the stop routine. + + If a driver has no stop function, the watchdog core will set WDOG_HW_RUNNING + and start calling the driver's keepalive pings function after the watchdog + device is closed. + + If a watchdog driver does not implement the stop function, it must set + max_hw_heartbeat_ms. +* ping: this is the routine that sends a keepalive ping to the watchdog timer + hardware. + + The routine needs a pointer to the watchdog timer device structure as a + parameter. It returns zero on success or a negative errno code for failure. + + Most hardware that does not support this as a separate function uses the + start function to restart the watchdog timer hardware. And that's also what + the watchdog timer driver core does: to send a keepalive ping to the watchdog + timer hardware it will either use the ping operation (when available) or the + start operation (when the ping operation is not available). + + (Note: the WDIOC_KEEPALIVE ioctl call will only be active when the + WDIOF_KEEPALIVEPING bit has been set in the option field on the watchdog's + info structure). +* status: this routine checks the status of the watchdog timer device. The + status of the device is reported with watchdog WDIOF_* status flags/bits. + + WDIOF_MAGICCLOSE and WDIOF_KEEPALIVEPING are reported by the watchdog core; + it is not necessary to report those bits from the driver. Also, if no status + function is provided by the driver, the watchdog core reports the status bits + provided in the bootstatus variable of struct watchdog_device. + +* set_timeout: this routine checks and changes the timeout of the watchdog + timer device. It returns 0 on success, -EINVAL for "parameter out of range" + and -EIO for "could not write value to the watchdog". On success this + routine should set the timeout value of the watchdog_device to the + achieved timeout value (which may be different from the requested one + because the watchdog does not necessarily have a 1 second resolution). + + Drivers implementing max_hw_heartbeat_ms set the hardware watchdog heartbeat + to the minimum of timeout and max_hw_heartbeat_ms. Those drivers set the + timeout value of the watchdog_device either to the requested timeout value + (if it is larger than max_hw_heartbeat_ms), or to the achieved timeout value. + (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the + watchdog's info structure). + + If the watchdog driver does not have to perform any action but setting the + watchdog_device.timeout, this callback can be omitted. + + If set_timeout is not provided but, WDIOF_SETTIMEOUT is set, the watchdog + infrastructure updates the timeout value of the watchdog_device internally + to the requested value. + + If the pretimeout feature is used (WDIOF_PRETIMEOUT), then set_timeout must + also take care of checking if pretimeout is still valid and set up the timer + accordingly. This can't be done in the core without races, so it is the + duty of the driver. +* set_pretimeout: this routine checks and changes the pretimeout value of + the watchdog. It is optional because not all watchdogs support pretimeout + notification. The timeout value is not an absolute time, but the number of + seconds before the actual timeout would happen. It returns 0 on success, + -EINVAL for "parameter out of range" and -EIO for "could not write value to + the watchdog". A value of 0 disables pretimeout notification. + + (Note: the WDIOF_PRETIMEOUT needs to be set in the options field of the + watchdog's info structure). + + If the watchdog driver does not have to perform any action but setting the + watchdog_device.pretimeout, this callback can be omitted. That means if + set_pretimeout is not provided but WDIOF_PRETIMEOUT is set, the watchdog + infrastructure updates the pretimeout value of the watchdog_device internally + to the requested value. + +* get_timeleft: this routines returns the time that's left before a reset. +* restart: this routine restarts the machine. It returns 0 on success or a + negative errno code for failure. +* ioctl: if this routine is present then it will be called first before we do + our own internal ioctl call handling. This routine should return -ENOIOCTLCMD + if a command is not supported. The parameters that are passed to the ioctl + call are: watchdog_device, cmd and arg. + +The status bits should (preferably) be set with the set_bit and clear_bit alike +bit-operations. The status bits that are defined are: + +* WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device + is active or not from user perspective. User space is expected to send + heartbeat requests to the driver while this flag is set. +* WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. + If this bit is set then the watchdog timer will not be able to stop. +* WDOG_HW_RUNNING: Set by the watchdog driver if the hardware watchdog is + running. The bit must be set if the watchdog timer hardware can not be + stopped. The bit may also be set if the watchdog timer is running after + booting, before the watchdog device is opened. If set, the watchdog + infrastructure will send keepalives to the watchdog hardware while + WDOG_ACTIVE is not set. + Note: when you register the watchdog timer device with this bit set, + then opening /dev/watchdog will skip the start operation but send a keepalive + request instead. + + To set the WDOG_NO_WAY_OUT status bit (before registering your watchdog + timer device) you can either: + + * set it statically in your watchdog_device struct with + + .status = WATCHDOG_NOWAYOUT_INIT_STATUS, + + (this will set the value the same as CONFIG_WATCHDOG_NOWAYOUT) or + * use the following helper function:: + + static inline void watchdog_set_nowayout(struct watchdog_device *wdd, + int nowayout) + +Note: + The WatchDog Timer Driver Core supports the magic close feature and + the nowayout feature. To use the magic close feature you must set the + WDIOF_MAGICCLOSE bit in the options field of the watchdog's info structure. + +The nowayout feature will overrule the magic close feature. + +To get or set driver specific data the following two helper functions should be +used:: + + static inline void watchdog_set_drvdata(struct watchdog_device *wdd, + void *data) + static inline void *watchdog_get_drvdata(struct watchdog_device *wdd) + +The watchdog_set_drvdata function allows you to add driver specific data. The +arguments of this function are the watchdog device where you want to add the +driver specific data to and a pointer to the data itself. + +The watchdog_get_drvdata function allows you to retrieve driver specific data. +The argument of this function is the watchdog device where you want to retrieve +data from. The function returns the pointer to the driver specific data. + +To initialize the timeout field, the following function can be used:: + + extern int watchdog_init_timeout(struct watchdog_device *wdd, + unsigned int timeout_parm, + struct device *dev); + +The watchdog_init_timeout function allows you to initialize the timeout field +using the module timeout parameter or by retrieving the timeout-sec property from +the device tree (if the module timeout parameter is invalid). Best practice is +to set the default timeout value as timeout value in the watchdog_device and +then use this function to set the user "preferred" timeout value. +This routine returns zero on success and a negative errno code for failure. + +To disable the watchdog on reboot, the user must call the following helper:: + + static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd); + +To disable the watchdog when unregistering the watchdog, the user must call +the following helper. Note that this will only stop the watchdog if the +nowayout flag is not set. + +:: + + static inline void watchdog_stop_on_unregister(struct watchdog_device *wdd); + +To change the priority of the restart handler the following helper should be +used:: + + void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority); + +User should follow the following guidelines for setting the priority: + +* 0: should be called in last resort, has limited restart capabilities +* 128: default restart handler, use if no other handler is expected to be + available, and/or if restart is sufficient to restart the entire system +* 255: highest priority, will preempt all other restart handlers + +To raise a pretimeout notification, the following function should be used:: + + void watchdog_notify_pretimeout(struct watchdog_device *wdd) + +The function can be called in the interrupt context. If watchdog pretimeout +governor framework (kbuild CONFIG_WATCHDOG_PRETIMEOUT_GOV symbol) is enabled, +an action is taken by a preconfigured pretimeout governor preassigned to +the watchdog device. If watchdog pretimeout governor framework is not +enabled, watchdog_notify_pretimeout() prints a notification message to +the kernel log buffer. + +To set the last known HW keepalive time for a watchdog, the following function +should be used:: + + int watchdog_set_last_hw_keepalive(struct watchdog_device *wdd, + unsigned int last_ping_ms) + +This function must be called immediately after watchdog registration. It +sets the last known hardware heartbeat to have happened last_ping_ms before +current time. Calling this is only needed if the watchdog is already running +when probe is called, and the watchdog can only be pinged after the +min_hw_heartbeat_ms time has passed from the last ping. |