Commit c45be7c4 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'for-linus-7.0-1' of https://github.com/cminyard/linux-ipmi

Pull IPMI driver fixes from Corey Minyard:
 "This mostly revolves around getting the driver to behave when the IPMI
  device misbehaves. Past attempts have not worked very well because I
  didn't have hardware I could make do this, and AI was fairly useless
  for help on this.

  So I modified qemu and my test suite so I could reproduce a
  misbehaving IPMI device, and with that I was able to fix the issues"

* tag 'for-linus-7.0-1' of https://github.com/cminyard/linux-ipmi:
  ipmi:si: Fix check for a misbehaving BMC
  ipmi:msghandler: Handle error returns from the SMI sender
  ipmi:si: Don't block module unload if the BMC is messed up
  ipmi:si: Use a long timeout when the BMC is misbehaving
  ipmi:si: Handle waiting messages when BMC failure detected
  ipmi:ls2k: Make ipmi_ls2k_platform_driver static
  ipmi: ipmb: initialise event handler read bytes
  ipmi: Consolidate the run to completion checking for xmit msgs lock
  ipmi: Fix use-after-free and list corruption on sender error
parents 3f4a08e6 cae66f1a
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -202,11 +202,16 @@ static int ipmi_ipmb_slave_cb(struct i2c_client *client,
		break;

	case I2C_SLAVE_READ_REQUESTED:
		*val = 0xff;
		ipmi_ipmb_check_msg_done(iidev);
		break;

	case I2C_SLAVE_STOP:
		ipmi_ipmb_check_msg_done(iidev);
		break;

	case I2C_SLAVE_READ_PROCESSED:
		*val = 0xff;
		break;
	}

+95 −46
Original line number Diff line number Diff line
@@ -602,6 +602,22 @@ static int __ipmi_bmc_register(struct ipmi_smi *intf,
static int __scan_channels(struct ipmi_smi *intf,
				struct ipmi_device_id *id, bool rescan);

static void ipmi_lock_xmit_msgs(struct ipmi_smi *intf, int run_to_completion,
				unsigned long *flags)
{
	if (run_to_completion)
		return;
	spin_lock_irqsave(&intf->xmit_msgs_lock, *flags);
}

static void ipmi_unlock_xmit_msgs(struct ipmi_smi *intf, int run_to_completion,
				  unsigned long *flags)
{
	if (run_to_completion)
		return;
	spin_unlock_irqrestore(&intf->xmit_msgs_lock, *flags);
}

static void free_ipmi_user(struct kref *ref)
{
	struct ipmi_user *user = container_of(ref, struct ipmi_user, refcount);
@@ -1869,21 +1885,32 @@ static struct ipmi_smi_msg *smi_add_send_msg(struct ipmi_smi *intf,
	return smi_msg;
}

static void smi_send(struct ipmi_smi *intf,
static int smi_send(struct ipmi_smi *intf,
		     const struct ipmi_smi_handlers *handlers,
		     struct ipmi_smi_msg *smi_msg, int priority)
{
	int run_to_completion = READ_ONCE(intf->run_to_completion);
	unsigned long flags = 0;
	int rv = 0;

	if (!run_to_completion)
		spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
	ipmi_lock_xmit_msgs(intf, run_to_completion, &flags);
	smi_msg = smi_add_send_msg(intf, smi_msg, priority);
	if (!run_to_completion)
		spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
	ipmi_unlock_xmit_msgs(intf, run_to_completion, &flags);

	if (smi_msg)
		handlers->sender(intf->send_info, smi_msg);
	if (smi_msg) {
		rv = handlers->sender(intf->send_info, smi_msg);
		if (rv) {
			ipmi_lock_xmit_msgs(intf, run_to_completion, &flags);
			intf->curr_msg = NULL;
			ipmi_unlock_xmit_msgs(intf, run_to_completion, &flags);
			/*
			 * Something may have been added to the transmit
			 * queue, so schedule a check for that.
			 */
			queue_work(system_wq, &intf->smi_work);
		}
	}
	return rv;
}

static bool is_maintenance_mode_cmd(struct kernel_ipmi_msg *msg)
@@ -2296,6 +2323,7 @@ static int i_ipmi_request(struct ipmi_user *user,
	struct ipmi_recv_msg *recv_msg;
	int run_to_completion = READ_ONCE(intf->run_to_completion);
	int rv = 0;
	bool in_seq_table = false;

	if (supplied_recv) {
		recv_msg = supplied_recv;
@@ -2349,33 +2377,50 @@ static int i_ipmi_request(struct ipmi_user *user,
		rv = i_ipmi_req_ipmb(intf, addr, msgid, msg, smi_msg, recv_msg,
				     source_address, source_lun,
				     retries, retry_time_ms);
		in_seq_table = true;
	} else if (is_ipmb_direct_addr(addr)) {
		rv = i_ipmi_req_ipmb_direct(intf, addr, msgid, msg, smi_msg,
					    recv_msg, source_lun);
	} else if (is_lan_addr(addr)) {
		rv = i_ipmi_req_lan(intf, addr, msgid, msg, smi_msg, recv_msg,
				    source_lun, retries, retry_time_ms);
		in_seq_table = true;
	} else {
		/* Unknown address type. */
		ipmi_inc_stat(intf, sent_invalid_commands);
		rv = -EINVAL;
	}

	if (rv) {
out_err:
		if (!supplied_smi)
			ipmi_free_smi_msg(smi_msg);
		if (!supplied_recv)
			ipmi_free_recv_msg(recv_msg);
	} else {
	if (!rv) {
		dev_dbg(intf->si_dev, "Send: %*ph\n",
			smi_msg->data_size, smi_msg->data);

		smi_send(intf, intf->handlers, smi_msg, priority);
		rv = smi_send(intf, intf->handlers, smi_msg, priority);
		if (rv != IPMI_CC_NO_ERROR)
			/* smi_send() returns an IPMI err, return a Linux one. */
			rv = -EIO;
		if (rv && in_seq_table) {
			/*
			 * If it's in the sequence table, it will be
			 * retried later, so ignore errors.
			 */
			rv = 0;
			/* But we need to fix the timeout. */
			intf_start_seq_timer(intf, smi_msg->msgid);
			ipmi_free_smi_msg(smi_msg);
			smi_msg = NULL;
		}
	}
out_err:
	if (!run_to_completion)
		mutex_unlock(&intf->users_mutex);

	if (rv) {
		if (!supplied_smi)
			ipmi_free_smi_msg(smi_msg);
		if (!supplied_recv)
			ipmi_free_recv_msg(recv_msg);
	}
	return rv;
}

@@ -3949,7 +3994,7 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf,
		dev_dbg(intf->si_dev, "Invalid command: %*ph\n",
			msg->data_size, msg->data);

		smi_send(intf, intf->handlers, msg, 0);
		if (smi_send(intf, intf->handlers, msg, 0) == IPMI_CC_NO_ERROR)
			/*
			 * We used the message, so return the value that
			 * causes it to not be freed or queued.
@@ -4028,7 +4073,7 @@ static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf,
		msg->data[4] = IPMI_INVALID_CMD_COMPLETION_CODE;
		msg->data_size = 5;

		smi_send(intf, intf->handlers, msg, 0);
		if (smi_send(intf, intf->handlers, msg, 0) == IPMI_CC_NO_ERROR)
			/*
			 * We used the message, so return the value that
			 * causes it to not be freed or queued.
@@ -4173,7 +4218,7 @@ static int handle_lan_get_msg_cmd(struct ipmi_smi *intf,
				  struct ipmi_smi_msg *msg)
{
	struct cmd_rcvr          *rcvr;
	int                      rv = 0;
	int                      rv = 0; /* Free by default */
	unsigned char            netfn;
	unsigned char            cmd;
	unsigned char            chan;
@@ -4226,7 +4271,7 @@ static int handle_lan_get_msg_cmd(struct ipmi_smi *intf,
		dev_dbg(intf->si_dev, "Invalid command: %*ph\n",
			msg->data_size, msg->data);

		smi_send(intf, intf->handlers, msg, 0);
		if (smi_send(intf, intf->handlers, msg, 0) == IPMI_CC_NO_ERROR)
			/*
			 * We used the message, so return the value that
			 * causes it to not be freed or queued.
@@ -4824,8 +4869,7 @@ static void smi_work(struct work_struct *t)
	 * message delivery.
	 */
restart:
	if (!run_to_completion)
		spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
	ipmi_lock_xmit_msgs(intf, run_to_completion, &flags);
	if (intf->curr_msg == NULL && !intf->in_shutdown) {
		struct list_head *entry = NULL;

@@ -4841,8 +4885,7 @@ static void smi_work(struct work_struct *t)
			intf->curr_msg = newmsg;
		}
	}
	if (!run_to_completion)
		spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
	ipmi_unlock_xmit_msgs(intf, run_to_completion, &flags);

	if (newmsg) {
		cc = intf->handlers->sender(intf->send_info, newmsg);
@@ -4850,8 +4893,11 @@ static void smi_work(struct work_struct *t)
			if (newmsg->recv_msg)
				deliver_err_response(intf,
						     newmsg->recv_msg, cc);
			else
			ipmi_lock_xmit_msgs(intf, run_to_completion, &flags);
			intf->curr_msg = NULL;
			ipmi_unlock_xmit_msgs(intf, run_to_completion, &flags);
			ipmi_free_smi_msg(newmsg);
			newmsg = NULL;
			goto restart;
		}
	}
@@ -4919,16 +4965,14 @@ void ipmi_smi_msg_received(struct ipmi_smi *intf,
		spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock,
				       flags);

	if (!run_to_completion)
		spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
	ipmi_lock_xmit_msgs(intf, run_to_completion, &flags);
	/*
	 * We can get an asynchronous event or receive message in addition
	 * to commands we send.
	 */
	if (msg == intf->curr_msg)
		intf->curr_msg = NULL;
	if (!run_to_completion)
		spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
	ipmi_unlock_xmit_msgs(intf, run_to_completion, &flags);

	if (run_to_completion)
		smi_work(&intf->smi_work);
@@ -5041,7 +5085,12 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
				ipmi_inc_stat(intf,
					      retransmitted_ipmb_commands);

			smi_send(intf, intf->handlers, smi_msg, 0);
			/* If this fails we'll retry later or timeout. */
			if (smi_send(intf, intf->handlers, smi_msg, 0) != IPMI_CC_NO_ERROR) {
				/* But fix the timeout. */
				intf_start_seq_timer(intf, smi_msg->msgid);
				ipmi_free_smi_msg(smi_msg);
			}
		} else
			ipmi_free_smi_msg(smi_msg);

+24 −13
Original line number Diff line number Diff line
@@ -809,6 +809,12 @@ static enum si_sm_result smi_event_handler(struct smi_info *smi_info,
			 */
			return_hosed_msg(smi_info, IPMI_BUS_ERR);
		}
		if (smi_info->waiting_msg != NULL) {
			/* Also handle if there was a message waiting. */
			smi_info->curr_msg = smi_info->waiting_msg;
			smi_info->waiting_msg = NULL;
			return_hosed_msg(smi_info, IPMI_BUS_ERR);
		}
		smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_HOSED);
		goto out;
	}
@@ -918,9 +924,14 @@ static int sender(void *send_info, struct ipmi_smi_msg *msg)
{
	struct smi_info   *smi_info = send_info;
	unsigned long     flags;
	int rv = IPMI_CC_NO_ERROR;

	debug_timestamp(smi_info, "Enqueue");

	/*
	 * Check here for run to completion mode.  A check under lock is
	 * later.
	 */
	if (smi_info->si_state == SI_HOSED)
		return IPMI_BUS_ERR;

@@ -934,18 +945,15 @@ static int sender(void *send_info, struct ipmi_smi_msg *msg)
	}

	spin_lock_irqsave(&smi_info->si_lock, flags);
	/*
	 * The following two lines don't need to be under the lock for
	 * the lock's sake, but they do need SMP memory barriers to
	 * avoid getting things out of order.  We are already claiming
	 * the lock, anyway, so just do it under the lock to avoid the
	 * ordering problem.
	 */
	if (smi_info->si_state == SI_HOSED) {
		rv = IPMI_BUS_ERR;
	} else {
		BUG_ON(smi_info->waiting_msg);
		smi_info->waiting_msg = msg;
		check_start_timer_thread(smi_info);
	}
	spin_unlock_irqrestore(&smi_info->si_lock, flags);
	return IPMI_CC_NO_ERROR;
	return rv;
}

static void set_run_to_completion(void *send_info, bool i_run_to_completion)
@@ -1113,7 +1121,9 @@ static void smi_timeout(struct timer_list *t)
		     * SI_USEC_PER_JIFFY);
	smi_result = smi_event_handler(smi_info, time_diff);

	if ((smi_info->io.irq) && (!smi_info->interrupt_disabled)) {
	if (smi_info->si_state == SI_HOSED) {
		timeout = jiffies + SI_TIMEOUT_HOSED;
	} else if ((smi_info->io.irq) && (!smi_info->interrupt_disabled)) {
		/* Running with interrupts, only do long timeouts. */
		timeout = jiffies + SI_TIMEOUT_JIFFIES;
		smi_inc_stat(smi_info, long_timeouts);
@@ -2226,7 +2236,8 @@ static void wait_msg_processed(struct smi_info *smi_info)
	unsigned long jiffies_now;
	long time_diff;

	while (smi_info->curr_msg || (smi_info->si_state != SI_NORMAL)) {
	while (smi_info->si_state != SI_HOSED &&
		    (smi_info->curr_msg || (smi_info->si_state != SI_NORMAL))) {
		jiffies_now = jiffies;
		time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies)
		     * SI_USEC_PER_JIFFY);
+1 −1
Original line number Diff line number Diff line
@@ -168,7 +168,7 @@ static void ipmi_ls2k_remove(struct platform_device *pdev)
	ipmi_si_remove_by_dev(&pdev->dev);
}

struct platform_driver ipmi_ls2k_platform_driver = {
static struct platform_driver ipmi_ls2k_platform_driver = {
	.driver = {
		.name = "ls2k-ipmi-si",
	},