File: [local] / sys / dev / raidframe / rf_paritylog.c (download)
Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:09:49 2008 UTC (16 years, 6 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD Changes since 1.1: +0 -0 lines
Import of OpenBSD 4.2 release kernel tree with initial code to support
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO
Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)
|
/* $OpenBSD: rf_paritylog.c,v 1.5 2002/12/16 07:01:04 tdeval Exp $ */
/* $NetBSD: rf_paritylog.c,v 1.5 2000/01/07 03:41:01 oster Exp $ */
/*
* Copyright (c) 1995 Carnegie-Mellon University.
* All rights reserved.
*
* Author: William V. Courtright II
*
* Permission to use, copy, modify and distribute this software and
* its documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
* FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*/
/*
* Code for manipulating in-core parity logs.
*/
#include "rf_archs.h"
#if RF_INCLUDE_PARITYLOGGING > 0
/*
* Append-only log for recording parity "update" and "overwrite" records.
*/
#include "rf_types.h"
#include "rf_threadstuff.h"
#include "rf_mcpair.h"
#include "rf_raid.h"
#include "rf_dag.h"
#include "rf_dagfuncs.h"
#include "rf_desc.h"
#include "rf_layout.h"
#include "rf_diskqueue.h"
#include "rf_etimer.h"
#include "rf_paritylog.h"
#include "rf_general.h"
#include "rf_map.h"
#include "rf_paritylogging.h"
#include "rf_paritylogDiskMgr.h"
RF_CommonLogData_t *rf_AllocParityLogCommonData(RF_Raid_t *);
void rf_FreeParityLogCommonData(RF_CommonLogData_t *);
RF_ParityLogData_t *rf_AllocParityLogData(RF_Raid_t *);
void rf_FreeParityLogData(RF_ParityLogData_t *);
void rf_EnqueueParityLogData(RF_ParityLogData_t *, RF_ParityLogData_t **,
RF_ParityLogData_t **);
RF_ParityLogData_t *rf_DequeueParityLogData(RF_Raid_t *, RF_ParityLogData_t **,
RF_ParityLogData_t **, int);
void rf_RequeueParityLogData(RF_ParityLogData_t *, RF_ParityLogData_t **,
RF_ParityLogData_t **);
RF_ParityLogData_t *rf_DequeueMatchingLogData(RF_Raid_t *,
RF_ParityLogData_t **, RF_ParityLogData_t **);
RF_ParityLog_t *rf_AcquireParityLog(RF_ParityLogData_t *, int);
void rf_ReintLog(RF_Raid_t *, int, RF_ParityLog_t *);
void rf_FlushLog(RF_Raid_t *, RF_ParityLog_t *);
int rf_DumpParityLogToDisk(int, RF_ParityLogData_t *);
RF_CommonLogData_t *
rf_AllocParityLogCommonData(RF_Raid_t *raidPtr)
{
RF_CommonLogData_t *common = NULL;
int rc;
/*
* Return a struct for holding common parity log information from the
* free list (rf_parityLogDiskQueue.freeCommonList). If the free list
* is empty, call RF_Malloc to create a new structure. NON-BLOCKING
*/
RF_LOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
if (raidPtr->parityLogDiskQueue.freeCommonList) {
common = raidPtr->parityLogDiskQueue.freeCommonList;
raidPtr->parityLogDiskQueue.freeCommonList =
raidPtr->parityLogDiskQueue.freeCommonList->next;
RF_UNLOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
} else {
RF_UNLOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
RF_Malloc(common, sizeof(RF_CommonLogData_t),
(RF_CommonLogData_t *));
rc = rf_mutex_init(&common->mutex);
if (rc) {
RF_ERRORMSG3("Unable to init mutex file %s line %d"
" rc=%d\n", __FILE__, __LINE__, rc);
RF_Free(common, sizeof(RF_CommonLogData_t));
common = NULL;
}
}
common->next = NULL;
return (common);
}
void
rf_FreeParityLogCommonData(RF_CommonLogData_t *common)
{
RF_Raid_t *raidPtr;
/*
* Insert a single struct for holding parity log information (data)
* into the free list (rf_parityLogDiskQueue.freeCommonList).
* NON-BLOCKING
*/
raidPtr = common->raidPtr;
RF_LOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
common->next = raidPtr->parityLogDiskQueue.freeCommonList;
raidPtr->parityLogDiskQueue.freeCommonList = common;
RF_UNLOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
}
RF_ParityLogData_t *
rf_AllocParityLogData(RF_Raid_t *raidPtr)
{
RF_ParityLogData_t *data = NULL;
/*
* Return a struct for holding parity log information from the free
* list (rf_parityLogDiskQueue.freeList). If the free list is empty,
* call RF_Malloc to create a new structure. NON-BLOCKING
*/
RF_LOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
if (raidPtr->parityLogDiskQueue.freeDataList) {
data = raidPtr->parityLogDiskQueue.freeDataList;
raidPtr->parityLogDiskQueue.freeDataList =
raidPtr->parityLogDiskQueue.freeDataList->next;
RF_UNLOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
} else {
RF_UNLOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
RF_Malloc(data, sizeof(RF_ParityLogData_t),
(RF_ParityLogData_t *));
}
data->next = NULL;
data->prev = NULL;
return (data);
}
void
rf_FreeParityLogData(RF_ParityLogData_t *data)
{
RF_ParityLogData_t *nextItem;
RF_Raid_t *raidPtr;
/*
* Insert a linked list of structs for holding parity log information
* (data) into the free list (parityLogDiskQueue.freeList).
* NON-BLOCKING
*/
raidPtr = data->common->raidPtr;
RF_LOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
while (data) {
nextItem = data->next;
data->next = raidPtr->parityLogDiskQueue.freeDataList;
raidPtr->parityLogDiskQueue.freeDataList = data;
data = nextItem;
}
RF_UNLOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
}
void
rf_EnqueueParityLogData(RF_ParityLogData_t *data, RF_ParityLogData_t **head,
RF_ParityLogData_t **tail)
{
RF_Raid_t *raidPtr;
/*
* Insert an in-core parity log (*data) into the head of a disk queue
* (*head, *tail). NON-BLOCKING
*/
raidPtr = data->common->raidPtr;
if (rf_parityLogDebug)
printf("[enqueueing parity log data, region %d,"
" raidAddress %d, numSector %d]\n", data->regionID,
(int) data->diskAddress.raidAddress,
(int) data->diskAddress.numSector);
RF_ASSERT(data->prev == NULL);
RF_ASSERT(data->next == NULL);
RF_LOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
if (*head) {
/* Insert into head of queue. */
RF_ASSERT((*head)->prev == NULL);
RF_ASSERT((*tail)->next == NULL);
data->next = *head;
(*head)->prev = data;
*head = data;
} else {
/* Insert into empty list. */
RF_ASSERT(*head == NULL);
RF_ASSERT(*tail == NULL);
*head = data;
*tail = data;
}
RF_ASSERT((*head)->prev == NULL);
RF_ASSERT((*tail)->next == NULL);
RF_UNLOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
}
RF_ParityLogData_t *
rf_DequeueParityLogData(RF_Raid_t *raidPtr, RF_ParityLogData_t **head,
RF_ParityLogData_t **tail, int ignoreLocks)
{
RF_ParityLogData_t *data;
/*
* Remove and return an in-core parity log from the tail of a disk
* queue (*head, *tail). NON-BLOCKING
*/
/* Remove from tail, preserving FIFO order. */
if (!ignoreLocks)
RF_LOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
data = *tail;
if (data) {
if (*head == *tail) {
/* Removing last item from queue. */
*head = NULL;
*tail = NULL;
} else {
*tail = (*tail)->prev;
(*tail)->next = NULL;
RF_ASSERT((*head)->prev == NULL);
RF_ASSERT((*tail)->next == NULL);
}
data->next = NULL;
data->prev = NULL;
if (rf_parityLogDebug)
printf("[dequeueing parity log data, region %d,"
" raidAddress %d, numSector %d]\n", data->regionID,
(int) data->diskAddress.raidAddress,
(int) data->diskAddress.numSector);
}
if (*head) {
RF_ASSERT((*head)->prev == NULL);
RF_ASSERT((*tail)->next == NULL);
}
if (!ignoreLocks)
RF_UNLOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
return (data);
}
void
rf_RequeueParityLogData(RF_ParityLogData_t *data, RF_ParityLogData_t **head,
RF_ParityLogData_t **tail)
{
RF_Raid_t *raidPtr;
/*
* Insert an in-core parity log (*data) into the tail of a disk queue
* (*head, *tail). NON-BLOCKING
*/
raidPtr = data->common->raidPtr;
RF_ASSERT(data);
if (rf_parityLogDebug)
printf("[requeueing parity log data, region %d,"
" raidAddress %d, numSector %d]\n", data->regionID,
(int) data->diskAddress.raidAddress,
(int) data->diskAddress.numSector);
RF_LOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
if (*tail) {
/* Append to tail of list. */
data->prev = *tail;
data->next = NULL;
(*tail)->next = data;
*tail = data;
} else {
/* Inserting into an empty list. */
*head = data;
*tail = data;
(*head)->prev = NULL;
(*tail)->next = NULL;
}
RF_ASSERT((*head)->prev == NULL);
RF_ASSERT((*tail)->next == NULL);
RF_UNLOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
}
RF_ParityLogData_t *
rf_CreateParityLogData(RF_ParityRecordType_t operation, RF_PhysDiskAddr_t *pda,
caddr_t bufPtr, RF_Raid_t *raidPtr,
int (*wakeFunc) (RF_DagNode_t * node, int status),
void *wakeArg, RF_AccTraceEntry_t *tracerec, RF_Etimer_t startTime)
{
RF_ParityLogData_t *data, *resultHead = NULL, *resultTail = NULL;
RF_CommonLogData_t *common;
RF_PhysDiskAddr_t *diskAddress;
int boundary, offset = 0;
/*
* Return an initialized struct of info to be logged. Build one item
* per physical disk address, one item per region.
*
* NON-BLOCKING
*/
diskAddress = pda;
common = rf_AllocParityLogCommonData(raidPtr);
RF_ASSERT(common);
common->operation = operation;
common->bufPtr = bufPtr;
common->raidPtr = raidPtr;
common->wakeFunc = wakeFunc;
common->wakeArg = wakeArg;
common->tracerec = tracerec;
common->startTime = startTime;
common->cnt = 0;
if (rf_parityLogDebug)
printf("[entering CreateParityLogData]\n");
while (diskAddress) {
common->cnt++;
data = rf_AllocParityLogData(raidPtr);
RF_ASSERT(data);
data->common = common;
data->next = NULL;
data->prev = NULL;
data->regionID = rf_MapRegionIDParityLogging(raidPtr,
diskAddress->startSector);
if (data->regionID == rf_MapRegionIDParityLogging(raidPtr,
diskAddress->startSector + diskAddress->numSector - 1)) {
/* Disk address does not cross a region boundary. */
data->diskAddress = *diskAddress;
data->bufOffset = offset;
offset = offset + diskAddress->numSector;
rf_EnqueueParityLogData(data, &resultHead, &resultTail);
/* Adjust disk address. */
diskAddress = diskAddress->next;
} else {
/* Disk address crosses a region boundary. */
/* Find address where region is crossed. */
boundary = 0;
while (data->regionID ==
rf_MapRegionIDParityLogging(raidPtr,
diskAddress->startSector + boundary))
boundary++;
/* Enter data before the boundary. */
data->diskAddress = *diskAddress;
data->diskAddress.numSector = boundary;
data->bufOffset = offset;
offset += boundary;
rf_EnqueueParityLogData(data, &resultHead, &resultTail);
/* Adjust disk address. */
diskAddress->startSector += boundary;
diskAddress->numSector -= boundary;
}
}
if (rf_parityLogDebug)
printf("[leaving CreateParityLogData]\n");
return (resultHead);
}
RF_ParityLogData_t *
rf_SearchAndDequeueParityLogData(RF_Raid_t *raidPtr, int regionID,
RF_ParityLogData_t **head, RF_ParityLogData_t **tail, int ignoreLocks)
{
RF_ParityLogData_t *w;
/*
* Remove and return an in-core parity log from a specified region
* (regionID). If a matching log is not found, return NULL.
*
* NON-BLOCKING
*/
/*
* walk backward through a list, looking for an entry with a matching
* region ID.
*/
if (!ignoreLocks)
RF_LOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
w = (*tail);
while (w) {
if (w->regionID == regionID) {
/* Remove an element from the list. */
if (w == *tail) {
if (*head == *tail) {
/* Removing only element in the list. */
*head = NULL;
*tail = NULL;
} else {
/* Removing last item in the list. */
*tail = (*tail)->prev;
(*tail)->next = NULL;
RF_ASSERT((*head)->prev == NULL);
RF_ASSERT((*tail)->next == NULL);
}
} else {
if (w == *head) {
/* Removing first item in the list. */
*head = (*head)->next;
(*head)->prev = NULL;
RF_ASSERT((*head)->prev == NULL);
RF_ASSERT((*tail)->next == NULL);
} else {
/*
* Removing an item from the middle of
* the list.
*/
w->prev->next = w->next;
w->next->prev = w->prev;
RF_ASSERT((*head)->prev == NULL);
RF_ASSERT((*tail)->next == NULL);
}
}
w->prev = NULL;
w->next = NULL;
if (rf_parityLogDebug)
printf("[dequeueing parity log data,"
" region %d, raidAddress %d,"
" numSector %d]\n", w->regionID,
(int) w->diskAddress.raidAddress,
(int) w->diskAddress.numSector);
return (w);
} else
w = w->prev;
}
if (!ignoreLocks)
RF_UNLOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
return (NULL);
}
RF_ParityLogData_t *
rf_DequeueMatchingLogData(RF_Raid_t *raidPtr, RF_ParityLogData_t **head,
RF_ParityLogData_t **tail)
{
RF_ParityLogData_t *logDataList, *logData;
int regionID;
/*
* Remove and return an in-core parity log from the tail of a disk
* queue (*head, *tail). Then remove all matching (identical
* regionIDs) logData and return as a linked list.
*
* NON-BLOCKING
*/
logDataList = rf_DequeueParityLogData(raidPtr, head, tail, RF_TRUE);
if (logDataList) {
regionID = logDataList->regionID;
logData = logDataList;
logData->next = rf_SearchAndDequeueParityLogData(raidPtr,
regionID, head, tail, RF_TRUE);
while (logData->next) {
logData = logData->next;
logData->next =
rf_SearchAndDequeueParityLogData(raidPtr, regionID,
head, tail, RF_TRUE);
}
}
return (logDataList);
}
RF_ParityLog_t *
rf_AcquireParityLog(RF_ParityLogData_t *logData, int finish)
{
RF_ParityLog_t *log = NULL;
RF_Raid_t *raidPtr;
/*
* Grab a log buffer from the pool and return it. If no buffers are
* available, return NULL. NON-BLOCKING
*/
raidPtr = logData->common->raidPtr;
RF_LOCK_MUTEX(raidPtr->parityLogPool.mutex);
if (raidPtr->parityLogPool.parityLogs) {
log = raidPtr->parityLogPool.parityLogs;
raidPtr->parityLogPool.parityLogs =
raidPtr->parityLogPool.parityLogs->next;
log->regionID = logData->regionID;
log->numRecords = 0;
log->next = NULL;
raidPtr->logsInUse++;
RF_ASSERT(raidPtr->logsInUse >= 0 &&
raidPtr->logsInUse <= raidPtr->numParityLogs);
} else {
/*
* No logs available, so place ourselves on the queue of work
* waiting on log buffers this is done while
* parityLogPool.mutex is held, to ensure synchronization with
* ReleaseParityLogs.
*/
if (rf_parityLogDebug)
printf("[blocked on log, region %d, finish %d]\n",
logData->regionID, finish);
if (finish)
rf_RequeueParityLogData(logData,
&raidPtr->parityLogDiskQueue.logBlockHead,
&raidPtr->parityLogDiskQueue.logBlockTail);
else
rf_EnqueueParityLogData(logData,
&raidPtr->parityLogDiskQueue.logBlockHead,
&raidPtr->parityLogDiskQueue.logBlockTail);
}
RF_UNLOCK_MUTEX(raidPtr->parityLogPool.mutex);
return (log);
}
void
rf_ReleaseParityLogs(RF_Raid_t *raidPtr, RF_ParityLog_t *firstLog)
{
RF_ParityLogData_t *logDataList;
RF_ParityLog_t *log, *lastLog;
int cnt;
/*
* Insert a linked list of parity logs (firstLog) to the free list
* (parityLogPool.parityLogPool)
*
* NON-BLOCKING
*/
RF_ASSERT(firstLog);
/*
* Before returning logs to global free list, service all requests
* which are blocked on logs. Holding mutexes for parityLogPool and
* parityLogDiskQueue forces synchronization with rf_AcquireParityLog().
*/
RF_LOCK_MUTEX(raidPtr->parityLogPool.mutex);
RF_LOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
logDataList = rf_DequeueMatchingLogData(raidPtr,
&raidPtr->parityLogDiskQueue.logBlockHead,
&raidPtr->parityLogDiskQueue.logBlockTail);
log = firstLog;
if (firstLog)
firstLog = firstLog->next;
log->numRecords = 0;
log->next = NULL;
while (logDataList && log) {
RF_UNLOCK_MUTEX(raidPtr->parityLogPool.mutex);
RF_UNLOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
rf_ParityLogAppend(logDataList, RF_TRUE, &log, RF_FALSE);
if (rf_parityLogDebug)
printf("[finishing up buf-blocked log data,"
" region %d]\n", logDataList->regionID);
if (log == NULL) {
log = firstLog;
if (firstLog) {
firstLog = firstLog->next;
log->numRecords = 0;
log->next = NULL;
}
}
RF_LOCK_MUTEX(raidPtr->parityLogPool.mutex);
RF_LOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
if (log)
logDataList = rf_DequeueMatchingLogData(raidPtr,
&raidPtr->parityLogDiskQueue.logBlockHead,
&raidPtr->parityLogDiskQueue.logBlockTail);
}
/* Return remaining logs to pool. */
if (log) {
log->next = firstLog;
firstLog = log;
}
if (firstLog) {
lastLog = firstLog;
raidPtr->logsInUse--;
RF_ASSERT(raidPtr->logsInUse >= 0 &&
raidPtr->logsInUse <= raidPtr->numParityLogs);
while (lastLog->next) {
lastLog = lastLog->next;
raidPtr->logsInUse--;
RF_ASSERT(raidPtr->logsInUse >= 0 &&
raidPtr->logsInUse <= raidPtr->numParityLogs);
}
lastLog->next = raidPtr->parityLogPool.parityLogs;
raidPtr->parityLogPool.parityLogs = firstLog;
cnt = 0;
log = raidPtr->parityLogPool.parityLogs;
while (log) {
cnt++;
log = log->next;
}
RF_ASSERT(cnt + raidPtr->logsInUse == raidPtr->numParityLogs);
}
RF_UNLOCK_MUTEX(raidPtr->parityLogPool.mutex);
RF_UNLOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
}
void
rf_ReintLog(RF_Raid_t *raidPtr, int regionID, RF_ParityLog_t *log)
{
RF_ASSERT(log);
/*
* Insert an in-core parity log (log) into the disk queue of
* reintegration work. Set the flag (reintInProgress) for the
* specified region (regionID) to indicate that reintegration is in
* progress for this region. NON-BLOCKING
*/
RF_LOCK_MUTEX(raidPtr->regionInfo[regionID].reintMutex);
/* Cleared when reint complete. */
raidPtr->regionInfo[regionID].reintInProgress = RF_TRUE;
if (rf_parityLogDebug)
printf("[requesting reintegration of region %d]\n",
log->regionID);
/* Move record to reintegration queue. */
RF_LOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
log->next = raidPtr->parityLogDiskQueue.reintQueue;
raidPtr->parityLogDiskQueue.reintQueue = log;
RF_UNLOCK_MUTEX(raidPtr->regionInfo[regionID].reintMutex);
RF_UNLOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
RF_SIGNAL_COND(raidPtr->parityLogDiskQueue.cond);
}
void
rf_FlushLog(RF_Raid_t *raidPtr, RF_ParityLog_t *log)
{
/*
* Insert a core log (log) into a list of logs
* (parityLogDiskQueue.flushQueue) waiting to be written to disk.
* NON-BLOCKING
*/
RF_ASSERT(log);
RF_ASSERT(log->numRecords == raidPtr->numSectorsPerLog);
RF_ASSERT(log->next == NULL);
/* Move log to flush queue. */
RF_LOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
log->next = raidPtr->parityLogDiskQueue.flushQueue;
raidPtr->parityLogDiskQueue.flushQueue = log;
RF_UNLOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
RF_SIGNAL_COND(raidPtr->parityLogDiskQueue.cond);
}
int
rf_DumpParityLogToDisk(int finish, RF_ParityLogData_t *logData)
{
int i, diskCount, regionID = logData->regionID;
RF_ParityLog_t *log;
RF_Raid_t *raidPtr;
raidPtr = logData->common->raidPtr;
/*
* Move a core log to disk. If the log disk is full, initiate
* reintegration.
*
* Return (0) if we can enqueue the dump immediately, otherwise return
* (1) to indicate we are blocked on reintegration and control of the
* thread should be relinquished.
*
* Caller must hold regionInfo[regionID].mutex.
*
* NON-BLOCKING
*/
if (rf_parityLogDebug)
printf("[dumping parity log to disk, region %d]\n", regionID);
log = raidPtr->regionInfo[regionID].coreLog;
RF_ASSERT(log->numRecords == raidPtr->numSectorsPerLog);
RF_ASSERT(log->next == NULL);
/* If reintegration is in progress, must queue work. */
RF_LOCK_MUTEX(raidPtr->regionInfo[regionID].reintMutex);
if (raidPtr->regionInfo[regionID].reintInProgress) {
/*
* Can not proceed since this region is currently being
* reintegrated. We can not block, so queue remaining work and
* return.
*/
if (rf_parityLogDebug)
printf("[region %d waiting on reintegration]\n",
regionID);
/*
* XXX Not sure about the use of finish - shouldn't this
* always be "Enqueue" ?
*/
if (finish)
rf_RequeueParityLogData(logData,
&raidPtr->parityLogDiskQueue.reintBlockHead,
&raidPtr->parityLogDiskQueue.reintBlockTail);
else
rf_EnqueueParityLogData(logData,
&raidPtr->parityLogDiskQueue.reintBlockHead,
&raidPtr->parityLogDiskQueue.reintBlockTail);
RF_UNLOCK_MUTEX(raidPtr->regionInfo[regionID].reintMutex);
return (1); /* Relenquish control of this thread. */
}
RF_UNLOCK_MUTEX(raidPtr->regionInfo[regionID].reintMutex);
raidPtr->regionInfo[regionID].coreLog = NULL;
if ((raidPtr->regionInfo[regionID].diskCount) <
raidPtr->regionInfo[regionID].capacity)
/*
* IMPORTANT !!! This loop bound assumes region disk holds an
* integral number of core logs.
*/
{
/* Update disk map for this region. */
diskCount = raidPtr->regionInfo[regionID].diskCount;
for (i = 0; i < raidPtr->numSectorsPerLog; i++) {
raidPtr->regionInfo[regionID].diskMap[i + diskCount]
.operation = log->records[i].operation;
raidPtr->regionInfo[regionID].diskMap[i + diskCount]
.parityAddr = log->records[i].parityAddr;
}
log->diskOffset = diskCount;
raidPtr->regionInfo[regionID].diskCount +=
raidPtr->numSectorsPerLog;
rf_FlushLog(raidPtr, log);
} else {
/*
* No room for log on disk, send it to disk manager and
* request reintegration.
*/
RF_ASSERT(raidPtr->regionInfo[regionID].diskCount ==
raidPtr->regionInfo[regionID].capacity);
rf_ReintLog(raidPtr, regionID, log);
}
if (rf_parityLogDebug)
printf("[finished dumping parity log to disk, region %d]\n",
regionID);
return (0);
}
int
rf_ParityLogAppend(RF_ParityLogData_t *logData, int finish,
RF_ParityLog_t **incomingLog, int clearReintFlag)
{
int regionID, logItem, itemDone;
RF_ParityLogData_t *item;
int punt, done = RF_FALSE;
RF_ParityLog_t *log;
RF_Raid_t *raidPtr;
RF_Etimer_t timer;
int (*wakeFunc) (RF_DagNode_t * node, int status);
void *wakeArg;
/*
* Add parity to the appropriate log, one sector at a time. This
* routine is called is called by dag functions ParityLogUpdateFunc
* and ParityLogOverwriteFunc and therefore MUST BE NONBLOCKING.
*
* Parity to be logged is contained in a linked-list (logData). When
* this routine returns, every sector in the list will be in one of
* three places: 1) entered into the parity log 2) queued, waiting on
* reintegration 3) queued, waiting on a core log.
*
* Blocked work is passed to the ParityLoggingDiskManager for
* completion. Later, as conditions which required the block are
* removed, the work reenters this routine with the "finish" parameter
* set to "RF_TRUE."
*
* NON-BLOCKING
*/
raidPtr = logData->common->raidPtr;
/* Lock the region for the first item in logData. */
RF_ASSERT(logData != NULL);
regionID = logData->regionID;
RF_LOCK_MUTEX(raidPtr->regionInfo[regionID].mutex);
RF_ASSERT(raidPtr->regionInfo[regionID].loggingEnabled);
if (clearReintFlag) {
/*
* Enable flushing for this region. Holding both locks
* provides a synchronization barrier with
* rf_DumpParityLogToDisk.
*/
RF_LOCK_MUTEX(raidPtr->regionInfo[regionID].reintMutex);
RF_LOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
RF_ASSERT(raidPtr->regionInfo[regionID].reintInProgress ==
RF_TRUE);
raidPtr->regionInfo[regionID].diskCount = 0;
raidPtr->regionInfo[regionID].reintInProgress = RF_FALSE;
/* Flushing is now enabled. */
RF_UNLOCK_MUTEX(raidPtr->regionInfo[regionID].reintMutex);
RF_UNLOCK_MUTEX(raidPtr->parityLogDiskQueue.mutex);
}
/* Process each item in logData. */
while (logData) {
/* Remove an item from logData. */
item = logData;
logData = logData->next;
item->next = NULL;
item->prev = NULL;
if (rf_parityLogDebug)
printf("[appending parity log data, region %d,"
" raidAddress %d, numSector %d]\n", item->regionID,
(int) item->diskAddress.raidAddress,
(int) item->diskAddress.numSector);
/* See if we moved to a new region. */
if (regionID != item->regionID) {
RF_UNLOCK_MUTEX(raidPtr->regionInfo[regionID].mutex);
regionID = item->regionID;
RF_LOCK_MUTEX(raidPtr->regionInfo[regionID].mutex);
RF_ASSERT(raidPtr->regionInfo[regionID].loggingEnabled);
}
punt = RF_FALSE;/*
* Set to RF_TRUE if work is blocked. This
* can happen in one of two ways: 1) no core
* log (rf_AcquireParityLog) 2) waiting on
* reintegration (rf_DumpParityLogToDisk).
* If punt is RF_TRUE, the dataItem was queued,
* so skip to next item.
*/
/*
* Process item, one sector at a time, until all sectors
* processed or we punt.
*/
if (item->diskAddress.numSector > 0)
done = RF_FALSE;
else
RF_ASSERT(0);
while (!punt && !done) {
/* Verify that a core log exists for this region. */
if (!raidPtr->regionInfo[regionID].coreLog) {
/*
* Attempt to acquire a parity log. If
* acquisition fails, queue remaining work in
* data item and move to nextItem.
*/
if (incomingLog) {
if (*incomingLog) {
RF_ASSERT((*incomingLog)->next
== NULL);
raidPtr->regionInfo[regionID]
.coreLog = *incomingLog;
raidPtr->regionInfo[regionID]
.coreLog->regionID =
regionID;
*incomingLog = NULL;
} else
raidPtr->regionInfo[regionID]
.coreLog =
rf_AcquireParityLog(item,
finish);
} else
raidPtr->regionInfo[regionID].coreLog =
rf_AcquireParityLog(item, finish);
/*
* Note: rf_AcquireParityLog either returns
* a log or enqueues currentItem.
*/
}
if (!raidPtr->regionInfo[regionID].coreLog)
punt = RF_TRUE; /* Failed to find a core log. */
else {
RF_ASSERT(raidPtr->regionInfo[regionID].coreLog
->next == NULL);
/*
* Verify that the log has room for new
* entries.
*/
/*
* If log is full, dump it to disk and grab a
* new log.
*/
if (raidPtr->regionInfo[regionID].coreLog
->numRecords == raidPtr->numSectorsPerLog)
{
/* Log is full, dump it to disk. */
if (rf_DumpParityLogToDisk(finish,
item))
/*
* Dump unsuccessful, blocked
* on reintegration.
*/
punt = RF_TRUE;
else {
/* Dump was successful. */
if (incomingLog) {
if (*incomingLog) {
RF_ASSERT(
(*incomingLog)->next ==
NULL);
raidPtr->
regionInfo[regionID].coreLog =
*incomingLog;
raidPtr->
regionInfo[regionID].coreLog->
regionID = regionID;
*incomingLog =
NULL;
} else
raidPtr->
regionInfo[regionID].coreLog =
rf_AcquireParityLog(item,
finish);
} else
raidPtr->regionInfo
[regionID].coreLog =
rf_AcquireParityLog(item,
finish);
/*
* If a core log is not
* available, must queue work
* and return.
*/
if (!raidPtr->regionInfo
[regionID].coreLog)
/*
* Blocked on log
* availability.
*/
punt = RF_TRUE;
}
}
}
/*
* If we didn't punt on this item, attempt to add a
* sector to the core log.
*/
if (!punt) {
RF_ASSERT(raidPtr->regionInfo[regionID].coreLog
->next == NULL);
/*
* At this point, we have a core log with
* enough room for a sector.
*/
/* Copy a sector into the log. */
log = raidPtr->regionInfo[regionID].coreLog;
RF_ASSERT(log->numRecords <
raidPtr->numSectorsPerLog);
logItem = log->numRecords++;
log->records[logItem].parityAddr =
item->diskAddress;
RF_ASSERT(log->records[logItem].parityAddr
.startSector >=
raidPtr->regionInfo[regionID]
.parityStartAddr);
RF_ASSERT(log->records[logItem].parityAddr
.startSector <
raidPtr->regionInfo[regionID]
.parityStartAddr +
raidPtr->regionInfo[regionID]
.numSectorsParity);
log->records[logItem].parityAddr.numSector = 1;
log->records[logItem].operation =
item->common->operation;
bcopy((item->common->bufPtr +
(item->bufOffset++ * (1 <<
item->common->raidPtr->logBytesPerSector))),
log->bufPtr + (logItem * (1 <<
item->common->raidPtr->logBytesPerSector)),
(1 << item->common->raidPtr
->logBytesPerSector));
item->diskAddress.numSector--;
item->diskAddress.startSector++;
if (item->diskAddress.numSector == 0)
done = RF_TRUE;
}
}
if (!punt) {
/*
* Processed this item completely, decrement count of
* items to be processed.
*/
RF_ASSERT(item->diskAddress.numSector == 0);
RF_LOCK_MUTEX(item->common->mutex);
item->common->cnt--;
if (item->common->cnt == 0)
itemDone = RF_TRUE;
else
itemDone = RF_FALSE;
RF_UNLOCK_MUTEX(item->common->mutex);
if (itemDone) {
/*
* Finished processing all log data for this
* IO Return structs to free list and invoke
* wakeup function.
*/
/* Grab initial value of timer. */
timer = item->common->startTime;
RF_ETIMER_STOP(timer);
RF_ETIMER_EVAL(timer);
item->common->tracerec->plog_us +=
RF_ETIMER_VAL_US(timer);
if (rf_parityLogDebug)
printf("[waking process for region"
" %d]\n", item->regionID);
wakeFunc = item->common->wakeFunc;
wakeArg = item->common->wakeArg;
rf_FreeParityLogCommonData(item->common);
rf_FreeParityLogData(item);
(wakeFunc) (wakeArg, 0);
} else
rf_FreeParityLogData(item);
}
}
RF_UNLOCK_MUTEX(raidPtr->regionInfo[regionID].mutex);
if (rf_parityLogDebug)
printf("[exiting ParityLogAppend]\n");
return (0);
}
void
rf_EnableParityLogging(RF_Raid_t *raidPtr)
{
int regionID;
for (regionID = 0; regionID < rf_numParityRegions; regionID++) {
RF_LOCK_MUTEX(raidPtr->regionInfo[regionID].mutex);
raidPtr->regionInfo[regionID].loggingEnabled = RF_TRUE;
RF_UNLOCK_MUTEX(raidPtr->regionInfo[regionID].mutex);
}
if (rf_parityLogDebug)
printf("[parity logging enabled]\n");
}
#endif /* RF_INCLUDE_PARITYLOGGING > 0 */