[BACK]Return to rf_dagdegwr.c CVS log [TXT][DIR] Up to [local] / sys / dev / raidframe

Annotation of sys/dev/raidframe/rf_dagdegwr.c, Revision 1.1.1.1

1.1       nbrk        1: /*     $OpenBSD: rf_dagdegwr.c,v 1.6 2006/07/09 22:10:05 mk Exp $      */
                      2: /*     $NetBSD: rf_dagdegwr.c,v 1.5 2000/01/07 03:40:57 oster Exp $    */
                      3:
                      4: /*
                      5:  * Copyright (c) 1995 Carnegie-Mellon University.
                      6:  * All rights reserved.
                      7:  *
                      8:  * Author: Mark Holland, Daniel Stodolsky, William V. Courtright II
                      9:  *
                     10:  * Permission to use, copy, modify and distribute this software and
                     11:  * its documentation is hereby granted, provided that both the copyright
                     12:  * notice and this permission notice appear in all copies of the
                     13:  * software, derivative works or modified versions, and any portions
                     14:  * thereof, and that both notices appear in supporting documentation.
                     15:  *
                     16:  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
                     17:  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
                     18:  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
                     19:  *
                     20:  * Carnegie Mellon requests users of this software to return to
                     21:  *
                     22:  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
                     23:  *  School of Computer Science
                     24:  *  Carnegie Mellon University
                     25:  *  Pittsburgh PA 15213-3890
                     26:  *
                     27:  * any improvements or extensions that they make and grant Carnegie the
                     28:  * rights to redistribute these changes.
                     29:  */
                     30:
                     31: /*
                     32:  * rf_dagdegwr.c
                     33:  *
                     34:  * Code for creating degraded write DAGs.
                     35:  *
                     36:  */
                     37:
                     38: #include "rf_types.h"
                     39: #include "rf_raid.h"
                     40: #include "rf_dag.h"
                     41: #include "rf_dagutils.h"
                     42: #include "rf_dagfuncs.h"
                     43: #include "rf_debugMem.h"
                     44: #include "rf_memchunk.h"
                     45: #include "rf_general.h"
                     46: #include "rf_dagdegwr.h"
                     47:
                     48:
                     49: /*****************************************************************************
                     50:  *
                     51:  * General comments on DAG creation:
                     52:  *
                     53:  * All DAGs in this file use roll-away error recovery. Each DAG has a single
                     54:  * commit node, usually called "Cmt". If an error occurs before the Cmt node
                     55:  * is reached, the execution engine will halt forward execution and work
                     56:  * backward through the graph, executing the undo functions. Assuming that
                     57:  * each node in the graph prior to the Cmt node are undoable and atomic - or -
                     58:  * does not make changes to permanent state, the graph will fail atomically.
                     59:  * If an error occurs after the Cmt node executes, the engine will roll-forward
                     60:  * through the graph, blindly executing nodes until it reaches the end.
                     61:  * If a graph reaches the end, it is assumed to have completed successfully.
                     62:  *
                     63:  * A graph has only 1 Cmt node.
                     64:  *
                     65:  *****************************************************************************/
                     66:
                     67:
                     68: /*****************************************************************************
                     69:  *
                     70:  * The following wrappers map the standard DAG creation interface to the
                     71:  * DAG creation routines. Additionally, these wrappers enable experimentation
                     72:  * with new DAG structures by providing an extra level of indirection, allowing
                     73:  * the DAG creation routines to be replaced at this single point.
                     74:  *
                     75:  *****************************************************************************/
                     76:
                     77: RF_CREATE_DAG_FUNC_DECL(rf_CreateSimpleDegradedWriteDAG)
                     78: {
                     79:        rf_CommonCreateSimpleDegradedWriteDAG(raidPtr, asmap, dag_h, bp,
                     80:            flags, allocList, 1, rf_RecoveryXorFunc, RF_TRUE);
                     81: }
                     82:
                     83: void
                     84: rf_CreateDegradedWriteDAG(RF_Raid_t *raidPtr, RF_AccessStripeMap_t *asmap,
                     85:     RF_DagHeader_t *dag_h, void *bp, RF_RaidAccessFlags_t flags,
                     86:     RF_AllocListElem_t *allocList)
                     87: {
                     88:        RF_RaidLayout_t *layoutPtr = &(raidPtr->Layout);
                     89:        RF_PhysDiskAddr_t *failedPDA = asmap->failedPDAs[0];
                     90:
                     91:        RF_ASSERT(asmap->numDataFailed == 1);
                     92:        dag_h->creator = "DegradedWriteDAG";
                     93:
                     94:        /*
                     95:         * If the access writes only a portion of the failed unit, and also
                     96:         * writes some portion of at least one surviving unit, we create two
                     97:         * DAGs, one for the failed component and one for the non-failed
                     98:         * component, and do them sequentially. Note that the fact that we're
                     99:         * accessing only a portion of the failed unit indicates that the
                    100:         * access either starts or ends in the failed unit, and hence we need
                    101:         * create only two dags. This is inefficient in that the same data or
                    102:         * parity can get read and written twice using this structure. I need
                    103:         * to fix this to do the access all at once.
                    104:         */
                    105:        RF_ASSERT(!(asmap->numStripeUnitsAccessed != 1 &&
                    106:            failedPDA->numSector != layoutPtr->sectorsPerStripeUnit));
                    107:        rf_CreateSimpleDegradedWriteDAG(raidPtr, asmap, dag_h, bp,
                    108:            flags, allocList);
                    109: }
                    110:
                    111:
                    112:
                    113: /*****************************************************************************
                    114:  *
                    115:  * DAG creation code begins here.
                    116:  *
                    117:  *****************************************************************************/
                    118:
                    119:
                    120: /*****************************************************************************
                    121:  *
                    122:  * CommonCreateSimpleDegradedWriteDAG -- creates a DAG to do a degraded-mode
                    123:  * write, which is as follows
                    124:  *
                    125:  *                                        / {Wnq} --\
                    126:  * hdr -> blockNode ->  Rod -> Xor -> Cmt -> Wnp ----> unblock -> term
                    127:  *                  \  {Rod} /            |  Wnd ---/
                    128:  *                                        \ {Wnd} -/
                    129:  *
                    130:  * Commit nodes: Xor, Wnd
                    131:  *
                    132:  * IMPORTANT:
                    133:  * This DAG generator does not work for double-degraded archs since it does not
                    134:  * generate Q.
                    135:  *
                    136:  * This dag is essentially identical to the large-write dag, except that the
                    137:  * write to the failed data unit is suppressed.
                    138:  *
                    139:  * IMPORTANT:  this dag does not work in the case where the access writes only
                    140:  * a portion of the failed unit, and also writes some portion of at least one
                    141:  * surviving SU. this case is handled in CreateDegradedWriteDAG above.
                    142:  *
                    143:  * The block & unblock nodes are leftovers from a previous version. They
                    144:  * do nothing, but I haven't deleted them because it would be a tremendous
                    145:  * effort to put them back in.
                    146:  *
                    147:  * This dag is used whenever one of the data units in a write has failed.
                    148:  * If it is the parity unit that failed, the nonredundant write dag (below)
                    149:  * is used.
                    150:  *
                    151:  *****************************************************************************/
                    152:
                    153: void
                    154: rf_CommonCreateSimpleDegradedWriteDAG(RF_Raid_t *raidPtr,
                    155:     RF_AccessStripeMap_t *asmap, RF_DagHeader_t *dag_h, void *bp,
                    156:     RF_RaidAccessFlags_t flags, RF_AllocListElem_t *allocList, int nfaults,
                    157:     int (*redFunc) (RF_DagNode_t *), int allowBufferRecycle)
                    158: {
                    159:        int nNodes, nRrdNodes, nWndNodes, nXorBufs, i, j, paramNum,
                    160:            rdnodesFaked;
                    161:        RF_DagNode_t *blockNode, *unblockNode, *wnpNode, *wnqNode, *termNode;
                    162:        RF_DagNode_t *nodes, *wndNodes, *rrdNodes, *xorNode, *commitNode;
                    163:        RF_SectorCount_t sectorsPerSU;
                    164:        RF_ReconUnitNum_t which_ru;
                    165:        char *xorTargetBuf = NULL;      /*
                    166:                                         * The target buffer for the XOR
                    167:                                         * operation.
                    168:                                         */
                    169:        char *overlappingPDAs;          /* A temporary array of flags. */
                    170:        RF_AccessStripeMapHeader_t *new_asm_h[2];
                    171:        RF_PhysDiskAddr_t *pda, *parityPDA;
                    172:        RF_StripeNum_t parityStripeID;
                    173:        RF_PhysDiskAddr_t *failedPDA;
                    174:        RF_RaidLayout_t *layoutPtr;
                    175:
                    176:        layoutPtr = &(raidPtr->Layout);
                    177:        parityStripeID = rf_RaidAddressToParityStripeID(layoutPtr,
                    178:            asmap->raidAddress, &which_ru);
                    179:        sectorsPerSU = layoutPtr->sectorsPerStripeUnit;
                    180:        /*
                    181:         * failedPDA points to the pda within the asm that targets
                    182:         * the failed disk.
                    183:         */
                    184:        failedPDA = asmap->failedPDAs[0];
                    185:
                    186:        if (rf_dagDebug)
                    187:                printf("[Creating degraded-write DAG]\n");
                    188:
                    189:        RF_ASSERT(asmap->numDataFailed == 1);
                    190:        dag_h->creator = "SimpleDegradedWriteDAG";
                    191:
                    192:        /*
                    193:         * Generate two ASMs identifying the surviving data
                    194:         * we need in order to recover the lost data.
                    195:         */
                    196:        /* overlappingPDAs array must be zero'd */
                    197:        RF_Calloc(overlappingPDAs, asmap->numStripeUnitsAccessed,
                    198:            sizeof(char), (char *));
                    199:        rf_GenerateFailedAccessASMs(raidPtr, asmap, failedPDA, dag_h,
                    200:            new_asm_h, &nXorBufs, NULL, overlappingPDAs, allocList);
                    201:
                    202:        /* Create all the nodes at once. */
                    203:        nWndNodes = asmap->numStripeUnitsAccessed - 1;  /*
                    204:                                                         * No access is
                    205:                                                         * generated for the
                    206:                                                         * failed pda.
                    207:                                                         */
                    208:
                    209:        nRrdNodes = ((new_asm_h[0]) ?
                    210:            new_asm_h[0]->stripeMap->numStripeUnitsAccessed : 0) +
                    211:            ((new_asm_h[1]) ? new_asm_h[1]->stripeMap->numStripeUnitsAccessed
                    212:                            : 0);
                    213:        /*
                    214:         * XXX
                    215:         *
                    216:         * There's a bug with a complete stripe overwrite- that means 0 reads
                    217:         * of old data, and the rest of the DAG generation code doesn't like
                    218:         * that. A release is coming, and I don't wanna risk breaking a
                    219:         * critical DAG generator, so here's what I'm gonna do- if there's
                    220:         * no read nodes, I'm gonna fake there being a read node, and I'm
                    221:         * gonna swap in a no-op node in its place (to make all the link-up
                    222:         * code happy).
                    223:         * This should be fixed at some point. --jimz
                    224:         */
                    225:        if (nRrdNodes == 0) {
                    226:                nRrdNodes = 1;
                    227:                rdnodesFaked = 1;
                    228:        } else {
                    229:                rdnodesFaked = 0;
                    230:        }
                    231:        /* Lock, unlock, xor, Wnd, Rrd, W(nfaults). */
                    232:        nNodes = 5 + nfaults + nWndNodes + nRrdNodes;
                    233:        RF_CallocAndAdd(nodes, nNodes, sizeof(RF_DagNode_t),
                    234:            (RF_DagNode_t *), allocList);
                    235:        i = 0;
                    236:        blockNode = &nodes[i];
                    237:        i += 1;
                    238:        commitNode = &nodes[i];
                    239:        i += 1;
                    240:        unblockNode = &nodes[i];
                    241:        i += 1;
                    242:        termNode = &nodes[i];
                    243:        i += 1;
                    244:        xorNode = &nodes[i];
                    245:        i += 1;
                    246:        wnpNode = &nodes[i];
                    247:        i += 1;
                    248:        wndNodes = &nodes[i];
                    249:        i += nWndNodes;
                    250:        rrdNodes = &nodes[i];
                    251:        i += nRrdNodes;
                    252:        if (nfaults == 2) {
                    253:                wnqNode = &nodes[i];
                    254:                i += 1;
                    255:        } else {
                    256:                wnqNode = NULL;
                    257:        }
                    258:        RF_ASSERT(i == nNodes);
                    259:
                    260:        /*
                    261:         * This dag can not commit until all rrd and xor Nodes have
                    262:         * completed.
                    263:         */
                    264:        dag_h->numCommitNodes = 1;
                    265:        dag_h->numCommits = 0;
                    266:        dag_h->numSuccedents = 1;
                    267:
                    268:        RF_ASSERT(nRrdNodes > 0);
                    269:        rf_InitNode(blockNode, rf_wait, RF_FALSE, rf_NullNodeFunc,
                    270:            rf_NullNodeUndoFunc, NULL, nRrdNodes, 0, 0, 0, dag_h,
                    271:            "Nil", allocList);
                    272:        rf_InitNode(commitNode, rf_wait, RF_TRUE, rf_NullNodeFunc,
                    273:            rf_NullNodeUndoFunc, NULL, nWndNodes + nfaults, 1, 0, 0,
                    274:            dag_h, "Cmt", allocList);
                    275:        rf_InitNode(unblockNode, rf_wait, RF_FALSE, rf_NullNodeFunc,
                    276:            rf_NullNodeUndoFunc, NULL, 1, nWndNodes + nfaults, 0, 0,
                    277:            dag_h, "Nil", allocList);
                    278:        rf_InitNode(termNode, rf_wait, RF_FALSE, rf_TerminateFunc,
                    279:            rf_TerminateUndoFunc, NULL, 0, 1, 0, 0, dag_h, "Trm", allocList);
                    280:        rf_InitNode(xorNode, rf_wait, RF_FALSE, redFunc, rf_NullNodeUndoFunc,
                    281:            NULL, 1, nRrdNodes, 2 * nXorBufs + 2, nfaults, dag_h, "Xrc",
                    282:            allocList);
                    283:
                    284:        /*
                    285:         * Fill in the Rrd nodes. If any of the rrd buffers are the same size
                    286:         * as the failed buffer, save a pointer to it so we can use it as the
                    287:         * target of the XOR. The pdas in the rrd nodes have been range-
                    288:         * restricted, so if a buffer is the same size as the failed buffer,
                    289:         * it must also be at the same alignment within the SU.
                    290:         */
                    291:        i = 0;
                    292:        if (new_asm_h[0]) {
                    293:                for (i = 0, pda = new_asm_h[0]->stripeMap->physInfo;
                    294:                    i < new_asm_h[0]->stripeMap->numStripeUnitsAccessed;
                    295:                    i++, pda = pda->next) {
                    296:                        rf_InitNode(&rrdNodes[i], rf_wait, RF_FALSE,
                    297:                            rf_DiskReadFunc, rf_DiskReadUndoFunc,
                    298:                            rf_GenericWakeupFunc, 1, 1, 4, 0, dag_h,
                    299:                            "Rrd", allocList);
                    300:                        RF_ASSERT(pda);
                    301:                        rrdNodes[i].params[0].p = pda;
                    302:                        rrdNodes[i].params[1].p = pda->bufPtr;
                    303:                        rrdNodes[i].params[2].v = parityStripeID;
                    304:                        rrdNodes[i].params[3].v = RF_CREATE_PARAM3(
                    305:                            RF_IO_NORMAL_PRIORITY, 0, 0, which_ru);
                    306:                }
                    307:        }
                    308:        /* i now equals the number of stripe units accessed in new_asm_h[0]. */
                    309:        if (new_asm_h[1]) {
                    310:                for (j = 0, pda = new_asm_h[1]->stripeMap->physInfo;
                    311:                    j < new_asm_h[1]->stripeMap->numStripeUnitsAccessed;
                    312:                    j++, pda = pda->next) {
                    313:                        rf_InitNode(&rrdNodes[i + j], rf_wait, RF_FALSE,
                    314:                            rf_DiskReadFunc, rf_DiskReadUndoFunc,
                    315:                            rf_GenericWakeupFunc, 1, 1, 4, 0, dag_h,
                    316:                            "Rrd", allocList);
                    317:                        RF_ASSERT(pda);
                    318:                        rrdNodes[i + j].params[0].p = pda;
                    319:                        rrdNodes[i + j].params[1].p = pda->bufPtr;
                    320:                        rrdNodes[i + j].params[2].v = parityStripeID;
                    321:                        rrdNodes[i + j].params[3].v = RF_CREATE_PARAM3(
                    322:                            RF_IO_NORMAL_PRIORITY, 0, 0, which_ru);
                    323:                        if (allowBufferRecycle &&
                    324:                            (pda->numSector == failedPDA->numSector))
                    325:                                xorTargetBuf = pda->bufPtr;
                    326:                }
                    327:        }
                    328:        if (rdnodesFaked) {
                    329:                /*
                    330:                 * This is where we'll init that fake noop read node.
                    331:                 * (XXX should the wakeup func be different ?)
                    332:                 */
                    333:                rf_InitNode(&rrdNodes[0], rf_wait, RF_FALSE, rf_NullNodeFunc,
                    334:                    rf_NullNodeUndoFunc, NULL, 1, 1, 0, 0, dag_h, "RrN",
                    335:                    allocList);
                    336:        }
                    337:        /*
                    338:         * Make a PDA for the parity unit. The parity PDA should start at
                    339:         * the same offset into the SU as the failed PDA.
                    340:         */
                    341:        /*
                    342:         * Danner comment: I don't think this copy is really necessary. We are
                    343:         * in one of two cases here.
                    344:         * (1) The entire failed unit is written. Then asmap->parityInfo will
                    345:         *     describe the entire parity.
                    346:         * (2) We are only writing a subset of the failed unit and nothing else.
                    347:         *     Then the asmap->parityInfo describes the failed unit and the copy
                    348:         *     can also be avoided.
                    349:         */
                    350:
                    351:        RF_MallocAndAdd(parityPDA, sizeof(RF_PhysDiskAddr_t),
                    352:            (RF_PhysDiskAddr_t *), allocList);
                    353:        parityPDA->row = asmap->parityInfo->row;
                    354:        parityPDA->col = asmap->parityInfo->col;
                    355:        parityPDA->startSector = ((asmap->parityInfo->startSector /
                    356:            sectorsPerSU) * sectorsPerSU) + (failedPDA->startSector %
                    357:            sectorsPerSU);
                    358:        parityPDA->numSector = failedPDA->numSector;
                    359:
                    360:        if (!xorTargetBuf) {
                    361:                RF_CallocAndAdd(xorTargetBuf, 1, rf_RaidAddressToByte(raidPtr,
                    362:                    failedPDA->numSector), (char *), allocList);
                    363:        }
                    364:        /* Init the Wnp node. */
                    365:        rf_InitNode(wnpNode, rf_wait, RF_FALSE, rf_DiskWriteFunc,
                    366:            rf_DiskWriteUndoFunc, rf_GenericWakeupFunc, 1, 1, 4, 0,
                    367:            dag_h, "Wnp", allocList);
                    368:        wnpNode->params[0].p = parityPDA;
                    369:        wnpNode->params[1].p = xorTargetBuf;
                    370:        wnpNode->params[2].v = parityStripeID;
                    371:        wnpNode->params[3].v = RF_CREATE_PARAM3(
                    372:            RF_IO_NORMAL_PRIORITY, 0, 0, which_ru);
                    373:
                    374:        /* Fill in the Wnq Node. */
                    375:        if (nfaults == 2) {
                    376:                {
                    377:                        RF_MallocAndAdd(parityPDA, sizeof(RF_PhysDiskAddr_t),
                    378:                            (RF_PhysDiskAddr_t *), allocList);
                    379:                        parityPDA->row = asmap->qInfo->row;
                    380:                        parityPDA->col = asmap->qInfo->col;
                    381:                        parityPDA->startSector = ((asmap->qInfo->startSector /
                    382:                            sectorsPerSU) * sectorsPerSU) +
                    383:                            (failedPDA->startSector % sectorsPerSU);
                    384:                        parityPDA->numSector = failedPDA->numSector;
                    385:
                    386:                        rf_InitNode(wnqNode, rf_wait, RF_FALSE,
                    387:                            rf_DiskWriteFunc, rf_DiskWriteUndoFunc,
                    388:                            rf_GenericWakeupFunc, 1, 1, 4, 0, dag_h,
                    389:                            "Wnq", allocList);
                    390:                        wnqNode->params[0].p = parityPDA;
                    391:                        RF_CallocAndAdd(xorNode->results[1], 1,
                    392:                            rf_RaidAddressToByte(raidPtr, failedPDA->numSector),
                    393:                            (char *), allocList);
                    394:                        wnqNode->params[1].p = xorNode->results[1];
                    395:                        wnqNode->params[2].v = parityStripeID;
                    396:                        wnqNode->params[3].v = RF_CREATE_PARAM3(
                    397:                            RF_IO_NORMAL_PRIORITY, 0, 0, which_ru);
                    398:                }
                    399:        }
                    400:        /* Fill in the Wnd nodes. */
                    401:        for (pda = asmap->physInfo, i = 0; i < nWndNodes;
                    402:             i++, pda = pda->next) {
                    403:                if (pda == failedPDA) {
                    404:                        i--;
                    405:                        continue;
                    406:                }
                    407:                rf_InitNode(&wndNodes[i], rf_wait, RF_FALSE, rf_DiskWriteFunc,
                    408:                    rf_DiskWriteUndoFunc, rf_GenericWakeupFunc, 1, 1, 4, 0,
                    409:                    dag_h, "Wnd", allocList);
                    410:                RF_ASSERT(pda);
                    411:                wndNodes[i].params[0].p = pda;
                    412:                wndNodes[i].params[1].p = pda->bufPtr;
                    413:                wndNodes[i].params[2].v = parityStripeID;
                    414:                wndNodes[i].params[3].v = RF_CREATE_PARAM3(
                    415:                    RF_IO_NORMAL_PRIORITY, 0, 0, which_ru);
                    416:        }
                    417:
                    418:        /* Fill in the results of the xor node. */
                    419:        xorNode->results[0] = xorTargetBuf;
                    420:
                    421:        /* Fill in the params of the xor node. */
                    422:
                    423:        paramNum = 0;
                    424:        if (rdnodesFaked == 0) {
                    425:                for (i = 0; i < nRrdNodes; i++) {
                    426:                        /* All the Rrd nodes need to be xored together. */
                    427:                        xorNode->params[paramNum++] = rrdNodes[i].params[0];
                    428:                        xorNode->params[paramNum++] = rrdNodes[i].params[1];
                    429:                }
                    430:        }
                    431:        for (i = 0; i < nWndNodes; i++) {
                    432:                /*
                    433:                 * Any Wnd nodes that overlap the failed access need to be
                    434:                 * xored in.
                    435:                 */
                    436:                if (overlappingPDAs[i]) {
                    437:                        RF_MallocAndAdd(pda, sizeof(RF_PhysDiskAddr_t),
                    438:                            (RF_PhysDiskAddr_t *), allocList);
                    439:                        bcopy((char *) wndNodes[i].params[0].p, (char *) pda,
                    440:                            sizeof(RF_PhysDiskAddr_t));
                    441:                        rf_RangeRestrictPDA(raidPtr, failedPDA, pda,
                    442:                            RF_RESTRICT_DOBUFFER, 0);
                    443:                        xorNode->params[paramNum++].p = pda;
                    444:                        xorNode->params[paramNum++].p = pda->bufPtr;
                    445:                }
                    446:        }
                    447:        RF_Free(overlappingPDAs, asmap->numStripeUnitsAccessed * sizeof(char));
                    448:
                    449:        /*
                    450:         * Install the failed PDA into the xor param list so that the
                    451:         * new data gets xor'd in.
                    452:         */
                    453:        xorNode->params[paramNum++].p = failedPDA;
                    454:        xorNode->params[paramNum++].p = failedPDA->bufPtr;
                    455:
                    456:        /*
                    457:         * The last 2 params to the recovery xor node are always the failed
                    458:         * PDA and the raidPtr. Install the failedPDA even though we have just
                    459:         * done so above. This allows us to use the same XOR function for both
                    460:         * degraded reads and degraded writes.
                    461:         */
                    462:        xorNode->params[paramNum++].p = failedPDA;
                    463:        xorNode->params[paramNum++].p = raidPtr;
                    464:        RF_ASSERT(paramNum == 2 * nXorBufs + 2);
                    465:
                    466:        /*
                    467:         * Code to link nodes begins here.
                    468:         */
                    469:
                    470:        /* Link header to block node. */
                    471:        RF_ASSERT(blockNode->numAntecedents == 0);
                    472:        dag_h->succedents[0] = blockNode;
                    473:
                    474:        /* Link block node to rd nodes. */
                    475:        RF_ASSERT(blockNode->numSuccedents == nRrdNodes);
                    476:        for (i = 0; i < nRrdNodes; i++) {
                    477:                RF_ASSERT(rrdNodes[i].numAntecedents == 1);
                    478:                blockNode->succedents[i] = &rrdNodes[i];
                    479:                rrdNodes[i].antecedents[0] = blockNode;
                    480:                rrdNodes[i].antType[0] = rf_control;
                    481:        }
                    482:
                    483:        /* Link read nodes to xor node. */
                    484:        RF_ASSERT(xorNode->numAntecedents == nRrdNodes);
                    485:        for (i = 0; i < nRrdNodes; i++) {
                    486:                RF_ASSERT(rrdNodes[i].numSuccedents == 1);
                    487:                rrdNodes[i].succedents[0] = xorNode;
                    488:                xorNode->antecedents[i] = &rrdNodes[i];
                    489:                xorNode->antType[i] = rf_trueData;
                    490:        }
                    491:
                    492:        /* Link xor node to commit node. */
                    493:        RF_ASSERT(xorNode->numSuccedents == 1);
                    494:        RF_ASSERT(commitNode->numAntecedents == 1);
                    495:        xorNode->succedents[0] = commitNode;
                    496:        commitNode->antecedents[0] = xorNode;
                    497:        commitNode->antType[0] = rf_control;
                    498:
                    499:        /* Link commit node to wnd nodes. */
                    500:        RF_ASSERT(commitNode->numSuccedents == nfaults + nWndNodes);
                    501:        for (i = 0; i < nWndNodes; i++) {
                    502:                RF_ASSERT(wndNodes[i].numAntecedents == 1);
                    503:                commitNode->succedents[i] = &wndNodes[i];
                    504:                wndNodes[i].antecedents[0] = commitNode;
                    505:                wndNodes[i].antType[0] = rf_control;
                    506:        }
                    507:
                    508:        /* Link the commit node to wnp, wnq nodes. */
                    509:        RF_ASSERT(wnpNode->numAntecedents == 1);
                    510:        commitNode->succedents[nWndNodes] = wnpNode;
                    511:        wnpNode->antecedents[0] = commitNode;
                    512:        wnpNode->antType[0] = rf_control;
                    513:        if (nfaults == 2) {
                    514:                RF_ASSERT(wnqNode->numAntecedents == 1);
                    515:                commitNode->succedents[nWndNodes + 1] = wnqNode;
                    516:                wnqNode->antecedents[0] = commitNode;
                    517:                wnqNode->antType[0] = rf_control;
                    518:        }
                    519:        /* Link write new data nodes to unblock node. */
                    520:        RF_ASSERT(unblockNode->numAntecedents == (nWndNodes + nfaults));
                    521:        for (i = 0; i < nWndNodes; i++) {
                    522:                RF_ASSERT(wndNodes[i].numSuccedents == 1);
                    523:                wndNodes[i].succedents[0] = unblockNode;
                    524:                unblockNode->antecedents[i] = &wndNodes[i];
                    525:                unblockNode->antType[i] = rf_control;
                    526:        }
                    527:
                    528:        /* Link write new parity node to unblock node. */
                    529:        RF_ASSERT(wnpNode->numSuccedents == 1);
                    530:        wnpNode->succedents[0] = unblockNode;
                    531:        unblockNode->antecedents[nWndNodes] = wnpNode;
                    532:        unblockNode->antType[nWndNodes] = rf_control;
                    533:
                    534:        /* Link write new q node to unblock node. */
                    535:        if (nfaults == 2) {
                    536:                RF_ASSERT(wnqNode->numSuccedents == 1);
                    537:                wnqNode->succedents[0] = unblockNode;
                    538:                unblockNode->antecedents[nWndNodes + 1] = wnqNode;
                    539:                unblockNode->antType[nWndNodes + 1] = rf_control;
                    540:        }
                    541:        /* Link unblock node to term node. */
                    542:        RF_ASSERT(unblockNode->numSuccedents == 1);
                    543:        RF_ASSERT(termNode->numAntecedents == 1);
                    544:        RF_ASSERT(termNode->numSuccedents == 0);
                    545:        unblockNode->succedents[0] = termNode;
                    546:        termNode->antecedents[0] = unblockNode;
                    547:        termNode->antType[0] = rf_control;
                    548: }
                    549:
                    550: #define        CONS_PDA(if,start,num)  do {                                    \
                    551:        pda_p->row = asmap->if->row;                                    \
                    552:        pda_p->col = asmap->if->col;                                    \
                    553:        pda_p->startSector = ((asmap->if->startSector / secPerSU) *     \
                    554:            secPerSU) + start;                                          \
                    555:        pda_p->numSector = num;                                         \
                    556:        pda_p->next = NULL;                                             \
                    557:        RF_MallocAndAdd(pda_p->bufPtr,                                  \
                    558:            rf_RaidAddressToByte(raidPtr,num),(char *), allocList);     \
                    559: } while (0)
                    560:
                    561: void
                    562: rf_WriteGenerateFailedAccessASMs(RF_Raid_t *raidPtr,
                    563:     RF_AccessStripeMap_t *asmap, RF_PhysDiskAddr_t **pdap, int *nNodep,
                    564:     RF_PhysDiskAddr_t **pqpdap, int *nPQNodep, RF_AllocListElem_t *allocList)
                    565: {
                    566:        RF_RaidLayout_t *layoutPtr = &(raidPtr->Layout);
                    567:        int PDAPerDisk, i;
                    568:        RF_SectorCount_t secPerSU = layoutPtr->sectorsPerStripeUnit;
                    569:        int numDataCol = layoutPtr->numDataCol;
                    570:        int state;
                    571:        unsigned napdas;
                    572:        RF_SectorNum_t fone_start, fone_end, ftwo_start = 0, ftwo_end;
                    573:        RF_PhysDiskAddr_t *fone = asmap->failedPDAs[0];
                    574:        RF_PhysDiskAddr_t *ftwo = asmap->failedPDAs[1];
                    575:        RF_PhysDiskAddr_t *pda_p;
                    576:        RF_RaidAddr_t sosAddr;
                    577:
                    578:        /*
                    579:         * Determine how many pda's we will have to generate per unaccessed
                    580:         * stripe. If there is only one failed data unit, it is one; if two,
                    581:         * possibly two, depending whether they overlap.
                    582:         */
                    583:
                    584:        fone_start = rf_StripeUnitOffset(layoutPtr, fone->startSector);
                    585:        fone_end = fone_start + fone->numSector;
                    586:
                    587:        if (asmap->numDataFailed == 1) {
                    588:                PDAPerDisk = 1;
                    589:                state = 1;
                    590:                RF_MallocAndAdd(*pqpdap, 2 * sizeof(RF_PhysDiskAddr_t),
                    591:                    (RF_PhysDiskAddr_t *), allocList);
                    592:                pda_p = *pqpdap;
                    593:                /* Build p. */
                    594:                CONS_PDA(parityInfo, fone_start, fone->numSector);
                    595:                pda_p->type = RF_PDA_TYPE_PARITY;
                    596:                pda_p++;
                    597:                /* Build q. */
                    598:                CONS_PDA(qInfo, fone_start, fone->numSector);
                    599:                pda_p->type = RF_PDA_TYPE_Q;
                    600:        } else {
                    601:                ftwo_start = rf_StripeUnitOffset(layoutPtr, ftwo->startSector);
                    602:                ftwo_end = ftwo_start + ftwo->numSector;
                    603:                if (fone->numSector + ftwo->numSector > secPerSU) {
                    604:                        PDAPerDisk = 1;
                    605:                        state = 2;
                    606:                        RF_MallocAndAdd(*pqpdap, 2 * sizeof(RF_PhysDiskAddr_t),
                    607:                            (RF_PhysDiskAddr_t *), allocList);
                    608:                        pda_p = *pqpdap;
                    609:                        CONS_PDA(parityInfo, 0, secPerSU);
                    610:                        pda_p->type = RF_PDA_TYPE_PARITY;
                    611:                        pda_p++;
                    612:                        CONS_PDA(qInfo, 0, secPerSU);
                    613:                        pda_p->type = RF_PDA_TYPE_Q;
                    614:                } else {
                    615:                        PDAPerDisk = 2;
                    616:                        state = 3;
                    617:                        /* Four of them, fone, then ftwo. */
                    618:                        RF_MallocAndAdd(*pqpdap, 4 * sizeof(RF_PhysDiskAddr_t),
                    619:                            (RF_PhysDiskAddr_t *), allocList);
                    620:                        pda_p = *pqpdap;
                    621:                        CONS_PDA(parityInfo, fone_start, fone->numSector);
                    622:                        pda_p->type = RF_PDA_TYPE_PARITY;
                    623:                        pda_p++;
                    624:                        CONS_PDA(qInfo, fone_start, fone->numSector);
                    625:                        pda_p->type = RF_PDA_TYPE_Q;
                    626:                        pda_p++;
                    627:                        CONS_PDA(parityInfo, ftwo_start, ftwo->numSector);
                    628:                        pda_p->type = RF_PDA_TYPE_PARITY;
                    629:                        pda_p++;
                    630:                        CONS_PDA(qInfo, ftwo_start, ftwo->numSector);
                    631:                        pda_p->type = RF_PDA_TYPE_Q;
                    632:                }
                    633:        }
                    634:        /* Figure out number of nonaccessed pda. */
                    635:        napdas = PDAPerDisk * (numDataCol - 2);
                    636:        *nPQNodep = PDAPerDisk;
                    637:
                    638:        *nNodep = napdas;
                    639:        if (napdas == 0)
                    640:                return;         /* Short circuit. */
                    641:
                    642:        /* Allocate up our list of pda's. */
                    643:
                    644:        RF_CallocAndAdd(pda_p, napdas, sizeof(RF_PhysDiskAddr_t),
                    645:            (RF_PhysDiskAddr_t *), allocList);
                    646:        *pdap = pda_p;
                    647:
                    648:        /* Link them together. */
                    649:        for (i = 0; i < (napdas - 1); i++)
                    650:                pda_p[i].next = pda_p + (i + 1);
                    651:
                    652:        sosAddr = rf_RaidAddressOfPrevStripeBoundary(layoutPtr,
                    653:            asmap->raidAddress);
                    654:        for (i = 0; i < numDataCol; i++) {
                    655:                if ((pda_p - (*pdap)) == napdas)
                    656:                        continue;
                    657:                pda_p->type = RF_PDA_TYPE_DATA;
                    658:                pda_p->raidAddress = sosAddr + (i * secPerSU);
                    659:                (raidPtr->Layout.map->MapSector) (raidPtr, pda_p->raidAddress,
                    660:                    &(pda_p->row), &(pda_p->col), &(pda_p->startSector), 0);
                    661:                /* Skip over dead disks. */
                    662:                if (RF_DEAD_DISK(raidPtr->Disks[pda_p->row][pda_p->col].status))
                    663:                        continue;
                    664:                switch (state) {
                    665:                case 1: /* Fone. */
                    666:                        pda_p->numSector = fone->numSector;
                    667:                        pda_p->raidAddress += fone_start;
                    668:                        pda_p->startSector += fone_start;
                    669:                        RF_MallocAndAdd(pda_p->bufPtr, rf_RaidAddressToByte(
                    670:                            raidPtr, pda_p->numSector), (char *), allocList);
                    671:                        break;
                    672:                case 2: /* Full stripe. */
                    673:                        pda_p->numSector = secPerSU;
                    674:                        RF_MallocAndAdd(pda_p->bufPtr, rf_RaidAddressToByte(
                    675:                            raidPtr, secPerSU), (char *), allocList);
                    676:                        break;
                    677:                case 3: /* Two slabs. */
                    678:                        pda_p->numSector = fone->numSector;
                    679:                        pda_p->raidAddress += fone_start;
                    680:                        pda_p->startSector += fone_start;
                    681:                        RF_MallocAndAdd(pda_p->bufPtr, rf_RaidAddressToByte(
                    682:                            raidPtr, pda_p->numSector), (char *), allocList);
                    683:                        pda_p++;
                    684:                        pda_p->type = RF_PDA_TYPE_DATA;
                    685:                        pda_p->raidAddress = sosAddr + (i * secPerSU);
                    686:                        (raidPtr->Layout.map->MapSector) (raidPtr,
                    687:                            pda_p->raidAddress, &(pda_p->row), &(pda_p->col),
                    688:                            &(pda_p->startSector), 0);
                    689:                        pda_p->numSector = ftwo->numSector;
                    690:                        pda_p->raidAddress += ftwo_start;
                    691:                        pda_p->startSector += ftwo_start;
                    692:                        RF_MallocAndAdd(pda_p->bufPtr, rf_RaidAddressToByte(
                    693:                            raidPtr, pda_p->numSector), (char *), allocList);
                    694:                        break;
                    695:                default:
                    696:                        RF_PANIC();
                    697:                }
                    698:                pda_p++;
                    699:        }
                    700:
                    701:        RF_ASSERT(pda_p - *pdap == napdas);
                    702:        return;
                    703: }
                    704:
                    705: #define        DISK_NODE_PDA(node)     ((node)->params[0].p)
                    706:
                    707: #define        DISK_NODE_PARAMS(_node_,_p_)    do {                            \
                    708:        (_node_).params[0].p = _p_ ;                                    \
                    709:        (_node_).params[1].p = (_p_)->bufPtr;                           \
                    710:        (_node_).params[2].v = parityStripeID;                          \
                    711:        (_node_).params[3].v = RF_CREATE_PARAM3(RF_IO_NORMAL_PRIORITY,  \
                    712:            0, 0, which_ru);                                            \
                    713: } while (0)
                    714:
                    715: void
                    716: rf_DoubleDegSmallWrite(RF_Raid_t *raidPtr, RF_AccessStripeMap_t *asmap,
                    717:     RF_DagHeader_t *dag_h, void *bp, RF_RaidAccessFlags_t flags,
                    718:     RF_AllocListElem_t *allocList, char *redundantReadNodeName,
                    719:     char *redundantWriteNodeName, char *recoveryNodeName,
                    720:     int (*recovFunc) (RF_DagNode_t *))
                    721: {
                    722:        RF_RaidLayout_t *layoutPtr = &(raidPtr->Layout);
                    723:        RF_DagNode_t *nodes, *wudNodes, *rrdNodes, *recoveryNode, *blockNode,
                    724:            *unblockNode, *rpNodes, *rqNodes, *wpNodes, *wqNodes, *termNode;
                    725:        RF_PhysDiskAddr_t *pda, *pqPDAs;
                    726:        RF_PhysDiskAddr_t *npdas;
                    727:        int nWriteNodes, nNodes, nReadNodes, nRrdNodes, nWudNodes, i;
                    728:        RF_ReconUnitNum_t which_ru;
                    729:        int nPQNodes;
                    730:        RF_StripeNum_t parityStripeID = rf_RaidAddressToParityStripeID(
                    731:            layoutPtr, asmap->raidAddress, &which_ru);
                    732:
                    733:        /*
                    734:         * Simple small write case - First part looks like a reconstruct-read
                    735:         * of the failed data units. Then a write of all data units not
                    736:         * failed.
                    737:         */
                    738:
                    739:
                    740:        /*
                    741:         * Hdr | ------Block- /  /         \   Rrd  Rrd ...  Rrd  Rp Rq \  \
                    742:         * /  -------PQ----- /   \   \ Wud   Wp  WQ          \    |   /
                    743:         * --Unblock- | T
                    744:         *
                    745:         * Rrd = read recovery data (potentially none)
                    746:         * Wud = write user data (not incl. failed disks)
                    747:         * Wp = Write P (could be two)
                    748:         * Wq = Write Q (could be two)
                    749:         *
                    750:         */
                    751:
                    752:        rf_WriteGenerateFailedAccessASMs(raidPtr, asmap, &npdas, &nRrdNodes,
                    753:            &pqPDAs, &nPQNodes, allocList);
                    754:
                    755:        RF_ASSERT(asmap->numDataFailed == 1);
                    756:
                    757:        nWudNodes = asmap->numStripeUnitsAccessed - (asmap->numDataFailed);
                    758:        nReadNodes = nRrdNodes + 2 * nPQNodes;
                    759:        nWriteNodes = nWudNodes + 2 * nPQNodes;
                    760:        nNodes = 4 + nReadNodes + nWriteNodes;
                    761:
                    762:        RF_CallocAndAdd(nodes, nNodes, sizeof(RF_DagNode_t), (RF_DagNode_t *),
                    763:            allocList);
                    764:        blockNode = nodes;
                    765:        unblockNode = blockNode + 1;
                    766:        termNode = unblockNode + 1;
                    767:        recoveryNode = termNode + 1;
                    768:        rrdNodes = recoveryNode + 1;
                    769:        rpNodes = rrdNodes + nRrdNodes;
                    770:        rqNodes = rpNodes + nPQNodes;
                    771:        wudNodes = rqNodes + nPQNodes;
                    772:        wpNodes = wudNodes + nWudNodes;
                    773:        wqNodes = wpNodes + nPQNodes;
                    774:
                    775:        dag_h->creator = "PQ_DDSimpleSmallWrite";
                    776:        dag_h->numSuccedents = 1;
                    777:        dag_h->succedents[0] = blockNode;
                    778:        rf_InitNode(termNode, rf_wait, RF_FALSE, rf_TerminateFunc,
                    779:            rf_TerminateUndoFunc, NULL, 0, 1, 0, 0, dag_h, "Trm", allocList);
                    780:        termNode->antecedents[0] = unblockNode;
                    781:        termNode->antType[0] = rf_control;
                    782:
                    783:        /* Init the block and unblock nodes. */
                    784:        /* The block node has all the read nodes as successors. */
                    785:        rf_InitNode(blockNode, rf_wait, RF_FALSE, rf_NullNodeFunc,
                    786:            rf_NullNodeUndoFunc, NULL, nReadNodes, 0, 0, 0, dag_h,
                    787:            "Nil", allocList);
                    788:        for (i = 0; i < nReadNodes; i++)
                    789:                blockNode->succedents[i] = rrdNodes + i;
                    790:
                    791:        /* The unblock node has all the writes as successors. */
                    792:        rf_InitNode(unblockNode, rf_wait, RF_FALSE, rf_NullNodeFunc,
                    793:            rf_NullNodeUndoFunc, NULL, 1, nWriteNodes, 0, 0, dag_h,
                    794:            "Nil", allocList);
                    795:        for (i = 0; i < nWriteNodes; i++) {
                    796:                unblockNode->antecedents[i] = wudNodes + i;
                    797:                unblockNode->antType[i] = rf_control;
                    798:        }
                    799:        unblockNode->succedents[0] = termNode;
                    800:
                    801: #define        INIT_READ_NODE(node,name)       do {                            \
                    802:        rf_InitNode(node, rf_wait, RF_FALSE, rf_DiskReadFunc,           \
                    803:            rf_DiskReadUndoFunc, rf_GenericWakeupFunc, 1, 1, 4, 0,      \
                    804:            dag_h, name, allocList);                                    \
                    805:        (node)->succedents[0] = recoveryNode;                           \
                    806:        (node)->antecedents[0] = blockNode;                             \
                    807:        (node)->antType[0] = rf_control;                                \
                    808: } while (0)
                    809:
                    810:        /* Build the read nodes. */
                    811:        pda = npdas;
                    812:        for (i = 0; i < nRrdNodes; i++, pda = pda->next) {
                    813:                INIT_READ_NODE(rrdNodes + i, "rrd");
                    814:                DISK_NODE_PARAMS(rrdNodes[i], pda);
                    815:        }
                    816:
                    817:        /* Read redundancy pdas. */
                    818:        pda = pqPDAs;
                    819:        INIT_READ_NODE(rpNodes, "Rp");
                    820:        RF_ASSERT(pda);
                    821:        DISK_NODE_PARAMS(rpNodes[0], pda);
                    822:        pda++;
                    823:        INIT_READ_NODE(rqNodes, redundantReadNodeName);
                    824:        RF_ASSERT(pda);
                    825:        DISK_NODE_PARAMS(rqNodes[0], pda);
                    826:        if (nPQNodes == 2) {
                    827:                pda++;
                    828:                INIT_READ_NODE(rpNodes + 1, "Rp");
                    829:                RF_ASSERT(pda);
                    830:                DISK_NODE_PARAMS(rpNodes[1], pda);
                    831:                pda++;
                    832:                INIT_READ_NODE(rqNodes + 1, redundantReadNodeName);
                    833:                RF_ASSERT(pda);
                    834:                DISK_NODE_PARAMS(rqNodes[1], pda);
                    835:        }
                    836:        /*
                    837:         * The recovery node has all reads as precedessors and all writes as
                    838:         * successors. It generates a result for every write P or write Q
                    839:         * node. As parameters, it takes a pda per read and a pda per stripe
                    840:         * of user data written. It also takes as the last params the raidPtr
                    841:         * and asm. For results, it takes PDA for P & Q.
                    842:         */
                    843:
                    844:        rf_InitNode(recoveryNode, rf_wait, RF_FALSE, recovFunc,
                    845:            rf_NullNodeUndoFunc, NULL,
                    846:            nWriteNodes,                /* succesors */
                    847:            nReadNodes,                 /* preds */
                    848:            nReadNodes + nWudNodes + 3, /* params */
                    849:            2 * nPQNodes,               /* results */
                    850:            dag_h, recoveryNodeName, allocList);
                    851:
                    852:
                    853:
                    854:        for (i = 0; i < nReadNodes; i++) {
                    855:                recoveryNode->antecedents[i] = rrdNodes + i;
                    856:                recoveryNode->antType[i] = rf_control;
                    857:                recoveryNode->params[i].p = DISK_NODE_PDA(rrdNodes + i);
                    858:        }
                    859:        for (i = 0; i < nWudNodes; i++) {
                    860:                recoveryNode->succedents[i] = wudNodes + i;
                    861:        }
                    862:        recoveryNode->params[nReadNodes + nWudNodes].p = asmap->failedPDAs[0];
                    863:        recoveryNode->params[nReadNodes + nWudNodes + 1].p = raidPtr;
                    864:        recoveryNode->params[nReadNodes + nWudNodes + 2].p = asmap;
                    865:
                    866:        for (; i < nWriteNodes; i++)
                    867:                recoveryNode->succedents[i] = wudNodes + i;
                    868:
                    869:        pda = pqPDAs;
                    870:        recoveryNode->results[0] = pda;
                    871:        pda++;
                    872:        recoveryNode->results[1] = pda;
                    873:        if (nPQNodes == 2) {
                    874:                pda++;
                    875:                recoveryNode->results[2] = pda;
                    876:                pda++;
                    877:                recoveryNode->results[3] = pda;
                    878:        }
                    879:        /* Fill writes. */
                    880: #define        INIT_WRITE_NODE(node,name)      do {                            \
                    881:        rf_InitNode(node, rf_wait, RF_FALSE, rf_DiskWriteFunc,          \
                    882:            rf_DiskWriteUndoFunc, rf_GenericWakeupFunc, 1, 1, 4, 0,     \
                    883:            dag_h, name, allocList);                                    \
                    884:        (node)->succedents[0] = unblockNode;                            \
                    885:        (node)->antecedents[0] = recoveryNode;                          \
                    886:        (node)->antType[0] = rf_control;                                \
                    887: } while (0)
                    888:
                    889:        pda = asmap->physInfo;
                    890:        for (i = 0; i < nWudNodes; i++) {
                    891:                INIT_WRITE_NODE(wudNodes + i, "Wd");
                    892:                DISK_NODE_PARAMS(wudNodes[i], pda);
                    893:                recoveryNode->params[nReadNodes + i].p =
                    894:                    DISK_NODE_PDA(wudNodes + i);
                    895:                pda = pda->next;
                    896:        }
                    897:        /* Write redundancy pdas. */
                    898:        pda = pqPDAs;
                    899:        INIT_WRITE_NODE(wpNodes, "Wp");
                    900:        RF_ASSERT(pda);
                    901:        DISK_NODE_PARAMS(wpNodes[0], pda);
                    902:        pda++;
                    903:        INIT_WRITE_NODE(wqNodes, "Wq");
                    904:        RF_ASSERT(pda);
                    905:        DISK_NODE_PARAMS(wqNodes[0], pda);
                    906:        if (nPQNodes == 2) {
                    907:                pda++;
                    908:                INIT_WRITE_NODE(wpNodes + 1, "Wp");
                    909:                RF_ASSERT(pda);
                    910:                DISK_NODE_PARAMS(wpNodes[1], pda);
                    911:                pda++;
                    912:                INIT_WRITE_NODE(wqNodes + 1, "Wq");
                    913:                RF_ASSERT(pda);
                    914:                DISK_NODE_PARAMS(wqNodes[1], pda);
                    915:        }
                    916: }

CVSweb