⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ipmi_msghandler.c

📁 h内核
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * 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/rwsem.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>#define PFX "IPMI message handler: "#define IPMI_MSGHANDLER_VERSION "v33"static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);static int ipmi_init_msghandler(void);static int initialized = 0;static struct proc_dir_entry *proc_ipmi_root = NULL;#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		60000struct ipmi_user{	struct list_head link;	/* 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;};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;};struct ipmi_proc_entry{	char                   *name;	struct ipmi_proc_entry *next;};#define IPMI_IPMB_NUM_SEQ	64#define IPMI_MAX_CHANNELS       8struct ipmi_smi{	/* What interface number are we? */	int intf_num;	/* The list of upper layers that are using me.  We read-lock           this when delivering messages to the upper layer to keep           the user from going away while we are processing the           message.  This means that you cannot add or delete a user           from the receive callback. */	rwlock_t                users_lock;	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;	/* 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. */	struct ipmi_proc_entry *proc_entries;	/* 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. */	rwlock_t	 cmd_rcvr_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? */	/* This will be non-null if someone registers to receive all	   IPMI commands (this is for interface emulation).  There	   may not be any things in the cmd_rcvrs list above when	   this is registered. */	ipmi_user_t all_cmd_rcvr;	/* My slave address.  This is initialized to IPMI_BMC_SLAVE_ADDR,	   but may be changed by the user. */	unsigned char my_address;	/* My LUN.  This should generally stay the SMS LUN, but just in	   case... */	unsigned char my_lun;	/* 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_smi_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 MAX_IPMI_INTERFACES 4static ipmi_smi_t ipmi_interfaces[MAX_IPMI_INTERFACES];/* Used to keep interfaces from going away while operations are   operating on interfaces.  Grab read if you are not modifying the   interfaces, write if you are. */static DECLARE_RWSEM(interfaces_sem);/* Directly protects the ipmi_interfaces data structure.  This is   claimed in the timer interrupt. */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);int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher){	int i;	down_read(&interfaces_sem);	down_write(&smi_watchers_sem);	list_add(&(watcher->link), &smi_watchers);	for (i=0; i<MAX_IPMI_INTERFACES; i++) {		if (ipmi_interfaces[i] != NULL) {			watcher->new_smi(i);		}	}	up_write(&smi_watchers_sem);	up_read(&interfaces_sem);	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;}int ipmi_validate_addr(struct ipmi_addr *addr, int len){	if (len < sizeof(struct ipmi_system_interface_addr)) {		return -EINVAL;	}	if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) {		if (addr->channel != IPMI_BMC_CHANNEL)			return -EINVAL;		return 0;	}	if ((addr->channel == IPMI_BMC_CHANNEL)	    || (addr->channel >= IPMI_NUM_CHANNELS)	    || (addr->channel < 0))		return -EINVAL;	if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE)	    || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))	{		if (len < sizeof(struct ipmi_ipmb_addr)) {			return -EINVAL;		}		return 0;	}	if (addr->addr_type == IPMI_LAN_ADDR_TYPE) {		if (len < sizeof(struct ipmi_lan_addr)) {			return -EINVAL;		}		return 0;	}	return -EINVAL;}unsigned int ipmi_addr_length(int addr_type){	if (addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)		return sizeof(struct ipmi_system_interface_addr);	if ((addr_type == IPMI_IPMB_ADDR_TYPE)	    || (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))	{		return sizeof(struct ipmi_ipmb_addr);	}	if (addr_type == IPMI_LAN_ADDR_TYPE)		return sizeof(struct ipmi_lan_addr);	return 0;}static void deliver_response(struct ipmi_recv_msg *msg){	msg->user->handler->ipmi_recv_hndl(msg, msg->user->handler_data);}/* Find the next sequence number not being used and add the given   message with the given timeout to the sequence table.  This must be   called with the interface's seq_lock held. */static int intf_next_seq(ipmi_smi_t           intf,			 struct ipmi_recv_msg *recv_msg,			 unsigned long        timeout,			 int                  retries,			 int                  broadcast,			 unsigned char        *seq,			 long                 *seqid){

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -