watchdog
Rev.2を表示中。最新版はこちら。
watchdogはシステムが停止した時に、リブートさせるものです。タイマによる定期的なチェックのため、その実装はシステム依存です。ここでは、システムに依存しないソフトドッグと称するwatchdogについてです。softdogドライバをinsmodすると、miscデバイスとして/dev/watchdogのキャラクタデバイスファイルが作成され、open/writeすることでwatchdogが作動します。(closeしても作動し続けます。) writeする内容は任意で、Vのみwatchdogを停止させます。停止はwriteしたタイミングでなく、/dev/watchdogをクローズしたタイミングとなります。
insmodの引数として、soft_margin=はタイマ周期(デフォルトは60秒)、nowayout=はV書き込みでのwatchdogを停止させるかどうか設定で、CONFIG_WATCHDOG_NOWAYOUTのコンパイル時、デフォルトは停止できません。
register_reboot_notifier()は、reboot_notifier_listにsoftdog_notifierをリストされ、システムが再起動させる前に.notifier_callがコールされます。
#define TIMER_MARGIN 60 static int soft_margin = TIMER_MARGIN; module_param(soft_margin, int, 0); #ifdef CONFIG_WATCHDOG_NOWAYOUT #define WATCHDOG_NOWAYOUT 1 #define WATCHDOG_NOWAYOUT_INIT_STATUS (1 << WDOG_NO_WAY_OUT) #else #define WATCHDOG_NOWAYOUT 0 #define WATCHDOG_NOWAYOUT_INIT_STATUS 0 #endif static int nowayout = WATCHDOG_NOWAYOUT; module_param(nowayout, int, 0); static struct miscdevice softdog_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &softdog_fops, }; static const struct file_operations softdog_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .write = softdog_write, .unlocked_ioctl = softdog_ioctl, .open = softdog_open, .release = softdog_release, }; static struct notifier_block softdog_notifier = { .notifier_call = softdog_notify_sys, }; static int __init watchdog_init(void) { int ret; /* Check that the soft_margin value is within it's range; if not reset to the default */ if (softdog_set_heartbeat(soft_margin)) { softdog_set_heartbeat(TIMER_MARGIN); printk(KERN_INFO PFX "soft_margin must be 0 < soft_margin < 65536, using %d\n", TIMER_MARGIN); } ret = register_reboot_notifier(&softdog_notifier); if (ret) { printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", ret); return ret; } ret = misc_register(&softdog_miscdev); if (ret) { printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", WATCHDOG_MINOR, ret); unregister_reboot_notifier(&softdog_notifier); return ret; } printk(banner, soft_noboot, soft_margin, soft_panic, nowayout); return 0; } static int softdog_set_heartbeat(int t) { if ((t < 0x0001) || (t > 0xFFFF)) return -EINVAL; soft_margin = t; return 0; } static int softdog_notify_sys(struct notifier_block *this, unsigned long code, void *unused) { if (code == SYS_DOWN || code == SYS_HALT) /* Turn the WDT off */ softdog_stop(); return NOTIFY_DONE; }/dev/watchdogをオープンした瞬間に、watchdogが作動します。mod_timer()は、soft_margin秒周期にwatchdog_fire()をコールすることになり、emergency_restart()でシステムをリブートします。
static int softdog_open(struct inode *inode, struct file *file) { if (test_and_set_bit(0, &driver_open)) return -EBUSY; if (!test_and_clear_bit(0, &orphan_timer)) __module_get(THIS_MODULE); softdog_keepalive(); return nonseekable_open(inode, file); } static struct timer_list watchdog_ticktock = TIMER_INITIALIZER(watchdog_fire, 0, 0); static int softdog_keepalive(void) { mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ)); return 0; } static void watchdog_fire(unsigned long data) { if (test_and_clear_bit(0, &orphan_timer)) module_put(THIS_MODULE); if (soft_noboot) printk(KERN_CRIT PFX "Triggered - Reboot ignored.\n"); else if (soft_panic) { printk(KERN_CRIT PFX "Initiating panic.\n"); panic("Software Watchdog Timer expired."); } else { printk(KERN_CRIT PFX "Initiating system reboot.\n"); emergency_restart(); printk(KERN_CRIT PFX "Reboot didn't ?????\n"); } }/dev/watchdogをwriteする事で、softdog_keepalive()し、改めてwatchdog_ticktockをリストを更新します。この時Vを書き込むと、expect_close = 42とし、/dev/watchdogのclose()時、タイマリストからwatchdog_ticktockコールバックを削除します。
static ssize_t softdog_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) { if (len) { if (!nowayout) { size_t i; expect_close = 0; for (i = 0; i != len; i++) { char c; if (get_user(c, data + i)) return -EFAULT; if (c == 'V') expect_close = 42; } } softdog_keepalive(); } return len; }ioctlでwatchdogの属性の取得および周期時間の設定ができます。また、nowayout=0により、writeでのV書き込みでのwatchdog停止ができなくても、WDIOC_KEEPALIVEコマンドで可能となります。
static long softdog_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; int __user *p = argp; int new_margin; static const struct watchdog_info ident = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .firmware_version = 0, .identity = "Software Watchdog", }; switch (cmd) { case WDIOC_GETSUPPORT: return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: return put_user(0, p); case WDIOC_KEEPALIVE: softdog_keepalive(); return 0; case WDIOC_SETTIMEOUT: if (get_user(new_margin, p)) return -EFAULT; if (softdog_set_heartbeat(new_margin)) return -EINVAL; softdog_keepalive(); case WDIOC_GETTIMEOUT: return put_user(soft_margin, p); default: return -ENOTTY; } }/dev/watchdogクローズ時にコールされ、V書き込みでのexpect_close == 42なら、softdog_stop()でタイマーリストからwatchdog_ticktockを削除します。
static int softdog_release(struct inode *inode, struct file *file) { if (expect_close == 42) { softdog_stop(); module_put(THIS_MODULE); } else { printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); set_bit(0, &orphan_timer); softdog_keepalive(); } clear_bit(0, &driver_open); expect_close = 0; return 0; } static int softdog_stop(void) { del_timer(&watchdog_ticktock); return 0; }