📄 ipmi_watchdog.c
字号:
&set_timeout_recv_msg, &send_heartbeat_now); if (rv) { up(&set_timeout_lock); } else { if ((do_heartbeat == IPMI_SET_TIMEOUT_FORCE_HB) || ((send_heartbeat_now) && (do_heartbeat == IPMI_SET_TIMEOUT_HB_IF_NECESSARY))) { rv = ipmi_heartbeat(); } } return rv;}static void dummy_smi_free(struct ipmi_smi_msg *msg){}static void dummy_recv_free(struct ipmi_recv_msg *msg){}static struct ipmi_smi_msg panic_halt_smi_msg ={ .done = dummy_smi_free};static struct ipmi_recv_msg panic_halt_recv_msg ={ .done = dummy_recv_free};/* Special call, doesn't claim any locks. This is only to be called at panic or halt time, in run-to-completion mode, when the caller is the only CPU and the only thing that will be going is these IPMI calls. */static void panic_halt_ipmi_set_timeout(void){ int send_heartbeat_now; int rv; rv = i_ipmi_set_timeout(&panic_halt_smi_msg, &panic_halt_recv_msg, &send_heartbeat_now); if (!rv) { if (send_heartbeat_now) panic_halt_ipmi_heartbeat(); }}/* We use a semaphore to make sure that only one thing can send a heartbeat at one time, because we only have one copy of the data. The semaphore is claimed when the set_timeout is sent and freed when both messages are free. */static atomic_t heartbeat_tofree = ATOMIC_INIT(0);static DECLARE_MUTEX(heartbeat_lock);static DECLARE_MUTEX_LOCKED(heartbeat_wait_lock);static void heartbeat_free_smi(struct ipmi_smi_msg *msg){ if (atomic_dec_and_test(&heartbeat_tofree)) up(&heartbeat_wait_lock);}static void heartbeat_free_recv(struct ipmi_recv_msg *msg){ if (atomic_dec_and_test(&heartbeat_tofree)) up(&heartbeat_wait_lock);}static struct ipmi_smi_msg heartbeat_smi_msg ={ .done = heartbeat_free_smi};static struct ipmi_recv_msg heartbeat_recv_msg ={ .done = heartbeat_free_recv}; static struct ipmi_smi_msg panic_halt_heartbeat_smi_msg ={ .done = dummy_smi_free};static struct ipmi_recv_msg panic_halt_heartbeat_recv_msg ={ .done = dummy_recv_free}; static int ipmi_heartbeat(void){ struct kernel_ipmi_msg msg; int rv; struct ipmi_system_interface_addr addr; if (ipmi_ignore_heartbeat) { return 0; } if (ipmi_start_timer_on_heartbeat) { ipmi_start_timer_on_heartbeat = 0; ipmi_watchdog_state = action_val; return ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); } else if (pretimeout_since_last_heartbeat) { /* A pretimeout occurred, make sure we set the timeout. We don't want to set the action, though, we want to leave that alone (thus it can't be combined with the above operation. */ pretimeout_since_last_heartbeat = 0; return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); } down(&heartbeat_lock); atomic_set(&heartbeat_tofree, 2); /* Don't reset the timer if we have the timer turned off, that re-enables the watchdog. */ if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) { up(&heartbeat_lock); return 0; } addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; addr.channel = IPMI_BMC_CHANNEL; addr.lun = 0; msg.netfn = 0x06; msg.cmd = IPMI_WDOG_RESET_TIMER; msg.data = NULL; msg.data_len = 0; rv = ipmi_request_supply_msgs(watchdog_user, (struct ipmi_addr *) &addr, 0, &msg, NULL, &heartbeat_smi_msg, &heartbeat_recv_msg, 1); if (rv) { up(&heartbeat_lock); printk(KERN_WARNING PFX "heartbeat failure: %d\n", rv); return rv; } /* Wait for the heartbeat to be sent. */ down(&heartbeat_wait_lock); if (heartbeat_recv_msg.msg.data[0] != 0) { /* Got an error in the heartbeat response. It was already reported in ipmi_wdog_msg_handler, but we should return an error here. */ rv = -EINVAL; } up(&heartbeat_lock); return rv;}static void panic_halt_ipmi_heartbeat(void){ struct kernel_ipmi_msg msg; struct ipmi_system_interface_addr addr; /* Don't reset the timer if we have the timer turned off, that re-enables the watchdog. */ if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) return; addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; addr.channel = IPMI_BMC_CHANNEL; addr.lun = 0; msg.netfn = 0x06; msg.cmd = IPMI_WDOG_RESET_TIMER; msg.data = NULL; msg.data_len = 0; ipmi_request_supply_msgs(watchdog_user, (struct ipmi_addr *) &addr, 0, &msg, NULL, &panic_halt_heartbeat_smi_msg, &panic_halt_heartbeat_recv_msg, 1);}static struct watchdog_info ident={ .options = 0, /* WDIOF_SETTIMEOUT, */ .firmware_version = 1, .identity = "IPMI"};static int ipmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ void __user *argp = (void __user *)arg; int i; int val; switch(cmd) { case WDIOC_GETSUPPORT: i = copy_to_user(argp, &ident, sizeof(ident)); return i ? -EFAULT : 0; case WDIOC_SETTIMEOUT: i = copy_from_user(&val, argp, sizeof(int)); if (i) return -EFAULT; timeout = val; return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); case WDIOC_GETTIMEOUT: i = copy_to_user(argp, &timeout, sizeof(timeout)); if (i) return -EFAULT; return 0; 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); } else { printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); ipmi_heartbeat(); } clear_bit(0, &ipmi_wdog_open); } ipmi_fasync (-1, filep, 0); expect_close = 0; return 0;}static struct file_operations ipmi_wdog_fops = { .owner = THIS_MODULE,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -