[Skiboot] [PATCH] bt: Cleanup bt state machine and fix SMS_ATN bug
Alistair Popple
alistair at popple.id.au
Tue Feb 17 13:34:18 AEDT 2015
Currenlty the SMS_ATN bit is processed by the bt interrupt
handler. This means that on systems without a functional bmc interrupt
(such as during early bring up) an SMS_ATN will not be noticed,
including the watchdog pre-timeout which results in the wdt expiring.
This patch moves the check for SMS_ATN into the main bt polling loop,
ensuring it is processed even without functional bmc interrupts. It
also cleans up the logic in bt_poll() to make it clearer and reduce
the number of lpc register reads.
Signed-off-by: Alistair Popple <alistair at popple.id.au>
---
hw/bt.c | 139 +++++++++++++++++++++++++++++++++++++---------------------------
1 file changed, 80 insertions(+), 59 deletions(-)
diff --git a/hw/bt.c b/hw/bt.c
index d5c71a8..2f4877d 100644
--- a/hw/bt.c
+++ b/hw/bt.c
@@ -120,7 +120,9 @@ static inline void bt_set_h_busy(bool value)
static inline bool bt_idle(void)
{
- return !(bt_inb(BT_CTRL) & (BT_CTRL_B_BUSY | BT_CTRL_H2B_ATN));
+ uint8_t bt_ctrl = bt_inb(BT_CTRL);
+
+ return !(bt_ctrl & BT_CTRL_B_BUSY) && !(bt_ctrl & BT_CTRL_H2B_ATN);
}
static inline void bt_set_state(enum bt_states next_state)
@@ -153,18 +155,17 @@ static void bt_reset_interface(void)
bt_init_interface();
}
-static bool bt_try_send_msg(void)
+/* Try and send a message from the message queue. Caller must hold
+ * bt.bt_lock and bt.msgq_lock and ensue the message queue is not
+ * empty. */
+static void bt_try_send_msg(void)
{
int i;
struct bt_msg *bt_msg;
struct ipmi_msg *ipmi_msg;
- lock(&bt.msgq_lock);
bt_msg = list_top(&bt.msgq, struct bt_msg, link);
- if (!bt_msg) {
- unlock(&bt.msgq_lock);
- return true;
- }
+ assert(bt_msg);
ipmi_msg = &bt_msg->ipmi_msg;
@@ -172,7 +173,7 @@ static bool bt_try_send_msg(void)
prerror("BT: Interface in an unexpected state, attempting reset\n");
bt_reset_interface();
unlock(&bt.msgq_lock);
- return false;
+ return;
}
/* Send the message */
@@ -197,9 +198,8 @@ static bool bt_try_send_msg(void)
bt_msg->tb = mftb();
bt_outb(BT_CTRL_H2B_ATN, BT_CTRL);
bt_set_state(BT_STATE_RESP_WAIT);
- unlock(&bt.msgq_lock);
- return true;
+ return;
}
static void bt_flush_msg(void)
@@ -208,7 +208,7 @@ static void bt_flush_msg(void)
bt_set_h_busy(false);
}
-static bool bt_get_resp(void)
+static void bt_get_resp(void)
{
int i;
struct bt_msg *bt_msg;
@@ -216,14 +216,11 @@ static bool bt_get_resp(void)
uint8_t resp_len, netfn, seq, cmd;
uint8_t cc = IPMI_CC_NO_ERROR;
- /* Wait for BMC to signal response */
+ /* Do this here not in the caller because we need to unlock
+ it here before calling ipmi_cmd_done() */
lock(&bt.msgq_lock);
- if (!(bt_inb(BT_CTRL) & BT_CTRL_B2H_ATN)) {
- unlock(&bt.msgq_lock);
- return true;
- }
- /* Indicate BMC that we are busy */
+ /* Indicate to the BMC that we are busy */
bt_set_h_busy(true);
/* Clear B2H_ATN and read pointer */
@@ -259,7 +256,7 @@ static bool bt_get_resp(void)
bt_flush_msg();
bt_set_state(BT_STATE_B_BUSY);
unlock(&bt.msgq_lock);
- return false;
+ return;
}
ipmi_msg = &bt_msg->ipmi_msg;
@@ -297,7 +294,7 @@ static bool bt_get_resp(void)
ipmi_cmd_done(cmd, netfn, cc, ipmi_msg);
/* Immediately send the next message */
- return false;
+ return;
}
static void bt_expire_old_msg(void)
@@ -322,53 +319,83 @@ static void bt_expire_old_msg(void)
unlock(&bt.msgq_lock);
}
+#if BT_QUEUE_DEBUG
+static void print_debug_queue_info(void)
+{
+ struct bt_msg *msg;
+ static bool printed = false;
+
+ if (!list_empty(&bt.msgq)) {
+ printed = false;
+ prlog(PR_DEBUG, "-------- BT Msg Queue --------\n");
+ list_for_each(&bt.msgq, msg, link) {
+ prlog(PR_DEBUG, "Seq: 0x%02x Cmd: 0x%02x\n", msg->seq, msg->ipmi_msg.cmd);
+ }
+ prlog(PR_DEBUG, "-----------------------------\n");
+ } else if (!printed) {
+ printed = true;
+ prlog(PR_DEBUG, "----- BT Msg Queue Empty -----\n");
+ }
+}
+#else
+static void print_debug_queue_info(void) {}
+#endif
+
static void bt_poll(struct timer *t __unused, void *data __unused)
{
- bool ret = true;
+ /* If we can't get the lock assume someone else will notice
+ * the new message and process it. */
+ if (try_lock(&bt.bt_lock)) {
+ while(true) {
+ uint8_t bt_ctrl = bt_inb(BT_CTRL);
- do {
+ print_debug_queue_info();
-#if BT_QUEUE_DEBUG
- struct bt_msg *msg;
- static bool printed = false;
- lock(&bt.msgq_lock);
- if (!list_empty(&bt.msgq)) {
- printed = false;
- prlog(PR_DEBUG, "-------- BT Msg Queue --------\n");
- list_for_each(&bt.msgq, msg, link) {
- prlog(PR_DEBUG, "Seq: 0x%02x Cmd: 0x%02x\n", msg->seq, msg->ipmi_msg.cmd);
- }
- prlog(PR_DEBUG, "-----------------------------\n");
- } else if (!printed) {
- printed = true;
- prlog(PR_DEBUG, "----- BT Msg Queue Empty -----\n");
- }
- unlock(&bt.msgq_lock);
-#endif
+ /* Is there a response waiting for us? */
+ if (bt.state == BT_STATE_RESP_WAIT &&
+ (bt_ctrl & BT_CTRL_B2H_ATN))
+ bt_get_resp();
+
+ if (bt.state == BT_STATE_B_BUSY && bt_idle())
+ bt_set_state(BT_STATE_IDLE);
- ret = true;
- if (try_lock(&bt.bt_lock)) {
bt_expire_old_msg();
- switch(bt.state) {
- case BT_STATE_IDLE:
- ret = bt_try_send_msg();
+ /* At this point we've made as much forward
+ progress as we can if we're in the middle
+ of sending a message, so if we're not in
+ the idle state we can just leave. */
+ if (bt.state != BT_STATE_IDLE) {
+ unlock(&bt.bt_lock);
break;
+ }
- case BT_STATE_RESP_WAIT:
- ret = bt_get_resp();
- break;
+ /* Check for sms_atn. This may add
+ * things to the message queue. */
+ if (bt_inb(BT_CTRL) & BT_CTRL_SMS_ATN) {
+ bt_outb(BT_CTRL_SMS_ATN, BT_CTRL);
+ ipmi_sms_attention();
+ }
- case BT_STATE_B_BUSY:
- if (bt_idle()) {
- bt_set_state(BT_STATE_IDLE);
- ret = false;
- }
+ lock(&bt.msgq_lock);
+ if (list_empty(&bt.msgq)) {
+ /* Nothing to do, bail. Ordering is
+ * important here - we need to unlock
+ * the bt_lock first otherwise a
+ * message could be added to the queue
+ * without us noticing.
+ */
+ unlock(&bt.bt_lock);
+ unlock(&bt.msgq_lock);
break;
}
- unlock(&bt.bt_lock);
+
+ /* There is a message in the queue. Start
+ * sending it. */
+ bt_try_send_msg();
+ unlock(&bt.msgq_lock);
}
- } while(!ret);
+ }
schedule_timer(&bt.poller,
bt.irq_ok ? TIMER_POLL : msecs_to_tb(BT_DEFAULT_POLL_MS));
@@ -418,18 +445,12 @@ static int bt_add_ipmi_msg(struct ipmi_msg *ipmi_msg)
void bt_irq(void)
{
uint8_t ireg = bt_inb(BT_INTMASK);
- uint8_t ctrl = bt_inb(BT_CTRL);
bt.irq_ok = true;
if (ireg & BT_INTMASK_B2H_IRQ) {
bt_outb(BT_INTMASK_B2H_IRQ | BT_INTMASK_B2H_IRQEN, BT_INTMASK);
bt_poll(NULL, NULL);
}
-
- if (ctrl & BT_CTRL_SMS_ATN) {
- bt_outb(BT_CTRL_SMS_ATN, BT_CTRL);
- ipmi_sms_attention();
- }
}
/*
--
1.8.3.2
More information about the Skiboot
mailing list