Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 31b0b073 authored by Corey Minyard's avatar Corey Minyard
Browse files

ipmi: Rescan channel list on BMC changes



If the BMC changes versions or a change is otherwise detected,
rescan the channels on the BMC.

Signed-off-by: default avatarCorey Minyard <cminyard@mvista.com>
parent 5fdb1fb2
Loading
Loading
Loading
Loading
+111 −58
Original line number Diff line number Diff line
@@ -242,11 +242,16 @@ struct seq_table {

#define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3ffffff)

#define IPMI_MAX_CHANNELS       16
struct ipmi_channel {
	unsigned char medium;
	unsigned char protocol;
};

struct ipmi_channel_set {
	struct ipmi_channel c[IPMI_MAX_CHANNELS];
};

struct ipmi_my_addrinfo {
	/*
	 * My slave address.  This is initialized to IPMI_BMC_SLAVE_ADDR,
@@ -398,7 +403,6 @@ enum ipmi_stat_indexes {


#define IPMI_IPMB_NUM_SEQ	64
#define IPMI_MAX_CHANNELS       16
struct ipmi_smi {
	/* What interface number are we? */
	int intf_num;
@@ -531,8 +535,11 @@ struct ipmi_smi {
	int curr_channel;

	/* Channel information */
	struct ipmi_channel channels[IPMI_MAX_CHANNELS];
	struct ipmi_channel_set *channel_list;
	unsigned int curr_working_cset; /* First index into the following. */
	struct ipmi_channel_set wchannels[2];
	struct ipmi_my_addrinfo addrinfo[IPMI_MAX_CHANNELS];
	bool channels_ready;

	/* Proc FS stuff. */
	struct proc_dir_entry *proc_dir;
@@ -554,6 +561,7 @@ static void __ipmi_bmc_unregister(ipmi_smi_t intf);
static int __ipmi_bmc_register(ipmi_smi_t intf,
			       struct ipmi_device_id *id,
			       bool guid_set, u8 *guid, int intf_num);
static int __scan_channels(ipmi_smi_t intf, struct ipmi_device_id *id);


/**
@@ -1775,6 +1783,7 @@ static int i_ipmi_request(ipmi_user_t user,
		unsigned char         ipmb_seq;
		long                  seqid;
		int                   broadcast = 0;
		struct ipmi_channel   *chans;

		if (addr->channel >= IPMI_MAX_CHANNELS) {
			ipmi_inc_stat(intf, sent_invalid_commands);
@@ -1782,8 +1791,9 @@ static int i_ipmi_request(ipmi_user_t user,
			goto out_err;
		}

		if (intf->channels[addr->channel].medium
					!= IPMI_CHANNEL_MEDIUM_IPMB) {
		chans = READ_ONCE(intf->channel_list)->c;

		if (chans[addr->channel].medium != IPMI_CHANNEL_MEDIUM_IPMB) {
			ipmi_inc_stat(intf, sent_invalid_commands);
			rv = -EINVAL;
			goto out_err;
@@ -1905,6 +1915,7 @@ static int i_ipmi_request(ipmi_user_t user,
		struct ipmi_lan_addr  *lan_addr;
		unsigned char         ipmb_seq;
		long                  seqid;
		struct ipmi_channel   *chans;

		if (addr->channel >= IPMI_MAX_CHANNELS) {
			ipmi_inc_stat(intf, sent_invalid_commands);
@@ -1912,9 +1923,11 @@ static int i_ipmi_request(ipmi_user_t user,
			goto out_err;
		}

		if ((intf->channels[addr->channel].medium
		chans = READ_ONCE(intf->channel_list)->c;

		if ((chans[addr->channel].medium
				!= IPMI_CHANNEL_MEDIUM_8023LAN)
		    && (intf->channels[addr->channel].medium
		    && (chans[addr->channel].medium
				!= IPMI_CHANNEL_MEDIUM_ASYNC)) {
			ipmi_inc_stat(intf, sent_invalid_commands);
			rv = -EINVAL;
@@ -2282,6 +2295,9 @@ static int __bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc,
		memcpy(intf->bmc->guid, guid, 16);
		if (__ipmi_bmc_register(intf, &id, guid_set, guid, intf_num))
			need_waiter(intf); /* Retry later on an error. */
		else
			__scan_channels(intf, &id);


		if (!intf_set) {
			/*
@@ -2298,7 +2314,9 @@ static int __bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc,
		bmc = intf->bmc;
		mutex_lock(&bmc->dyn_mutex);
		goto out_noprocessing;
	}
	} else if (memcmp(&bmc->fetch_id, &bmc->id, sizeof(bmc->id)))
		/* Version info changes, scan the channels again. */
		__scan_channels(intf, &bmc->fetch_id);

	bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY;

@@ -3212,7 +3230,9 @@ static void
channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
{
	int rv = 0;
	int chan;
	int ch;
	unsigned int set = intf->curr_working_cset;
	struct ipmi_channel *chans;

	if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
	    && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE)
@@ -3228,12 +3248,13 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
				 * assume it has one IPMB at channel
				 * zero.
				 */
				intf->channels[0].medium
				intf->wchannels[set].c[0].medium
					= IPMI_CHANNEL_MEDIUM_IPMB;
				intf->channels[0].protocol
				intf->wchannels[set].c[0].protocol
					= IPMI_CHANNEL_PROTOCOL_IPMB;

				intf->curr_channel = IPMI_MAX_CHANNELS;
				intf->channel_list = intf->wchannels + set;
				intf->channels_ready = true;
				wake_up(&intf->waitq);
				goto out;
			}
@@ -3243,16 +3264,22 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
			/* Message not big enough, just go on. */
			goto next_channel;
		}
		chan = intf->curr_channel;
		intf->channels[chan].medium = msg->msg.data[2] & 0x7f;
		intf->channels[chan].protocol = msg->msg.data[3] & 0x1f;
		ch = intf->curr_channel;
		chans = intf->wchannels[set].c;
		chans[ch].medium = msg->msg.data[2] & 0x7f;
		chans[ch].protocol = msg->msg.data[3] & 0x1f;

 next_channel:
		intf->curr_channel++;
		if (intf->curr_channel >= IPMI_MAX_CHANNELS)
		if (intf->curr_channel >= IPMI_MAX_CHANNELS) {
			intf->channel_list = intf->wchannels + set;
			intf->channels_ready = true;
			wake_up(&intf->waitq);
		else
		} else {
			intf->channel_list = intf->wchannels + set;
			intf->channels_ready = true;
			rv = send_channel_info_cmd(intf, intf->curr_channel);
		}

		if (rv) {
			/* Got an error somehow, just give up. */
@@ -3260,7 +3287,8 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
			       "Error sending channel information for channel"
			       " %d: %d\n", intf->curr_channel, rv);

			intf->curr_channel = IPMI_MAX_CHANNELS;
			intf->channel_list = intf->wchannels + set;
			intf->channels_ready = true;
			wake_up(&intf->waitq);
		}
	}
@@ -3268,6 +3296,53 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
	return;
}

/*
 * Must be holding intf->bmc_reg_mutex to call this.
 */
static int __scan_channels(ipmi_smi_t intf, struct ipmi_device_id *id)
{
	int rv;

	if (ipmi_version_major(id) > 1
			|| (ipmi_version_major(id) == 1
			    && ipmi_version_minor(id) >= 5)) {
		unsigned int set;

		/*
		 * Start scanning the channels to see what is
		 * available.
		 */
		set = !intf->curr_working_cset;
		intf->curr_working_cset = set;
		memset(&intf->wchannels[set], 0,
		       sizeof(struct ipmi_channel_set));

		intf->null_user_handler = channel_handler;
		intf->curr_channel = 0;
		rv = send_channel_info_cmd(intf, 0);
		if (rv) {
			dev_warn(intf->si_dev,
				 "Error sending channel information for channel 0, %d\n",
				 rv);
			return -EIO;
		}

		/* Wait for the channel info to be read. */
		wait_event(intf->waitq, intf->channels_ready);
		intf->null_user_handler = NULL;
	} else {
		unsigned int set = intf->curr_working_cset;

		/* Assume a single IPMB channel at zero. */
		intf->wchannels[set].c[0].medium = IPMI_CHANNEL_MEDIUM_IPMB;
		intf->wchannels[set].c[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB;
		intf->channel_list = intf->wchannels + set;
		intf->channels_ready = true;
	}

	return 0;
}

static void ipmi_poll(ipmi_smi_t intf)
{
	if (intf->handlers->poll)
@@ -3402,35 +3477,11 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
		goto out;
	}

	if (ipmi_version_major(&id) > 1
			|| (ipmi_version_major(&id) == 1
			    && ipmi_version_minor(&id) >= 5)) {
		/*
		 * Start scanning the channels to see what is
		 * available.
		 */
	mutex_lock(&intf->bmc_reg_mutex);
		intf->null_user_handler = channel_handler;
		intf->curr_channel = 0;
		rv = send_channel_info_cmd(intf, 0);
		if (rv) {
			printk(KERN_WARNING PFX
			       "Error sending channel information for channel"
			       " 0, %d\n", rv);
			goto out;
		}

		/* Wait for the channel info to be read. */
		wait_event(intf->waitq,
			   intf->curr_channel >= IPMI_MAX_CHANNELS);
		intf->null_user_handler = NULL;
	rv = __scan_channels(intf, &id);
	mutex_unlock(&intf->bmc_reg_mutex);
	} else {
		/* Assume a single IPMB channel at zero. */
		intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB;
		intf->channels[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB;
		intf->curr_channel = IPMI_MAX_CHANNELS;
	}
	if (rv)
		goto out;

	rv = add_proc_entries(intf, i);

@@ -4259,6 +4310,8 @@ static int handle_one_recv_msg(ipmi_smi_t intf,
		deliver_response(recv_msg);
	} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
		   && (msg->rsp[1] == IPMI_GET_MSG_CMD)) {
		struct ipmi_channel   *chans;

		/* It's from the receive queue. */
		chan = msg->rsp[3] & 0xf;
		if (chan >= IPMI_MAX_CHANNELS) {
@@ -4273,12 +4326,14 @@ static int handle_one_recv_msg(ipmi_smi_t intf,
		 * equal to or greater than IPMI_MAX_CHANNELS when all the
		 * channels for this interface have been initialized.
		 */
		if (intf->curr_channel < IPMI_MAX_CHANNELS) {
		if (!intf->channels_ready) {
			requeue = 0; /* Throw the message away */
			goto out;
		}

		switch (intf->channels[chan].medium) {
		chans = READ_ONCE(intf->channel_list)->c;

		switch (chans[chan].medium) {
		case IPMI_CHANNEL_MEDIUM_IPMB:
			if (msg->rsp[4] & 0x04) {
				/*
@@ -4315,9 +4370,8 @@ static int handle_one_recv_msg(ipmi_smi_t intf,
		default:
			/* Check for OEM Channels.  Clients had better
			   register for these commands. */
			if ((intf->channels[chan].medium
			     >= IPMI_CHANNEL_MEDIUM_OEM_MIN)
			    && (intf->channels[chan].medium
			if ((chans[chan].medium >= IPMI_CHANNEL_MEDIUM_OEM_MIN)
			    && (chans[chan].medium
				<= IPMI_CHANNEL_MEDIUM_OEM_MAX)) {
				requeue = handle_oem_get_msg_cmd(intf, msg);
			} else {
@@ -4479,15 +4533,14 @@ void ipmi_smi_msg_received(ipmi_smi_t intf,
		    && (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR)
		    && (msg->rsp[2] != IPMI_BUS_ERR)
		    && (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) {
			int chan = msg->rsp[3] & 0xf;
			int ch = msg->rsp[3] & 0xf;
			struct ipmi_channel *chans;

			/* Got an error sending the message, handle it. */
			if (chan >= IPMI_MAX_CHANNELS)
				; /* This shouldn't happen */
			else if ((intf->channels[chan].medium
				  == IPMI_CHANNEL_MEDIUM_8023LAN)
				 || (intf->channels[chan].medium
				     == IPMI_CHANNEL_MEDIUM_ASYNC))

			chans = READ_ONCE(intf->channel_list)->c;
			if ((chans[ch].medium == IPMI_CHANNEL_MEDIUM_8023LAN)
			    || (chans[ch].medium == IPMI_CHANNEL_MEDIUM_ASYNC))
				ipmi_inc_stat(intf, sent_lan_command_errs);
			else
				ipmi_inc_stat(intf, sent_ipmb_command_errs);