📄 ipmi_msghandler.c
字号:
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; /* Lock the user lock so the user can't go away while we are working on it. */ read_lock(&(intf->users_lock)); if ((msg->data_size >= 2) && (msg->data[1] == IPMI_SEND_MSG_CMD)) { /* This is the local response to a send, start the timer for these. */ intf_start_seq_timer(intf, msg->msgid); ipmi_free_smi_msg(msg); goto out_unlock; } /* 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)); goto out_unlock; } 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) { ipmi_free_smi_msg(msg); } out_unlock: 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 voidsend_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg, struct ipmi_smi_msg *smi_msg, unsigned char seq, long seqid){ if (!smi_msg) smi_msg = ipmi_alloc_smi_msg(); if (!smi_msg) /* If we can't allocate the message, then just return, we get 4 retries, so this should be ok. */ return; memcpy(smi_msg->data, recv_msg->msg.data, recv_msg->msg.data_len); smi_msg->data_size = recv_msg->msg.data_len; smi_msg->msgid = STORE_SEQ_IN_MSGID(seq, seqid); /* Send the new message. We send with a zero priority. It timed out, I doubt time is that critical now, and high priority messages are really only for messages to the local MC, which don't get resent. */ intf->handlers->sender(intf->send_info, smi_msg, 0);#if DEBUG_MSGING { int m; printk("Resend: "); for (m=0; m<smi_msg->data_size; m++) printk(" %2.2x", smi_msg->data[m]); printk("\n"); }#endif}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); ipmi_free_smi_msg(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++) { struct seq_table *ent = &(intf->seq_table[j]); if (!ent->inuse) continue; ent->timeout -= timeout_period; if (ent->timeout > 0) continue; if (ent->retries_left == 0) { /* The message has used all its retries. */ ent->inuse = 0; msg = ent->recv_msg; list_add_tail(&(msg->link), &timeouts); } else { /* More retries, send again. */ /* Start with the max timer, set to normal timer after the message is sent. */ ent->timeout = MAX_MSG_TIMEOUT; ent->retries_left--; send_from_recv_msg(intf, ent->recv_msg, NULL, j, ent->seqid); } } 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);}static atomic_t smi_msg_inuse_count = ATOMIC_INIT(0);static atomic_t recv_msg_inuse_count = ATOMIC_INIT(0);/* FIXME - convert these to slabs. */static void free_smi_msg(struct ipmi_smi_msg *msg){ atomic_dec(&smi_msg_inuse_count); 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; atomic_inc(&smi_msg_inuse_count); } return rv;}static void free_recv_msg(struct ipmi_recv_msg *msg){ atomic_dec(&recv_msg_inuse_count); 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; atomic_inc(&recv_msg_inuse_count); } 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){ int count; 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; /* Check for buffer leaks. */ count = atomic_read(&smi_msg_inuse_count); if (count != 0) printk("ipmi_msghandler: SMI message count %d at exit\n", count); count = atomic_read(&recv_msg_inuse_count); if (count != 0) printk("ipmi_msghandler: recv message count %d at exit\n", count);}module_exit(cleanup_ipmi);module_init(ipmi_init_msghandler);MODULE_LICENSE("GPL");EXPORT_SYMBOL(ipmi_alloc_recv_msg);EXPORT_SYMBOL(ipmi_create_user);EXPORT_SYMBOL(ipmi_destroy_user);EXPORT_SYMBOL(ipmi_get_version);EXPORT_SYMBOL(ipmi_request);EXPORT_SYMBOL(ipmi_request_supply_msgs);EXPORT_SYMBOL(ipmi_request_with_source);EXPORT_SYMBOL(ipmi_register_smi);EXPORT_SYMBOL(ipmi_unregister_smi);EXPORT_SYMBOL(ipmi_register_for_cmd);EXPORT_SYMBOL(ipmi_unregister_for_cmd);EXPORT_SYMBOL(ipmi_smi_msg_received);EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout);EXPORT_SYMBOL(ipmi_alloc_smi_msg);EXPORT_SYMBOL(ipmi_register_all_cmd_rcvr);EXPORT_SYMBOL(ipmi_unregister_all_cmd_rcvr);EXPORT_SYMBOL(ipmi_addr_length);EXPORT_SYMBOL(ipmi_validate_addr);EXPORT_SYMBOL(ipmi_set_gets_events);EXPORT_SYMBOL(ipmi_addr_equal);EXPORT_SYMBOL(ipmi_smi_watcher_register);EXPORT_SYMBOL(ipmi_smi_watcher_unregister);EXPORT_SYMBOL(ipmi_set_my_address);EXPORT_SYMBOL(ipmi_get_my_address);EXPORT_SYMBOL(ipmi_set_my_LUN);EXPORT_SYMBOL(ipmi_get_my_LUN);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -