📄 ipmi_msghandler.c
字号:
/* * ipmi_msghandler.c * * Incoming and outgoing message routing for an IPMI interface. * * Author: MontaVista Software, Inc. * Corey Minyard <minyard@mvista.com> * source@mvista.com * * Copyright 2002 MontaVista Software Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/config.h>#include <linux/module.h>#include <linux/errno.h>#include <asm/system.h>#include <linux/sched.h>#include <linux/poll.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/ipmi.h>#include <linux/ipmi_smi.h>#include <linux/notifier.h>#include <linux/init.h>#include <linux/proc_fs.h>#include <linux/rcupdate.h>#define PFX "IPMI message handler: "#define IPMI_DRIVER_VERSION "38.0"static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);static int ipmi_init_msghandler(void);static int initialized = 0;#ifdef CONFIG_PROC_FSstruct proc_dir_entry *proc_ipmi_root = NULL;#endif /* CONFIG_PROC_FS */#define MAX_EVENTS_IN_QUEUE 25/* Don't let a message sit in a queue forever, always time it with at lest the max message timer. This is in milliseconds. */#define MAX_MSG_TIMEOUT 60000/* * The main "user" data structure. */struct ipmi_user{ struct list_head link; /* Set to "0" when the user is destroyed. */ int valid; struct kref refcount; /* The upper layer that handles receive messages. */ struct ipmi_user_hndl *handler; void *handler_data; /* The interface this user is bound to. */ ipmi_smi_t intf; /* Does this interface receive IPMI events? */ int gets_events;};struct cmd_rcvr{ struct list_head link; ipmi_user_t user; unsigned char netfn; unsigned char cmd; /* * This is used to form a linked lised during mass deletion. * Since this is in an RCU list, we cannot use the link above * or change any data until the RCU period completes. So we * use this next variable during mass deletion so we can have * a list and don't have to wait and restart the search on * every individual deletion of a command. */ struct cmd_rcvr *next;};struct seq_table{ unsigned int inuse : 1; unsigned int broadcast : 1; unsigned long timeout; unsigned long orig_timeout; unsigned int retries_left; /* To verify on an incoming send message response that this is the message that the response is for, we keep a sequence id and increment it every time we send a message. */ long seqid; /* This is held so we can properly respond to the message on a timeout, and it is used to hold the temporary data for retransmission, too. */ struct ipmi_recv_msg *recv_msg;};/* Store the information in a msgid (long) to allow us to find a sequence table entry from the msgid. */#define STORE_SEQ_IN_MSGID(seq, seqid) (((seq&0xff)<<26) | (seqid&0x3ffffff))#define GET_SEQ_FROM_MSGID(msgid, seq, seqid) \ do { \ seq = ((msgid >> 26) & 0x3f); \ seqid = (msgid & 0x3fffff); \ } while (0)#define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3fffff)struct ipmi_channel{ unsigned char medium; unsigned char protocol; /* My slave address. This is initialized to IPMI_BMC_SLAVE_ADDR, but may be changed by the user. */ unsigned char address; /* My LUN. This should generally stay the SMS LUN, but just in case... */ unsigned char lun;};#ifdef CONFIG_PROC_FSstruct ipmi_proc_entry{ char *name; struct ipmi_proc_entry *next;};#endif#define IPMI_IPMB_NUM_SEQ 64#define IPMI_MAX_CHANNELS 16struct ipmi_smi{ /* What interface number are we? */ int intf_num; struct kref refcount; /* The list of upper layers that are using me. seq_lock * protects this. */ struct list_head users; /* Used for wake ups at startup. */ wait_queue_head_t waitq; /* The IPMI version of the BMC on the other end. */ unsigned char version_major; unsigned char version_minor; /* This is the lower-layer's sender routine. */ struct ipmi_smi_handlers *handlers; void *send_info;#ifdef CONFIG_PROC_FS /* A list of proc entries for this interface. This does not need a lock, only one thread creates it and only one thread destroys it. */ spinlock_t proc_entry_lock; struct ipmi_proc_entry *proc_entries;#endif /* A table of sequence numbers for this interface. We use the sequence numbers for IPMB messages that go out of the interface to match them up with their responses. A routine is called periodically to time the items in this list. */ spinlock_t seq_lock; struct seq_table seq_table[IPMI_IPMB_NUM_SEQ]; int curr_seq; /* Messages that were delayed for some reason (out of memory, for instance), will go in here to be processed later in a periodic timer interrupt. */ spinlock_t waiting_msgs_lock; struct list_head waiting_msgs; /* The list of command receivers that are registered for commands on this interface. */ struct semaphore cmd_rcvrs_lock; struct list_head cmd_rcvrs; /* Events that were queues because no one was there to receive them. */ spinlock_t events_lock; /* For dealing with event stuff. */ struct list_head waiting_events; unsigned int waiting_events_count; /* How many events in queue? */ /* The event receiver for my BMC, only really used at panic shutdown as a place to store this. */ unsigned char event_receiver; unsigned char event_receiver_lun; unsigned char local_sel_device; unsigned char local_event_generator; /* A cheap hack, if this is non-null and a message to an interface comes in with a NULL user, call this routine with it. Note that the message will still be freed by the caller. This only works on the system interface. */ void (*null_user_handler)(ipmi_smi_t intf, struct ipmi_recv_msg *msg); /* When we are scanning the channels for an SMI, this will tell which channel we are scanning. */ int curr_channel; /* Channel information */ struct ipmi_channel channels[IPMI_MAX_CHANNELS]; /* Proc FS stuff. */ struct proc_dir_entry *proc_dir; char proc_dir_name[10]; spinlock_t counter_lock; /* For making counters atomic. */ /* Commands we got that were invalid. */ unsigned int sent_invalid_commands; /* Commands we sent to the MC. */ unsigned int sent_local_commands; /* Responses from the MC that were delivered to a user. */ unsigned int handled_local_responses; /* Responses from the MC that were not delivered to a user. */ unsigned int unhandled_local_responses; /* Commands we sent out to the IPMB bus. */ unsigned int sent_ipmb_commands; /* Commands sent on the IPMB that had errors on the SEND CMD */ unsigned int sent_ipmb_command_errs; /* Each retransmit increments this count. */ unsigned int retransmitted_ipmb_commands; /* When a message times out (runs out of retransmits) this is incremented. */ unsigned int timed_out_ipmb_commands; /* This is like above, but for broadcasts. Broadcasts are *not* included in the above count (they are expected to time out). */ unsigned int timed_out_ipmb_broadcasts; /* Responses I have sent to the IPMB bus. */ unsigned int sent_ipmb_responses; /* The response was delivered to the user. */ unsigned int handled_ipmb_responses; /* The response had invalid data in it. */ unsigned int invalid_ipmb_responses; /* The response didn't have anyone waiting for it. */ unsigned int unhandled_ipmb_responses; /* Commands we sent out to the IPMB bus. */ unsigned int sent_lan_commands; /* Commands sent on the IPMB that had errors on the SEND CMD */ unsigned int sent_lan_command_errs; /* Each retransmit increments this count. */ unsigned int retransmitted_lan_commands; /* When a message times out (runs out of retransmits) this is incremented. */ unsigned int timed_out_lan_commands; /* Responses I have sent to the IPMB bus. */ unsigned int sent_lan_responses; /* The response was delivered to the user. */ unsigned int handled_lan_responses; /* The response had invalid data in it. */ unsigned int invalid_lan_responses; /* The response didn't have anyone waiting for it. */ unsigned int unhandled_lan_responses; /* The command was delivered to the user. */ unsigned int handled_commands; /* The command had invalid data in it. */ unsigned int invalid_commands; /* The command didn't have anyone waiting for it. */ unsigned int unhandled_commands; /* Invalid data in an event. */ unsigned int invalid_events; /* Events that were received with the proper format. */ unsigned int events;};/* Used to mark an interface entry that cannot be used but is not a * free entry, either, primarily used at creation and deletion time so * a slot doesn't get reused too quickly. */#define IPMI_INVALID_INTERFACE_ENTRY ((ipmi_smi_t) ((long) 1))#define IPMI_INVALID_INTERFACE(i) (((i) == NULL) \ || (i == IPMI_INVALID_INTERFACE_ENTRY))#define MAX_IPMI_INTERFACES 4static ipmi_smi_t ipmi_interfaces[MAX_IPMI_INTERFACES];/* Directly protects the ipmi_interfaces data structure. */static DEFINE_SPINLOCK(interfaces_lock);/* List of watchers that want to know when smi's are added and deleted. */static struct list_head smi_watchers = LIST_HEAD_INIT(smi_watchers);static DECLARE_RWSEM(smi_watchers_sem);static void free_recv_msg_list(struct list_head *q){ struct ipmi_recv_msg *msg, *msg2; list_for_each_entry_safe(msg, msg2, q, link) { list_del(&msg->link); ipmi_free_recv_msg(msg); }}static void clean_up_interface_data(ipmi_smi_t intf){ int i; struct cmd_rcvr *rcvr, *rcvr2; struct list_head list; free_recv_msg_list(&intf->waiting_msgs); free_recv_msg_list(&intf->waiting_events); /* Wholesale remove all the entries from the list in the * interface and wait for RCU to know that none are in use. */ down(&intf->cmd_rcvrs_lock); list_add_rcu(&list, &intf->cmd_rcvrs); list_del_rcu(&intf->cmd_rcvrs); up(&intf->cmd_rcvrs_lock); synchronize_rcu(); list_for_each_entry_safe(rcvr, rcvr2, &list, link) kfree(rcvr); 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); } }}static void intf_free(struct kref *ref){ ipmi_smi_t intf = container_of(ref, struct ipmi_smi, refcount); clean_up_interface_data(intf); kfree(intf);}int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher){ int i; unsigned long flags; down_write(&smi_watchers_sem); list_add(&(watcher->link), &smi_watchers); up_write(&smi_watchers_sem); spin_lock_irqsave(&interfaces_lock, flags); for (i = 0; i < MAX_IPMI_INTERFACES; i++) { ipmi_smi_t intf = ipmi_interfaces[i]; if (IPMI_INVALID_INTERFACE(intf)) continue; spin_unlock_irqrestore(&interfaces_lock, flags); watcher->new_smi(i); spin_lock_irqsave(&interfaces_lock, flags); } spin_unlock_irqrestore(&interfaces_lock, flags); return 0;}int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher){ down_write(&smi_watchers_sem); list_del(&(watcher->link)); up_write(&smi_watchers_sem); return 0;}static voidcall_smi_watchers(int i){ struct ipmi_smi_watcher *w; down_read(&smi_watchers_sem); list_for_each_entry(w, &smi_watchers, link) { if (try_module_get(w->owner)) { w->new_smi(i); module_put(w->owner); } } up_read(&smi_watchers_sem);}static intipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2){ if (addr1->addr_type != addr2->addr_type) return 0; if (addr1->channel != addr2->channel) return 0; if (addr1->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { struct ipmi_system_interface_addr *smi_addr1 = (struct ipmi_system_interface_addr *) addr1; struct ipmi_system_interface_addr *smi_addr2 = (struct ipmi_system_interface_addr *) addr2; return (smi_addr1->lun == smi_addr2->lun); } if ((addr1->addr_type == IPMI_IPMB_ADDR_TYPE) || (addr1->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) { struct ipmi_ipmb_addr *ipmb_addr1 = (struct ipmi_ipmb_addr *) addr1; struct ipmi_ipmb_addr *ipmb_addr2 = (struct ipmi_ipmb_addr *) addr2; return ((ipmb_addr1->slave_addr == ipmb_addr2->slave_addr) && (ipmb_addr1->lun == ipmb_addr2->lun)); } if (addr1->addr_type == IPMI_LAN_ADDR_TYPE) { struct ipmi_lan_addr *lan_addr1 = (struct ipmi_lan_addr *) addr1; struct ipmi_lan_addr *lan_addr2 = (struct ipmi_lan_addr *) addr2; return ((lan_addr1->remote_SWID == lan_addr2->remote_SWID) && (lan_addr1->local_SWID == lan_addr2->local_SWID) && (lan_addr1->session_handle == lan_addr2->session_handle) && (lan_addr1->lun == lan_addr2->lun)); } return 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -