📄 ipmi_msghandler.c
字号:
{ return i_ipmi_request(user, user->intf, addr, msgid, msg, NULL, NULL, priority, source_address, source_lun);}int ipmi_register_smi(struct ipmi_smi_handlers *handlers, void *send_info, unsigned char version_major, unsigned char version_minor, ipmi_smi_t *intf){ int i, j; int rv; ipmi_smi_t new_intf; struct list_head *entry; unsigned long flags; /* Make sure the driver is actually initialized, this handles problems with initialization order. */ if (!initialized) { rv = ipmi_init_msghandler(); if (rv) return rv; /* The init code doesn't return an error if it was turned off, but it won't initialize. Check that. */ if (!initialized) return -ENODEV; } new_intf = kmalloc(sizeof(*new_intf), GFP_KERNEL); if (!new_intf) return -ENOMEM; rv = -ENOMEM; down_write(&interfaces_sem); for (i=0; i<MAX_IPMI_INTERFACES; i++) { if (ipmi_interfaces[i] == NULL) { new_intf->version_major = version_major; new_intf->version_minor = version_minor; new_intf->my_address = IPMI_BMC_SLAVE_ADDR; new_intf->my_lun = 2; /* the SMS LUN. */ rwlock_init(&(new_intf->users_lock)); INIT_LIST_HEAD(&(new_intf->users)); new_intf->handlers = handlers; new_intf->send_info = send_info; spin_lock_init(&(new_intf->seq_lock)); for (j=0; j<IPMI_IPMB_NUM_SEQ; j++) { new_intf->seq_table[j].inuse = 0; new_intf->seq_table[j].seqid = 0; } new_intf->curr_seq = 0; spin_lock_init(&(new_intf->waiting_msgs_lock)); INIT_LIST_HEAD(&(new_intf->waiting_msgs)); spin_lock_init(&(new_intf->events_lock)); INIT_LIST_HEAD(&(new_intf->waiting_events)); new_intf->waiting_events_count = 0; rwlock_init(&(new_intf->cmd_rcvr_lock)); INIT_LIST_HEAD(&(new_intf->cmd_rcvrs)); new_intf->all_cmd_rcvr = NULL; spin_lock_irqsave(&interfaces_lock, flags); ipmi_interfaces[i] = new_intf; spin_unlock_irqrestore(&interfaces_lock, flags); rv = 0; *intf = new_intf; break; } } /* We convert to a read semaphore here. It's possible the interface was removed between the calls, we have to recheck afterwards. */ up_write(&interfaces_sem); down_read(&interfaces_sem); if (ipmi_interfaces[i] != new_intf) /* Well, it went away. Just return. */ goto out; if (rv == 0) { /* Call all the watcher interfaces to tell them that a new interface is available. */ down_read(&smi_watchers_sem); list_for_each(entry, &smi_watchers) { struct ipmi_smi_watcher *w; w = list_entry(entry, struct ipmi_smi_watcher, link); w->new_smi(i); } up_read(&smi_watchers_sem); } out: up_read(&interfaces_sem); if (rv) kfree(new_intf); return rv;}static void free_recv_msg_list(struct list_head *q){ struct list_head *entry, *entry2; struct ipmi_recv_msg *msg; list_for_each_safe(entry, entry2, q) { msg = list_entry(entry, struct ipmi_recv_msg, link); list_del(entry); ipmi_free_recv_msg(msg); }}static void free_cmd_rcvr_list(struct list_head *q){ struct list_head *entry, *entry2; struct cmd_rcvr *rcvr; list_for_each_safe(entry, entry2, q) { rcvr = list_entry(entry, struct cmd_rcvr, link); list_del(entry); kfree(rcvr); }}static void clean_up_interface_data(ipmi_smi_t intf){ int i; free_recv_msg_list(&(intf->waiting_msgs)); free_recv_msg_list(&(intf->waiting_events)); free_cmd_rcvr_list(&(intf->cmd_rcvrs)); for (i=0; i<IPMI_IPMB_NUM_SEQ; i++) { if ((intf->seq_table[i].inuse) && (intf->seq_table[i].recv_msg)) { ipmi_free_recv_msg(intf->seq_table[i].recv_msg); } }}int ipmi_unregister_smi(ipmi_smi_t intf){ int rv = -ENODEV; int i; struct list_head *entry; unsigned long flags; down_write(&interfaces_sem); if (list_empty(&(intf->users))) { for (i=0; i<MAX_IPMI_INTERFACES; i++) { if (ipmi_interfaces[i] == intf) { spin_lock_irqsave(&interfaces_lock, flags); ipmi_interfaces[i] = NULL; clean_up_interface_data(intf); spin_unlock_irqrestore(&interfaces_lock,flags); kfree(intf); rv = 0; goto out_call_watcher; } } } else { rv = -EBUSY; } up_write(&interfaces_sem); return rv; out_call_watcher: /* Convert to a read semaphore so callbacks don't bite us. */ up_write(&interfaces_sem); down_read(&interfaces_sem); /* Call all the watcher interfaces to tell them that an interface is gone. */ down_read(&smi_watchers_sem); list_for_each(entry, &smi_watchers) { struct ipmi_smi_watcher *w; w = list_entry(entry, struct ipmi_smi_watcher, link); w->smi_gone(i); } up_read(&smi_watchers_sem); up_read(&interfaces_sem); return 0;}static int handle_get_msg_rsp(ipmi_smi_t intf, struct ipmi_smi_msg *msg){ struct ipmi_ipmb_addr ipmb_addr; struct ipmi_recv_msg *recv_msg; if (msg->rsp_size < 11) /* Message not big enough, just ignore it. */ return 0; if (msg->rsp[2] != 0) /* An error getting the response, just ignore it. */ return 0; ipmb_addr.addr_type = IPMI_IPMB_ADDR_TYPE; ipmb_addr.slave_addr = msg->rsp[6]; ipmb_addr.channel = msg->rsp[3] & 0x0f; ipmb_addr.lun = msg->rsp[7] & 3; /* It's a response from a remote entity. Look up the sequence number and handle the response. */ if (intf_find_seq(intf, msg->rsp[7] >> 2, msg->rsp[3] & 0x0f, msg->rsp[8], (msg->rsp[4] >> 2) & (~1), (struct ipmi_addr *) &(ipmb_addr), &recv_msg)) { /* We were unable to find the sequence number, so just nuke the message. */ return 0; } memcpy(recv_msg->msg_data, &(msg->rsp[9]), msg->rsp_size - 9); /* THe other fields matched, so no need to set them, except for netfn, which needs to be the response that was returned, not the request value. */ recv_msg->msg.netfn = msg->rsp[4] >> 2; recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = msg->rsp_size - 10; recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; deliver_response(recv_msg); return 0;}static int handle_get_msg_cmd(ipmi_smi_t intf, struct ipmi_smi_msg *msg){ struct list_head *entry; struct cmd_rcvr *rcvr; int rv = 0; unsigned char netfn; unsigned char cmd; ipmi_user_t user = NULL; struct ipmi_ipmb_addr *ipmb_addr; struct ipmi_recv_msg *recv_msg; if (msg->rsp_size < 10) /* Message not big enough, just ignore it. */ return 0; if (msg->rsp[2] != 0) { /* An error getting the response, just ignore it. */ return 0; } netfn = msg->rsp[4] >> 2; cmd = msg->rsp[8]; read_lock(&(intf->cmd_rcvr_lock)); if (intf->all_cmd_rcvr) { user = intf->all_cmd_rcvr; } else { /* Find the command/netfn. */ list_for_each(entry, &(intf->cmd_rcvrs)) { rcvr = list_entry(entry, struct cmd_rcvr, link); if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)) { user = rcvr->user; break; } } } read_unlock(&(intf->cmd_rcvr_lock)); if (user == NULL) { /* We didn't find a user, deliver an error response. */ msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); msg->data[1] = IPMI_SEND_MSG_CMD; msg->data[2] = msg->rsp[3]; msg->data[3] = msg->rsp[6]; msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3); msg->data[5] = ipmb_checksum(&(msg->data[3]), 2); msg->data[6] = intf->my_address; /* rqseq/lun */ msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3); msg->data[8] = msg->rsp[8]; /* cmd */ msg->data[9] = IPMI_INVALID_CMD_COMPLETION_CODE; msg->data[10] = ipmb_checksum(&(msg->data[6]), 4); msg->data_size = 11; intf->handlers->sender(intf->send_info, msg, 0); rv = -1; /* We used the message, so return the value that causes it to not be freed or queued. */ } else { /* Deliver the message to the user. */ recv_msg = ipmi_alloc_recv_msg(); if (! recv_msg) { /* We couldn't allocate memory for the message, so requeue it for handling later. */ rv = 1; } else { ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr; ipmb_addr->addr_type = IPMI_IPMB_ADDR_TYPE; ipmb_addr->slave_addr = msg->rsp[6]; ipmb_addr->lun = msg->rsp[7] & 3; ipmb_addr->channel = msg->rsp[3]; recv_msg->user = user; recv_msg->recv_type = IPMI_CMD_RECV_TYPE; recv_msg->msgid = msg->rsp[7] >> 2; recv_msg->msg.netfn = msg->rsp[4] >> 2; recv_msg->msg.cmd = msg->rsp[8]; recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = msg->rsp_size - 10; memcpy(recv_msg->msg_data, &(msg->rsp[9]), msg->rsp_size - 10); deliver_response(recv_msg); } } return rv;}static void copy_event_into_recv_msg(struct ipmi_recv_msg *recv_msg, struct ipmi_smi_msg *msg){ struct ipmi_system_interface_addr *smi_addr; recv_msg->msgid = 0; 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->recv_type = IPMI_ASYNC_EVENT_RECV_TYPE; recv_msg->msg.netfn = msg->rsp[0] >> 2; recv_msg->msg.cmd = msg->rsp[1]; memcpy(recv_msg->msg_data, &(msg->rsp[3]), msg->rsp_size - 3); recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = msg->rsp_size - 3;}/* This will be called with the intf->users_lock read-locked, so no need to do that here. */static int handle_read_event_rsp(ipmi_smi_t intf, struct ipmi_smi_msg *msg){ struct ipmi_recv_msg *recv_msg; struct list_head msgs; struct list_head *entry, *entry2; ipmi_user_t user; int rv = 0; int deliver_count = 0; unsigned long flags; if (msg->rsp_size < 19) { /* Message is too small to be an IPMB event. */ return 0; } if (msg->rsp[2] != 0) { /* An error getting the event, just ignore it. */ return 0; } INIT_LIST_HEAD(&msgs); spin_lock_irqsave(&(intf->events_lock), flags); /* Allocate and fill in one message for every user that is getting events. */ list_for_each(entry, &(intf->users)) { user = list_entry(entry, struct ipmi_user, link); if (! user->gets_events) continue; recv_msg = ipmi_alloc_recv_msg(); if (! recv_msg) { list_for_each_safe(entry, entry2, &msgs) { recv_msg = list_entry(entry, struct ipmi_recv_msg, link); list_del(entry); ipmi_free_recv_msg(recv_msg); } /* We couldn't allocate memory for the message, so requeue it for handling later. */ rv = 1; goto out; } deliver_count++; copy_event_into_recv_msg(recv_msg, msg); recv_msg->user = user; list_add_tail(&(recv_msg->link), &msgs); } if (deliver_count) { /* Now deliver all the messages. */ list_for_each_safe(entry, entry2, &msgs) { recv_msg = list_entry(entry, struct ipmi_recv_msg, link); list_del(entry); deliver_response(recv_msg); } } else if (intf->waiting_events_count < MAX_EVENTS_IN_QUEUE) { /* No one to receive the message, put it in queue if there's not already too many things in the queue. */ recv_msg = ipmi_alloc_recv_msg(); if (! recv_msg) { /* We couldn't allocate memory for the message, so requeue it for handling later. */ rv = 1; goto out; } copy_event_into_recv_msg(recv_msg, msg); list_add_tail(&(recv_msg->link), &(intf->waiting_events)); } else { /* There's too many things in the queue, discard this message. */ printk(KERN_WARNING "ipmi: Event queue full, discarding an" " incoming event\n"); } out: spin_unlock_irqrestore(&(intf->events_lock), flags); return rv;}static int handle_bmc_rsp(ipmi_smi_t intf, struct ipmi_smi_msg *msg){ struct ipmi_recv_msg *recv_msg; int found = 0; struct list_head *entry; recv_msg = (struct ipmi_recv_msg *) msg->user_data; /* Make sure the user still exists. */ list_for_each(entry, &(intf->users)) { if (list_entry(entry, struct ipmi_user, link) == recv_msg->user) { /* Found it, so we can deliver it */ found = 1; break; } } if (!found) { /* The user for the message went away, so give up. */ ipmi_free_recv_msg(recv_msg); } else { struct ipmi_system_interface_addr *smi_addr; 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -