📄 ipmi_msghandler.c
字号:
recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; recv_msg->msgid = msg->msgid; smi_addr = ((struct ipmi_system_interface_addr *) &(recv_msg->addr)); smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; smi_addr->channel = IPMI_BMC_CHANNEL; smi_addr->lun = msg->rsp[0] & 3; recv_msg->msg.netfn = msg->rsp[0] >> 2; recv_msg->msg.cmd = msg->rsp[1]; memcpy(recv_msg->msg_data, &(msg->rsp[2]), msg->rsp_size - 2); recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = msg->rsp_size - 2; deliver_response(recv_msg); } return 0;}/* Handle a new message. Return 1 if the message should be requeued, 0 if the message should be freed, or -1 if the message should not be freed or requeued. */static int handle_new_recv_msg(ipmi_smi_t intf, struct ipmi_smi_msg *msg){ int requeue; if (msg->rsp_size < 2) { /* Message is too small to be correct. */ requeue = 0; } else if (msg->rsp[1] == IPMI_GET_MSG_CMD) {#if DEBUG_MSGING int m; printk("Response:"); for (m=0; m<msg->rsp_size; m++) printk(" %2.2x", msg->rsp[m]); printk("\n");#endif /* It's from the receive queue. */ if (msg->rsp[4] & 0x04) { /* It's a response, so find the requesting message and send it up. */ requeue = handle_get_msg_rsp(intf, msg); } else { /* It's a command to the SMS from some other entity. Handle that. */ requeue = handle_get_msg_cmd(intf, msg); } } else if (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD) { /* It's an asyncronous event. */ requeue = handle_read_event_rsp(intf, msg); } else { /* It's a response from the local BMC. */ requeue = handle_bmc_rsp(intf, msg); } return requeue;}/* Handle a new message from the lower layer. */void ipmi_smi_msg_received(ipmi_smi_t intf, struct ipmi_smi_msg *msg){ unsigned long flags; int rv; if ((msg->data_size >= 2) && (msg->data[1] == IPMI_SEND_MSG_CMD)) { /* This is the local response to a send, we just ignore these. */ msg->done(msg); return; } /* Lock the user lock so the user can't go away while we are working on it. */ read_lock(&(intf->users_lock)); /* To preserve message order, if the list is not empty, we tack this message onto the end of the list. */ spin_lock_irqsave(&(intf->waiting_msgs_lock), flags); if (!list_empty(&(intf->waiting_msgs))) { list_add_tail(&(msg->link), &(intf->waiting_msgs)); spin_unlock(&(intf->waiting_msgs_lock)); return; } spin_unlock_irqrestore(&(intf->waiting_msgs_lock), flags); rv = handle_new_recv_msg(intf, msg); if (rv > 0) { /* Could not handle the message now, just add it to a list to handle later. */ spin_lock(&(intf->waiting_msgs_lock)); list_add_tail(&(msg->link), &(intf->waiting_msgs)); spin_unlock(&(intf->waiting_msgs_lock)); } else if (rv == 0) { msg->done(msg); } read_unlock(&(intf->users_lock));}void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf){ struct list_head *entry; ipmi_user_t user; read_lock(&(intf->users_lock)); list_for_each(entry, &(intf->users)) { user = list_entry(entry, struct ipmi_user, link); if (! user->handler->ipmi_watchdog_pretimeout) continue; user->handler->ipmi_watchdog_pretimeout(user->handler_data); } read_unlock(&(intf->users_lock));}static voidhandle_msg_timeout(struct ipmi_recv_msg *msg){ msg->recv_type = IPMI_RESPONSE_RECV_TYPE; msg->msg_data[0] = IPMI_TIMEOUT_COMPLETION_CODE; msg->msg.netfn |= 1; /* Convert to a response. */ msg->msg.data_len = 1; msg->msg.data = msg->msg_data; deliver_response(msg);}static voidipmi_timeout_handler(long timeout_period){ ipmi_smi_t intf; struct list_head timeouts; struct ipmi_recv_msg *msg; struct ipmi_smi_msg *smi_msg; unsigned long flags; struct list_head *entry, *entry2; int i, j; INIT_LIST_HEAD(&timeouts); spin_lock(&interfaces_lock); for (i=0; i<MAX_IPMI_INTERFACES; i++) { intf = ipmi_interfaces[i]; if (intf == NULL) continue; read_lock(&(intf->users_lock)); /* See if any waiting messages need to be processed. */ spin_lock_irqsave(&(intf->waiting_msgs_lock), flags); list_for_each_safe(entry, entry2, &(intf->waiting_msgs)) { smi_msg = list_entry(entry, struct ipmi_smi_msg, link); if (! handle_new_recv_msg(intf, smi_msg)) { list_del(entry); smi_msg->done(smi_msg); } else { /* To preserve message order, quit if we can't handle a message. */ break; } } spin_unlock_irqrestore(&(intf->waiting_msgs_lock), flags); /* Go through the seq table and find any messages that have timed out, putting them in the timeouts list. */ spin_lock_irqsave(&(intf->seq_lock), flags); for (j=0; j<IPMI_IPMB_NUM_SEQ; j++) { if (intf->seq_table[j].inuse) { intf->seq_table[j].timeout -= timeout_period; if (intf->seq_table[j].timeout <= 0) { intf->seq_table[j].inuse = 0; msg = intf->seq_table[j].recv_msg; list_add_tail(&(msg->link), &timeouts); } } } spin_unlock_irqrestore(&(intf->seq_lock), flags); list_for_each_safe(entry, entry2, &timeouts) { msg = list_entry(entry, struct ipmi_recv_msg, link); handle_msg_timeout(msg); } read_unlock(&(intf->users_lock)); } spin_unlock(&interfaces_lock);}static void ipmi_request_event(void){ ipmi_smi_t intf; int i; spin_lock(&interfaces_lock); for (i=0; i<MAX_IPMI_INTERFACES; i++) { intf = ipmi_interfaces[i]; if (intf == NULL) continue; intf->handlers->request_events(intf->send_info); } spin_unlock(&interfaces_lock);}static struct timer_list ipmi_timer;/* Call every 100 ms. */#define IPMI_TIMEOUT_TIME 100#define IPMI_TIMEOUT_JIFFIES (IPMI_TIMEOUT_TIME/(1000/HZ))/* Request events from the queue every second. Hopefully, in the future, IPMI will add a way to know immediately if an event is in the queue. */#define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME))static volatile int stop_operation = 0;static volatile int timer_stopped = 0;static unsigned int ticks_to_req_ev = IPMI_REQUEST_EV_TIME;static void ipmi_timeout(unsigned long data){ if (stop_operation) { timer_stopped = 1; return; } ticks_to_req_ev--; if (ticks_to_req_ev == 0) { ipmi_request_event(); ticks_to_req_ev = IPMI_REQUEST_EV_TIME; } ipmi_timeout_handler(IPMI_TIMEOUT_TIME); ipmi_timer.expires += IPMI_TIMEOUT_JIFFIES; add_timer(&ipmi_timer);}/* FIXME - convert these to slabs. */static void free_smi_msg(struct ipmi_smi_msg *msg){ kfree(msg);}struct ipmi_smi_msg *ipmi_alloc_smi_msg(void){ struct ipmi_smi_msg *rv; rv = kmalloc(sizeof(struct ipmi_smi_msg), GFP_ATOMIC); if (rv) rv->done = free_smi_msg; return rv;}static void free_recv_msg(struct ipmi_recv_msg *msg){ kfree(msg);}struct ipmi_recv_msg *ipmi_alloc_recv_msg(void){ struct ipmi_recv_msg *rv; rv = kmalloc(sizeof(struct ipmi_recv_msg), GFP_ATOMIC); if (rv) rv->done = free_recv_msg; return rv;}#ifdef CONFIG_IPMI_PANIC_EVENTstatic void dummy_smi_done_handler(struct ipmi_smi_msg *msg){}static void dummy_recv_done_handler(struct ipmi_recv_msg *msg){}static void send_panic_events(void){ struct ipmi_msg msg; ipmi_smi_t intf; unsigned char data[8]; int i; struct ipmi_system_interface_addr addr; struct ipmi_smi_msg smi_msg; struct ipmi_recv_msg recv_msg; addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; addr.channel = IPMI_BMC_CHANNEL; /* Fill in an event telling that we have failed. */ msg.netfn = 0x04; /* Sensor or Event. */ msg.cmd = 2; /* Platform event command. */ msg.data = data; msg.data_len = 8; data[0] = 0x21; /* Kernel generator ID, IPMI table 5-4 */ data[1] = 0x03; /* This is for IPMI 1.0. */ data[2] = 0x20; /* OS Critical Stop, IPMI table 36-3 */ data[4] = 0x6f; /* Sensor specific, IPMI table 36-1 */ data[5] = 0xa1; /* Runtime stop OEM bytes 2 & 3. */ /* These used to have the first three bytes of the panic string, but not only is that not terribly useful, it's not available any more. */ data[3] = 0; data[6] = 0; data[7] = 0; smi_msg.done = dummy_smi_done_handler; recv_msg.done = dummy_recv_done_handler; /* For every registered interface, send the event. */ for (i=0; i<MAX_IPMI_INTERFACES; i++) { intf = ipmi_interfaces[i]; if (intf == NULL) continue; intf->handlers->set_run_to_completion(intf->send_info, 1); i_ipmi_request(NULL, intf, (struct ipmi_addr *) &addr, 0, &msg, &smi_msg, &recv_msg, 0, intf->my_address, intf->my_lun); }}#endif /* CONFIG_IPMI_PANIC_EVENT */static int has_paniced = 0;static int panic_event(struct notifier_block *this, unsigned long event, void *ptr){ int i; ipmi_smi_t intf; if (has_paniced) return NOTIFY_DONE; has_paniced = 1; /* For every registered interface, set it to run to completion. */ for (i=0; i<MAX_IPMI_INTERFACES; i++) { intf = ipmi_interfaces[i]; if (intf == NULL) continue; intf->handlers->set_run_to_completion(intf->send_info, 1); }#ifdef CONFIG_IPMI_PANIC_EVENT send_panic_events();#endif return NOTIFY_DONE;}static struct notifier_block panic_block = { panic_event, NULL, 200 /* priority: INT_MAX >= x >= 0 */};static __init int ipmi_init_msghandler(void){ int i; if (initialized) return 0; for (i=0; i<MAX_IPMI_INTERFACES; i++) { ipmi_interfaces[i] = NULL; } init_timer(&ipmi_timer); ipmi_timer.data = 0; ipmi_timer.function = ipmi_timeout; ipmi_timer.expires = jiffies + IPMI_TIMEOUT_JIFFIES; add_timer(&ipmi_timer); notifier_chain_register(&panic_notifier_list, &panic_block); initialized = 1; printk(KERN_INFO "ipmi: message handler initialized\n"); return 0;}static __exit void cleanup_ipmi(void){ if (!initialized) return; notifier_chain_unregister(&panic_notifier_list, &panic_block); /* This can't be called if any interfaces exist, so no worry about shutting down the interfaces. */ /* Tell the timer to stop, then wait for it to stop. This avoids problems with race conditions removing the timer here. */ stop_operation = 1; while (!timer_stopped) { schedule_timeout(1); } initialized = 0;}module_exit(cleanup_ipmi);module_init(ipmi_init_msghandler);MODULE_LICENSE("GPL");EXPORT_SYMBOL_GPL(ipmi_alloc_recv_msg);EXPORT_SYMBOL_GPL(ipmi_create_user);EXPORT_SYMBOL_GPL(ipmi_destroy_user);EXPORT_SYMBOL_GPL(ipmi_get_version);EXPORT_SYMBOL_GPL(ipmi_request);EXPORT_SYMBOL_GPL(ipmi_request_supply_msgs);EXPORT_SYMBOL_GPL(ipmi_request_with_source);EXPORT_SYMBOL_GPL(ipmi_register_smi);EXPORT_SYMBOL_GPL(ipmi_unregister_smi);EXPORT_SYMBOL_GPL(ipmi_register_for_cmd);EXPORT_SYMBOL_GPL(ipmi_unregister_for_cmd);EXPORT_SYMBOL_GPL(ipmi_smi_msg_received);EXPORT_SYMBOL_GPL(ipmi_smi_watchdog_pretimeout);EXPORT_SYMBOL_GPL(ipmi_alloc_smi_msg);EXPORT_SYMBOL_GPL(ipmi_register_all_cmd_rcvr);EXPORT_SYMBOL_GPL(ipmi_unregister_all_cmd_rcvr);EXPORT_SYMBOL_GPL(ipmi_addr_length);EXPORT_SYMBOL_GPL(ipmi_validate_addr);EXPORT_SYMBOL_GPL(ipmi_set_gets_events);EXPORT_SYMBOL_GPL(ipmi_addr_equal);EXPORT_SYMBOL_GPL(ipmi_smi_watcher_register);EXPORT_SYMBOL_GPL(ipmi_smi_watcher_unregister);EXPORT_SYMBOL_GPL(ipmi_set_my_address);EXPORT_SYMBOL_GPL(ipmi_get_my_address);EXPORT_SYMBOL_GPL(ipmi_set_my_LUN);EXPORT_SYMBOL_GPL(ipmi_get_my_LUN);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -