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 哈哈哈哈
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 哈哈哈哈