任何实现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);
}
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);
}