ipmi: Periodically check to see if irqs and messages are set right

The BMC can be reset while we are running; that means the interrupt
and event message buffer settings may be wrong.  So periodically
check to see if these values are correct, and fix them if they
are wrong.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
Tested-by: Tony Rex <tony.rex@ericsson.com>
Tested-by: Magnus Johansson E <magnus.e.johansson@ericsson.com>
This commit is contained in:
Corey Minyard 2014-11-19 04:03:26 -06:00
parent 6a11e5c67a
commit d9b7e4f717

View File

@ -92,12 +92,9 @@ enum si_intf_state {
SI_GETTING_FLAGS, SI_GETTING_FLAGS,
SI_GETTING_EVENTS, SI_GETTING_EVENTS,
SI_CLEARING_FLAGS, SI_CLEARING_FLAGS,
SI_CLEARING_FLAGS_THEN_SET_IRQ,
SI_GETTING_MESSAGES, SI_GETTING_MESSAGES,
SI_ENABLE_INTERRUPTS1, SI_CHECKING_ENABLES,
SI_ENABLE_INTERRUPTS2, SI_SETTING_ENABLES
SI_DISABLE_INTERRUPTS1,
SI_DISABLE_INTERRUPTS2
/* FIXME - add watchdog stuff. */ /* FIXME - add watchdog stuff. */
}; };
@ -260,6 +257,11 @@ struct smi_info {
*/ */
bool interrupt_disabled; bool interrupt_disabled;
/*
* Does the BMC support events?
*/
bool supports_event_msg_buff;
/* From the get device id response... */ /* From the get device id response... */
struct ipmi_device_id device_id; struct ipmi_device_id device_id;
@ -386,22 +388,7 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info)
return rv; return rv;
} }
static void start_enable_irq(struct smi_info *smi_info) static void start_check_enables(struct smi_info *smi_info)
{
unsigned char msg[2];
/*
* If we are enabling interrupts, we have to tell the
* BMC to use them.
*/
msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD;
smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2);
smi_info->si_state = SI_ENABLE_INTERRUPTS1;
}
static void start_disable_irq(struct smi_info *smi_info)
{ {
unsigned char msg[2]; unsigned char msg[2];
@ -409,7 +396,7 @@ static void start_disable_irq(struct smi_info *smi_info)
msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD;
smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2); smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2);
smi_info->si_state = SI_DISABLE_INTERRUPTS1; smi_info->si_state = SI_CHECKING_ENABLES;
} }
static void start_clear_flags(struct smi_info *smi_info) static void start_clear_flags(struct smi_info *smi_info)
@ -467,8 +454,8 @@ static void smi_mod_timer(struct smi_info *smi_info, unsigned long new_val)
static inline bool disable_si_irq(struct smi_info *smi_info) static inline bool disable_si_irq(struct smi_info *smi_info)
{ {
if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { if ((smi_info->irq) && (!smi_info->interrupt_disabled)) {
start_disable_irq(smi_info);
smi_info->interrupt_disabled = true; smi_info->interrupt_disabled = true;
start_check_enables(smi_info);
return true; return true;
} }
return false; return false;
@ -477,8 +464,8 @@ static inline bool disable_si_irq(struct smi_info *smi_info)
static inline bool enable_si_irq(struct smi_info *smi_info) static inline bool enable_si_irq(struct smi_info *smi_info)
{ {
if ((smi_info->irq) && (smi_info->interrupt_disabled)) { if ((smi_info->irq) && (smi_info->interrupt_disabled)) {
start_enable_irq(smi_info);
smi_info->interrupt_disabled = false; smi_info->interrupt_disabled = false;
start_check_enables(smi_info);
return true; return true;
} }
return false; return false;
@ -538,6 +525,36 @@ static void handle_flags(struct smi_info *smi_info)
smi_info->si_state = SI_NORMAL; smi_info->si_state = SI_NORMAL;
} }
/*
* Global enables we care about.
*/
#define GLOBAL_ENABLES_MASK (IPMI_BMC_EVT_MSG_BUFF | IPMI_BMC_RCV_MSG_INTR | \
IPMI_BMC_EVT_MSG_INTR)
static u8 current_global_enables(struct smi_info *smi_info, u8 base)
{
u8 enables = 0;
if (smi_info->supports_event_msg_buff)
enables |= IPMI_BMC_EVT_MSG_BUFF;
else
enables &= ~IPMI_BMC_EVT_MSG_BUFF;
if (smi_info->irq && !smi_info->interrupt_disabled)
enables |= IPMI_BMC_RCV_MSG_INTR;
else
enables &= ~IPMI_BMC_RCV_MSG_INTR;
if (smi_info->supports_event_msg_buff &&
smi_info->irq && !smi_info->interrupt_disabled)
enables |= IPMI_BMC_EVT_MSG_INTR;
else
enables &= ~IPMI_BMC_EVT_MSG_INTR;
return enables;
}
static void handle_transaction_done(struct smi_info *smi_info) static void handle_transaction_done(struct smi_info *smi_info)
{ {
struct ipmi_smi_msg *msg; struct ipmi_smi_msg *msg;
@ -592,7 +609,6 @@ static void handle_transaction_done(struct smi_info *smi_info)
} }
case SI_CLEARING_FLAGS: case SI_CLEARING_FLAGS:
case SI_CLEARING_FLAGS_THEN_SET_IRQ:
{ {
unsigned char msg[3]; unsigned char msg[3];
@ -603,10 +619,7 @@ static void handle_transaction_done(struct smi_info *smi_info)
dev_warn(smi_info->dev, dev_warn(smi_info->dev,
"Error clearing flags: %2.2x\n", msg[2]); "Error clearing flags: %2.2x\n", msg[2]);
} }
if (smi_info->si_state == SI_CLEARING_FLAGS_THEN_SET_IRQ) smi_info->si_state = SI_NORMAL;
start_enable_irq(smi_info);
else
smi_info->si_state = SI_NORMAL;
break; break;
} }
@ -686,9 +699,10 @@ static void handle_transaction_done(struct smi_info *smi_info)
break; break;
} }
case SI_ENABLE_INTERRUPTS1: case SI_CHECKING_ENABLES:
{ {
unsigned char msg[4]; unsigned char msg[4];
u8 enables;
/* We got the flags from the SMI, now handle them. */ /* We got the flags from the SMI, now handle them. */
smi_info->handlers->get_result(smi_info->si_sm, msg, 4); smi_info->handlers->get_result(smi_info->si_sm, msg, 4);
@ -698,72 +712,50 @@ static void handle_transaction_done(struct smi_info *smi_info)
dev_warn(smi_info->dev, dev_warn(smi_info->dev,
"Maybe ok, but ipmi might run very slowly.\n"); "Maybe ok, but ipmi might run very slowly.\n");
smi_info->si_state = SI_NORMAL; smi_info->si_state = SI_NORMAL;
} else { break;
}
enables = current_global_enables(smi_info, 0);
if (enables != (msg[3] & GLOBAL_ENABLES_MASK)) {
/* Enables are not correct, fix them. */
msg[0] = (IPMI_NETFN_APP_REQUEST << 2); msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
msg[2] = (msg[3] | msg[2] = enables | (msg[3] & ~GLOBAL_ENABLES_MASK);
IPMI_BMC_RCV_MSG_INTR |
IPMI_BMC_EVT_MSG_INTR);
smi_info->handlers->start_transaction( smi_info->handlers->start_transaction(
smi_info->si_sm, msg, 3); smi_info->si_sm, msg, 3);
smi_info->si_state = SI_ENABLE_INTERRUPTS2; smi_info->si_state = SI_SETTING_ENABLES;
} } else if (smi_info->supports_event_msg_buff) {
break; smi_info->curr_msg = ipmi_alloc_smi_msg();
} if (!smi_info->curr_msg) {
smi_info->si_state = SI_NORMAL;
case SI_ENABLE_INTERRUPTS2: break;
{ }
unsigned char msg[4]; start_getting_msg_queue(smi_info);
} else {
/* We got the flags from the SMI, now handle them. */
smi_info->handlers->get_result(smi_info->si_sm, msg, 4);
if (msg[2] != 0) {
dev_warn(smi_info->dev,
"Couldn't set irq info: %x.\n", msg[2]);
dev_warn(smi_info->dev,
"Maybe ok, but ipmi might run very slowly.\n");
} else
smi_info->interrupt_disabled = false;
/* We enabled interrupts, flags may be pending. */
handle_flags(smi_info);
break;
}
case SI_DISABLE_INTERRUPTS1:
{
unsigned char msg[4];
/* We got the flags from the SMI, now handle them. */
smi_info->handlers->get_result(smi_info->si_sm, msg, 4);
if (msg[2] != 0) {
dev_warn(smi_info->dev, "Could not disable interrupts"
", failed get.\n");
smi_info->si_state = SI_NORMAL; smi_info->si_state = SI_NORMAL;
} else {
msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
msg[2] = (msg[3] &
~(IPMI_BMC_RCV_MSG_INTR |
IPMI_BMC_EVT_MSG_INTR));
smi_info->handlers->start_transaction(
smi_info->si_sm, msg, 3);
smi_info->si_state = SI_DISABLE_INTERRUPTS2;
} }
break; break;
} }
case SI_DISABLE_INTERRUPTS2: case SI_SETTING_ENABLES:
{ {
unsigned char msg[4]; unsigned char msg[4];
/* We got the flags from the SMI, now handle them. */
smi_info->handlers->get_result(smi_info->si_sm, msg, 4); smi_info->handlers->get_result(smi_info->si_sm, msg, 4);
if (msg[2] != 0) { if (msg[2] != 0)
dev_warn(smi_info->dev, "Could not disable interrupts" dev_warn(smi_info->dev,
", failed set.\n"); "Could not set the global enables: 0x%x.\n",
msg[2]);
if (smi_info->supports_event_msg_buff) {
smi_info->curr_msg = ipmi_alloc_smi_msg();
if (!smi_info->curr_msg) {
smi_info->si_state = SI_NORMAL;
break;
}
start_getting_msg_queue(smi_info);
} else {
smi_info->si_state = SI_NORMAL;
} }
smi_info->si_state = SI_NORMAL;
break; break;
} }
} }
@ -859,19 +851,21 @@ static enum si_sm_result smi_event_handler(struct smi_info *smi_info,
*/ */
atomic_set(&smi_info->req_events, 0); atomic_set(&smi_info->req_events, 0);
smi_info->curr_msg = ipmi_alloc_smi_msg(); /*
if (!smi_info->curr_msg) * Take this opportunity to check the interrupt and
goto out; * message enable state for the BMC. The BMC can be
* asynchronously reset, and may thus get interrupts
* disable and messages disabled.
*/
if (smi_info->supports_event_msg_buff || smi_info->irq) {
start_check_enables(smi_info);
} else {
smi_info->curr_msg = alloc_msg_handle_irq(smi_info);
if (!smi_info->curr_msg)
goto out;
smi_info->curr_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); start_getting_events(smi_info);
smi_info->curr_msg->data[1] = IPMI_READ_EVENT_MSG_BUFFER_CMD; }
smi_info->curr_msg->data_size = 2;
smi_info->handlers->start_transaction(
smi_info->si_sm,
smi_info->curr_msg->data,
smi_info->curr_msg->data_size);
smi_info->si_state = SI_GETTING_EVENTS;
goto restart; goto restart;
} }
out: out:
@ -2918,9 +2912,11 @@ static int try_enable_event_buffer(struct smi_info *smi_info)
goto out; goto out;
} }
if (resp[3] & IPMI_BMC_EVT_MSG_BUFF) if (resp[3] & IPMI_BMC_EVT_MSG_BUFF) {
/* buffer is already enabled, nothing to do. */ /* buffer is already enabled, nothing to do. */
smi_info->supports_event_msg_buff = true;
goto out; goto out;
}
msg[0] = IPMI_NETFN_APP_REQUEST << 2; msg[0] = IPMI_NETFN_APP_REQUEST << 2;
msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
@ -2953,6 +2949,9 @@ static int try_enable_event_buffer(struct smi_info *smi_info)
* that the event buffer is not supported. * that the event buffer is not supported.
*/ */
rv = -ENOENT; rv = -ENOENT;
else
smi_info->supports_event_msg_buff = true;
out: out:
kfree(resp); kfree(resp);
return rv; return rv;
@ -3392,9 +3391,15 @@ static int try_smi_init(struct smi_info *new_smi)
* timer to avoid racing with the timer. * timer to avoid racing with the timer.
*/ */
start_clear_flags(new_smi); start_clear_flags(new_smi);
/* IRQ is defined to be set when non-zero. */
if (new_smi->irq) /*
new_smi->si_state = SI_CLEARING_FLAGS_THEN_SET_IRQ; * IRQ is defined to be set when non-zero. req_events will
* cause a global flags check that will enable interrupts.
*/
if (new_smi->irq) {
new_smi->interrupt_disabled = false;
atomic_set(&new_smi->req_events, 1);
}
if (!new_smi->dev) { if (!new_smi->dev) {
/* /*