#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: rf_reconstruct.c,v 1.129 2023/09/17 20:07:39 oster Exp $");
#include <sys/param.h>
#include <sys/time.h>
#include <sys/buf.h>
#include <sys/errno.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/vnode.h>
#include <sys/namei.h>
#include <dev/raidframe/raidframevar.h>
#include <miscfs/specfs/specdev.h>
#include "rf_raid.h"
#include "rf_reconutil.h"
#include "rf_revent.h"
#include "rf_reconbuffer.h"
#include "rf_acctrace.h"
#include "rf_etimer.h"
#include "rf_dag.h"
#include "rf_desc.h"
#include "rf_debugprint.h"
#include "rf_general.h"
#include "rf_driver.h"
#include "rf_utils.h"
#include "rf_shutdown.h"
#include "rf_kintf.h"
#if RF_DEBUG_RECON
#define Dprintf(s) if (rf_reconDebug) rf_debug_printf(s,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL)
#define Dprintf1(s,a) if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),NULL,NULL,NULL,NULL,NULL,NULL,NULL)
#define Dprintf2(s,a,b) if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),NULL,NULL,NULL,NULL,NULL,NULL)
#define Dprintf3(s,a,b,c) if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),(void *)((unsigned long)c),NULL,NULL,NULL,NULL,NULL)
#define Dprintf4(s,a,b,c,d) if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),(void *)((unsigned long)c),(void *)((unsigned long)d),NULL,NULL,NULL,NULL)
#define Dprintf5(s,a,b,c,d,e) if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),(void *)((unsigned long)c),(void *)((unsigned long)d),(void *)((unsigned long)e),NULL,NULL,NULL)
#define Dprintf6(s,a,b,c,d,e,f) if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),(void *)((unsigned long)c),(void *)((unsigned long)d),(void *)((unsigned long)e),(void *)((unsigned long)f),NULL,NULL)
#define Dprintf7(s,a,b,c,d,e,f,g) if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),(void *)((unsigned long)c),(void *)((unsigned long)d),(void *)((unsigned long)e),(void *)((unsigned long)f),(void *)((unsigned long)g),NULL)
#define DDprintf1(s,a) if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),NULL,NULL,NULL,NULL,NULL,NULL,NULL)
#define DDprintf2(s,a,b) if (rf_reconDebug) rf_debug_printf(s,(void *)((unsigned long)a),(void *)((unsigned long)b),NULL,NULL,NULL,NULL,NULL,NULL)
#else
#define Dprintf(s) {}
#define Dprintf1(s,a) {}
#define Dprintf2(s,a,b) {}
#define Dprintf3(s,a,b,c) {}
#define Dprintf4(s,a,b,c,d) {}
#define Dprintf5(s,a,b,c,d,e) {}
#define Dprintf6(s,a,b,c,d,e,f) {}
#define Dprintf7(s,a,b,c,d,e,f,g) {}
#define DDprintf1(s,a) {}
#define DDprintf2(s,a,b) {}
#endif
#define RF_RECON_DONE_READS 1
#define RF_RECON_READ_ERROR 2
#define RF_RECON_WRITE_ERROR 3
#define RF_RECON_READ_STOPPED 4
#define RF_RECON_WRITE_DONE 5
#define RF_MAX_FREE_RECONBUFFER 32
#define RF_MIN_FREE_RECONBUFFER 16
static RF_RaidReconDesc_t *AllocRaidReconDesc(RF_Raid_t *, RF_RowCol_t,
RF_RaidDisk_t *, int, RF_RowCol_t);
static void FreeReconDesc(RF_RaidReconDesc_t *);
static int ProcessReconEvent(RF_Raid_t *, RF_ReconEvent_t *);
static int IssueNextReadRequest(RF_Raid_t *, RF_RowCol_t);
static int TryToRead(RF_Raid_t *, RF_RowCol_t);
static int ComputePSDiskOffsets(RF_Raid_t *, RF_StripeNum_t, RF_RowCol_t,
RF_SectorNum_t *, RF_SectorNum_t *, RF_RowCol_t *,
RF_SectorNum_t *);
static int IssueNextWriteRequest(RF_Raid_t *);
static void ReconReadDoneProc(void *, int);
static void ReconWriteDoneProc(void *, int);
static void CheckForNewMinHeadSep(RF_Raid_t *, RF_HeadSepLimit_t);
static int CheckHeadSeparation(RF_Raid_t *, RF_PerDiskReconCtrl_t *,
RF_RowCol_t, RF_HeadSepLimit_t,
RF_ReconUnitNum_t);
static int CheckForcedOrBlockedReconstruction(RF_Raid_t *,
RF_ReconParityStripeStatus_t *,
RF_PerDiskReconCtrl_t *,
RF_RowCol_t, RF_StripeNum_t,
RF_ReconUnitNum_t);
static void ForceReconReadDoneProc(void *, int);
static void rf_ShutdownReconstruction(void *);
struct RF_ReconDoneProc_s {
void (*proc) (RF_Raid_t *, void *);
void *arg;
RF_ReconDoneProc_t *next;
};
static void
rf_ShutdownReconstruction(void *arg)
{
RF_Raid_t *raidPtr;
raidPtr = (RF_Raid_t *) arg;
pool_destroy(&raidPtr->pools.reconbuffer);
}
int
rf_ConfigureReconstruction(RF_ShutdownList_t **listp, RF_Raid_t *raidPtr,
RF_Config_t *cfgPtr)
{
rf_pool_init(raidPtr, raidPtr->poolNames.reconbuffer, &raidPtr->pools.reconbuffer, sizeof(RF_ReconBuffer_t),
"reconbuf", RF_MIN_FREE_RECONBUFFER, RF_MAX_FREE_RECONBUFFER);
rf_ShutdownCreate(listp, rf_ShutdownReconstruction, raidPtr);
return (0);
}
static RF_RaidReconDesc_t *
AllocRaidReconDesc(RF_Raid_t *raidPtr, RF_RowCol_t col,
RF_RaidDisk_t *spareDiskPtr, int numDisksDone,
RF_RowCol_t scol)
{
RF_RaidReconDesc_t *reconDesc;
reconDesc = RF_Malloc(sizeof(*reconDesc));
reconDesc->raidPtr = raidPtr;
reconDesc->col = col;
reconDesc->spareDiskPtr = spareDiskPtr;
reconDesc->numDisksDone = numDisksDone;
reconDesc->scol = scol;
reconDesc->next = NULL;
return (reconDesc);
}
static void
FreeReconDesc(RF_RaidReconDesc_t *reconDesc)
{
#if RF_RECON_STATS > 0
printf("raid%d: %lu recon event waits, %lu recon delays\n",
reconDesc->raidPtr->raidid,
(long) reconDesc->numReconEventWaits,
(long) reconDesc->numReconExecDelays);
#endif
printf("raid%d: %lu max exec ticks\n",
reconDesc->raidPtr->raidid,
(long) reconDesc->maxReconExecTicks);
RF_Free(reconDesc, sizeof(RF_RaidReconDesc_t));
}
int
rf_ReconstructFailedDisk(RF_Raid_t *raidPtr, RF_RowCol_t col)
{
const RF_LayoutSW_t *lp;
int rc;
lp = raidPtr->Layout.map;
if (lp->SubmitReconBuffer) {
rf_lock_mutex2(raidPtr->mutex);
while (raidPtr->reconInProgress) {
rf_wait_cond2(raidPtr->waitForReconCond, raidPtr->mutex);
}
raidPtr->reconInProgress++;
rf_unlock_mutex2(raidPtr->mutex);
rc = rf_ReconstructFailedDiskBasic(raidPtr, col);
rf_lock_mutex2(raidPtr->mutex);
raidPtr->reconInProgress--;
} else {
RF_ERRORMSG1("RECON: no way to reconstruct failed disk for arch %c\n",
lp->parityConfig);
rc = EIO;
rf_lock_mutex2(raidPtr->mutex);
}
rf_signal_cond2(raidPtr->waitForReconCond);
rf_unlock_mutex2(raidPtr->mutex);
return (rc);
}
int
rf_ReconstructFailedDiskBasic(RF_Raid_t *raidPtr, RF_RowCol_t col)
{
RF_ComponentLabel_t *c_label;
RF_RaidDisk_t *spareDiskPtr = NULL;
RF_RaidReconDesc_t *reconDesc;
RF_RowCol_t scol;
int numDisksDone = 0, rc;
rf_lock_mutex2(raidPtr->mutex);
RF_ASSERT(raidPtr->Disks[col].status == rf_ds_failed);
#if RF_INCLUDE_PARITY_DECLUSTERING_DS > 0
if (raidPtr->Layout.map->flags & RF_DISTRIBUTE_SPARE) {
if (raidPtr->status != rf_rs_degraded) {
RF_ERRORMSG1("Unable to reconstruct disk at col %d because status not degraded\n", col);
rf_unlock_mutex2(raidPtr->mutex);
return (EINVAL);
}
scol = (-1);
} else {
#endif
for (scol = raidPtr->numCol; scol < raidPtr->numCol + raidPtr->numSpare; scol++) {
if (raidPtr->Disks[scol].status == rf_ds_spare) {
spareDiskPtr = &raidPtr->Disks[scol];
spareDiskPtr->status = rf_ds_rebuilding_spare;
break;
}
}
if (!spareDiskPtr) {
RF_ERRORMSG1("Unable to reconstruct disk at col %d because no spares are available\n", col);
rf_unlock_mutex2(raidPtr->mutex);
return (ENOSPC);
}
printf("RECON: initiating reconstruction on col %d -> spare at col %d\n", col, scol);
#if RF_INCLUDE_PARITY_DECLUSTERING_DS > 0
}
#endif
rf_unlock_mutex2(raidPtr->mutex);
reconDesc = AllocRaidReconDesc((void *) raidPtr, col, spareDiskPtr, numDisksDone, scol);
raidPtr->reconDesc = (void *) reconDesc;
#if RF_RECON_STATS > 0
reconDesc->hsStallCount = 0;
reconDesc->numReconExecDelays = 0;
reconDesc->numReconEventWaits = 0;
#endif
reconDesc->reconExecTimerRunning = 0;
reconDesc->reconExecTicks = 0;
reconDesc->maxReconExecTicks = 0;
rc = rf_ContinueReconstructFailedDisk(reconDesc);
if (!rc) {
c_label = raidget_component_label(raidPtr, col);
raid_init_component_label(raidPtr, c_label);
c_label->row = 0;
c_label->column = col;
c_label->clean = RF_RAID_DIRTY;
c_label->status = rf_ds_optimal;
rf_component_label_set_partitionsize(c_label,
raidPtr->Disks[col].partitionSize);
rf_lock_mutex2(raidPtr->mutex);
raidPtr->parity_good = RF_RAID_CLEAN;
rf_unlock_mutex2(raidPtr->mutex);
raidflush_component_label(raidPtr, col);
} else {
rf_lock_mutex2(raidPtr->mutex);
raidPtr->Disks[col].status = rf_ds_failed;
spareDiskPtr->status = rf_ds_spare;
rf_unlock_mutex2(raidPtr->mutex);
}
rf_update_component_labels(raidPtr, RF_NORMAL_COMPONENT_UPDATE);
return (rc);
}
int
rf_ReconstructInPlace(RF_Raid_t *raidPtr, RF_RowCol_t col)
{
RF_RaidDisk_t *spareDiskPtr = NULL;
RF_RaidReconDesc_t *reconDesc;
const RF_LayoutSW_t *lp;
RF_ComponentLabel_t *c_label;
int numDisksDone = 0, rc;
uint64_t numsec;
unsigned int secsize;
struct pathbuf *pb;
struct vnode *vp;
int retcode;
int ac;
rf_lock_mutex2(raidPtr->mutex);
lp = raidPtr->Layout.map;
if (!lp->SubmitReconBuffer) {
RF_ERRORMSG1("RECON: no way to reconstruct failed disk for arch %c\n",
lp->parityConfig);
rf_signal_cond2(raidPtr->waitForReconCond);
rf_unlock_mutex2(raidPtr->mutex);
return(EIO);
}
if (raidPtr->Disks[col].status != rf_ds_failed) {
raidPtr->numFailures++;
raidPtr->Disks[col].status = rf_ds_failed;
raidPtr->status = rf_rs_degraded;
rf_unlock_mutex2(raidPtr->mutex);
rf_update_component_labels(raidPtr,
RF_NORMAL_COMPONENT_UPDATE);
rf_lock_mutex2(raidPtr->mutex);
}
while (raidPtr->reconInProgress) {
rf_wait_cond2(raidPtr->waitForReconCond, raidPtr->mutex);
}
raidPtr->reconInProgress++;
#if RF_INCLUDE_PARITY_DECLUSTERING_DS > 0
if (raidPtr->Layout.map->flags & RF_DISTRIBUTE_SPARE) {
RF_ERRORMSG1("Unable to reconstruct to disk at col %d: operation not supported for RF_DISTRIBUTE_SPARE\n", col);
raidPtr->reconInProgress--;
rf_signal_cond2(raidPtr->waitForReconCond);
rf_unlock_mutex2(raidPtr->mutex);
return (EINVAL);
}
#endif
if (raidPtr->raid_cinfo[col].ci_vp != NULL) {
#if 0
printf("Closed the open device: %s\n",
raidPtr->Disks[col].devname);
#endif
vp = raidPtr->raid_cinfo[col].ci_vp;
ac = raidPtr->Disks[col].auto_configured;
rf_unlock_mutex2(raidPtr->mutex);
rf_close_component(raidPtr, vp, ac);
rf_lock_mutex2(raidPtr->mutex);
raidPtr->raid_cinfo[col].ci_vp = NULL;
}
raidPtr->Disks[col].auto_configured = 0;
#if 0
printf("About to (re-)open the device for rebuilding: %s\n",
raidPtr->Disks[col].devname);
#endif
rf_unlock_mutex2(raidPtr->mutex);
pb = pathbuf_create(raidPtr->Disks[col].devname);
if (pb == NULL) {
retcode = ENOMEM;
} else {
retcode = vn_bdev_openpath(pb, &vp, curlwp);
pathbuf_destroy(pb);
}
if (retcode) {
printf("raid%d: rebuilding: open device: %s failed: %d!\n",raidPtr->raidid,
raidPtr->Disks[col].devname, retcode);
rf_lock_mutex2(raidPtr->mutex);
raidPtr->reconInProgress--;
rf_signal_cond2(raidPtr->waitForReconCond);
rf_unlock_mutex2(raidPtr->mutex);
return(retcode);
}
retcode = getdisksize(vp, &numsec, &secsize);
if (retcode) {
vn_close(vp, FREAD | FWRITE, kauth_cred_get());
rf_lock_mutex2(raidPtr->mutex);
raidPtr->reconInProgress--;
rf_signal_cond2(raidPtr->waitForReconCond);
rf_unlock_mutex2(raidPtr->mutex);
return(retcode);
}
rf_lock_mutex2(raidPtr->mutex);
raidPtr->Disks[col].blockSize = secsize;
raidPtr->Disks[col].numBlocks = numsec - rf_protectedSectors;
raidPtr->raid_cinfo[col].ci_vp = vp;
raidPtr->raid_cinfo[col].ci_dev = vp->v_rdev;
raidPtr->Disks[col].dev = vp->v_rdev;
raidPtr->Disks[col].numBlocks = raidPtr->Disks[col].numBlocks *
rf_sizePercentage / 100;
rf_unlock_mutex2(raidPtr->mutex);
spareDiskPtr = &raidPtr->Disks[col];
spareDiskPtr->status = rf_ds_rebuilding_spare;
printf("raid%d: initiating in-place reconstruction on column %d\n",
raidPtr->raidid, col);
reconDesc = AllocRaidReconDesc((void *) raidPtr, col, spareDiskPtr,
numDisksDone, col);
raidPtr->reconDesc = (void *) reconDesc;
#if RF_RECON_STATS > 0
reconDesc->hsStallCount = 0;
reconDesc->numReconExecDelays = 0;
reconDesc->numReconEventWaits = 0;
#endif
reconDesc->reconExecTimerRunning = 0;
reconDesc->reconExecTicks = 0;
reconDesc->maxReconExecTicks = 0;
rc = rf_ContinueReconstructFailedDisk(reconDesc);
if (!rc) {
rf_lock_mutex2(raidPtr->mutex);
raidPtr->Disks[col].status = rf_ds_optimal;
raidPtr->status = rf_rs_optimal;
rf_unlock_mutex2(raidPtr->mutex);
c_label = raidget_component_label(raidPtr, col);
rf_lock_mutex2(raidPtr->mutex);
raid_init_component_label(raidPtr, c_label);
c_label->row = 0;
c_label->column = col;
raidPtr->parity_good = RF_RAID_CLEAN;
rf_unlock_mutex2(raidPtr->mutex);
raidflush_component_label(raidPtr, col);
} else {
rf_lock_mutex2(raidPtr->mutex);
raidPtr->Disks[col].status = rf_ds_failed;
rf_unlock_mutex2(raidPtr->mutex);
}
rf_update_component_labels(raidPtr, RF_NORMAL_COMPONENT_UPDATE);
rf_lock_mutex2(raidPtr->mutex);
raidPtr->reconInProgress--;
rf_signal_cond2(raidPtr->waitForReconCond);
rf_unlock_mutex2(raidPtr->mutex);
return (rc);
}
int
rf_ContinueReconstructFailedDisk(RF_RaidReconDesc_t *reconDesc)
{
RF_Raid_t *raidPtr = reconDesc->raidPtr;
RF_RowCol_t col = reconDesc->col;
RF_RowCol_t scol = reconDesc->scol;
RF_ReconMap_t *mapPtr;
RF_ReconCtrl_t *tmp_reconctrl;
RF_ReconEvent_t *event;
RF_StripeCount_t incPSID,lastPSID,num_writes,pending_writes,prev;
#if RF_INCLUDE_RAID5_RS > 0
RF_StripeCount_t startPSID,endPSID,aPSID,bPSID,offPSID;
#endif
RF_ReconUnitCount_t RUsPerPU;
struct timeval etime, elpsd;
unsigned long xor_s, xor_resid_us;
int i, ds;
int status, done;
int recon_error, write_error;
raidPtr->accumXorTimeUs = 0;
#if RF_ACC_TRACE > 0
raidPtr->recon_tracerecs =
RF_Malloc(raidPtr->numCol * sizeof(*raidPtr->recon_tracerecs));
#endif
Dprintf("RECON: begin request suspend\n");
rf_SuspendNewRequestsAndWait(raidPtr);
Dprintf("RECON: end request suspend\n");
tmp_reconctrl = rf_MakeReconControl(reconDesc, col, scol);
rf_lock_mutex2(raidPtr->mutex);
raidPtr->reconControl = tmp_reconctrl;
mapPtr = raidPtr->reconControl->reconMap;
raidPtr->reconControl->numRUsTotal = mapPtr->totalRUs;
raidPtr->reconControl->numRUsComplete = 0;
raidPtr->status = rf_rs_reconstructing;
raidPtr->Disks[col].status = rf_ds_reconstructing;
raidPtr->Disks[col].spareCol = scol;
rf_unlock_mutex2(raidPtr->mutex);
RF_GETTIME(raidPtr->reconControl->starttime);
Dprintf("RECON: resume requests\n");
rf_ResumeNewRequests(raidPtr);
mapPtr = raidPtr->reconControl->reconMap;
incPSID = RF_RECONMAP_SIZE;
lastPSID = raidPtr->Layout.numStripe / raidPtr->Layout.SUsPerPU - 1;
RUsPerPU = raidPtr->Layout.SUsPerPU / raidPtr->Layout.SUsPerRU;
recon_error = 0;
write_error = 0;
pending_writes = incPSID;
raidPtr->reconControl->lastPSID = incPSID - 1;
if (raidPtr->reconControl->lastPSID > lastPSID)
raidPtr->reconControl->lastPSID = lastPSID;
if (pending_writes > lastPSID)
pending_writes = lastPSID + 1;
done = 0;
while (!done) {
if (raidPtr->waitShutdown ||
raidPtr->abortRecon[col]) {
recon_error = 1;
raidPtr->abortRecon[col] = 0;
break;
}
num_writes = 0;
#if RF_INCLUDE_RAID5_RS > 0
if (raidPtr->Layout.numDataCol <
raidPtr->numCol - raidPtr->Layout.numParityCol) {
startPSID = raidPtr->reconControl->lastPSID - pending_writes + 1;
endPSID = raidPtr->reconControl->lastPSID;
offPSID = raidPtr->numCol - col - 1;
aPSID = startPSID - startPSID % raidPtr->numCol + offPSID;
if (aPSID < startPSID) {
aPSID += raidPtr->numCol;
}
bPSID = endPSID - ((endPSID - offPSID) % raidPtr->numCol);
if (aPSID < endPSID) {
num_writes = ((bPSID - aPSID) / raidPtr->numCol) + 1;
}
if ((aPSID == endPSID) && (bPSID == endPSID)) {
num_writes++;
}
}
#endif
reconDesc->numDisksDone = 0;
for (i = 0; i < raidPtr->numCol; i++) {
if (i != col) {
if (IssueNextReadRequest(raidPtr, i)) {
Dprintf1("RECON: done issuing for c%d\n", i);
reconDesc->numDisksDone++;
}
}
}
while (reconDesc->numDisksDone < raidPtr->numCol - 1) {
event = rf_GetNextReconEvent(reconDesc);
status = ProcessReconEvent(raidPtr, event);
if (status == RF_RECON_DONE_READS) {
reconDesc->numDisksDone++;
} else if ((status == RF_RECON_READ_ERROR) ||
(status == RF_RECON_WRITE_ERROR)) {
recon_error = 1;
raidPtr->reconControl->error = 1;
if (status == RF_RECON_READ_ERROR)
reconDesc->numDisksDone++;
if (status == RF_RECON_WRITE_ERROR) {
write_error = 1;
num_writes++;
}
} else if (status == RF_RECON_READ_STOPPED) {
reconDesc->numDisksDone++;
} else if (status == RF_RECON_WRITE_DONE) {
num_writes++;
}
if (recon_error) {
rf_WakeupHeadSepCBWaiters(raidPtr);
}
raidPtr->reconControl->numRUsTotal =
mapPtr->totalRUs;
raidPtr->reconControl->numRUsComplete =
mapPtr->totalRUs -
rf_UnitsLeftToReconstruct(mapPtr);
#if RF_DEBUG_RECON
raidPtr->reconControl->percentComplete =
(raidPtr->reconControl->numRUsComplete * 100 / raidPtr->reconControl->numRUsTotal);
if (rf_prReconSched) {
rf_PrintReconSchedule(raidPtr->reconControl->reconMap, &(raidPtr->reconControl->starttime));
}
#endif
}
rf_WakeupHeadSepCBWaiters(raidPtr);
while (!recon_error && (num_writes < pending_writes)) {
event = rf_GetNextReconEvent(reconDesc);
status = ProcessReconEvent(raidPtr, event);
if (status == RF_RECON_WRITE_ERROR) {
num_writes++;
recon_error = 1;
raidPtr->reconControl->error = 1;
} else if (status == RF_RECON_WRITE_DONE) {
num_writes++;
}
}
if (recon_error ||
(raidPtr->reconControl->lastPSID == lastPSID)) {
done = 1;
break;
}
prev = raidPtr->reconControl->lastPSID;
raidPtr->reconControl->lastPSID += incPSID;
if (raidPtr->reconControl->lastPSID > lastPSID) {
pending_writes = lastPSID - prev;
raidPtr->reconControl->lastPSID = lastPSID;
}
for (i = 0; i < raidPtr->numCol; i++) {
if (i != col) {
raidPtr->reconControl->perDiskInfo[i].curPSID--;
raidPtr->reconControl->perDiskInfo[i].ru_count = RUsPerPU - 1;
}
}
}
mapPtr = raidPtr->reconControl->reconMap;
if (rf_reconDebug) {
printf("RECON: all reads completed\n");
}
while (!recon_error && rf_UnitsLeftToReconstruct(raidPtr->reconControl->reconMap) > 0) {
event = rf_GetNextReconEvent(reconDesc);
status = ProcessReconEvent(raidPtr, event);
if (status == RF_RECON_WRITE_ERROR) {
recon_error = 1;
raidPtr->reconControl->error = 1;
} else {
#if RF_DEBUG_RECON
raidPtr->reconControl->percentComplete = 100 - (rf_UnitsLeftToReconstruct(mapPtr) * 100 / mapPtr->totalRUs);
if (rf_prReconSched) {
rf_PrintReconSchedule(raidPtr->reconControl->reconMap, &(raidPtr->reconControl->starttime));
}
#endif
}
}
if (recon_error) {
printf("raid%d: reconstruction failed.\n", raidPtr->raidid);
rf_SuspendNewRequestsAndWait(raidPtr);
rf_lock_mutex2(raidPtr->mutex);
raidPtr->status = rf_rs_degraded;
rf_unlock_mutex2(raidPtr->mutex);
rf_ResumeNewRequests(raidPtr);
if (!write_error) {
while (raidPtr->reconControl->pending_writes > 0) {
event = rf_GetNextReconEvent(reconDesc);
status = ProcessReconEvent(raidPtr, event);
if (status == RF_RECON_WRITE_ERROR) {
raidPtr->reconControl->error = 1;
break;
}
}
}
rf_DrainReconEventQueue(reconDesc);
rf_FreeReconControl(raidPtr);
#if RF_ACC_TRACE > 0
RF_Free(raidPtr->recon_tracerecs, raidPtr->numCol * sizeof(RF_AccTraceEntry_t));
#endif
FreeReconDesc(reconDesc);
return (1);
}
rf_SuspendNewRequestsAndWait(raidPtr);
rf_lock_mutex2(raidPtr->mutex);
raidPtr->numFailures--;
ds = (raidPtr->Layout.map->flags & RF_DISTRIBUTE_SPARE);
raidPtr->Disks[col].status = (ds) ? rf_ds_dist_spared : rf_ds_spared;
raidPtr->status = (ds) ? rf_rs_reconfigured : rf_rs_optimal;
if (col != scol) {
rf_swap_components(raidPtr, col, scol);
raidPtr->Disks[col].status = rf_ds_optimal;
for (i = scol; i < raidPtr->numCol+raidPtr->numSpare-1; i++) {
rf_swap_components(raidPtr, i, i+1);
}
raidPtr->numSpare--;
}
rf_unlock_mutex2(raidPtr->mutex);
RF_GETTIME(etime);
RF_TIMEVAL_DIFF(&(raidPtr->reconControl->starttime), &etime, &elpsd);
rf_ResumeNewRequests(raidPtr);
printf("raid%d: Reconstruction of disk at col %d completed\n",
raidPtr->raidid, col);
xor_s = raidPtr->accumXorTimeUs / 1000000;
xor_resid_us = raidPtr->accumXorTimeUs % 1000000;
printf("raid%d: Recon time was %d.%06d seconds, accumulated XOR time was %ld us (%ld.%06ld)\n",
raidPtr->raidid,
(int) elpsd.tv_sec, (int) elpsd.tv_usec,
raidPtr->accumXorTimeUs, xor_s, xor_resid_us);
printf("raid%d: (start time %d sec %d usec, end time %d sec %d usec)\n",
raidPtr->raidid,
(int) raidPtr->reconControl->starttime.tv_sec,
(int) raidPtr->reconControl->starttime.tv_usec,
(int) etime.tv_sec, (int) etime.tv_usec);
#if RF_RECON_STATS > 0
printf("raid%d: Total head-sep stall count was %d\n",
raidPtr->raidid, (int) reconDesc->hsStallCount);
#endif
rf_FreeReconControl(raidPtr);
#if RF_ACC_TRACE > 0
RF_Free(raidPtr->recon_tracerecs, raidPtr->numCol * sizeof(RF_AccTraceEntry_t));
#endif
FreeReconDesc(reconDesc);
return (0);
}
static int
ProcessReconEvent(RF_Raid_t *raidPtr, RF_ReconEvent_t *event)
{
int retcode = 0, submitblocked;
RF_ReconBuffer_t *rbuf;
RF_SectorCount_t sectorsPerRU;
retcode = RF_RECON_READ_STOPPED;
Dprintf1("RECON: ProcessReconEvent type %d\n", event->type);
switch (event->type) {
case RF_REVENT_READDONE:
rbuf = raidPtr->reconControl->perDiskInfo[event->col].rbuf;
Dprintf2("RECON: READDONE EVENT: col %d psid %ld\n",
event->col, rbuf->parityStripeID);
Dprintf7("RECON: done read psid %ld buf %lx %02x %02x %02x %02x %02x\n",
rbuf->parityStripeID, rbuf->buffer, rbuf->buffer[0] & 0xff, rbuf->buffer[1] & 0xff,
rbuf->buffer[2] & 0xff, rbuf->buffer[3] & 0xff, rbuf->buffer[4] & 0xff);
rf_FreeDiskQueueData((RF_DiskQueueData_t *) rbuf->arg);
if (!raidPtr->reconControl->error) {
submitblocked = rf_SubmitReconBuffer(rbuf, 0, 0);
Dprintf1("RECON: submitblocked=%d\n", submitblocked);
if (!submitblocked)
retcode = IssueNextReadRequest(raidPtr, event->col);
else
retcode = 0;
}
break;
case RF_REVENT_WRITEDONE:
#if RF_DEBUG_RECON
if (rf_floatingRbufDebug) {
rf_CheckFloatingRbufCount(raidPtr, 1);
}
#endif
sectorsPerRU = raidPtr->Layout.sectorsPerStripeUnit * raidPtr->Layout.SUsPerRU;
rbuf = (RF_ReconBuffer_t *) event->arg;
rf_FreeDiskQueueData((RF_DiskQueueData_t *) rbuf->arg);
Dprintf3("RECON: WRITEDONE EVENT: psid %d ru %d (%d %% complete)\n",
rbuf->parityStripeID, rbuf->which_ru, raidPtr->reconControl->percentComplete);
rf_ReconMapUpdate(raidPtr, raidPtr->reconControl->reconMap,
rbuf->failedDiskSectorOffset, rbuf->failedDiskSectorOffset + sectorsPerRU - 1);
rf_RemoveFromActiveReconTable(raidPtr, rbuf->parityStripeID, rbuf->which_ru);
rf_lock_mutex2(raidPtr->reconControl->rb_mutex);
raidPtr->reconControl->pending_writes--;
rf_unlock_mutex2(raidPtr->reconControl->rb_mutex);
if (rbuf->type == RF_RBUF_TYPE_FLOATING) {
rf_lock_mutex2(raidPtr->reconControl->rb_mutex);
while(raidPtr->reconControl->rb_lock) {
rf_wait_cond2(raidPtr->reconControl->rb_cv,
raidPtr->reconControl->rb_mutex);
}
raidPtr->reconControl->rb_lock = 1;
rf_unlock_mutex2(raidPtr->reconControl->rb_mutex);
raidPtr->numFullReconBuffers--;
rf_ReleaseFloatingReconBuffer(raidPtr, rbuf);
rf_lock_mutex2(raidPtr->reconControl->rb_mutex);
raidPtr->reconControl->rb_lock = 0;
rf_broadcast_cond2(raidPtr->reconControl->rb_cv);
rf_unlock_mutex2(raidPtr->reconControl->rb_mutex);
} else
if (rbuf->type == RF_RBUF_TYPE_FORCED)
rf_FreeReconBuffer(rbuf);
else
RF_ASSERT(0);
retcode = RF_RECON_WRITE_DONE;
break;
case RF_REVENT_BUFCLEAR:
Dprintf1("RECON: BUFCLEAR EVENT: col %d\n", event->col);
if (!raidPtr->reconControl->error) {
submitblocked = rf_SubmitReconBuffer(raidPtr->reconControl->perDiskInfo[event->col].rbuf,
0, (int) (long) event->arg);
RF_ASSERT(!submitblocked);
retcode = IssueNextReadRequest(raidPtr, event->col);
}
break;
case RF_REVENT_BLOCKCLEAR:
DDprintf1("RECON: BLOCKCLEAR EVENT: col %d\n", event->col);
if (!raidPtr->reconControl->error) {
retcode = TryToRead(raidPtr, event->col);
}
break;
case RF_REVENT_HEADSEPCLEAR:
Dprintf1("RECON: HEADSEPCLEAR EVENT: col %d\n", event->col);
if (!raidPtr->reconControl->error) {
retcode = TryToRead(raidPtr, event->col);
}
break;
case RF_REVENT_BUFREADY:
Dprintf1("RECON: BUFREADY EVENT: col %d\n", event->col);
if (!raidPtr->reconControl->error) {
retcode = IssueNextWriteRequest(raidPtr);
#if RF_DEBUG_RECON
if (rf_floatingRbufDebug) {
rf_CheckFloatingRbufCount(raidPtr, 1);
}
#endif
}
break;
case RF_REVENT_SKIP:
DDprintf1("RECON: SKIP EVENT: col %d\n", event->col);
if (!raidPtr->reconControl->error) {
retcode = IssueNextReadRequest(raidPtr, event->col);
}
break;
case RF_REVENT_FORCEDREADDONE:
rbuf = (RF_ReconBuffer_t *) event->arg;
rf_FreeDiskQueueData((RF_DiskQueueData_t *) rbuf->arg);
DDprintf1("RECON: FORCEDREADDONE EVENT: col %d\n", event->col);
if (!raidPtr->reconControl->error) {
submitblocked = rf_SubmitReconBuffer(rbuf, 1, 0);
RF_ASSERT(!submitblocked);
retcode = 0;
}
break;
case RF_REVENT_READ_FAILED:
retcode = RF_RECON_READ_ERROR;
break;
case RF_REVENT_WRITE_FAILED:
retcode = RF_RECON_WRITE_ERROR;
rf_lock_mutex2(raidPtr->reconControl->rb_mutex);
raidPtr->reconControl->pending_writes--;
rf_unlock_mutex2(raidPtr->reconControl->rb_mutex);
rbuf = (RF_ReconBuffer_t *) event->arg;
rf_FreeDiskQueueData((RF_DiskQueueData_t *) rbuf->arg);
rf_FreeReconBuffer(rbuf);
break;
case RF_REVENT_FORCEDREAD_FAILED:
retcode = RF_RECON_READ_ERROR;
break;
default:
RF_PANIC();
}
rf_FreeReconEventDesc(raidPtr, event);
return (retcode);
}
static int
IssueNextReadRequest(RF_Raid_t *raidPtr, RF_RowCol_t col)
{
RF_PerDiskReconCtrl_t *ctrl = &raidPtr->reconControl->perDiskInfo[col];
RF_RaidLayout_t *layoutPtr = &raidPtr->Layout;
RF_ReconBuffer_t *rbuf = ctrl->rbuf;
RF_ReconUnitCount_t RUsPerPU = layoutPtr->SUsPerPU / layoutPtr->SUsPerRU;
RF_SectorCount_t sectorsPerRU = layoutPtr->sectorsPerStripeUnit * layoutPtr->SUsPerRU;
int do_new_check = 0, retcode = 0, status;
if (ctrl->headSepCounter <= raidPtr->reconControl->minHeadSepCounter)
do_new_check = 1;
while (1) {
ctrl->ru_count++;
if (ctrl->ru_count < RUsPerPU) {
ctrl->diskOffset += sectorsPerRU;
rbuf->failedDiskSectorOffset += sectorsPerRU;
} else {
ctrl->curPSID++;
ctrl->ru_count = 0;
if (ctrl->curPSID > raidPtr->reconControl->lastPSID) {
CheckForNewMinHeadSep(raidPtr, ++(ctrl->headSepCounter));
return (RF_RECON_DONE_READS);
}
status = ComputePSDiskOffsets(raidPtr, ctrl->curPSID, col, &ctrl->diskOffset, &rbuf->failedDiskSectorOffset,
&rbuf->spCol, &rbuf->spOffset);
if (status) {
ctrl->ru_count = RUsPerPU - 1;
continue;
}
}
rbuf->which_ru = ctrl->ru_count;
if (rf_CheckRUReconstructed(raidPtr->reconControl->reconMap, rbuf->failedDiskSectorOffset)) {
Dprintf2("Skipping psid %ld ru %d: already reconstructed\n", ctrl->curPSID, ctrl->ru_count);
continue;
}
break;
}
ctrl->headSepCounter++;
if (do_new_check)
CheckForNewMinHeadSep(raidPtr, ctrl->headSepCounter);
rbuf->parityStripeID = ctrl->curPSID;
rbuf->which_ru = ctrl->ru_count;
#if RF_ACC_TRACE > 0
memset(&raidPtr->recon_tracerecs[col], 0,
sizeof(raidPtr->recon_tracerecs[col]));
raidPtr->recon_tracerecs[col].reconacc = 1;
RF_ETIMER_START(raidPtr->recon_tracerecs[col].recon_timer);
#endif
retcode = TryToRead(raidPtr, col);
return (retcode);
}
static int
TryToRead(RF_Raid_t *raidPtr, RF_RowCol_t col)
{
RF_PerDiskReconCtrl_t *ctrl = &raidPtr->reconControl->perDiskInfo[col];
RF_SectorCount_t sectorsPerRU = raidPtr->Layout.sectorsPerStripeUnit * raidPtr->Layout.SUsPerRU;
RF_StripeNum_t psid = ctrl->curPSID;
RF_ReconUnitNum_t which_ru = ctrl->ru_count;
RF_DiskQueueData_t *req;
int status;
RF_ReconParityStripeStatus_t *pssPtr, *newpssPtr;
if (CheckHeadSeparation(raidPtr, ctrl, col, ctrl->headSepCounter, which_ru))
return (0);
newpssPtr = rf_AllocPSStatus(raidPtr);
RF_LOCK_PSS_MUTEX(raidPtr, psid);
pssPtr = rf_LookupRUStatus(raidPtr, raidPtr->reconControl->pssTable, psid, which_ru, RF_PSS_CREATE, newpssPtr);
if (pssPtr != newpssPtr) {
rf_FreePSStatus(raidPtr, newpssPtr);
}
status = CheckForcedOrBlockedReconstruction(raidPtr, pssPtr, ctrl, col, psid, which_ru);
if (status == RF_PSS_RECON_BLOCKED) {
Dprintf2("RECON: Stalling psid %ld ru %d: recon blocked\n", psid, which_ru);
goto out;
} else
if (status == RF_PSS_FORCED_ON_WRITE) {
rf_CauseReconEvent(raidPtr, col, NULL, RF_REVENT_SKIP);
goto out;
}
if (rf_CheckRUReconstructed(raidPtr->reconControl->reconMap, ctrl->rbuf->failedDiskSectorOffset)) {
Dprintf2("RECON: Skipping psid %ld ru %d: prior recon after stall\n", psid, which_ru);
if (pssPtr == newpssPtr)
rf_PSStatusDelete(raidPtr, raidPtr->reconControl->pssTable, pssPtr);
rf_CauseReconEvent(raidPtr, col, NULL, RF_REVENT_SKIP);
goto out;
}
Dprintf4("RECON: Read for psid %ld on col %d offset %ld buf %lx\n",
psid, col, ctrl->diskOffset, ctrl->rbuf->buffer);
#if RF_ACC_TRACE > 0
RF_ETIMER_STOP(raidPtr->recon_tracerecs[col].recon_timer);
RF_ETIMER_EVAL(raidPtr->recon_tracerecs[col].recon_timer);
raidPtr->recon_tracerecs[col].specific.recon.recon_start_to_fetch_us =
RF_ETIMER_VAL_US(raidPtr->recon_tracerecs[col].recon_timer);
RF_ETIMER_START(raidPtr->recon_tracerecs[col].recon_timer);
#endif
req = rf_CreateDiskQueueData(RF_IO_TYPE_READ, ctrl->diskOffset, sectorsPerRU, ctrl->rbuf->buffer, psid, which_ru,
ReconReadDoneProc, (void *) ctrl,
#if RF_ACC_TRACE > 0
&raidPtr->recon_tracerecs[col],
#else
NULL,
#endif
(void *) raidPtr, 0, NULL);
ctrl->rbuf->arg = (void *) req;
rf_DiskIOEnqueue(&raidPtr->Queues[col], req, RF_IO_RECON_PRIORITY);
pssPtr->issued[col] = 1;
out:
RF_UNLOCK_PSS_MUTEX(raidPtr, psid);
return (0);
}
static int
ComputePSDiskOffsets(RF_Raid_t *raidPtr, RF_StripeNum_t psid,
RF_RowCol_t col, RF_SectorNum_t *outDiskOffset,
RF_SectorNum_t *outFailedDiskSectorOffset,
RF_RowCol_t *spCol, RF_SectorNum_t *spOffset)
{
RF_RaidLayout_t *layoutPtr = &raidPtr->Layout;
RF_RowCol_t fcol = raidPtr->reconControl->fcol;
RF_RaidAddr_t sosRaidAddress;
RF_RowCol_t *diskids;
u_int i, j, k, i_offset, j_offset;
RF_RowCol_t pcol;
int testcol;
RF_SectorNum_t poffset;
char i_is_parity = 0, j_is_parity = 0;
RF_RowCol_t stripeWidth = layoutPtr->numDataCol + layoutPtr->numParityCol;
sosRaidAddress = rf_ParityStripeIDToRaidAddress(layoutPtr, psid);
(layoutPtr->map->IdentifyStripe) (raidPtr, sosRaidAddress, &diskids);
RF_ASSERT(diskids);
for (i = 0; i < stripeWidth; i++) {
if (col == diskids[i])
break;
}
if (i == stripeWidth)
goto skipit;
for (j = 0; j < stripeWidth; j++) {
if (fcol == diskids[j])
break;
}
if (j == stripeWidth) {
goto skipit;
}
(layoutPtr->map->MapParity) (raidPtr, sosRaidAddress, &pcol, &poffset, RF_DONT_REMAP);
for (k = 0; k < stripeWidth; k++)
if (diskids[k] == pcol)
break;
RF_ASSERT(k < stripeWidth);
i_offset = i;
j_offset = j;
if (k < i)
i_offset--;
else
if (k == i) {
i_is_parity = 1;
i_offset = 0;
}
if (k < j)
j_offset--;
else
if (k == j) {
j_is_parity = 1;
j_offset = 0;
}
if (i_is_parity)
layoutPtr->map->MapParity(raidPtr, sosRaidAddress + i_offset * layoutPtr->sectorsPerStripeUnit, &testcol, outDiskOffset, RF_DONT_REMAP);
else
layoutPtr->map->MapSector(raidPtr, sosRaidAddress + i_offset * layoutPtr->sectorsPerStripeUnit, &testcol, outDiskOffset, RF_DONT_REMAP);
RF_ASSERT(col == testcol);
if (j_is_parity)
layoutPtr->map->MapParity(raidPtr, sosRaidAddress + j_offset * layoutPtr->sectorsPerStripeUnit, &testcol, outFailedDiskSectorOffset, RF_DONT_REMAP);
else
layoutPtr->map->MapSector(raidPtr, sosRaidAddress + j_offset * layoutPtr->sectorsPerStripeUnit, &testcol, outFailedDiskSectorOffset, RF_DONT_REMAP);
RF_ASSERT(fcol == testcol);
#if RF_INCLUDE_PARITY_DECLUSTERING_DS > 0
if (layoutPtr->map->flags & RF_DISTRIBUTE_SPARE) {
if (j_is_parity)
layoutPtr->map->MapParity(raidPtr, sosRaidAddress + j_offset * layoutPtr->sectorsPerStripeUnit, spCol, spOffset, RF_REMAP);
else
layoutPtr->map->MapSector(raidPtr, sosRaidAddress + j_offset * layoutPtr->sectorsPerStripeUnit, spCol, spOffset, RF_REMAP);
} else {
#endif
*spCol = raidPtr->reconControl->spareCol;
*spOffset = *outFailedDiskSectorOffset;
#if RF_INCLUDE_PARITY_DECLUSTERING_DS > 0
}
#endif
return (0);
skipit:
Dprintf2("RECON: Skipping psid %ld: nothing needed from c%d\n",
psid, col);
return (1);
}
static int
IssueNextWriteRequest(RF_Raid_t *raidPtr)
{
RF_RaidLayout_t *layoutPtr = &raidPtr->Layout;
RF_SectorCount_t sectorsPerRU = layoutPtr->sectorsPerStripeUnit * layoutPtr->SUsPerRU;
#if RF_ACC_TRACE > 0
RF_RowCol_t fcol = raidPtr->reconControl->fcol;
#endif
RF_ReconBuffer_t *rbuf;
RF_DiskQueueData_t *req;
rbuf = rf_GetFullReconBuffer(raidPtr->reconControl);
RF_ASSERT(rbuf);
RF_ASSERT(rbuf->pssPtr);
rbuf->pssPtr->writeRbuf = rbuf;
rbuf->pssPtr = NULL;
Dprintf6("RECON: New write (c %d offs %d) for psid %ld ru %d (failed disk offset %ld) buf %lx\n",
rbuf->spCol, rbuf->spOffset, rbuf->parityStripeID,
rbuf->which_ru, rbuf->failedDiskSectorOffset, rbuf->buffer);
Dprintf6("RECON: new write psid %ld %02x %02x %02x %02x %02x\n",
rbuf->parityStripeID, rbuf->buffer[0] & 0xff, rbuf->buffer[1] & 0xff,
rbuf->buffer[2] & 0xff, rbuf->buffer[3] & 0xff, rbuf->buffer[4] & 0xff);
req = rf_CreateDiskQueueData(RF_IO_TYPE_WRITE, rbuf->spOffset,
sectorsPerRU, rbuf->buffer,
rbuf->parityStripeID, rbuf->which_ru,
ReconWriteDoneProc, (void *) rbuf,
#if RF_ACC_TRACE > 0
&raidPtr->recon_tracerecs[fcol],
#else
NULL,
#endif
(void *) raidPtr, 0, NULL);
rbuf->arg = (void *) req;
rf_lock_mutex2(raidPtr->reconControl->rb_mutex);
raidPtr->reconControl->pending_writes++;
rf_unlock_mutex2(raidPtr->reconControl->rb_mutex);
rf_DiskIOEnqueue(&raidPtr->Queues[rbuf->spCol], req, RF_IO_RECON_PRIORITY);
return (0);
}
static void
ReconReadDoneProc(void *arg, int status)
{
RF_PerDiskReconCtrl_t *ctrl = (RF_PerDiskReconCtrl_t *) arg;
RF_Raid_t *raidPtr;
if (ctrl->reconCtrl == NULL)
return;
raidPtr = ctrl->reconCtrl->reconDesc->raidPtr;
if (status) {
printf("raid%d: Recon read failed: %d\n", raidPtr->raidid, status);
rf_CauseReconEvent(raidPtr, ctrl->col, NULL, RF_REVENT_READ_FAILED);
return;
}
#if RF_ACC_TRACE > 0
RF_ETIMER_STOP(raidPtr->recon_tracerecs[ctrl->col].recon_timer);
RF_ETIMER_EVAL(raidPtr->recon_tracerecs[ctrl->col].recon_timer);
raidPtr->recon_tracerecs[ctrl->col].specific.recon.recon_fetch_to_return_us =
RF_ETIMER_VAL_US(raidPtr->recon_tracerecs[ctrl->col].recon_timer);
RF_ETIMER_START(raidPtr->recon_tracerecs[ctrl->col].recon_timer);
#endif
rf_CauseReconEvent(raidPtr, ctrl->col, NULL, RF_REVENT_READDONE);
return;
}
static void
ReconWriteDoneProc(void *arg, int status)
{
RF_ReconBuffer_t *rbuf = (RF_ReconBuffer_t *) arg;
if (rbuf->raidPtr->reconControl == NULL)
return;
Dprintf2("Reconstruction completed on psid %ld ru %d\n", rbuf->parityStripeID, rbuf->which_ru);
if (status) {
printf("raid%d: Recon write failed (status %d(0x%x))!\n", rbuf->raidPtr->raidid,status,status);
rf_CauseReconEvent(rbuf->raidPtr, rbuf->col, arg, RF_REVENT_WRITE_FAILED);
return;
}
rf_CauseReconEvent(rbuf->raidPtr, rbuf->col, arg, RF_REVENT_WRITEDONE);
}
static void
CheckForNewMinHeadSep(RF_Raid_t *raidPtr, RF_HeadSepLimit_t hsCtr)
{
RF_ReconCtrl_t *reconCtrlPtr = raidPtr->reconControl;
RF_HeadSepLimit_t new_min;
RF_RowCol_t i;
RF_CallbackValueDesc_t *p;
RF_ASSERT(hsCtr >= reconCtrlPtr->minHeadSepCounter);
rf_lock_mutex2(reconCtrlPtr->rb_mutex);
while(reconCtrlPtr->rb_lock) {
rf_wait_cond2(reconCtrlPtr->rb_cv, reconCtrlPtr->rb_mutex);
}
reconCtrlPtr->rb_lock = 1;
rf_unlock_mutex2(reconCtrlPtr->rb_mutex);
new_min = ~(1L << (8 * sizeof(long) - 1));
for (i = 0; i < raidPtr->numCol; i++)
if (i != reconCtrlPtr->fcol) {
if (reconCtrlPtr->perDiskInfo[i].headSepCounter < new_min)
new_min = reconCtrlPtr->perDiskInfo[i].headSepCounter;
}
if (new_min != reconCtrlPtr->minHeadSepCounter) {
reconCtrlPtr->minHeadSepCounter = new_min;
Dprintf1("RECON: new min head pos counter val is %ld\n", new_min);
while (reconCtrlPtr->headSepCBList) {
if (reconCtrlPtr->headSepCBList->v > new_min)
break;
p = reconCtrlPtr->headSepCBList;
reconCtrlPtr->headSepCBList = p->next;
p->next = NULL;
rf_CauseReconEvent(raidPtr, p->col, NULL, RF_REVENT_HEADSEPCLEAR);
rf_FreeCallbackValueDesc(raidPtr, p);
}
}
rf_lock_mutex2(reconCtrlPtr->rb_mutex);
reconCtrlPtr->rb_lock = 0;
rf_broadcast_cond2(reconCtrlPtr->rb_cv);
rf_unlock_mutex2(reconCtrlPtr->rb_mutex);
}
static int
CheckHeadSeparation(RF_Raid_t *raidPtr, RF_PerDiskReconCtrl_t *ctrl,
RF_RowCol_t col, RF_HeadSepLimit_t hsCtr,
RF_ReconUnitNum_t which_ru)
{
RF_ReconCtrl_t *reconCtrlPtr = raidPtr->reconControl;
RF_CallbackValueDesc_t *cb, *p, *pt;
int retval = 0;
rf_lock_mutex2(reconCtrlPtr->rb_mutex);
while(reconCtrlPtr->rb_lock) {
rf_wait_cond2(reconCtrlPtr->rb_cv, reconCtrlPtr->rb_mutex);
}
reconCtrlPtr->rb_lock = 1;
rf_unlock_mutex2(reconCtrlPtr->rb_mutex);
if ((raidPtr->headSepLimit >= 0) &&
((ctrl->headSepCounter - reconCtrlPtr->minHeadSepCounter) > raidPtr->headSepLimit)) {
Dprintf5("raid%d: RECON: head sep stall: col %d hsCtr %ld minHSCtr %ld limit %ld\n",
raidPtr->raidid, col, ctrl->headSepCounter,
reconCtrlPtr->minHeadSepCounter,
raidPtr->headSepLimit);
cb = rf_AllocCallbackValueDesc(raidPtr);
cb->v = (ctrl->headSepCounter - raidPtr->headSepLimit + raidPtr->headSepLimit / 5);
cb->col = col;
cb->next = NULL;
p = reconCtrlPtr->headSepCBList;
if (!p)
reconCtrlPtr->headSepCBList = cb;
else
if (cb->v < p->v) {
cb->next = reconCtrlPtr->headSepCBList;
reconCtrlPtr->headSepCBList = cb;
} else {
for (pt = p, p = p->next; p && (p->v < cb->v); pt = p, p = p->next);
cb->next = p;
pt->next = cb;
}
retval = 1;
#if RF_RECON_STATS > 0
ctrl->reconCtrl->reconDesc->hsStallCount++;
#endif
}
rf_lock_mutex2(reconCtrlPtr->rb_mutex);
reconCtrlPtr->rb_lock = 0;
rf_broadcast_cond2(reconCtrlPtr->rb_cv);
rf_unlock_mutex2(reconCtrlPtr->rb_mutex);
return (retval);
}
static int
CheckForcedOrBlockedReconstruction(RF_Raid_t *raidPtr,
RF_ReconParityStripeStatus_t *pssPtr,
RF_PerDiskReconCtrl_t *ctrl,
RF_RowCol_t col,
RF_StripeNum_t psid,
RF_ReconUnitNum_t which_ru)
{
RF_CallbackValueDesc_t *cb;
int retcode = 0;
if ((pssPtr->flags & RF_PSS_FORCED_ON_READ) || (pssPtr->flags & RF_PSS_FORCED_ON_WRITE))
retcode = RF_PSS_FORCED_ON_WRITE;
else
if (pssPtr->flags & RF_PSS_RECON_BLOCKED) {
Dprintf3("RECON: col %d blocked at psid %ld ru %d\n", col, psid, which_ru);
cb = rf_AllocCallbackValueDesc(raidPtr);
cb->col = col;
cb->next = pssPtr->blockWaitList;
pssPtr->blockWaitList = cb;
retcode = RF_PSS_RECON_BLOCKED;
}
if (!retcode)
pssPtr->flags |= RF_PSS_UNDER_RECON;
return (retcode);
}
int
rf_ForceOrBlockRecon(RF_Raid_t *raidPtr, RF_AccessStripeMap_t *asmap,
void (*cbFunc)(void *), void *cbArg)
{
RF_StripeNum_t stripeID = asmap->stripeID;
RF_SectorCount_t sectorsPerRU = raidPtr->Layout.sectorsPerStripeUnit * raidPtr->Layout.SUsPerRU;
RF_ReconParityStripeStatus_t *pssPtr, *newpssPtr;
RF_StripeNum_t psid;
RF_SectorNum_t offset, fd_offset;
RF_RowCol_t *diskids;
RF_ReconUnitNum_t which_ru;
RF_RowCol_t fcol, diskno, i;
RF_ReconBuffer_t *new_rbuf;
RF_DiskQueueData_t *req;
RF_CallbackFuncDesc_t *cb;
int nPromoted;
psid = rf_MapStripeIDToParityStripeID(&raidPtr->Layout, stripeID, &which_ru);
newpssPtr = rf_AllocPSStatus(raidPtr);
RF_LOCK_PSS_MUTEX(raidPtr, psid);
pssPtr = rf_LookupRUStatus(raidPtr, raidPtr->reconControl->pssTable, psid, which_ru, RF_PSS_CREATE | RF_PSS_RECON_BLOCKED, newpssPtr);
if (pssPtr != newpssPtr) {
rf_FreePSStatus(raidPtr, newpssPtr);
}
if (!(pssPtr->flags & RF_PSS_UNDER_RECON)) {
RF_UNLOCK_PSS_MUTEX(raidPtr, psid);
return (0);
}
if (!(pssPtr->flags & RF_PSS_FORCED_ON_WRITE) && !(pssPtr->flags & RF_PSS_FORCED_ON_READ)) {
DDprintf1("Forcing recon on psid %ld\n", psid);
pssPtr->flags |= RF_PSS_FORCED_ON_WRITE;
pssPtr->flags &= ~RF_PSS_RECON_BLOCKED;
fcol = raidPtr->reconControl->fcol;
(raidPtr->Layout.map->IdentifyStripe) (raidPtr, asmap->raidAddress, &diskids);
for (i = 0; i < raidPtr->Layout.numDataCol + raidPtr->Layout.numParityCol; i++)
if ((diskno = diskids[i]) != fcol) {
if (pssPtr->issued[diskno]) {
nPromoted = rf_DiskIOPromote(&raidPtr->Queues[diskno], psid, which_ru);
if (rf_reconDebug && nPromoted)
printf("raid%d: promoted read from col %d\n", raidPtr->raidid, diskno);
} else {
new_rbuf = rf_MakeReconBuffer(raidPtr, diskno, RF_RBUF_TYPE_FORCED);
ComputePSDiskOffsets(raidPtr, psid, diskno, &offset, &fd_offset,
&new_rbuf->spCol, &new_rbuf->spOffset);
new_rbuf->parityStripeID = psid;
new_rbuf->which_ru = which_ru;
new_rbuf->failedDiskSectorOffset = fd_offset;
new_rbuf->priority = RF_IO_NORMAL_PRIORITY;
req = rf_CreateDiskQueueData(RF_IO_TYPE_READ, offset + which_ru * sectorsPerRU, sectorsPerRU, new_rbuf->buffer,
psid, which_ru,
ForceReconReadDoneProc,
(void *) new_rbuf,
NULL, (void *) raidPtr, 0, NULL);
new_rbuf->arg = req;
rf_DiskIOEnqueue(&raidPtr->Queues[diskno], req, RF_IO_NORMAL_PRIORITY);
Dprintf2("raid%d: Issued new read req on col %d\n", raidPtr->raidid, diskno);
}
}
if (rf_DiskIOPromote(&raidPtr->Queues[fcol], psid, which_ru))
if (rf_reconDebug)
printf("raid%d: promoted write to col %d\n",
raidPtr->raidid, fcol);
}
cb = rf_AllocCallbackFuncDesc(raidPtr);
cb->callbackFunc = cbFunc;
cb->callbackArg = cbArg;
cb->next = pssPtr->procWaitList;
pssPtr->procWaitList = cb;
DDprintf2("raid%d: Waiting for forced recon on psid %ld\n",
raidPtr->raidid, psid);
RF_UNLOCK_PSS_MUTEX(raidPtr, psid);
return (1);
}
static void
ForceReconReadDoneProc(void *arg, int status)
{
RF_ReconBuffer_t *rbuf = arg;
if (rbuf->raidPtr->reconControl == NULL)
return;
if (status) {
printf("raid%d: Forced recon read failed!\n", rbuf->raidPtr->raidid);
rf_CauseReconEvent(rbuf->raidPtr, rbuf->col, (void *) rbuf, RF_REVENT_FORCEDREAD_FAILED);
return;
}
rf_CauseReconEvent(rbuf->raidPtr, rbuf->col, (void *) rbuf, RF_REVENT_FORCEDREADDONE);
}
int
rf_UnblockRecon(RF_Raid_t *raidPtr, RF_AccessStripeMap_t *asmap)
{
RF_StripeNum_t stripeID = asmap->stripeID;
RF_ReconParityStripeStatus_t *pssPtr;
RF_ReconUnitNum_t which_ru;
RF_StripeNum_t psid;
RF_CallbackValueDesc_t *cb;
psid = rf_MapStripeIDToParityStripeID(&raidPtr->Layout, stripeID, &which_ru);
RF_LOCK_PSS_MUTEX(raidPtr, psid);
pssPtr = rf_LookupRUStatus(raidPtr, raidPtr->reconControl->pssTable, psid, which_ru, RF_PSS_NONE, NULL);
if (!pssPtr) {
#if (RF_DEBUG_RECON > 0) || (RF_DEBUG_PSS > 0)
if (rf_reconDebug || rf_pssDebug)
printf("Warning: no pss descriptor upon unblock on psid %ld RU %d\n", (long) psid, which_ru);
#endif
goto out;
}
pssPtr->blockCount--;
Dprintf3("raid%d: unblocking recon on psid %ld: blockcount is %d\n",
raidPtr->raidid, psid, pssPtr->blockCount);
if (pssPtr->blockCount == 0) {
pssPtr->flags &= ~RF_PSS_RECON_BLOCKED;
while (pssPtr->blockWaitList) {
cb = pssPtr->blockWaitList;
pssPtr->blockWaitList = cb->next;
cb->next = NULL;
rf_CauseReconEvent(raidPtr, cb->col, NULL, RF_REVENT_BLOCKCLEAR);
rf_FreeCallbackValueDesc(raidPtr, cb);
}
if (!(pssPtr->flags & RF_PSS_UNDER_RECON)) {
rf_PSStatusDelete(raidPtr, raidPtr->reconControl->pssTable, pssPtr);
}
}
out:
RF_UNLOCK_PSS_MUTEX(raidPtr, psid);
return (0);
}
void
rf_WakeupHeadSepCBWaiters(RF_Raid_t *raidPtr)
{
RF_CallbackValueDesc_t *p;
rf_lock_mutex2(raidPtr->reconControl->rb_mutex);
while(raidPtr->reconControl->rb_lock) {
rf_wait_cond2(raidPtr->reconControl->rb_cv,
raidPtr->reconControl->rb_mutex);
}
raidPtr->reconControl->rb_lock = 1;
rf_unlock_mutex2(raidPtr->reconControl->rb_mutex);
while (raidPtr->reconControl->headSepCBList) {
p = raidPtr->reconControl->headSepCBList;
raidPtr->reconControl->headSepCBList = p->next;
p->next = NULL;
rf_CauseReconEvent(raidPtr, p->col, NULL, RF_REVENT_HEADSEPCLEAR);
rf_FreeCallbackValueDesc(raidPtr, p);
}
rf_lock_mutex2(raidPtr->reconControl->rb_mutex);
raidPtr->reconControl->rb_lock = 0;
rf_broadcast_cond2(raidPtr->reconControl->rb_cv);
rf_unlock_mutex2(raidPtr->reconControl->rb_mutex);
}