#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: rf_states.c,v 1.53 2021/07/23 02:35:14 oster Exp $");
#include <sys/errno.h>
#include "rf_archs.h"
#include "rf_threadstuff.h"
#include "rf_raid.h"
#include "rf_dag.h"
#include "rf_desc.h"
#include "rf_aselect.h"
#include "rf_general.h"
#include "rf_states.h"
#include "rf_dagutils.h"
#include "rf_driver.h"
#include "rf_engine.h"
#include "rf_map.h"
#include "rf_etimer.h"
#include "rf_kintf.h"
#include "rf_paritymap.h"
#ifndef RF_DEBUG_STATES
#define RF_DEBUG_STATES 0
#endif
#if RF_DEBUG_STATES
static char *
StateName(RF_AccessState_t state)
{
switch (state) {
case rf_QuiesceState:return "QuiesceState";
case rf_MapState:
return "MapState";
case rf_LockState:
return "LockState";
case rf_CreateDAGState:
return "CreateDAGState";
case rf_ExecuteDAGState:
return "ExecuteDAGState";
case rf_ProcessDAGState:
return "ProcessDAGState";
case rf_CleanupState:
return "CleanupState";
case rf_LastState:
return "LastState";
case rf_IncrAccessesCountState:
return "IncrAccessesCountState";
case rf_DecrAccessesCountState:
return "DecrAccessesCountState";
default:
return "!!! UnnamedState !!!";
}
}
#endif
void
rf_ContinueRaidAccess(void *v)
{
RF_RaidAccessDesc_t *desc = v;
int suspended = RF_FALSE;
int current_state_index = desc->state;
RF_AccessState_t current_state = desc->states[current_state_index];
#if RF_DEBUG_STATES
int unit = desc->raidPtr->raidid;
#endif
do {
current_state_index = desc->state;
current_state = desc->states[current_state_index];
switch (current_state) {
case rf_QuiesceState:
suspended = rf_State_Quiesce(desc);
break;
case rf_IncrAccessesCountState:
suspended = rf_State_IncrAccessCount(desc);
break;
case rf_MapState:
suspended = rf_State_Map(desc);
break;
case rf_LockState:
suspended = rf_State_Lock(desc);
break;
case rf_CreateDAGState:
suspended = rf_State_CreateDAG(desc);
break;
case rf_ExecuteDAGState:
suspended = rf_State_ExecuteDAG(desc);
break;
case rf_ProcessDAGState:
suspended = rf_State_ProcessDAG(desc);
break;
case rf_CleanupState:
suspended = rf_State_Cleanup(desc);
break;
case rf_DecrAccessesCountState:
suspended = rf_State_DecrAccessCount(desc);
break;
case rf_LastState:
suspended = rf_State_LastState(desc);
break;
}
#if RF_DEBUG_STATES
if (rf_printStatesDebug) {
printf("raid%d: State: %-24s StateIndex: %3i desc: 0x%ld %s\n",
unit, StateName(current_state),
current_state_index, (long) desc,
suspended ? "callback scheduled" : "looping");
}
#endif
} while (!suspended && current_state != rf_LastState);
return;
}
void
rf_ContinueDagAccess(RF_DagList_t *dagList)
{
#if RF_ACC_TRACE > 0
RF_AccTraceEntry_t *tracerec = &(dagList->desc->tracerec);
RF_Etimer_t timer;
#endif
RF_RaidAccessDesc_t *desc;
RF_DagHeader_t *dag_h;
int i;
desc = dagList->desc;
#if RF_ACC_TRACE > 0
timer = tracerec->timer;
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
tracerec->specific.user.exec_us = RF_ETIMER_VAL_US(timer);
RF_ETIMER_START(tracerec->timer);
#endif
dag_h = dagList->dags;
for (i = 0; i < dagList->numDagsDone; i++) {
dag_h = dag_h->next;
}
if (dag_h->status == rf_rollBackward) {
desc->status = 1;
#if 0
printf("raid%d: DAG failure: %c addr 0x%lx "
"(%ld) nblk 0x%x (%d) buf 0x%lx state %d\n",
desc->raidPtr->raidid, desc->type,
(long) desc->raidAddress,
(long) desc->raidAddress, (int) desc->numBlocks,
(int) desc->numBlocks,
(unsigned long) (desc->bufPtr), desc->state);
#endif
}
dagList->numDagsDone++;
rf_ContinueRaidAccess(desc);
}
int
rf_State_LastState(RF_RaidAccessDesc_t *desc)
{
void (*callbackFunc) (void *) = desc->callbackFunc;
void * callbackArg = desc->callbackArg;
if (desc->raidPtr->parity_map != NULL &&
desc->type == RF_IO_TYPE_WRITE)
rf_paritymap_end(desc->raidPtr->parity_map,
desc->raidAddress, desc->numBlocks);
raiddone(desc->raidPtr, desc->bp);
if (callbackFunc)
callbackFunc(callbackArg);
rf_FreeRaidAccDesc(desc);
return RF_FALSE;
}
int
rf_State_IncrAccessCount(RF_RaidAccessDesc_t *desc)
{
RF_Raid_t *raidPtr;
raidPtr = desc->raidPtr;
rf_lock_mutex2(raidPtr->access_suspend_mutex);
raidPtr->accs_in_flight++;
rf_unlock_mutex2(raidPtr->access_suspend_mutex);
desc->state++;
return RF_FALSE;
}
int
rf_State_DecrAccessCount(RF_RaidAccessDesc_t *desc)
{
RF_Raid_t *raidPtr;
raidPtr = desc->raidPtr;
rf_lock_mutex2(raidPtr->access_suspend_mutex);
raidPtr->accs_in_flight--;
if (raidPtr->accesses_suspended && raidPtr->accs_in_flight == 0) {
rf_SignalQuiescenceLock(raidPtr);
}
rf_unlock_mutex2(raidPtr->access_suspend_mutex);
desc->state++;
return RF_FALSE;
}
int
rf_State_Quiesce(RF_RaidAccessDesc_t *desc)
{
#if RF_ACC_TRACE > 0
RF_AccTraceEntry_t *tracerec = &desc->tracerec;
RF_Etimer_t timer;
#endif
RF_CallbackFuncDesc_t *cb;
RF_Raid_t *raidPtr;
int suspended = RF_FALSE;
int need_cb, used_cb;
raidPtr = desc->raidPtr;
#if RF_ACC_TRACE > 0
RF_ETIMER_START(timer);
RF_ETIMER_START(desc->timer);
#endif
need_cb = 0;
used_cb = 0;
cb = NULL;
rf_lock_mutex2(raidPtr->access_suspend_mutex);
if (raidPtr->accesses_suspended) {
need_cb = 1;
}
rf_unlock_mutex2(raidPtr->access_suspend_mutex);
if (need_cb) {
cb = rf_AllocCallbackFuncDesc(raidPtr);
}
rf_lock_mutex2(raidPtr->access_suspend_mutex);
if (raidPtr->accesses_suspended) {
cb->callbackFunc = rf_ContinueRaidAccess;
cb->callbackArg = desc;
cb->next = raidPtr->quiesce_wait_list;
raidPtr->quiesce_wait_list = cb;
suspended = RF_TRUE;
used_cb = 1;
}
rf_unlock_mutex2(raidPtr->access_suspend_mutex);
if ((need_cb == 1) && (used_cb == 0)) {
rf_FreeCallbackFuncDesc(raidPtr, cb);
}
#if RF_ACC_TRACE > 0
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
tracerec->specific.user.suspend_ovhd_us += RF_ETIMER_VAL_US(timer);
#endif
#if RF_DEBUG_QUIESCE
if (suspended && rf_quiesceDebug)
printf("Stalling access due to quiescence lock\n");
#endif
desc->state++;
return suspended;
}
int
rf_State_Map(RF_RaidAccessDesc_t *desc)
{
RF_Raid_t *raidPtr = desc->raidPtr;
#if RF_ACC_TRACE > 0
RF_AccTraceEntry_t *tracerec = &desc->tracerec;
RF_Etimer_t timer;
RF_ETIMER_START(timer);
#endif
if (!(desc->asmap = rf_MapAccess(raidPtr, desc->raidAddress, desc->numBlocks,
desc->bufPtr, RF_DONT_REMAP)))
RF_PANIC();
#if RF_ACC_TRACE > 0
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
tracerec->specific.user.map_us = RF_ETIMER_VAL_US(timer);
#endif
desc->state++;
return RF_FALSE;
}
int
rf_State_Lock(RF_RaidAccessDesc_t *desc)
{
#if RF_ACC_TRACE > 0
RF_AccTraceEntry_t *tracerec = &desc->tracerec;
RF_Etimer_t timer;
#endif
RF_Raid_t *raidPtr = desc->raidPtr;
RF_AccessStripeMapHeader_t *asmh = desc->asmap;
RF_AccessStripeMap_t *asm_p;
RF_StripeNum_t lastStripeID = -1;
int suspended = RF_FALSE;
#if RF_ACC_TRACE > 0
RF_ETIMER_START(timer);
#endif
for (asm_p = asmh->stripeMap; asm_p; asm_p = asm_p->next) {
RF_ASSERT(RF_IO_IS_R_OR_W(desc->type));
if (!rf_suppressLocksAndLargeWrites &&
asm_p->parityInfo &&
!(desc->flags & RF_DAG_SUPPRESS_LOCKS) &&
!(asm_p->flags & RF_ASM_FLAGS_LOCK_TRIED)) {
asm_p->flags |= RF_ASM_FLAGS_LOCK_TRIED;
RF_ASSERT(asm_p->stripeID > lastStripeID);
lastStripeID = asm_p->stripeID;
RF_INIT_LOCK_REQ_DESC(asm_p->lockReqDesc, desc->type,
rf_ContinueRaidAccess, desc, asm_p,
raidPtr->Layout.dataSectorsPerStripe);
if (rf_AcquireStripeLock(raidPtr, raidPtr->lockTable, asm_p->stripeID,
&asm_p->lockReqDesc)) {
suspended = RF_TRUE;
break;
}
}
if (desc->type == RF_IO_TYPE_WRITE &&
raidPtr->status == rf_rs_reconstructing) {
if (!(asm_p->flags & RF_ASM_FLAGS_FORCE_TRIED)) {
int val;
asm_p->flags |= RF_ASM_FLAGS_FORCE_TRIED;
val = rf_ForceOrBlockRecon(raidPtr, asm_p,
rf_ContinueRaidAccess, desc);
if (val == 0) {
asm_p->flags |= RF_ASM_FLAGS_RECON_BLOCKED;
} else {
suspended = RF_TRUE;
break;
}
} else {
#if RF_DEBUG_PSS > 0
if (rf_pssDebug) {
printf("raid%d: skipping force/block because already done, psid %ld\n",
desc->raidPtr->raidid,
(long) asm_p->stripeID);
}
#endif
}
} else {
#if RF_DEBUG_PSS > 0
if (rf_pssDebug) {
printf("raid%d: skipping force/block because not write or not under recon, psid %ld\n",
desc->raidPtr->raidid,
(long) asm_p->stripeID);
}
#endif
}
}
#if RF_ACC_TRACE > 0
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
tracerec->specific.user.lock_us += RF_ETIMER_VAL_US(timer);
#endif
if (suspended)
return (RF_TRUE);
desc->state++;
return (RF_FALSE);
}
int
rf_State_CreateDAG(RF_RaidAccessDesc_t *desc)
{
#if RF_ACC_TRACE > 0
RF_AccTraceEntry_t *tracerec = &desc->tracerec;
RF_Etimer_t timer;
#endif
RF_DagHeader_t *dag_h;
RF_DagList_t *dagList;
struct buf *bp;
int i, selectStatus;
#if RF_ACC_TRACE > 0
RF_ETIMER_START(timer);
#endif
selectStatus = rf_SelectAlgorithm(desc, desc->flags | RF_DAG_SUPPRESS_LOCKS);
#if RF_DEBUG_VALIDATE_DAG
if (rf_printDAGsDebug) {
dagList = desc->dagList;
for (i = 0; i < desc->numStripes; i++) {
rf_PrintDAGList(dagList->dags);
dagList = dagList->next;
}
}
#endif
#if RF_ACC_TRACE > 0
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
tracerec->specific.user.dag_create_us = RF_ETIMER_VAL_US(timer);
#endif
desc->status = 0;
if (selectStatus || (desc->numRetries > RF_RETRY_THRESHOLD)) {
if (selectStatus) {
printf("raid%d: failed to create a dag. "
"Too many component failures.\n",
desc->raidPtr->raidid);
} else {
printf("raid%d: IO failed after %d retries.\n",
desc->raidPtr->raidid, RF_RETRY_THRESHOLD);
}
desc->status = 1;
desc->state = rf_CleanupState;
bp = (struct buf *)desc->bp;
bp->b_error = EIO;
bp->b_resid = bp->b_bcount;
} else {
dagList = desc->dagList;
for (i = 0; i < desc->numStripes; i++) {
dag_h = dagList->dags;
while (dag_h) {
dag_h->bp = (struct buf *) desc->bp;
#if RF_ACC_TRACE > 0
dag_h->tracerec = tracerec;
#endif
dag_h = dag_h->next;
}
dagList = dagList->next;
}
desc->flags |= RF_DAG_DISPATCH_RETURNED;
desc->state++;
}
return RF_FALSE;
}
int
rf_State_ExecuteDAG(RF_RaidAccessDesc_t *desc)
{
int i;
RF_DagHeader_t *dag_h;
RF_DagList_t *dagList;
desc->state++;
dagList = desc->dagList;
for (i = 0; i < desc->numStripes; i++) {
RF_ASSERT(dagList->numDags > 0);
RF_ASSERT(dagList->numDagsDone == 0);
RF_ASSERT(dagList->numDagsFired == 0);
#if RF_ACC_TRACE > 0
RF_ETIMER_START(dagList->tracerec.timer);
#endif
dag_h = dagList->dags;
RF_ASSERT(dag_h);
dagList->numDagsFired++;
rf_DispatchDAG(dag_h, (void (*) (void *)) rf_ContinueDagAccess, dagList);
dagList = dagList->next;
}
return RF_TRUE;
}
int
rf_State_ProcessDAG(RF_RaidAccessDesc_t *desc)
{
RF_AccessStripeMapHeader_t *asmh = desc->asmap;
RF_Raid_t *raidPtr = desc->raidPtr;
RF_DagHeader_t *dag_h;
int i, j, done = RF_TRUE;
RF_DagList_t *dagList, *temp;
dagList = desc->dagList;
for (i = 0; i < desc->numStripes; i++) {
if (dagList->numDags != dagList->numDagsDone)
done = RF_FALSE;
dagList = dagList->next;
}
if (done) {
if (desc->status) {
dagList = desc->dagList;
for (i = 0; i < desc->numStripes; i++) {
rf_FreeDAG(dagList->dags);
temp = dagList;
dagList = dagList->next;
rf_FreeDAGList(raidPtr, temp);
}
desc->dagList = NULL;
rf_MarkFailuresInASMList(raidPtr, asmh);
desc->numRetries++;
desc->state = desc->state - 2;
return RF_FALSE;
} else {
desc->state++;
}
return RF_FALSE;
} else {
dagList = desc->dagList;
for (i = 0; i < desc->numStripes; i++) {
if ((dagList->numDagsDone < dagList->numDags)
&& (dagList->numDagsDone == dagList->numDagsFired)
&& (dagList->numDagsFired > 0)) {
#if RF_ACC_TRACE > 0
RF_ETIMER_START(dagList->tracerec.timer);
#endif
dag_h = dagList->dags;
for (j = 0; j < dagList->numDagsDone; j++)
dag_h = dag_h->next;
dagList->numDagsFired++;
rf_DispatchDAG(dag_h, (void (*) (void *)) rf_ContinueDagAccess,
dagList);
}
dagList = dagList->next;
}
return RF_TRUE;
}
}
int
rf_State_Cleanup(RF_RaidAccessDesc_t *desc)
{
#if RF_ACC_TRACE > 0
RF_AccTraceEntry_t *tracerec = &desc->tracerec;
RF_Etimer_t timer;
#endif
RF_AccessStripeMapHeader_t *asmh = desc->asmap;
RF_Raid_t *raidPtr = desc->raidPtr;
RF_AccessStripeMap_t *asm_p;
RF_DagList_t *dagList;
int i;
desc->state++;
#if RF_ACC_TRACE > 0
timer = tracerec->timer;
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
tracerec->specific.user.dag_retry_us = RF_ETIMER_VAL_US(timer);
tracerec->specific.user.dag_retry_us = 0;
RF_ETIMER_START(timer);
#endif
dagList = desc->dagList;
for (i = 0; i < desc->numStripes; i++) {
rf_FreeDAG(dagList->dags);
dagList = dagList->next;
}
#if RF_ACC_TRACE > 0
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
tracerec->specific.user.cleanup_us = RF_ETIMER_VAL_US(timer);
RF_ETIMER_START(timer);
#endif
for (asm_p = asmh->stripeMap; asm_p; asm_p = asm_p->next) {
if (!rf_suppressLocksAndLargeWrites &&
asm_p->parityInfo &&
!(desc->flags & RF_DAG_SUPPRESS_LOCKS)) {
RF_ASSERT_VALID_LOCKREQ(&asm_p->lockReqDesc);
rf_ReleaseStripeLock(raidPtr,
raidPtr->lockTable,
asm_p->stripeID,
&asm_p->lockReqDesc);
}
if (asm_p->flags & RF_ASM_FLAGS_RECON_BLOCKED) {
rf_UnblockRecon(raidPtr, asm_p);
}
}
#if RF_ACC_TRACE > 0
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
tracerec->specific.user.lock_us += RF_ETIMER_VAL_US(timer);
RF_ETIMER_START(timer);
#endif
rf_FreeAccessStripeMap(raidPtr, asmh);
#if RF_ACC_TRACE > 0
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
tracerec->specific.user.cleanup_us += RF_ETIMER_VAL_US(timer);
RF_ETIMER_STOP(desc->timer);
RF_ETIMER_EVAL(desc->timer);
timer = desc->tracerec.tot_timer;
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
desc->tracerec.total_us = RF_ETIMER_VAL_US(timer);
rf_LogTraceRec(raidPtr, tracerec);
#endif
desc->flags |= RF_DAG_ACCESS_COMPLETE;
return RF_FALSE;
}