mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-21 23:16:50 +08:00
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
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user