static int watchdog_open(struct inode *inode, struct file *file) { struct watchdog_core_data *wd_data; struct watchdog_device *wdd; int err; /* Get the corresponding watchdog device */ if (imajor(inode) == MISC_MAJOR) wd_data = old_wd_data; else wd_data = container_of(inode->i_cdev, struct watchdog_core_data, cdev); /* the watchdog is single open! */ if (test_and_set_bit(_WDOG_DEV_OPEN, &wd_data->status)) return -EBUSY; wdd = wd_data->wdd; /* * If the /dev/watchdog device is open, we don't want the module * to be unloaded. */ if (!watchdog_hw_running(wdd) && !try_module_get(wdd->ops->owner)) { err = -EBUSY; goto out_clear; } err = watchdog_start(wdd); if (err < 0) goto out_mod; file->private_data = wd_data; if (!watchdog_hw_running(wdd)) kref_get(&wd_data->kref); /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ return nonseekable_open(inode, file); out_mod: module_put(wd_data->wdd->ops->owner); out_clear: clear_bit(_WDOG_DEV_OPEN, &wd_data->status); return err; }
static int watchdog_ping(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; if (!watchdog_active(wdd) && !watchdog_hw_running(wdd)) return 0; set_bit(_WDOG_KEEPALIVE, &wd_data->status); wd_data->last_keepalive = jiffies; return __watchdog_ping(wdd); }
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; if (wdd && (watchdog_active(wdd) || watchdog_hw_running(wdd))) __watchdog_ping(wdd); mutex_unlock(&wd_data->lock); }
static int watchdog_release(struct inode *inode, struct file *file) { struct watchdog_core_data *wd_data = file->private_data; struct watchdog_device *wdd; int err = -EBUSY; bool running; mutex_lock(&wd_data->lock); wdd = wd_data->wdd; if (!wdd) goto done; /* * We only stop the watchdog if we received the magic character * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then * watchdog_stop will fail. */ if (!test_bit(WDOG_ACTIVE, &wdd->status)) err = 0; else if (test_and_clear_bit(_WDOG_ALLOW_RELEASE, &wd_data->status) || !(wdd->info->options & WDIOF_MAGICCLOSE)) err = watchdog_stop(wdd); /* If the watchdog was not stopped, send a keepalive ping */ if (err < 0) { pr_crit("watchdog%d: watchdog did not stop!\n", wdd->id); watchdog_ping(wdd); } watchdog_update_worker(wdd); /* make sure that /dev/watchdog can be re-opened */ clear_bit(_WDOG_DEV_OPEN, &wd_data->status); done: running = wdd && watchdog_hw_running(wdd); mutex_unlock(&wd_data->lock); /* * 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. */ if (!running) { module_put(wd_data->cdev.owner); kref_put(&wd_data->kref, watchdog_core_data_release); } return 0; }
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. * * Alternatively, if userspace has not opened the watchdog * device, we take care of feeding the watchdog if it is * running. */ return (hm && watchdog_active(wdd) && t > hm) || (t && !watchdog_active(wdd) && watchdog_hw_running(wdd)); }
static int watchdog_start(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; unsigned long started_at; int err; if (watchdog_active(wdd)) return 0; set_bit(_WDOG_KEEPALIVE, &wd_data->status); started_at = jiffies; if (watchdog_hw_running(wdd) && wdd->ops->ping) err = wdd->ops->ping(wdd); else err = wdd->ops->start(wdd); if (err == 0) { set_bit(WDOG_ACTIVE, &wdd->status); wd_data->last_keepalive = started_at; watchdog_update_worker(wdd); } return err; }
static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) { struct watchdog_core_data *wd_data; int err; 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; if (!watchdog_wq) return -ENODEV; INIT_DELAYED_WORK(&wd_data->work, watchdog_ping_work); if (wdd->id == 0) { old_wd_data = wd_data; watchdog_miscdev.parent = wdd->parent; err = misc_register(&watchdog_miscdev); if (err != 0) { pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n", wdd->info->identity, WATCHDOG_MINOR, err); if (err == -EBUSY) pr_err("%s: a legacy watchdog module is probably present.\n", wdd->info->identity); old_wd_data = NULL; kfree(wd_data); return err; } } /* Fill in the data structures */ cdev_init(&wd_data->cdev, &watchdog_fops); wd_data->cdev.owner = wdd->ops->owner; /* Add the device */ err = cdev_add(&wd_data->cdev, devno, 1); if (err) { pr_err("watchdog%d unable to add device %d:%d\n", wdd->id, MAJOR(watchdog_devt), wdd->id); if (wdd->id == 0) { misc_deregister(&watchdog_miscdev); old_wd_data = NULL; kref_put(&wd_data->kref, watchdog_core_data_release); } return err; } /* Record time of most recent heartbeat as 'just before now'. */ wd_data->last_hw_keepalive = jiffies - 1; /* * 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; }
static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) { struct watchdog_core_data *wd_data; int err; 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; if (IS_ERR_OR_NULL(watchdog_kworker)) return -ENODEV; kthread_init_work(&wd_data->work, watchdog_ping_work); hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); wd_data->timer.function = watchdog_timer_expired; if (wdd->id == 0) { old_wd_data = wd_data; watchdog_miscdev.parent = wdd->parent; err = misc_register(&watchdog_miscdev); if (err != 0) { pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n", wdd->info->identity, WATCHDOG_MINOR, err); if (err == -EBUSY) pr_err("%s: a legacy watchdog module is probably present.\n", wdd->info->identity); old_wd_data = NULL; kfree(wd_data); return err; } } /* Fill in the data structures */ cdev_init(&wd_data->cdev, &watchdog_fops); wd_data->cdev.owner = wdd->ops->owner; /* Add the device */ err = cdev_add(&wd_data->cdev, devno, 1); if (err) { pr_err("watchdog%d unable to add device %d:%d\n", wdd->id, MAJOR(watchdog_devt), wdd->id); if (wdd->id == 0) { misc_deregister(&watchdog_miscdev); old_wd_data = NULL; kref_put(&wd_data->kref, watchdog_core_data_release); } return err; } /* Record time of most recent heartbeat as 'just before now'. */ wd_data->last_hw_keepalive = ktime_sub(ktime_get(), 1); /* * 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); if (handle_boot_enabled) hrtimer_start(&wd_data->timer, 0, HRTIMER_MODE_REL); else pr_info("watchdog%d running and kernel based pre-userspace handler disabled\n", wdd->id); } return 0; }
static bool watchdog_worker_should_ping(struct watchdog_core_data *wd_data) { struct watchdog_device *wdd = wd_data->wdd; return wdd && (watchdog_active(wdd) || watchdog_hw_running(wdd)); }