Explorar o código

[PATCH] ipmi: add per-channel IPMB addresses

IPMI allows multiple IPMB channels on a single interface, and each channel
might have a different IPMB address.  However, the driver has only one IPMB
address that it uses for everything.  This patch adds new IOCTLS and a new
internal interface for setting per-channel IPMB addresses and LUNs.  New
systems are coming out with support for multiple IPMB channels, and they are
broken without this patch.

Signed-off-by: Corey Minyard <minyard@acm.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Corey Minyard %!s(int64=20) %!d(string=hai) anos
pai
achega
c14979b993
Modificáronse 3 ficheiros con 201 adicións e 60 borrados
  1. 84 10
      drivers/char/ipmi/ipmi_devintf.c
  2. 94 43
      drivers/char/ipmi/ipmi_msghandler.c
  3. 23 7
      include/linux/ipmi.h

+ 84 - 10
drivers/char/ipmi/ipmi_devintf.c

@@ -411,6 +411,7 @@ static int ipmi_ioctl(struct inode  *inode,
 		break;
 		break;
 	}
 	}
 
 
+	/* The next four are legacy, not per-channel. */
 	case IPMICTL_SET_MY_ADDRESS_CMD:
 	case IPMICTL_SET_MY_ADDRESS_CMD:
 	{
 	{
 		unsigned int val;
 		unsigned int val;
@@ -420,22 +421,25 @@ static int ipmi_ioctl(struct inode  *inode,
 			break;
 			break;
 		}
 		}
 
 
-		ipmi_set_my_address(priv->user, val);
-		rv = 0;
+		rv = ipmi_set_my_address(priv->user, 0, val);
 		break;
 		break;
 	}
 	}
 
 
 	case IPMICTL_GET_MY_ADDRESS_CMD:
 	case IPMICTL_GET_MY_ADDRESS_CMD:
 	{
 	{
-		unsigned int val;
+		unsigned int  val;
+		unsigned char rval;
+
+		rv = ipmi_get_my_address(priv->user, 0, &rval);
+		if (rv)
+			break;
 
 
-		val = ipmi_get_my_address(priv->user);
+		val = rval;
 
 
 		if (copy_to_user(arg, &val, sizeof(val))) {
 		if (copy_to_user(arg, &val, sizeof(val))) {
 			rv = -EFAULT;
 			rv = -EFAULT;
 			break;
 			break;
 		}
 		}
-		rv = 0;
 		break;
 		break;
 	}
 	}
 
 
@@ -448,24 +452,94 @@ static int ipmi_ioctl(struct inode  *inode,
 			break;
 			break;
 		}
 		}
 
 
-		ipmi_set_my_LUN(priv->user, val);
-		rv = 0;
+		rv = ipmi_set_my_LUN(priv->user, 0, val);
 		break;
 		break;
 	}
 	}
 
 
 	case IPMICTL_GET_MY_LUN_CMD:
 	case IPMICTL_GET_MY_LUN_CMD:
 	{
 	{
-		unsigned int val;
+		unsigned int  val;
+		unsigned char rval;
+
+		rv = ipmi_get_my_LUN(priv->user, 0, &rval);
+		if (rv)
+			break;
 
 
-		val = ipmi_get_my_LUN(priv->user);
+		val = rval;
 
 
 		if (copy_to_user(arg, &val, sizeof(val))) {
 		if (copy_to_user(arg, &val, sizeof(val))) {
 			rv = -EFAULT;
 			rv = -EFAULT;
 			break;
 			break;
 		}
 		}
-		rv = 0;
 		break;
 		break;
 	}
 	}
+
+	case IPMICTL_SET_MY_CHANNEL_ADDRESS_CMD:
+	{
+		struct ipmi_channel_lun_address_set val;
+
+		if (copy_from_user(&val, arg, sizeof(val))) {
+			rv = -EFAULT;
+			break;
+		}
+
+		return ipmi_set_my_address(priv->user, val.channel, val.value);
+		break;
+	}
+
+	case IPMICTL_GET_MY_CHANNEL_ADDRESS_CMD:
+	{
+		struct ipmi_channel_lun_address_set val;
+
+		if (copy_from_user(&val, arg, sizeof(val))) {
+			rv = -EFAULT;
+			break;
+		}
+
+		rv = ipmi_get_my_address(priv->user, val.channel, &val.value);
+		if (rv)
+			break;
+
+		if (copy_to_user(arg, &val, sizeof(val))) {
+			rv = -EFAULT;
+			break;
+		}
+		break;
+	}
+
+	case IPMICTL_SET_MY_CHANNEL_LUN_CMD:
+	{
+		struct ipmi_channel_lun_address_set val;
+
+		if (copy_from_user(&val, arg, sizeof(val))) {
+			rv = -EFAULT;
+			break;
+		}
+
+		rv = ipmi_set_my_LUN(priv->user, val.channel, val.value);
+		break;
+	}
+
+	case IPMICTL_GET_MY_CHANNEL_LUN_CMD:
+	{
+		struct ipmi_channel_lun_address_set val;
+
+		if (copy_from_user(&val, arg, sizeof(val))) {
+			rv = -EFAULT;
+			break;
+		}
+
+		rv = ipmi_get_my_LUN(priv->user, val.channel, &val.value);
+		if (rv)
+			break;
+
+		if (copy_to_user(arg, &val, sizeof(val))) {
+			rv = -EFAULT;
+			break;
+		}
+		break;
+	}
+
 	case IPMICTL_SET_TIMING_PARMS_CMD:
 	case IPMICTL_SET_TIMING_PARMS_CMD:
 	{
 	{
 		struct ipmi_timing_parms parms;
 		struct ipmi_timing_parms parms;

+ 94 - 43
drivers/char/ipmi/ipmi_msghandler.c

@@ -124,6 +124,14 @@ struct ipmi_channel
 {
 {
 	unsigned char medium;
 	unsigned char medium;
 	unsigned char protocol;
 	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_FS
 #ifdef CONFIG_PROC_FS
@@ -135,7 +143,7 @@ struct ipmi_proc_entry
 #endif
 #endif
 
 
 #define IPMI_IPMB_NUM_SEQ	64
 #define IPMI_IPMB_NUM_SEQ	64
-#define IPMI_MAX_CHANNELS       8
+#define IPMI_MAX_CHANNELS       16
 struct ipmi_smi
 struct ipmi_smi
 {
 {
 	/* What interface number are we? */
 	/* What interface number are we? */
@@ -199,14 +207,6 @@ struct ipmi_smi
 	   this is registered. */
 	   this is registered. */
 	ipmi_user_t all_cmd_rcvr;
 	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
 	/* The event receiver for my BMC, only really used at panic
 	   shutdown as a place to store this. */
 	   shutdown as a place to store this. */
 	unsigned char event_receiver;
 	unsigned char event_receiver;
@@ -766,26 +766,44 @@ void ipmi_get_version(ipmi_user_t   user,
 	*minor = user->intf->version_minor;
 	*minor = user->intf->version_minor;
 }
 }
 
 
-void ipmi_set_my_address(ipmi_user_t   user,
-			 unsigned char address)
+int ipmi_set_my_address(ipmi_user_t   user,
+			unsigned int  channel,
+			unsigned char address)
 {
 {
-	user->intf->my_address = address;
+	if (channel >= IPMI_MAX_CHANNELS)
+		return -EINVAL;
+	user->intf->channels[channel].address = address;
+	return 0;
 }
 }
 
 
-unsigned char ipmi_get_my_address(ipmi_user_t user)
+int ipmi_get_my_address(ipmi_user_t   user,
+			unsigned int  channel,
+			unsigned char *address)
 {
 {
-	return user->intf->my_address;
+	if (channel >= IPMI_MAX_CHANNELS)
+		return -EINVAL;
+	*address = user->intf->channels[channel].address;
+	return 0;
 }
 }
 
 
-void ipmi_set_my_LUN(ipmi_user_t   user,
-		     unsigned char LUN)
+int ipmi_set_my_LUN(ipmi_user_t   user,
+		    unsigned int  channel,
+		    unsigned char LUN)
 {
 {
-	user->intf->my_lun = LUN & 0x3;
+	if (channel >= IPMI_MAX_CHANNELS)
+		return -EINVAL;
+	user->intf->channels[channel].lun = LUN & 0x3;
+	return 0;
 }
 }
 
 
-unsigned char ipmi_get_my_LUN(ipmi_user_t user)
+int ipmi_get_my_LUN(ipmi_user_t   user,
+		    unsigned int  channel,
+		    unsigned char *address)
 {
 {
-	return user->intf->my_lun;
+	if (channel >= IPMI_MAX_CHANNELS)
+		return -EINVAL;
+	*address = user->intf->channels[channel].lun;
+	return 0;
 }
 }
 
 
 int ipmi_set_gets_events(ipmi_user_t user, int val)
 int ipmi_set_gets_events(ipmi_user_t user, int val)
@@ -1213,7 +1231,7 @@ static inline int i_ipmi_request(ipmi_user_t          user,
 		unsigned char         ipmb_seq;
 		unsigned char         ipmb_seq;
 		long                  seqid;
 		long                  seqid;
 
 
-		if (addr->channel > IPMI_NUM_CHANNELS) {
+		if (addr->channel >= IPMI_NUM_CHANNELS) {
 			spin_lock_irqsave(&intf->counter_lock, flags);
 			spin_lock_irqsave(&intf->counter_lock, flags);
 			intf->sent_invalid_commands++;
 			intf->sent_invalid_commands++;
 			spin_unlock_irqrestore(&intf->counter_lock, flags);
 			spin_unlock_irqrestore(&intf->counter_lock, flags);
@@ -1346,6 +1364,18 @@ static inline int i_ipmi_request(ipmi_user_t          user,
 	return rv;
 	return rv;
 }
 }
 
 
+static int check_addr(ipmi_smi_t       intf,
+		      struct ipmi_addr *addr,
+		      unsigned char    *saddr,
+		      unsigned char    *lun)
+{
+	if (addr->channel >= IPMI_MAX_CHANNELS)
+		return -EINVAL;
+	*lun = intf->channels[addr->channel].lun;
+	*saddr = intf->channels[addr->channel].address;
+	return 0;
+}
+
 int ipmi_request_settime(ipmi_user_t      user,
 int ipmi_request_settime(ipmi_user_t      user,
 			 struct ipmi_addr *addr,
 			 struct ipmi_addr *addr,
 			 long             msgid,
 			 long             msgid,
@@ -1355,6 +1385,12 @@ int ipmi_request_settime(ipmi_user_t      user,
 			 int              retries,
 			 int              retries,
 			 unsigned int     retry_time_ms)
 			 unsigned int     retry_time_ms)
 {
 {
+	unsigned char saddr, lun;
+	int           rv;
+
+	rv = check_addr(user->intf, addr, &saddr, &lun);
+	if (rv)
+		return rv;
 	return i_ipmi_request(user,
 	return i_ipmi_request(user,
 			      user->intf,
 			      user->intf,
 			      addr,
 			      addr,
@@ -1363,8 +1399,8 @@ int ipmi_request_settime(ipmi_user_t      user,
 			      user_msg_data,
 			      user_msg_data,
 			      NULL, NULL,
 			      NULL, NULL,
 			      priority,
 			      priority,
-			      user->intf->my_address,
-			      user->intf->my_lun,
+			      saddr,
+			      lun,
 			      retries,
 			      retries,
 			      retry_time_ms);
 			      retry_time_ms);
 }
 }
@@ -1378,6 +1414,12 @@ int ipmi_request_supply_msgs(ipmi_user_t          user,
 			     struct ipmi_recv_msg *supplied_recv,
 			     struct ipmi_recv_msg *supplied_recv,
 			     int                  priority)
 			     int                  priority)
 {
 {
+	unsigned char saddr, lun;
+	int           rv;
+
+	rv = check_addr(user->intf, addr, &saddr, &lun);
+	if (rv)
+		return rv;
 	return i_ipmi_request(user,
 	return i_ipmi_request(user,
 			      user->intf,
 			      user->intf,
 			      addr,
 			      addr,
@@ -1387,8 +1429,8 @@ int ipmi_request_supply_msgs(ipmi_user_t          user,
 			      supplied_smi,
 			      supplied_smi,
 			      supplied_recv,
 			      supplied_recv,
 			      priority,
 			      priority,
-			      user->intf->my_address,
-			      user->intf->my_lun,
+			      saddr,
+			      lun,
 			      -1, 0);
 			      -1, 0);
 }
 }
 
 
@@ -1397,8 +1439,15 @@ static int ipmb_file_read_proc(char *page, char **start, off_t off,
 {
 {
 	char       *out = (char *) page;
 	char       *out = (char *) page;
 	ipmi_smi_t intf = data;
 	ipmi_smi_t intf = data;
+	int        i;
+	int        rv= 0;
 
 
-	return sprintf(out, "%x\n", intf->my_address);
+	for (i=0; i<IPMI_MAX_CHANNELS; i++)
+		rv += sprintf(out+rv, "%x ", intf->channels[i].address);
+	out[rv-1] = '\n'; /* Replace the final space with a newline */
+	out[rv] = '\0';
+	rv++;
+	return rv;
 }
 }
 
 
 static int version_file_read_proc(char *page, char **start, off_t off,
 static int version_file_read_proc(char *page, char **start, off_t off,
@@ -1592,8 +1641,8 @@ send_channel_info_cmd(ipmi_smi_t intf, int chan)
 			      NULL,
 			      NULL,
 			      NULL,
 			      NULL,
 			      0,
 			      0,
-			      intf->my_address,
-			      intf->my_lun,
+			      intf->channels[0].address,
+			      intf->channels[0].lun,
 			      -1, 0);
 			      -1, 0);
 }
 }
 
 
@@ -1696,11 +1745,13 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
 			new_intf->intf_num = i;
 			new_intf->intf_num = i;
 			new_intf->version_major = version_major;
 			new_intf->version_major = version_major;
 			new_intf->version_minor = version_minor;
 			new_intf->version_minor = version_minor;
-			if (slave_addr == 0)
-				new_intf->my_address = IPMI_BMC_SLAVE_ADDR;
-			else
-				new_intf->my_address = slave_addr;
-			new_intf->my_lun = 2;  /* the SMS LUN. */
+			for (j=0; j<IPMI_MAX_CHANNELS; j++) {
+				new_intf->channels[j].address
+					= IPMI_BMC_SLAVE_ADDR;
+				new_intf->channels[j].lun = 2;
+			}
+			if (slave_addr != 0)
+				new_intf->channels[0].address = slave_addr;
 			rwlock_init(&(new_intf->users_lock));
 			rwlock_init(&(new_intf->users_lock));
 			INIT_LIST_HEAD(&(new_intf->users));
 			INIT_LIST_HEAD(&(new_intf->users));
 			new_intf->handlers = handlers;
 			new_intf->handlers = handlers;
@@ -1985,7 +2036,7 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t          intf,
 		msg->data[3] = msg->rsp[6];
 		msg->data[3] = msg->rsp[6];
                 msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3);
                 msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3);
 		msg->data[5] = ipmb_checksum(&(msg->data[3]), 2);
 		msg->data[5] = ipmb_checksum(&(msg->data[3]), 2);
-		msg->data[6] = intf->my_address;
+		msg->data[6] = intf->channels[msg->rsp[3] & 0xf].address;
                 /* rqseq/lun */
                 /* rqseq/lun */
                 msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3);
                 msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3);
 		msg->data[8] = msg->rsp[8]; /* cmd */
 		msg->data[8] = msg->rsp[8]; /* cmd */
@@ -2919,8 +2970,8 @@ static void send_panic_events(char *str)
 			       &smi_msg,
 			       &smi_msg,
 			       &recv_msg,
 			       &recv_msg,
 			       0,
 			       0,
-			       intf->my_address,
-			       intf->my_lun,
+			       intf->channels[0].address,
+			       intf->channels[0].lun,
 			       0, 1); /* Don't retry, and don't wait. */
 			       0, 1); /* Don't retry, and don't wait. */
 	}
 	}
 
 
@@ -2965,8 +3016,8 @@ static void send_panic_events(char *str)
 			       &smi_msg,
 			       &smi_msg,
 			       &recv_msg,
 			       &recv_msg,
 			       0,
 			       0,
-			       intf->my_address,
-			       intf->my_lun,
+			       intf->channels[0].address,
+			       intf->channels[0].lun,
 			       0, 1); /* Don't retry, and don't wait. */
 			       0, 1); /* Don't retry, and don't wait. */
 
 
 		if (intf->local_event_generator) {
 		if (intf->local_event_generator) {
@@ -2985,8 +3036,8 @@ static void send_panic_events(char *str)
 				       &smi_msg,
 				       &smi_msg,
 				       &recv_msg,
 				       &recv_msg,
 				       0,
 				       0,
-				       intf->my_address,
-				       intf->my_lun,
+				       intf->channels[0].address,
+				       intf->channels[0].lun,
 				       0, 1); /* no retry, and no wait. */
 				       0, 1); /* no retry, and no wait. */
 		}
 		}
 		intf->null_user_handler = NULL;
 		intf->null_user_handler = NULL;
@@ -2996,7 +3047,7 @@ static void send_panic_events(char *str)
 		   be zero, and it must not be my address. */
 		   be zero, and it must not be my address. */
                 if (((intf->event_receiver & 1) == 0)
                 if (((intf->event_receiver & 1) == 0)
 		    && (intf->event_receiver != 0)
 		    && (intf->event_receiver != 0)
-		    && (intf->event_receiver != intf->my_address))
+		    && (intf->event_receiver != intf->channels[0].address))
 		{
 		{
 			/* The event receiver is valid, send an IPMB
 			/* The event receiver is valid, send an IPMB
 			   message. */
 			   message. */
@@ -3031,7 +3082,7 @@ static void send_panic_events(char *str)
 			data[0] = 0;
 			data[0] = 0;
 			data[1] = 0;
 			data[1] = 0;
 			data[2] = 0xf0; /* OEM event without timestamp. */
 			data[2] = 0xf0; /* OEM event without timestamp. */
-			data[3] = intf->my_address;
+			data[3] = intf->channels[0].address;
 			data[4] = j++; /* sequence # */
 			data[4] = j++; /* sequence # */
 			/* Always give 11 bytes, so strncpy will fill
 			/* Always give 11 bytes, so strncpy will fill
 			   it with zeroes for me. */
 			   it with zeroes for me. */
@@ -3047,8 +3098,8 @@ static void send_panic_events(char *str)
 				       &smi_msg,
 				       &smi_msg,
 				       &recv_msg,
 				       &recv_msg,
 				       0,
 				       0,
-				       intf->my_address,
-				       intf->my_lun,
+				       intf->channels[0].address,
+				       intf->channels[0].lun,
 				       0, 1); /* no retry, and no wait. */
 				       0, 1); /* no retry, and no wait. */
 		}
 		}
 	}	
 	}	

+ 23 - 7
include/linux/ipmi.h

@@ -298,13 +298,19 @@ void ipmi_get_version(ipmi_user_t   user,
    this user, so it will affect all users of this interface.  This is
    this user, so it will affect all users of this interface.  This is
    so some initialization code can come in and do the OEM-specific
    so some initialization code can come in and do the OEM-specific
    things it takes to determine your address (if not the BMC) and set
    things it takes to determine your address (if not the BMC) and set
-   it for everyone else. */
-void ipmi_set_my_address(ipmi_user_t   user,
-			 unsigned char address);
-unsigned char ipmi_get_my_address(ipmi_user_t user);
-void ipmi_set_my_LUN(ipmi_user_t   user,
-		     unsigned char LUN);
-unsigned char ipmi_get_my_LUN(ipmi_user_t user);
+   it for everyone else.  Note that each channel can have its own address. */
+int ipmi_set_my_address(ipmi_user_t   user,
+			unsigned int  channel,
+			unsigned char address);
+int ipmi_get_my_address(ipmi_user_t   user,
+			unsigned int  channel,
+			unsigned char *address);
+int ipmi_set_my_LUN(ipmi_user_t   user,
+		    unsigned int  channel,
+		    unsigned char LUN);
+int ipmi_get_my_LUN(ipmi_user_t   user,
+		    unsigned int  channel,
+		    unsigned char *LUN);
 
 
 /*
 /*
  * Like ipmi_request, but lets you specify the number of retries and
  * Like ipmi_request, but lets you specify the number of retries and
@@ -585,6 +591,16 @@ struct ipmi_cmdspec
  * things it takes to determine your address (if not the BMC) and set
  * things it takes to determine your address (if not the BMC) and set
  * it for everyone else.  You should probably leave the LUN alone.
  * it for everyone else.  You should probably leave the LUN alone.
  */
  */
+struct ipmi_channel_lun_address_set
+{
+	unsigned short channel;
+	unsigned char  value;
+};
+#define IPMICTL_SET_MY_CHANNEL_ADDRESS_CMD _IOR(IPMI_IOC_MAGIC, 24, struct ipmi_channel_lun_address_set)
+#define IPMICTL_GET_MY_CHANNEL_ADDRESS_CMD _IOR(IPMI_IOC_MAGIC, 25, struct ipmi_channel_lun_address_set)
+#define IPMICTL_SET_MY_CHANNEL_LUN_CMD	   _IOR(IPMI_IOC_MAGIC, 26, struct ipmi_channel_lun_address_set)
+#define IPMICTL_GET_MY_CHANNEL_LUN_CMD	   _IOR(IPMI_IOC_MAGIC, 27, struct ipmi_channel_lun_address_set)
+/* Legacy interfaces, these only set IPMB 0. */
 #define IPMICTL_SET_MY_ADDRESS_CMD	_IOR(IPMI_IOC_MAGIC, 17, unsigned int)
 #define IPMICTL_SET_MY_ADDRESS_CMD	_IOR(IPMI_IOC_MAGIC, 17, unsigned int)
 #define IPMICTL_GET_MY_ADDRESS_CMD	_IOR(IPMI_IOC_MAGIC, 18, unsigned int)
 #define IPMICTL_GET_MY_ADDRESS_CMD	_IOR(IPMI_IOC_MAGIC, 18, unsigned int)
 #define IPMICTL_SET_MY_LUN_CMD		_IOR(IPMI_IOC_MAGIC, 19, unsigned int)
 #define IPMICTL_SET_MY_LUN_CMD		_IOR(IPMI_IOC_MAGIC, 19, unsigned int)