ipmi: kcs error0 delay
PATCH 8/9 ipmi: kcs error0 delay
BMCs can get into ERROR0 state while flashing new firmware, particularly while the BMC is erasing the next flash block, which may take a just under 2 seconds on a Dell PowerEdge 2800 (1.75 seconds typical), during which time the single-threaded firmware may not be able to process new commands. In particular, clearing OBF may not take effect immediately.
We want it to delay in ERROR0 after clearing OBF a bit waiting for OBF to actually be clear before proceeding.
This introduces a new return value from the LLDD's event loop, SI_SM_CALL_WITH_TICK_DELAY. This means the calling thread/timer should schedule_timeout() at least 1 tick, rather than busy-wait. This is a longer delay than SI_SM_CALL_WITH_DELAY, which is typically a 250us busy-wait.
drivers/char/ipmi/ipmi_kcs_sm.c +8 -0
@@ -41,6 +41,7 @@#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/string.h> +#include <linux/jiffies.h> #include <linux/ipmi_msgdefs.h> /* for completion codes */ #include "ipmi_si_sm.h"
@@ -99,6 +100,7 @@ enum kcs_states {#define IBF_RETRY_TIMEOUT 1000000#define OBF_RETRY_TIMEOUT 1000000#define MAX_ERROR_RETRIES 10 +#define ERROR0_OBF_WAIT_JIFFIES (2*HZ)struct si_sm_data {
@@ -115,6 +117,7 @@ struct si_sm_dataunsigned int error_retries;long ibf_timeout;long obf_timeout; +unsigned long error0_timeout; }; static unsigned int init_kcs_data(struct si_sm_data *kcs,
@@ -187,6 +190,7 @@ static inline void start_error_recovery(printk (KERN_DEBUG "ipmi_kcs_sm: kcs hosed: %s\n" , reason); kcs->state =KCS_HOSED ; }else { + kcs->error0_timeout = jiffies +ERROR0_OBF_WAIT_JIFFIES ; kcs->state =KCS_ERROR0 ; } }
@@ -423,6 +427,10 @@ static enum si_sm_result kcs_event(struccase KCS_ERROR0 :clear_obf (kcs, status); + status =read_status (kcs); +if (GET_STATUS_OBF (status))/* controller isn't responding */ +if (time_before (jiffies, kcs->error0_timeout)) +return SI_SM_CALL_WITH_TICK_DELAY ;write_cmd (kcs,KCS_GET_STATUS_ABORT ); kcs->state =KCS_ERROR1 ;break ;
drivers/char/ipmi/ipmi_si_intf.c +2 -1
@@ -1965,7 +1965,8 @@ static int try_get_dev_id(struct smi_inf smi_result = smi_info->handlers->event (smi_info->si_sm,0 );for (;;) { -if (smi_result ==SI_SM_CALL_WITH_DELAY ) { +if (smi_result ==SI_SM_CALL_WITH_DELAY || + smi_result ==SI_SM_CALL_WITH_TICK_DELAY ) {schedule_timeout_uninterruptible (1 ); smi_result = smi_info->handlers->event ( smi_info->si_sm,100 );
drivers/char/ipmi/ipmi_si_sm.h +1 -0
@@ -62,6 +62,7 @@ enum si_sm_result {SI_SM_CALL_WITHOUT_DELAY ,/* Call the driver again immediately */ SI_SM_CALL_WITH_DELAY ,/* Delay some before calling again. */ +SI_SM_CALL_WITH_TICK_DELAY ,/* Delay at least 1 tick before calling again. */ SI_SM_TRANSACTION_COMPLETE ,/* A transaction is finished. */ SI_SM_IDLE ,/* The SM is in idle state. */ SI_SM_HOSED, /* The hardware violated the state machine. */