📄 ipmi_watchdog.c
字号:
static ssize_t ipmi_write(struct file *file, const char *buf, size_t len, loff_t *ppos){ int rv; /* Can't seek (pwrite) on this device */ if (ppos != &file->f_pos) return -ESPIPE; if (len) { rv = ipmi_heartbeat(); if (rv) return rv; return 1; } return 0;}static ssize_t ipmi_read(struct file *file, char *buf, size_t count, loff_t *ppos){ int rv = 0; wait_queue_t wait; /* Can't seek (pread) on this device */ if (ppos != &file->f_pos) return -ESPIPE; if (count <= 0) return 0; /* Reading returns if the pretimeout has gone off, and it only does it once per pretimeout. */ spin_lock(&ipmi_read_lock); if (!data_to_read) { if (file->f_flags & O_NONBLOCK) { rv = -EAGAIN; goto out; } init_waitqueue_entry(&wait, current); add_wait_queue(&read_q, &wait); while (!data_to_read) { set_current_state(TASK_INTERRUPTIBLE); spin_unlock(&ipmi_read_lock); schedule(); spin_lock(&ipmi_read_lock); } remove_wait_queue(&read_q, &wait); if (signal_pending(current)) { rv = -ERESTARTSYS; goto out; } } data_to_read = 0; out: spin_unlock(&ipmi_read_lock); if (rv == 0) { if (copy_to_user(buf, &data_to_read, 1)) rv = -EFAULT; else rv = 1; } return rv;}static int ipmi_open(struct inode *ino, struct file *filep){ switch (minor(ino->i_rdev)) { case WATCHDOG_MINOR: if (ipmi_wdog_open) return -EBUSY; ipmi_wdog_open = 1; /* Don't start the timer now, let it start on the first heartbeat. */ ipmi_start_timer_on_heartbeat = 1; return(0); default: return (-ENODEV); }}static unsigned int ipmi_poll(struct file *file, poll_table *wait){ unsigned int mask = 0; poll_wait(file, &read_q, wait); spin_lock(&ipmi_read_lock); if (data_to_read) mask |= (POLLIN | POLLRDNORM); spin_unlock(&ipmi_read_lock); return mask;}static int ipmi_fasync(int fd, struct file *file, int on){ int result; result = fasync_helper(fd, file, on, &fasync_q); return (result);}static int ipmi_close(struct inode *ino, struct file *filep){ if (minor(ino->i_rdev)==WATCHDOG_MINOR) {#ifndef CONFIG_WATCHDOG_NOWAYOUT ipmi_watchdog_state = WDOG_TIMEOUT_NONE; ipmi_set_timeout();#endif ipmi_wdog_open = 0; } ipmi_fasync (-1, filep, 0); return 0;}static struct file_operations ipmi_wdog_fops = { .owner = THIS_MODULE, .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 = { WATCHDOG_MINOR, "watchdog", &ipmi_wdog_fops};static DECLARE_RWSEM(register_sem);static void ipmi_wdog_msg_handler(struct ipmi_recv_msg *msg, void *handler_data){ if (msg->msg.data[0] != 0) { printk(KERN_ERR "IPMI Watchdog 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) 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); /* 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; spin_unlock(&ipmi_read_lock); } }}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_read(®ister_sem); if (watchdog_user) goto out; rv = ipmi_create_user(ipmi_intf, &ipmi_hndlrs, NULL, &watchdog_user); if (rv < 0) { printk("IPMI watchdog: 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("IPMI watchdog: 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(); printk("Starting IPMI Watchdog now!\n"); }}#ifdef HAVE_NMI_HANDLERstatic intipmi_nmi(void *dev_id, struct pt_regs *regs, int cpu, int handled){ /* If no one else handled the NMI, we assume it was the IPMI watchdog. */ if ((!handled) && (preop_val == WDOG_PREOP_PANIC)) panic("IPMI watchdog pre-timeout"); return NOTIFY_DONE;}static struct nmi_handler ipmi_nmi_handler ={ .dev_name = "ipmi_watchdog", .dev_id = NULL, .handler = ipmi_nmi, .priority = 0, /* Call us last. */};#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 = { wdog_reboot_handler, NULL, 0};extern int panic_timeout; /* Why isn't this defined anywhere? */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 && (panic_timeout > 0)) { /* Make sure the panic doesn't hang, and make sure we do this only once. */ panic_event_handled = 1; timeout = panic_timeout + 120; if (timeout > 255) 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 = { wdog_panic_handler, NULL, 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 ={ .new_smi = ipmi_new_smi, .smi_gone = ipmi_smi_gone};static int __init ipmi_wdog_init(void){ int rv; if (strcmp(action, "reset") == 0) { action_val = WDOG_TIMEOUT_RESET; } else if (strcmp(action, "none") == 0) { action_val = WDOG_TIMEOUT_NONE; } else if (strcmp(action, "power_cycle") == 0) { action_val = WDOG_TIMEOUT_POWER_CYCLE; } else if (strcmp(action, "power_off") == 0) { action_val = WDOG_TIMEOUT_POWER_DOWN; } else { action_val = WDOG_TIMEOUT_RESET; printk("ipmi_watchdog: Unknown action '%s', defaulting to" " reset\n", action); } if (strcmp(preaction, "pre_none") == 0) { preaction_val = WDOG_PRETIMEOUT_NONE; } else if (strcmp(preaction, "pre_smi") == 0) { preaction_val = WDOG_PRETIMEOUT_SMI;#ifdef HAVE_NMI_HANDLER } else if (strcmp(preaction, "pre_nmi") == 0) { preaction_val = WDOG_PRETIMEOUT_NMI;#endif } else if (strcmp(preaction, "pre_int") == 0) { preaction_val = WDOG_PRETIMEOUT_MSG_INT; } else { action_val = WDOG_PRETIMEOUT_NONE; printk("ipmi_watchdog: Unknown preaction '%s', defaulting to" " none\n", preaction); } if (strcmp(preop, "preop_none") == 0) { preop_val = WDOG_PREOP_NONE; } else if (strcmp(preop, "preop_panic") == 0) { preop_val = WDOG_PREOP_PANIC; } else if (strcmp(preop, "preop_give_data") == 0) { preop_val = WDOG_PREOP_GIVE_DATA; } else { action_val = WDOG_PREOP_NONE; printk("ipmi_watchdog: Unknown preop '%s', defaulting to" " none\n", preop); }#ifdef HAVE_NMI_HANDLER if (preaction_val == WDOG_PRETIMEOUT_NMI) { if (preop_val == WDOG_PREOP_GIVE_DATA) { printk(KERN_WARNING "ipmi_watchdog: Pretimeout op is to give data" " but NMI pretimeout is enabled, setting" " pretimeout op to none\n"); preop_val = WDOG_PREOP_NONE; }#ifdef CONFIG_X86_LOCAL_APIC if (nmi_watchdog == NMI_IO_APIC) { printk(KERN_WARNING "ipmi_watchdog: 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; } else {#endif rv = request_nmi(&ipmi_nmi_handler); if (rv) { printk(KERN_WARNING "ipmi_watchdog: Can't register nmi handler\n"); return rv; }#ifdef CONFIG_X86_LOCAL_APIC }#endif }#endif 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 "ipmi_watchdog: 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 "IPMI watchdog by " "Corey Minyard (minyard@mvista.com)\n"); return 0;}#ifdef MODULEstatic void ipmi_unregister_watchdog(void){ int rv; down_write(®ister_sem);#ifdef HAVE_NMI_HANDLER if (preaction_val == WDOG_PRETIMEOUT_NMI) 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); /* Disable the timer. */ ipmi_watchdog_state = WDOG_TIMEOUT_NONE; ipmi_set_timeout(); /* 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(1); } /* Disconnect from IPMI. */ rv = ipmi_destroy_user(watchdog_user); if (rv) { printk(KERN_WARNING "IPMI Watchdog, 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);#elsestatic int __init ipmi_wdog_setup(char *str){ int val; int rv; char *option; rv = get_option(&str, &val); if (rv == 0) return 1; if (val > 0) timeout = val; if (rv == 1) return 1; rv = get_option(&str, &val); if (rv == 0) return 1; if (val >= 0) pretimeout = val; if (rv == 1) return 1; while ((option = strsep(&str, ",")) != NULL) { if (strcmp(option, "reset") == 0) { action = "reset"; } else if (strcmp(option, "none") == 0) { action = "none"; } else if (strcmp(option, "power_cycle") == 0) { action = "power_cycle"; } else if (strcmp(option, "power_off") == 0) { action = "power_off"; } else if (strcmp(option, "pre_none") == 0) { preaction = "pre_none"; } else if (strcmp(option, "pre_smi") == 0) { preaction = "pre_smi"; }#ifdef HAVE_NMI_HANDLER else if (strcmp(option, "pre_nmi") == 0) { preaction = "pre_nmi"; }#endif else if (strcmp(option, "pre_int") == 0) { preaction = "pre_int"; } else if (strcmp(option, "start_now") == 0) { start_now = 1; } else if (strcmp(option, "preop_none") == 0) { preop = "preop_none"; } else if (strcmp(option, "preop_panic") == 0) { preop = "preop_panic"; } else if (strcmp(option, "preop_none") == 0) { preop = "preop_give_data"; } else { printk("Unknown IPMI watchdog option: '%s'\n", option); } } return 1;}__setup("ipmi_wdog=", ipmi_wdog_setup);#endifEXPORT_SYMBOL_GPL(ipmi_delayed_shutdown);module_init(ipmi_wdog_init);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -