ipmi_watchdog.c
字号:
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; if (watchdog_user) goto out; if ((ifnum_to_use >= 0) && (ifnum_to_use != ipmi_intf)) goto out; watchdog_ifnum = ipmi_intf; 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"); }#ifdef HAVE_DIE_NMI if (nmi_handler_registered) { int old_pretimeout = pretimeout; int old_timeout = timeout; int old_preop_val = preop_val; /* Set the pretimeout to go off in a second and give ourselves plenty of time to stop the timer. */ ipmi_watchdog_state = WDOG_TIMEOUT_RESET; preop_val = WDOG_PREOP_NONE; /* Make sure nothing happens */ pretimeout = 99; timeout = 100; testing_nmi = 1; rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); if (rv) { printk(KERN_WARNING PFX "Error starting timer to" " test NMI: 0x%x. The NMI pretimeout will" " likely not work\n", rv); rv = 0; goto out_restore; } msleep(1500); if (testing_nmi != 2) { printk(KERN_WARNING PFX "IPMI NMI didn't seem to" " occur. The NMI pretimeout will" " likely not work\n"); } out_restore: testing_nmi = 0; preop_val = old_preop_val; pretimeout = old_pretimeout; timeout = old_timeout; }#endif out: 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"); } else { /* Stop the timer now. */ ipmi_watchdog_state = WDOG_TIMEOUT_NONE; ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); }}static void ipmi_unregister_watchdog(int ipmi_intf){ int rv; if (!watchdog_user) goto out; if (watchdog_ifnum != ipmi_intf) 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: return;}#ifdef HAVE_DIE_NMIstatic intipmi_nmi(struct notifier_block *self, unsigned long val, void *data){ struct die_args *args = data; if (val != DIE_NMI) return NOTIFY_OK; /* Hack, if it's a memory or I/O error, ignore it. */ if (args->err & 0xc0) return NOTIFY_OK; /* * If we get here, it's an NMI that's not a memory or I/O * error. We can't truly tell if it's from IPMI or not * without sending a message, and sending a message is almost * impossible because of locking. */ if (testing_nmi) { testing_nmi = 2; return NOTIFY_STOP; } /* If we are not expecting a timeout, ignore it. */ if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) return NOTIFY_OK; if (preaction_val != WDOG_PRETIMEOUT_NMI) return NOTIFY_OK; /* If no one else handled the NMI, we assume it was the IPMI watchdog. */ if (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_STOP;}static struct notifier_block ipmi_nmi_handler = { .notifier_call = ipmi_nmi};#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_POWER_OFF || code == SYS_HALT) { /* Disable the WDT if we are shutting down. */ ipmi_watchdog_state = WDOG_TIMEOUT_NONE; panic_halt_ipmi_set_timeout(); } else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) { /* Set a long timer to let the reboot happens, but reboot if it hangs, but only if the watchdog timer was already running. */ 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 to extend the watchdog timer to a reasonable value to complete the panic, if the watchdog timer is running. Plus the pretimeout is meaningless at panic time. */ if (watchdog_user && !panic_event_handled && ipmi_watchdog_state != WDOG_TIMEOUT_NONE) { /* Make sure we do this only once. */ panic_event_handled = 1; timeout = 255; pretimeout = 0; 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, struct device *device){ ipmi_register_watchdog(if_num);}static void ipmi_smi_gone(int if_num){ ipmi_unregister_watchdog(if_num);}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_DIE_NMI 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_DIE_NMI 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; } } if (do_nmi && !nmi_handler_registered) { rv = register_die_notifier(&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) { unregister_die_notifier(&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(); register_reboot_notifier(&wdog_reboot_notifier); atomic_notifier_chain_register(&panic_notifier_list, &wdog_panic_notifier); rv = ipmi_smi_watcher_register(&smi_watcher); if (rv) {#ifdef HAVE_DIE_NMI if (nmi_handler_registered) unregister_die_notifier(&ipmi_nmi_handler);#endif atomic_notifier_chain_unregister(&panic_notifier_list, &wdog_panic_notifier); unregister_reboot_notifier(&wdog_reboot_notifier); printk(KERN_WARNING PFX "can't register smi watcher\n"); return rv; } printk(KERN_INFO PFX "driver initialized\n"); return 0;}static void __exit ipmi_wdog_exit(void){ ipmi_smi_watcher_unregister(&smi_watcher); ipmi_unregister_watchdog(watchdog_ifnum);#ifdef HAVE_DIE_NMI if (nmi_handler_registered) unregister_die_notifier(&ipmi_nmi_handler);#endif atomic_notifier_chain_unregister(&panic_notifier_list, &wdog_panic_notifier); unregister_reboot_notifier(&wdog_reboot_notifier);}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 + -