ipmi_msghandler.c
来自「linux 内核源代码」· C语言 代码 · 共 2,407 行 · 第 1/5 页
C
2,407 行
/* * 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/module.h>#include <linux/errno.h>#include <asm/system.h>#include <linux/poll.h>#include <linux/spinlock.h>#include <linux/mutex.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 "39.1"static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);static int ipmi_init_msghandler(void);static int initialized;#ifdef CONFIG_PROC_FSstatic struct proc_dir_entry *proc_ipmi_root;#endif /* CONFIG_PROC_FS *//* Remain in auto-maintenance mode for this amount of time (in ms). */#define IPMI_MAINTENANCE_MODE_TIMEOUT 30000#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; unsigned int chans; /* * 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;};#endifstruct bmc_device{ struct platform_device *dev; struct ipmi_device_id id; unsigned char guid[16]; int guid_set; struct kref refcount; /* bmc device attributes */ struct device_attribute device_id_attr; struct device_attribute provides_dev_sdrs_attr; struct device_attribute revision_attr; struct device_attribute firmware_rev_attr; struct device_attribute version_attr; struct device_attribute add_dev_support_attr; struct device_attribute manufacturer_id_attr; struct device_attribute product_id_attr; struct device_attribute guid_attr; struct device_attribute aux_firmware_rev_attr;};#define IPMI_IPMB_NUM_SEQ 64#define IPMI_MAX_CHANNELS 16struct ipmi_smi{ /* What interface number are we? */ int intf_num; struct kref refcount; /* Used for a list of interfaces. */ struct list_head link; /* The list of upper layers that are using me. seq_lock * protects this. */ struct list_head users; /* Information to supply to users. */ unsigned char ipmi_version_major; unsigned char ipmi_version_minor; /* Used for wake ups at startup. */ wait_queue_head_t waitq; struct bmc_device *bmc; char *my_dev_name; char *sysfs_name; /* This is the lower-layer's sender routine. Note that you * must either be holding the ipmi_interfaces_mutex or be in * an umpreemptible region to use this. You must fetch the * value into a local variable and make sure it is not NULL. */ struct ipmi_smi_handlers *handlers; void *send_info;#ifdef CONFIG_PROC_FS /* A list of proc entries for this interface. */ struct mutex proc_entry_lock; struct ipmi_proc_entry *proc_entries;#endif /* Driver-model device for the system interface. */ struct device *si_dev; /* 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 mutex cmd_rcvrs_mutex; 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? */ int delivering_events; /* 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; /* For handling of maintenance mode. */ int maintenance_mode; int maintenance_mode_enable; int auto_maintenance_timeout; spinlock_t maintenance_mode_lock; /* Used in a timer... */ /* 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;};#define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev)/** * The driver model view of the IPMI messaging driver. */static struct device_driver ipmidriver = { .name = "ipmi", .bus = &platform_bus_type};static DEFINE_MUTEX(ipmidriver_mutex);static struct list_head ipmi_interfaces = LIST_HEAD_INIT(ipmi_interfaces);static DEFINE_MUTEX(ipmi_interfaces_mutex);/* 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 DEFINE_MUTEX(smi_watchers_mutex);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 free_smi_msg_list(struct list_head *q){ struct ipmi_smi_msg *msg, *msg2; list_for_each_entry_safe(msg, msg2, q, link) { list_del(&msg->link); ipmi_free_smi_msg(msg); }}static void clean_up_interface_data(ipmi_smi_t intf){ int i; struct cmd_rcvr *rcvr, *rcvr2; struct list_head list; free_smi_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. */ mutex_lock(&intf->cmd_rcvrs_mutex); INIT_LIST_HEAD(&list); list_splice_init_rcu(&intf->cmd_rcvrs, &list, synchronize_rcu); mutex_unlock(&intf->cmd_rcvrs_mutex); 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);}struct watcher_entry { int intf_num; ipmi_smi_t intf; struct list_head link;};int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher){ ipmi_smi_t intf; struct list_head to_deliver = LIST_HEAD_INIT(to_deliver); struct watcher_entry *e, *e2; mutex_lock(&smi_watchers_mutex); mutex_lock(&ipmi_interfaces_mutex); /* Build a list of things to deliver. */ list_for_each_entry(intf, &ipmi_interfaces, link) { if (intf->intf_num == -1) continue; e = kmalloc(sizeof(*e), GFP_KERNEL); if (!e) goto out_err; kref_get(&intf->refcount); e->intf = intf; e->intf_num = intf->intf_num; list_add_tail(&e->link, &to_deliver); } /* We will succeed, so add it to the list. */ list_add(&watcher->link, &smi_watchers); mutex_unlock(&ipmi_interfaces_mutex); list_for_each_entry_safe(e, e2, &to_deliver, link) { list_del(&e->link); watcher->new_smi(e->intf_num, e->intf->si_dev); kref_put(&e->intf->refcount, intf_free); kfree(e); } mutex_unlock(&smi_watchers_mutex); return 0; out_err: mutex_unlock(&ipmi_interfaces_mutex); mutex_unlock(&smi_watchers_mutex);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?