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
This commit is contained in:
Linus Torvalds
2026-02-26 14:34:21 -08:00
4 changed files with 126 additions and 61 deletions

View File

@@ -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;
}

View File

@@ -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. */
/* Unknown address type. */
ipmi_inc_stat(intf, sent_invalid_commands);
rv = -EINVAL;
}
if (rv) {
if (!rv) {
dev_dbg(intf->si_dev, "Send: %*ph\n",
smi_msg->data_size, smi_msg->data);
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);
} else {
dev_dbg(intf->si_dev, "Send: %*ph\n",
smi_msg->data_size, smi_msg->data);
smi_send(intf, intf->handlers, smi_msg, priority);
}
if (!run_to_completion)
mutex_unlock(&intf->users_mutex);
return rv;
}
@@ -3949,12 +3994,12 @@ 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);
/*
* We used the message, so return the value that
* causes it to not be freed or queued.
*/
rv = -1;
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.
*/
rv = -1;
} else if (!IS_ERR(recv_msg)) {
/* Extract the source address from the data. */
ipmb_addr = (struct ipmi_ipmb_addr *) &recv_msg->addr;
@@ -4028,12 +4073,12 @@ 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);
/*
* We used the message, so return the value that
* causes it to not be freed or queued.
*/
rv = -1;
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.
*/
rv = -1;
} else if (!IS_ERR(recv_msg)) {
/* Extract the source address from the data. */
daddr = (struct ipmi_ipmb_direct_addr *)&recv_msg->addr;
@@ -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,12 +4271,12 @@ 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);
/*
* We used the message, so return the value that
* causes it to not be freed or queued.
*/
rv = -1;
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.
*/
rv = -1;
} else if (!IS_ERR(recv_msg)) {
/* Extract the source address from the data. */
lan_addr = (struct ipmi_lan_addr *) &recv_msg->addr;
@@ -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 @@ restart:
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 @@ restart:
if (newmsg->recv_msg)
deliver_err_response(intf,
newmsg->recv_msg, cc);
else
ipmi_free_smi_msg(newmsg);
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);

View File

@@ -809,6 +809,12 @@ restart:
*/
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.
*/
BUG_ON(smi_info->waiting_msg);
smi_info->waiting_msg = msg;
check_start_timer_thread(smi_info);
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);

View File

@@ -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",
},