watchdog 提供ioctl和通过delayqueue来喂狗

来源:互联网 发布:淘宝网c2c商业模式 编辑:程序博客网 时间:2024/06/05 10:56
watchdog_dev_register最终会调用watchdog_cdev_register来注册watchdog对应的字符设备。
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;
//初始化喂狗的work
    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 */
//注册字符设备对应的fops,这样就可以通过ioctl来控制watchdog
    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.
     */
通过queue_delayed_work 来延迟运行喂狗的work
    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;
}
通过watchdog_fops,用户空间就可以通过ioctl来控制watchdog,例如在用户空间设置timeout的时间
    int timeout = 45;
    ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
    printf("The timeout was set to %d seconds\n", timeout);
在watchdog_cdev_register 中会初始化一个delaywork来定时的喂狗
    INIT_DELAYED_WORK(&wd_data->work, watchdog_ping_work);
然后通过queue_delayed_work(watchdog_wq, &wd_data->work, 0);来运行
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);
}
看到watchdog的喂狗在kernel叫ping啊,所以这里检测watchdog还在运行的话,就掉用__watchdog_ping
static int __watchdog_ping(struct watchdog_device *wdd)
{
    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);
    int err;

    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;

    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;
}
这里就调用watchdog device注册的ping函数,以sbsa watchdog 为例的话,就是
static int sbsa_gwdt_keepalive(struct watchdog_device *wdd)
{
    struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);

    /*
     * Writing WRR for an explicit watchdog refresh.
     * You can write anyting (like 0).
     */
    writel(0, gwdt->refresh_base + SBSA_GWDT_WRR);

    return 0;
}
简单说就是直接写寄存器清零watchdog的计数器而已。
回到__watchdog_ping 中执行ping操作后,调用watchdog_update_worker来更新下一次delaywork运行的时间
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);
    }
}
可以看到这里是调用mod_delayed_work,mod_delayed_work_on就是delay work提供的标准API了
static inline bool mod_delayed_work(struct workqueue_struct *wq,
                    struct delayed_work *dwork,
                    unsigned long delay)
{
    return mod_delayed_work_on(WORK_CPU_UNBOUND, wq, dwork, delay);
}

所以用kernel 提供的watchdog framwork的好处就是不用自己喂狗了,官方说法就ping 哈哈哈哈


原创粉丝点击