// SPDX-License-Identifier: GPL-2.0+ /* * watchdog_dev.c * * (c) Copyright 2008-2011 Alan Cox <[email protected]>, * All Rights Reserved. * * (c) Copyright 2008-2011 Wim Van Sebroeck <[email protected]>. * * (c) Copyright 2021 Hewlett Packard Enterprise Development LP. * * 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 <[email protected]>, * Rob Radez <[email protected]>, * Rusty Lynch <[email protected]> * Satyam Sharma <[email protected]> * Randy Dunlap <[email protected]> * * 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) … #include <linux/cdev.h> /* For character device */ #include <linux/errno.h> /* For the -ENODEV/... values */ #include <linux/fs.h> /* For file operations */ #include <linux/init.h> /* For __init/__exit/... */ #include <linux/hrtimer.h> /* For hrtimers */ #include <linux/kernel.h> /* For printk/panic/... */ #include <linux/kstrtox.h> /* For kstrto* */ #include <linux/kthread.h> /* For kthread_work */ #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 */ #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ #include "watchdog_core.h" #include "watchdog_pretimeout.h" #include <trace/events/watchdog.h> /* the dev_t structure to store the dynamically allocated watchdog devices */ static dev_t watchdog_devt; /* Reference to watchdog device behind /dev/watchdog */ static struct watchdog_core_data *old_wd_data; static struct kthread_worker *watchdog_kworker; static bool handle_boot_enabled = … IS_ENABLED(…); static unsigned open_timeout = …; static bool watchdog_past_open_deadline(struct watchdog_core_data *data) { … } static void watchdog_set_open_deadline(struct watchdog_core_data *data) { … } static inline bool watchdog_need_worker(struct watchdog_device *wdd) { … } static ktime_t watchdog_next_keepalive(struct watchdog_device *wdd) { … } static inline void watchdog_update_worker(struct watchdog_device *wdd) { … } static int __watchdog_ping(struct watchdog_device *wdd) { … } /* * watchdog_ping - ping the watchdog * @wdd: The watchdog device to ping * * If the watchdog has no own ping operation then it needs to be * restarted via the start operation. This wrapper function does * exactly that. * We only ping when the watchdog device is running. * The caller must hold wd_data->lock. * * Return: 0 on success, error otherwise. */ static int watchdog_ping(struct watchdog_device *wdd) { … } static bool watchdog_worker_should_ping(struct watchdog_core_data *wd_data) { … } static void watchdog_ping_work(struct kthread_work *work) { … } static enum hrtimer_restart watchdog_timer_expired(struct hrtimer *timer) { … } /* * watchdog_start - wrapper to start the watchdog * @wdd: The watchdog device to start * * Start the watchdog if it is not active and mark it active. * The caller must hold wd_data->lock. * * Return: 0 on success or a negative errno code for failure. */ static int watchdog_start(struct watchdog_device *wdd) { … } /* * watchdog_stop - wrapper to stop the watchdog * @wdd: The watchdog device to stop * * Stop the watchdog if it is still active and unmark it active. * If the 'nowayout' feature was set, the watchdog cannot be stopped. * The caller must hold wd_data->lock. * * Return: 0 on success or a negative errno code for failure. */ static int watchdog_stop(struct watchdog_device *wdd) { … } /* * watchdog_get_status - wrapper to get the watchdog status * @wdd: The watchdog device to get the status from * * Get the watchdog's status flags. * The caller must hold wd_data->lock. * * Return: watchdog's status flags. */ static unsigned int watchdog_get_status(struct watchdog_device *wdd) { … } /* * watchdog_set_timeout - set the watchdog timer timeout * @wdd: The watchdog device to set the timeout for * @timeout: Timeout to set in seconds * * The caller must hold wd_data->lock. * * Return: 0 if successful, error otherwise. */ static int watchdog_set_timeout(struct watchdog_device *wdd, unsigned int timeout) { … } /* * watchdog_set_pretimeout - set the watchdog timer pretimeout * @wdd: The watchdog device to set the timeout for * @timeout: pretimeout to set in seconds * * Return: 0 if successful, error otherwise. */ static int watchdog_set_pretimeout(struct watchdog_device *wdd, unsigned int timeout) { … } /* * watchdog_get_timeleft - wrapper to get the time left before a reboot * @wdd: The watchdog device to get the remaining time from * @timeleft: The time that's left * * Get the time before a watchdog will reboot (if not pinged). * The caller must hold wd_data->lock. * * Return: 0 if successful, error otherwise. */ static int watchdog_get_timeleft(struct watchdog_device *wdd, unsigned int *timeleft) { … } #ifdef CONFIG_WATCHDOG_SYSFS static ssize_t nowayout_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t nowayout_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { … } static DEVICE_ATTR_RW(nowayout); static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(status); static ssize_t bootstatus_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(bootstatus); static ssize_t timeleft_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(timeleft); static ssize_t timeout_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(timeout); static ssize_t min_timeout_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(min_timeout); static ssize_t max_timeout_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(max_timeout); static ssize_t pretimeout_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(pretimeout); static ssize_t options_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(options); static ssize_t fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(fw_version); static ssize_t identity_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(identity); static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(state); static ssize_t pretimeout_available_governors_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(pretimeout_available_governors); static ssize_t pretimeout_governor_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static ssize_t pretimeout_governor_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static DEVICE_ATTR_RW(pretimeout_governor); static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, int n) { … } static struct attribute *wdt_attrs[] = …; static const struct attribute_group wdt_group = …; __ATTRIBUTE_GROUPS(…); #else #define wdt_groups … #endif /* * watchdog_ioctl_op - call the watchdog drivers ioctl op if defined * @wdd: The watchdog device to do the ioctl on * @cmd: Watchdog command * @arg: Argument pointer * * The caller must hold wd_data->lock. * * Return: 0 if successful, error otherwise. */ static int watchdog_ioctl_op(struct watchdog_device *wdd, unsigned int cmd, unsigned long arg) { … } /* * 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. * Writing the magic 'V' sequence allows the next close to turn * off the watchdog (if 'nowayout' is not set). * * Return: @len if successful, error otherwise. */ static ssize_t watchdog_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) { … } /* * 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. * * Return: 0 if successful, error otherwise. */ static long watchdog_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { … } /* * watchdog_open - open the /dev/watchdog* devices * @inode: Inode of device * @file: File handle to device * * When the /dev/watchdog* device gets opened, we start the watchdog. * Watch out: the /dev/watchdog device is single open, so we make sure * it can only be opened once. * * Return: 0 if successful, error otherwise. */ static int watchdog_open(struct inode *inode, struct file *file) { … } static void watchdog_core_data_release(struct device *dev) { … } /* * watchdog_release - release the watchdog device * @inode: Inode of device * @file: File handle to device * * This is the code for when /dev/watchdog gets closed. We will only * stop the watchdog when we have received the magic char (and nowayout * was not set), else the watchdog will keep running. * * Always returns 0. */ static int watchdog_release(struct inode *inode, struct file *file) { … } static const struct file_operations watchdog_fops = …; static struct miscdevice watchdog_miscdev = …; static const struct class watchdog_class = …; /* * watchdog_cdev_register - register watchdog character device * @wdd: Watchdog device * * Register a watchdog character device including handling the legacy * /dev/watchdog node. /dev/watchdog is actually a miscdevice and * thus we set it up like that. * * Return: 0 if successful, error otherwise. */ static int watchdog_cdev_register(struct watchdog_device *wdd) { … } /* * watchdog_cdev_unregister - unregister watchdog character device * @wdd: Watchdog device * * Unregister watchdog character device and if needed the legacy * /dev/watchdog device. */ static void watchdog_cdev_unregister(struct watchdog_device *wdd) { … } /** * 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. * * Return: 0 if successful, error otherwise. */ int watchdog_dev_register(struct watchdog_device *wdd) { … } /** * watchdog_dev_unregister - unregister a watchdog device * @wdd: watchdog device * * Unregister watchdog device and if needed the legacy * /dev/watchdog device. */ void watchdog_dev_unregister(struct watchdog_device *wdd) { … } /** * watchdog_set_last_hw_keepalive - set last HW keepalive time for watchdog * @wdd: Watchdog device * @last_ping_ms: Time since last HW heartbeat * * Adjusts the last known HW keepalive time for a watchdog timer. * This is needed if the watchdog is already running when the probe * function is called, and it can't be pinged immediately. This * function must be called immediately after watchdog registration, * and min_hw_heartbeat_ms must be set for this to be useful. * * Return: 0 if successful, error otherwise. */ int watchdog_set_last_hw_keepalive(struct watchdog_device *wdd, unsigned int last_ping_ms) { … } EXPORT_SYMBOL_GPL(…); /** * watchdog_dev_init - init dev part of watchdog core * * Allocate a range of chardev nodes to use for watchdog devices. * * Return: 0 if successful, error otherwise. */ int __init watchdog_dev_init(void) { … } /** * 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) { … } int watchdog_dev_suspend(struct watchdog_device *wdd) { … } int watchdog_dev_resume(struct watchdog_device *wdd) { … } module_param(handle_boot_enabled, bool, 0444); MODULE_PARM_DESC(…) …; module_param(open_timeout, uint, 0644); MODULE_PARM_DESC(…) …;