Skip to content

Commit

Permalink
ipmi: ensure forward progress on ipmi_queue_msg_sync()
Browse files Browse the repository at this point in the history
[ Upstream commit f01cd77 ]

BT responses are handled using a timer doing the polling. To hope to
get an answer to an IPMI synchronous message, the timer needs to run.

We can't just check all timers though as there may be a timer that
wants a lock that's held by a code path calling ipmi_queue_msg_sync(),
and if we did enforce that as a requirement, it's a pretty subtle
API that is asking to be broken.

So, if we just run a poll function to crank anything that the IPMI
backend needs, then we should be fine.

This issue shows up very quickly under QEMU when loading the first
flash resource with the IPMI HIOMAP backend.

Reported-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Stewart Smith <stewart@linux.ibm.com>
Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
Signed-off-by: Oliver O'Halloran <oohall@gmail.com>
Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
  • Loading branch information
stewartsmith authored and oohal committed May 10, 2019
1 parent 290fbf3 commit 2cc067e
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 1 deletion.
12 changes: 11 additions & 1 deletion core/ipmi.c
Expand Up @@ -182,8 +182,18 @@ void ipmi_queue_msg_sync(struct ipmi_msg *msg)
ipmi_queue_msg_head(msg);
unlock(&sync_lock);

while (sync_msg == msg)
/*
* BT response handling relies on a timer. We can't just run all
* timers because we may have been called with a lock that a timer
* wants, and they're generally not written to cope with that.
* So, just run whatever the IPMI backend needs to make forward
* progress.
*/
while (sync_msg == msg) {
if (msg->backend->poll)
msg->backend->poll();
time_wait_ms(10);
}
}

static void ipmi_read_event_complete(struct ipmi_msg *msg)
Expand Down
6 changes: 6 additions & 0 deletions hw/bt.c
Expand Up @@ -526,6 +526,11 @@ static void bt_poll(struct timer *t __unused, void *data __unused,
bt.irq_ok ? TIMER_POLL : msecs_to_tb(BT_DEFAULT_POLL_MS));
}

static void bt_ipmi_poll(void)
{
bt_poll(NULL, NULL, mftb());
}

static void bt_add_msg(struct bt_msg *bt_msg)
{
bt_msg->tb = 0;
Expand Down Expand Up @@ -647,6 +652,7 @@ static struct ipmi_backend bt_backend = {
.queue_msg_head = bt_add_ipmi_msg_head,
.dequeue_msg = bt_del_ipmi_msg,
.disable_retry = bt_disable_ipmi_msg_retry,
.poll = bt_ipmi_poll,
};

static struct lpc_client bt_lpc_client = {
Expand Down
2 changes: 2 additions & 0 deletions hw/fsp/fsp-ipmi.c
Expand Up @@ -254,6 +254,8 @@ static struct ipmi_backend fsp_ipmi_backend = {
.queue_msg = fsp_ipmi_queue_msg,
.queue_msg_head = fsp_ipmi_queue_msg_head,
.dequeue_msg = fsp_ipmi_dequeue_msg,
/* FIXME if ever use ipmi_queue_msg_sync on FSP */
.poll = NULL,
};

static bool fsp_ipmi_send_response(uint32_t cmd)
Expand Down
9 changes: 9 additions & 0 deletions include/ipmi.h
Expand Up @@ -182,6 +182,15 @@ struct ipmi_backend {
int (*queue_msg_head)(struct ipmi_msg *);
int (*dequeue_msg)(struct ipmi_msg *);
void (*disable_retry)(struct ipmi_msg *);
/*
* When processing a synchronous IPMI message, pollers may not run, and
* neither may timers (as the synchronous IPMI message may be being
* done with locks held, which a timer may then try to also take).
*
* So, ensure we have a way to drive any state machines that an IPMI
* backend may neeed to crank to ensure forward progress.
*/
void (*poll)(void);
};

extern struct ipmi_backend *ipmi_backend;
Expand Down

0 comments on commit 2cc067e

Please sign in to comment.