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 + -
显示快捷键?