📄 ipmi_watchdog.c
字号:
case WDIOC_SET_PRETIMEOUT: i = copy_from_user(&val, argp, sizeof(int)); if (i) return -EFAULT; pretimeout = val; return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); case WDIOC_GET_PRETIMEOUT: i = copy_to_user(argp, &pretimeout, sizeof(pretimeout)); if (i) return -EFAULT; return 0; case WDIOC_KEEPALIVE: return ipmi_heartbeat(); case WDIOC_SETOPTIONS: i = copy_from_user(&val, argp, sizeof(int)); if (i) return -EFAULT; if (val & WDIOS_DISABLECARD) { ipmi_watchdog_state = WDOG_TIMEOUT_NONE; ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); ipmi_start_timer_on_heartbeat = 0; } if (val & WDIOS_ENABLECARD) { ipmi_watchdog_state = action_val; ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); } return 0; case WDIOC_GETSTATUS: val = 0; i = copy_to_user(argp, &val, sizeof(val)); if (i) return -EFAULT; return 0; default: return -ENOIOCTLCMD; }}static ssize_t ipmi_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos){ int rv; if (len) { if (!nowayout) { size_t i; /* In case it was set long ago */ expect_close = 0; for (i = 0; i != len; i++) { char c; if (get_user(c, buf + i)) return -EFAULT; if (c == 'V') expect_close = 42; } } rv = ipmi_heartbeat(); if (rv) return rv; return 1; } return 0;}static ssize_t ipmi_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ int rv = 0; wait_queue_t wait; 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 (iminor(ino)) { case WATCHDOG_MINOR: if(test_and_set_bit(0, &ipmi_wdog_open)) return -EBUSY; /* Don't start the timer now, let it start on the first heartbeat. */ ipmi_start_timer_on_heartbeat = 1; return nonseekable_open(ino, filep); 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 (iminor(ino)==WATCHDOG_MINOR) { if (expect_close == 42) { ipmi_watchdog_state = WDOG_TIMEOUT_NONE; ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); clear_bit(0, &ipmi_wdog_open); } else { printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); ipmi_heartbeat(); } } ipmi_fasync (-1, filep, 0); expect_close = 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 = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &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 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) 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 no one else handled the NMI, we assume it was the IPMI watchdog. */ if ((!handled) && (preop_val == WDOG_PREOP_PANIC)) panic(PFX "pre-timeout"); /* 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; 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. */};#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 __init ipmi_wdog_init(void){ int rv; printk(KERN_INFO PFX "driver version " IPMI_WATCHDOG_VERSION "\n"); 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(KERN_INFO PFX "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 { preaction_val = WDOG_PRETIMEOUT_NONE; printk(KERN_INFO PFX "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 { preop_val = WDOG_PREOP_NONE; printk(KERN_INFO PFX "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 PFX "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 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; } else {#endif rv = request_nmi(&ipmi_nmi_handler); if (rv) { printk(KERN_WARNING PFX "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 PFX "can't register smi watcher\n"); return rv; } register_reboot_notifier(&wdog_reboot_notifier); notifier_chain_register(&panic_notifier_list, &wdog_panic_notifier); return 0;}static __exit 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); /* 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)) { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(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");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -