📄 ipmi_watchdog.c
字号:
.read = ipmi_read, .poll = ipmi_poll, .write = ipmi_write, .ioctl = ipmi_ioctl, .open = ipmi_open, .release = ipmi_close, .fasync = ipmi_fasync,};static struct miscdevice ipmi_wdog_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &ipmi_wdog_fops};static void ipmi_wdog_msg_handler(struct ipmi_recv_msg *msg, void *handler_data){ if (msg->msg.data[0] != 0) { printk(KERN_ERR PFX "response: Error %x on cmd %x\n", msg->msg.data[0], msg->msg.cmd); } ipmi_free_recv_msg(msg);}static void ipmi_wdog_pretimeout_handler(void *handler_data){ if (preaction_val != WDOG_PRETIMEOUT_NONE) { if (preop_val == WDOG_PREOP_PANIC) { if (atomic_inc_and_test(&preop_panic_excl)) panic("Watchdog pre-timeout"); } else if (preop_val == WDOG_PREOP_GIVE_DATA) { spin_lock(&ipmi_read_lock); data_to_read = 1; wake_up_interruptible(&read_q); kill_fasync(&fasync_q, SIGIO, POLL_IN); spin_unlock(&ipmi_read_lock); } } /* On some machines, the heartbeat will give an error and not work unless we re-enable the timer. So do so. */ pretimeout_since_last_heartbeat = 1;}static struct ipmi_user_hndl ipmi_hndlrs ={ .ipmi_recv_hndl = ipmi_wdog_msg_handler, .ipmi_watchdog_pretimeout = ipmi_wdog_pretimeout_handler};static void ipmi_register_watchdog(int ipmi_intf){ int rv = -EBUSY; down_write(®ister_sem); if (watchdog_user) goto out; rv = ipmi_create_user(ipmi_intf, &ipmi_hndlrs, NULL, &watchdog_user); if (rv < 0) { printk(KERN_CRIT PFX "Unable to register with ipmi\n"); goto out; } ipmi_get_version(watchdog_user, &ipmi_version_major, &ipmi_version_minor); rv = misc_register(&ipmi_wdog_miscdev); if (rv < 0) { ipmi_destroy_user(watchdog_user); watchdog_user = NULL; printk(KERN_CRIT PFX "Unable to register misc device\n"); } out: up_write(®ister_sem); if ((start_now) && (rv == 0)) { /* Run from startup, so start the timer now. */ start_now = 0; /* Disable this function after first startup. */ ipmi_watchdog_state = action_val; ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); printk(KERN_INFO PFX "Starting now!\n"); }}#ifdef HAVE_NMI_HANDLERstatic intipmi_nmi(void *dev_id, struct pt_regs *regs, int cpu, int handled){ /* If we are not expecting a timeout, ignore it. */ if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) return NOTIFY_DONE; /* If no one else handled the NMI, we assume it was the IPMI watchdog. */ if ((!handled) && (preop_val == WDOG_PREOP_PANIC)) { /* On some machines, the heartbeat will give an error and not work unless we re-enable the timer. So do so. */ pretimeout_since_last_heartbeat = 1; if (atomic_inc_and_test(&preop_panic_excl)) panic(PFX "pre-timeout"); } return NOTIFY_DONE;}static struct nmi_handler ipmi_nmi_handler ={ .link = LIST_HEAD_INIT(ipmi_nmi_handler.link), .dev_name = "ipmi_watchdog", .dev_id = NULL, .handler = ipmi_nmi, .priority = 0, /* Call us last. */};int nmi_handler_registered;#endifstatic int wdog_reboot_handler(struct notifier_block *this, unsigned long code, void *unused){ static int reboot_event_handled = 0; if ((watchdog_user) && (!reboot_event_handled)) { /* Make sure we only do this once. */ reboot_event_handled = 1; if (code == SYS_DOWN || code == SYS_HALT) { /* Disable the WDT if we are shutting down. */ ipmi_watchdog_state = WDOG_TIMEOUT_NONE; panic_halt_ipmi_set_timeout(); } else { /* Set a long timer to let the reboot happens, but reboot if it hangs. */ timeout = 120; pretimeout = 0; ipmi_watchdog_state = WDOG_TIMEOUT_RESET; panic_halt_ipmi_set_timeout(); } } return NOTIFY_OK;}static struct notifier_block wdog_reboot_notifier = { .notifier_call = wdog_reboot_handler, .next = NULL, .priority = 0};static int wdog_panic_handler(struct notifier_block *this, unsigned long event, void *unused){ static int panic_event_handled = 0; /* On a panic, if we have a panic timeout, make sure that the thing reboots, even if it hangs during that panic. */ if (watchdog_user && !panic_event_handled) { /* Make sure the panic doesn't hang, and make sure we do this only once. */ panic_event_handled = 1; timeout = 255; pretimeout = 0; ipmi_watchdog_state = WDOG_TIMEOUT_RESET; panic_halt_ipmi_set_timeout(); } return NOTIFY_OK;}static struct notifier_block wdog_panic_notifier = { .notifier_call = wdog_panic_handler, .next = NULL, .priority = 150 /* priority: INT_MAX >= x >= 0 */};static void ipmi_new_smi(int if_num){ ipmi_register_watchdog(if_num);}static void ipmi_smi_gone(int if_num){ /* This can never be called, because once the watchdog is registered, the interface can't go away until the watchdog is unregistered. */}static struct ipmi_smi_watcher smi_watcher ={ .owner = THIS_MODULE, .new_smi = ipmi_new_smi, .smi_gone = ipmi_smi_gone};static int action_op(const char *inval, char *outval){ if (outval) strcpy(outval, action); if (!inval) return 0; if (strcmp(inval, "reset") == 0) action_val = WDOG_TIMEOUT_RESET; else if (strcmp(inval, "none") == 0) action_val = WDOG_TIMEOUT_NONE; else if (strcmp(inval, "power_cycle") == 0) action_val = WDOG_TIMEOUT_POWER_CYCLE; else if (strcmp(inval, "power_off") == 0) action_val = WDOG_TIMEOUT_POWER_DOWN; else return -EINVAL; strcpy(action, inval); return 0;}static int preaction_op(const char *inval, char *outval){ if (outval) strcpy(outval, preaction); if (!inval) return 0; if (strcmp(inval, "pre_none") == 0) preaction_val = WDOG_PRETIMEOUT_NONE; else if (strcmp(inval, "pre_smi") == 0) preaction_val = WDOG_PRETIMEOUT_SMI;#ifdef HAVE_NMI_HANDLER else if (strcmp(inval, "pre_nmi") == 0) preaction_val = WDOG_PRETIMEOUT_NMI;#endif else if (strcmp(inval, "pre_int") == 0) preaction_val = WDOG_PRETIMEOUT_MSG_INT; else return -EINVAL; strcpy(preaction, inval); return 0;}static int preop_op(const char *inval, char *outval){ if (outval) strcpy(outval, preop); if (!inval) return 0; if (strcmp(inval, "preop_none") == 0) preop_val = WDOG_PREOP_NONE; else if (strcmp(inval, "preop_panic") == 0) preop_val = WDOG_PREOP_PANIC; else if (strcmp(inval, "preop_give_data") == 0) preop_val = WDOG_PREOP_GIVE_DATA; else return -EINVAL; strcpy(preop, inval); return 0;}static void check_parms(void){#ifdef HAVE_NMI_HANDLER int do_nmi = 0; int rv; if (preaction_val == WDOG_PRETIMEOUT_NMI) { do_nmi = 1; if (preop_val == WDOG_PREOP_GIVE_DATA) { printk(KERN_WARNING PFX "Pretimeout op is to give data" " but NMI pretimeout is enabled, setting" " pretimeout op to none\n"); preop_op("preop_none", NULL); do_nmi = 0; }#ifdef CONFIG_X86_LOCAL_APIC if (nmi_watchdog == NMI_IO_APIC) { printk(KERN_WARNING PFX "nmi_watchdog is set to IO APIC" " mode (value is %d), that is incompatible" " with using NMI in the IPMI watchdog." " Disabling IPMI nmi pretimeout.\n", nmi_watchdog); preaction_val = WDOG_PRETIMEOUT_NONE; do_nmi = 0; }#endif } if (do_nmi && !nmi_handler_registered) { rv = request_nmi(&ipmi_nmi_handler); if (rv) { printk(KERN_WARNING PFX "Can't register nmi handler\n"); return; } else nmi_handler_registered = 1; } else if (!do_nmi && nmi_handler_registered) { release_nmi(&ipmi_nmi_handler); nmi_handler_registered = 0; }#endif}static int __init ipmi_wdog_init(void){ int rv; if (action_op(action, NULL)) { action_op("reset", NULL); printk(KERN_INFO PFX "Unknown action '%s', defaulting to" " reset\n", action); } if (preaction_op(preaction, NULL)) { preaction_op("pre_none", NULL); printk(KERN_INFO PFX "Unknown preaction '%s', defaulting to" " none\n", preaction); } if (preop_op(preop, NULL)) { preop_op("preop_none", NULL); printk(KERN_INFO PFX "Unknown preop '%s', defaulting to" " none\n", preop); } check_parms(); rv = ipmi_smi_watcher_register(&smi_watcher); if (rv) {#ifdef HAVE_NMI_HANDLER if (preaction_val == WDOG_PRETIMEOUT_NMI) release_nmi(&ipmi_nmi_handler);#endif printk(KERN_WARNING PFX "can't register smi watcher\n"); return rv; } register_reboot_notifier(&wdog_reboot_notifier); notifier_chain_register(&panic_notifier_list, &wdog_panic_notifier); printk(KERN_INFO PFX "driver initialized\n"); return 0;}static __exit void ipmi_unregister_watchdog(void){ int rv; down_write(®ister_sem);#ifdef HAVE_NMI_HANDLER if (nmi_handler_registered) release_nmi(&ipmi_nmi_handler);#endif notifier_chain_unregister(&panic_notifier_list, &wdog_panic_notifier); unregister_reboot_notifier(&wdog_reboot_notifier); if (! watchdog_user) goto out; /* Make sure no one can call us any more. */ misc_deregister(&ipmi_wdog_miscdev); /* Wait to make sure the message makes it out. The lower layer has pointers to our buffers, we want to make sure they are done before we release our memory. */ while (atomic_read(&set_timeout_tofree)) schedule_timeout_uninterruptible(1); /* Disconnect from IPMI. */ rv = ipmi_destroy_user(watchdog_user); if (rv) { printk(KERN_WARNING PFX "error unlinking from IPMI: %d\n", rv); } watchdog_user = NULL; out: up_write(®ister_sem);}static void __exit ipmi_wdog_exit(void){ ipmi_smi_watcher_unregister(&smi_watcher); ipmi_unregister_watchdog();}module_exit(ipmi_wdog_exit);module_init(ipmi_wdog_init);MODULE_LICENSE("GPL");MODULE_AUTHOR("Corey Minyard <minyard@mvista.com>");MODULE_DESCRIPTION("watchdog timer based upon the IPMI interface.");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -