watchdog 注册字符设备和sys 系统来让user space可以控制watchdog

来源:互联网 发布:淘宝同款插件 编辑:程序博客网 时间:2024/05/29 04:44
任何实现watchdog的driver都会调用watchdog_register_device来向kernel 提供的watchdog framework注册device
int watchdog_register_device(struct watchdog_device *wdd)
{
    int ret;

    mutex_lock(&wtd_deferred_reg_mutex);
    if (wtd_deferred_reg_done)
        ret = __watchdog_register_device(wdd);
    else
        ret = watchdog_deferred_registration_add(wdd);
    mutex_unlock(&wtd_deferred_reg_mutex);
    return ret;
}
这里wtd_deferred_reg_done 为true ,直接调用__watchdog_register_device
static int __watchdog_register_device(struct watchdog_device *wdd)
{
    int ret, id = -1;
//可见wdd也就是要注册的watchdog_device 为null或者wdd->info或者wdd->ops 为null的话,就返回了,显然没有必要再往下走了

    if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL)
        return -EINVAL;
//可见如果没有提供watchdog start函数也就返回了,如果提供了start函数才判断是否提供stop函数和max_hw_heartbeat_ms,这就是短路原则,这种判断某种程度上可以替代if-else结构
    /* Mandatory operations need to be supported */
    if (!wdd->ops->start || (!wdd->ops->stop && !wdd->max_hw_heartbeat_ms))
        return -EINVAL;
//判断min_timeout和 max_timeout 是否合理,不合理的话都清零.
    watchdog_check_min_max_timeout(wdd);

    /*
     * Note: now that all watchdog_device data has been verified, we
     * will not check this anymore in other functions. If data gets
     * corrupted in a later stage then we expect a kernel panic!
     */

    /* Use alias for watchdog id if possible */
    if (wdd->parent) {
        ret = of_alias_get_id(wdd->parent->of_node, "watchdog");
        if (ret >= 0)
            id = ida_simple_get(&watchdog_ida, ret,
                        ret + 1, GFP_KERNEL);
    }
// 如果有多个watchdog的话,一般会从0开会,例如/dev/watchdog0
    if (id < 0)
        id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL);

    if (id < 0)
        return id;
    wdd->id = id;
// 注册watchdog的字符设备.
    ret = watchdog_dev_register(wdd);
    if (ret) {
        ida_simple_remove(&watchdog_ida, id);
        if (!(id == 0 && ret == -EBUSY))
            return ret;

        /* Retry in case a legacy watchdog module exists */
        id = ida_simple_get(&watchdog_ida, 1, MAX_DOGS, GFP_KERNEL);
        if (id < 0)
            return id;
        wdd->id = id;

        ret = watchdog_dev_register(wdd);
        if (ret) {
            ida_simple_remove(&watchdog_ida, id);
            return ret;
        }
    }
//包含WDOG_STOP_ON_REBOOT的话,则注册一个reboot的通知链,告诉感兴趣的driver,系统马上要reboot了
    if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) {
        wdd->reboot_nb.notifier_call = watchdog_reboot_notifier;

        ret = register_reboot_notifier(&wdd->reboot_nb);
        if (ret) {
            pr_err("watchdog%d: Cannot register reboot notifier (%d)\n",
                   wdd->id, ret);
            watchdog_dev_unregister(wdd);
            ida_simple_remove(&watchdog_ida, wdd->id);
            return ret;
        }
    }
// wdd->ops->restart 不为null的话,也就是希望收到restart这个通知链,后面我们在分析收到这个restart 通知链后watchdog 到底做了啥事.
    if (wdd->ops->restart) {
        wdd->restart_nb.notifier_call = watchdog_restart_notifier;

        ret = register_restart_handler(&wdd->restart_nb);
        if (ret)
            pr_warn("watchdog%d: Cannot register restart handler (%d)\n",
                wdd->id, ret);
    }

    return 0;
}
先看watchdog_check_min_max_timeout
//判断min_timeout和 max_timeout 是否合理,不合理的话都清零.
static void watchdog_check_min_max_timeout(struct watchdog_device *wdd)
{
    /*
     * Check that we have valid min and max timeout values, if
     * not reset them both to 0 (=not used or unknown)
     */
    if (!wdd->max_hw_heartbeat_ms && wdd->min_timeout > wdd->max_timeout) {
        pr_info("Invalid min and max timeout values, resetting to 0!\n");
        wdd->min_timeout = 0;
        wdd->max_timeout = 0;
    }
}

int watchdog_dev_register(struct watchdog_device *wdd)
{
    struct device *dev;
    dev_t devno;
    int ret;
//得到字符设备的主设备号
    devno = MKDEV(MAJOR(watchdog_devt), wdd->id);
// 注册字符设备,这就可以看到/dev/watchdog0等字符设备,最动是通过cdev_add 添加字符设备,这个函数的实现就是注册字符设备的标准flow.
    ret = watchdog_cdev_register(wdd, devno);
    if (ret)
        return ret;
//在sys/class/watchdog下创建attibute,这就就可以通过sys/class/watchdog 来控制watchdog
    dev = device_create_with_groups(&watchdog_class, wdd->parent,
                    devno, wdd, wdd->groups,
                    "watchdog%d", wdd->id);
    if (IS_ERR(dev)) {
        watchdog_cdev_unregister(wdd);
        return PTR_ERR(dev);
    }
// 如果没有开kernel config的话,watchdog_register_pretimeout 为空函数
    ret = watchdog_register_pretimeout(wdd);
    if (ret) {
        device_destroy(&watchdog_class, devno);
        watchdog_cdev_unregister(wdd);
    }

    return ret;
}
需要值得注意的是我们在定义watchdog_device的时候,并没有定义attibute group,而是在调用device_create_with_groups的时候直接用的watchdog_class 这个的attibute group
struct device *device_create_with_groups(struct class *class,
                     struct device *parent, dev_t devt,
                     void *drvdata,
                     const struct attribute_group **groups,
                     const char *fmt, ...)
{
    va_list vargs;
    struct device *dev;

    va_start(vargs, fmt);
    dev = device_create_groups_vargs(class, parent, devt, drvdata, groups,
                     fmt, vargs);
    va_end(vargs);
    return dev;
}
继续看device_create_groups_vargs
device_create_groups_vargs(struct class *class, struct device *parent,
               dev_t devt, void *drvdata,
               const struct attribute_group **groups,
               const char *fmt, va_list args)
{
    struct device *dev = NULL;
    int retval = -ENODEV;

    if (class == NULL || IS_ERR(class))
        goto error;

    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev) {
        retval = -ENOMEM;
        goto error;
    }

    device_initialize(dev);
    dev->devt = devt;
    dev->class = class;
    dev->parent = parent;
// 关键一句,这样我们就可以通过/sys/class/watchdog 下的group来控制watchdog设备
    dev->groups = groups;
    dev->release = device_create_release;
    dev_set_drvdata(dev, drvdata);

    retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
    if (retval)
        goto error;

    retval = device_add(dev);
    if (retval)
        goto error;

    return dev;

error:
    put_device(dev);
    return ERR_PTR(retval);
}

那我们看看watchdog_class 都实现了哪些group
static struct class watchdog_class = {
    .name =        "watchdog",
    .owner =    THIS_MODULE,
    .dev_groups =    wdt_groups,
};
static struct attribute *wdt_attrs[] = {
    &dev_attr_state.attr,
    &dev_attr_identity.attr,
    &dev_attr_timeout.attr,
    &dev_attr_pretimeout.attr,
    &dev_attr_timeleft.attr,
    &dev_attr_bootstatus.attr,
    &dev_attr_status.attr,
    &dev_attr_nowayout.attr,
    &dev_attr_pretimeout_governor.attr,
    &dev_attr_pretimeout_available_governors.attr,
    NULL,
};

static const struct attribute_group wdt_group = {
    .attrs = wdt_attrs,
    .is_visible = wdt_is_visible,
};
__ATTRIBUTE_GROUPS(wdt);
原来实现可以通过sys控制的属性包括state/identity/timeout/pretimeout/timeleft/bootstatus/status/nowayout/pretimeout_governor/pretimeout_available_governors
举例看看timeleft和timeout
通过下面的code,原来timeleft_show就是查看离timeout还剩多少时间
static ssize_t timeleft_show(struct device *dev, struct device_attribute *attr,
                char *buf)
{
    struct watchdog_device *wdd = dev_get_drvdata(dev);
    struct watchdog_core_data *wd_data = wdd->wd_data;
    ssize_t status;
    unsigned int val;

    mutex_lock(&wd_data->lock);
    status = watchdog_get_timeleft(wdd, &val);
    mutex_unlock(&wd_data->lock);
    if (!status)
        status = sprintf(buf, "%u\n", val);

    return status;
}
static DEVICE_ATTR_RO(timeleft);
而timeout是查看这个watchdog的timeout时间是多少
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\n", wdd->timeout);
}