rf_evenodd_dagfuncs.c revision 1.11
1/*	$NetBSD: rf_evenodd_dagfuncs.c,v 1.11 2001/11/13 07:11:14 lukem Exp $	*/
2/*
3 * Copyright (c) 1995 Carnegie-Mellon University.
4 * All rights reserved.
5 *
6 * Author: ChangMing Wu
7 *
8 * Permission to use, copy, modify and distribute this software and
9 * its documentation is hereby granted, provided that both the copyright
10 * notice and this permission notice appear in all copies of the
11 * software, derivative works or modified versions, and any portions
12 * thereof, and that both notices appear in supporting documentation.
13 *
14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
15 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
16 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17 *
18 * Carnegie Mellon requests users of this software to return to
19 *
20 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
21 *  School of Computer Science
22 *  Carnegie Mellon University
23 *  Pittsburgh PA 15213-3890
24 *
25 * any improvements or extensions that they make and grant Carnegie the
26 * rights to redistribute these changes.
27 */
28
29/*
30 * Code for RAID-EVENODD  architecture.
31 */
32
33#include <sys/cdefs.h>
34__KERNEL_RCSID(0, "$NetBSD: rf_evenodd_dagfuncs.c,v 1.11 2001/11/13 07:11:14 lukem Exp $");
35
36#include "rf_archs.h"
37
38#if RF_INCLUDE_EVENODD > 0
39
40#include <dev/raidframe/raidframevar.h>
41
42#include "rf_raid.h"
43#include "rf_dag.h"
44#include "rf_dagffrd.h"
45#include "rf_dagffwr.h"
46#include "rf_dagdegrd.h"
47#include "rf_dagdegwr.h"
48#include "rf_dagutils.h"
49#include "rf_dagfuncs.h"
50#include "rf_etimer.h"
51#include "rf_general.h"
52#include "rf_parityscan.h"
53#include "rf_evenodd.h"
54#include "rf_evenodd_dagfuncs.h"
55
56/* These redundant functions are for small write */
57RF_RedFuncs_t rf_EOSmallWritePFuncs = {rf_RegularXorFunc, "Regular Old-New P", rf_SimpleXorFunc, "Simple Old-New P"};
58RF_RedFuncs_t rf_EOSmallWriteEFuncs = {rf_RegularONEFunc, "Regular Old-New E", rf_SimpleONEFunc, "Regular Old-New E"};
59/* These redundant functions are for degraded read */
60RF_RedFuncs_t rf_eoPRecoveryFuncs = {rf_RecoveryXorFunc, "Recovery Xr", rf_RecoveryXorFunc, "Recovery Xr"};
61RF_RedFuncs_t rf_eoERecoveryFuncs = {rf_RecoveryEFunc, "Recovery E Func", rf_RecoveryEFunc, "Recovery E Func"};
62/**********************************************************************************************
63 *   the following encoding node functions is used in  EO_000_CreateLargeWriteDAG
64 **********************************************************************************************/
65int
66rf_RegularPEFunc(node)
67	RF_DagNode_t *node;
68{
69	rf_RegularESubroutine(node, node->results[1]);
70	rf_RegularXorFunc(node);/* does the wakeup here! */
71#if 1
72	return (0);		/* XXX This was missing... GO */
73#endif
74}
75
76
77/************************************************************************************************
78 *  For EO_001_CreateSmallWriteDAG, there are (i)RegularONEFunc() and (ii)SimpleONEFunc() to
79 *  be used. The previous case is when write access at least sectors of full stripe unit.
80 *  The later function is used when the write access two stripe units but with total sectors
81 *  less than sectors per SU. In this case, the access of parity and 'E' are shown as disconnected
82 *  areas in their stripe unit and  parity write and 'E' write are both devided into two distinct
83 *  writes( totally four). This simple old-new write and regular old-new write happen as in RAID-5
84 ************************************************************************************************/
85
86/* Algorithm:
87     1. Store the difference of old data and new data in the Rod buffer.
88     2. then encode this buffer into the buffer which already have old 'E' information inside it,
89	the result can be shown to be the new 'E' information.
90     3. xor the Wnd buffer into the difference buffer to recover the  original old data.
91   Here we have another alternative: to allocate a temporary buffer for storing the difference of
92   old data and new data, then encode temp buf into old 'E' buf to form new 'E', but this approach
93   take the same speed as the previous, and need more memory.
94*/
95int
96rf_RegularONEFunc(node)
97	RF_DagNode_t *node;
98{
99	RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[node->numParams - 1].p;
100	RF_RaidLayout_t *layoutPtr = (RF_RaidLayout_t *) & raidPtr->Layout;
101	int     EpdaIndex = (node->numParams - 1) / 2 - 1;	/* the parameter of node
102								 * where you can find
103								 * e-pda */
104	int     i, k, retcode = 0;
105	int     suoffset, length;
106	RF_RowCol_t scol;
107	char   *srcbuf, *destbuf;
108	RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
109	RF_Etimer_t timer;
110	RF_PhysDiskAddr_t *pda;
111#ifdef RAID_DIAGNOSTIC
112	RF_PhysDiskAddr_t *EPDA =
113	    (RF_PhysDiskAddr_t *) node->params[EpdaIndex].p;
114	int     ESUOffset = rf_StripeUnitOffset(layoutPtr, EPDA->startSector);
115#endif /* RAID_DIAGNOSTIC */
116
117	RF_ASSERT(EPDA->type == RF_PDA_TYPE_Q);
118	RF_ASSERT(ESUOffset == 0);
119
120	RF_ETIMER_START(timer);
121
122	/* Xor the Wnd buffer into Rod buffer, the difference of old data and
123	 * new data is stored in Rod buffer */
124	for (k = 0; k < EpdaIndex; k += 2) {
125		length = rf_RaidAddressToByte(raidPtr, ((RF_PhysDiskAddr_t *) node->params[k].p)->numSector);
126		retcode = rf_bxor(node->params[k + EpdaIndex + 3].p, node->params[k + 1].p, length, node->dagHdr->bp);
127	}
128	/* Start to encoding the buffer storing the difference of old data and
129	 * new data into 'E' buffer  */
130	for (i = 0; i < EpdaIndex; i += 2)
131		if (node->params[i + 1].p != node->results[0]) {	/* results[0] is buf ptr
132									 * of E */
133			pda = (RF_PhysDiskAddr_t *) node->params[i].p;
134			srcbuf = (char *) node->params[i + 1].p;
135			scol = rf_EUCol(layoutPtr, pda->raidAddress);
136			suoffset = rf_StripeUnitOffset(layoutPtr, pda->startSector);
137			destbuf = ((char *) node->results[0]) + rf_RaidAddressToByte(raidPtr, suoffset);
138			rf_e_encToBuf(raidPtr, scol, srcbuf, RF_EO_MATRIX_DIM - 2, destbuf, pda->numSector);
139		}
140	/* Recover the original old data to be used by parity encoding
141	 * function in XorNode */
142	for (k = 0; k < EpdaIndex; k += 2) {
143		length = rf_RaidAddressToByte(raidPtr, ((RF_PhysDiskAddr_t *) node->params[k].p)->numSector);
144		retcode = rf_bxor(node->params[k + EpdaIndex + 3].p, node->params[k + 1].p, length, node->dagHdr->bp);
145	}
146	RF_ETIMER_STOP(timer);
147	RF_ETIMER_EVAL(timer);
148	tracerec->q_us += RF_ETIMER_VAL_US(timer);
149	rf_GenericWakeupFunc(node, 0);
150#if 1
151	return (0);		/* XXX this was missing.. GO */
152#endif
153}
154
155int
156rf_SimpleONEFunc(node)
157	RF_DagNode_t *node;
158{
159	RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[node->numParams - 1].p;
160	RF_RaidLayout_t *layoutPtr = (RF_RaidLayout_t *) & raidPtr->Layout;
161	RF_PhysDiskAddr_t *pda = (RF_PhysDiskAddr_t *) node->params[0].p;
162	int     retcode = 0;
163	char   *srcbuf, *destbuf;
164	RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
165	int     length;
166	RF_RowCol_t scol;
167	RF_Etimer_t timer;
168
169	RF_ASSERT(((RF_PhysDiskAddr_t *) node->params[2].p)->type == RF_PDA_TYPE_Q);
170	if (node->dagHdr->status == rf_enable) {
171		RF_ETIMER_START(timer);
172		length = rf_RaidAddressToByte(raidPtr, ((RF_PhysDiskAddr_t *) node->params[4].p)->numSector);	/* this is a pda of
173														 * writeDataNodes */
174		/* bxor to buffer of readDataNodes */
175		retcode = rf_bxor(node->params[5].p, node->params[1].p, length, node->dagHdr->bp);
176		/* find out the corresponding colume in encoding matrix for
177		 * write colume to be encoded into redundant disk 'E' */
178		scol = rf_EUCol(layoutPtr, pda->raidAddress);
179		srcbuf = node->params[1].p;
180		destbuf = node->params[3].p;
181		/* Start encoding process */
182		rf_e_encToBuf(raidPtr, scol, srcbuf, RF_EO_MATRIX_DIM - 2, destbuf, pda->numSector);
183		rf_bxor(node->params[5].p, node->params[1].p, length, node->dagHdr->bp);
184		RF_ETIMER_STOP(timer);
185		RF_ETIMER_EVAL(timer);
186		tracerec->q_us += RF_ETIMER_VAL_US(timer);
187
188	}
189	return (rf_GenericWakeupFunc(node, retcode));	/* call wake func
190							 * explicitly since no
191							 * I/O in this node */
192}
193
194
195/****** called by rf_RegularPEFunc(node) and rf_RegularEFunc(node) in f.f. large write  ********/
196void
197rf_RegularESubroutine(node, ebuf)
198	RF_DagNode_t *node;
199	char   *ebuf;
200{
201	RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[node->numParams - 1].p;
202	RF_RaidLayout_t *layoutPtr = (RF_RaidLayout_t *) & raidPtr->Layout;
203	RF_PhysDiskAddr_t *pda;
204	int     i, suoffset;
205	RF_RowCol_t scol;
206	char   *srcbuf, *destbuf;
207	RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
208	RF_Etimer_t timer;
209
210	RF_ETIMER_START(timer);
211	for (i = 0; i < node->numParams - 2; i += 2) {
212		RF_ASSERT(node->params[i + 1].p != ebuf);
213		pda = (RF_PhysDiskAddr_t *) node->params[i].p;
214		suoffset = rf_StripeUnitOffset(layoutPtr, pda->startSector);
215		scol = rf_EUCol(layoutPtr, pda->raidAddress);
216		srcbuf = (char *) node->params[i + 1].p;
217		destbuf = ebuf + rf_RaidAddressToByte(raidPtr, suoffset);
218		rf_e_encToBuf(raidPtr, scol, srcbuf, RF_EO_MATRIX_DIM - 2, destbuf, pda->numSector);
219	}
220	RF_ETIMER_STOP(timer);
221	RF_ETIMER_EVAL(timer);
222	tracerec->xor_us += RF_ETIMER_VAL_US(timer);
223}
224
225
226/*******************************************************************************************
227 *			 Used in  EO_001_CreateLargeWriteDAG
228 ******************************************************************************************/
229int
230rf_RegularEFunc(node)
231	RF_DagNode_t *node;
232{
233	rf_RegularESubroutine(node, node->results[0]);
234	rf_GenericWakeupFunc(node, 0);
235#if 1
236	return (0);		/* XXX this was missing?.. GO */
237#endif
238}
239/*******************************************************************************************
240 * This degraded function allow only two case:
241 *  1. when write access the full failed stripe unit, then the access can be more than
242 *     one tripe units.
243 *  2. when write access only part of the failed SU, we assume accesses of more than
244 *     one stripe unit is not allowed so that the write can be dealt with like a
245 *     large write.
246 *  The following function is based on these assumptions. So except in the second case,
247 *  it looks the same as a large write encodeing function. But this is not exactly the
248 *  normal way for doing a degraded write, since raidframe have to break cases of access
249 *  other than the above two into smaller accesses. We may have to change
250 *  DegrESubroutin in the future.
251 *******************************************************************************************/
252void
253rf_DegrESubroutine(node, ebuf)
254	RF_DagNode_t *node;
255	char   *ebuf;
256{
257	RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[node->numParams - 1].p;
258	RF_RaidLayout_t *layoutPtr = (RF_RaidLayout_t *) & raidPtr->Layout;
259	RF_PhysDiskAddr_t *failedPDA = (RF_PhysDiskAddr_t *) node->params[node->numParams - 2].p;
260	RF_PhysDiskAddr_t *pda;
261	int     i, suoffset, failedSUOffset = rf_StripeUnitOffset(layoutPtr, failedPDA->startSector);
262	RF_RowCol_t scol;
263	char   *srcbuf, *destbuf;
264	RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
265	RF_Etimer_t timer;
266
267	RF_ETIMER_START(timer);
268	for (i = 0; i < node->numParams - 2; i += 2) {
269		RF_ASSERT(node->params[i + 1].p != ebuf);
270		pda = (RF_PhysDiskAddr_t *) node->params[i].p;
271		suoffset = rf_StripeUnitOffset(layoutPtr, pda->startSector);
272		scol = rf_EUCol(layoutPtr, pda->raidAddress);
273		srcbuf = (char *) node->params[i + 1].p;
274		destbuf = ebuf + rf_RaidAddressToByte(raidPtr, suoffset - failedSUOffset);
275		rf_e_encToBuf(raidPtr, scol, srcbuf, RF_EO_MATRIX_DIM - 2, destbuf, pda->numSector);
276	}
277
278	RF_ETIMER_STOP(timer);
279	RF_ETIMER_EVAL(timer);
280	tracerec->q_us += RF_ETIMER_VAL_US(timer);
281}
282
283
284/**************************************************************************************
285 * This function is used in case where one data disk failed and both redundant disks
286 * alive. It is used in the EO_100_CreateWriteDAG. Note: if there is another disk
287 * failed in the stripe but not accessed at this time, then we should, instead, use
288 * the rf_EOWriteDoubleRecoveryFunc().
289 **************************************************************************************/
290int
291rf_Degraded_100_EOFunc(node)
292	RF_DagNode_t *node;
293{
294	rf_DegrESubroutine(node, node->results[1]);
295	rf_RecoveryXorFunc(node);	/* does the wakeup here! */
296#if 1
297	return (0);		/* XXX this was missing... SHould these be
298				 * void functions??? GO */
299#endif
300}
301/**************************************************************************************
302 * This function is to encode one sector in one of the data disks to the E disk.
303 * However, in evenodd this function can also be used as decoding function to recover
304 * data from dead disk in the case of parity failure and a single data failure.
305 **************************************************************************************/
306void
307rf_e_EncOneSect(
308    RF_RowCol_t srcLogicCol,
309    char *srcSecbuf,
310    RF_RowCol_t destLogicCol,
311    char *destSecbuf,
312    int bytesPerSector)
313{
314	int     S_index;	/* index of the EU in the src col which need
315				 * be Xored into all EUs in a dest sector */
316	int     numRowInEncMatix = (RF_EO_MATRIX_DIM) - 1;
317	RF_RowCol_t j, indexInDest,	/* row index of an encoding unit in
318					 * the destination colume of encoding
319					 * matrix */
320	        indexInSrc;	/* row index of an encoding unit in the source
321				 * colume used for recovery */
322	int     bytesPerEU = bytesPerSector / numRowInEncMatix;
323
324#if RF_EO_MATRIX_DIM > 17
325	int     shortsPerEU = bytesPerEU / sizeof(short);
326	short  *destShortBuf, *srcShortBuf1, *srcShortBuf2;
327	short temp1;
328#elif RF_EO_MATRIX_DIM == 17
329	int     longsPerEU = bytesPerEU / sizeof(long);
330	long   *destLongBuf, *srcLongBuf1, *srcLongBuf2;
331	long temp1;
332#endif
333
334#if RF_EO_MATRIX_DIM > 17
335	RF_ASSERT(sizeof(short) == 2 || sizeof(short) == 1);
336	RF_ASSERT(bytesPerEU % sizeof(short) == 0);
337#elif RF_EO_MATRIX_DIM == 17
338	RF_ASSERT(sizeof(long) == 8 || sizeof(long) == 4);
339	RF_ASSERT(bytesPerEU % sizeof(long) == 0);
340#endif
341
342	S_index = rf_EO_Mod((RF_EO_MATRIX_DIM - 1 + destLogicCol - srcLogicCol), RF_EO_MATRIX_DIM);
343#if RF_EO_MATRIX_DIM > 17
344	srcShortBuf1 = (short *) (srcSecbuf + S_index * bytesPerEU);
345#elif RF_EO_MATRIX_DIM == 17
346	srcLongBuf1 = (long *) (srcSecbuf + S_index * bytesPerEU);
347#endif
348
349	for (indexInDest = 0; indexInDest < numRowInEncMatix; indexInDest++) {
350		indexInSrc = rf_EO_Mod((indexInDest + destLogicCol - srcLogicCol), RF_EO_MATRIX_DIM);
351
352#if RF_EO_MATRIX_DIM > 17
353		destShortBuf = (short *) (destSecbuf + indexInDest * bytesPerEU);
354		srcShortBuf2 = (short *) (srcSecbuf + indexInSrc * bytesPerEU);
355		for (j = 0; j < shortsPerEU; j++) {
356			temp1 = destShortBuf[j] ^ srcShortBuf1[j];
357			/* note: S_index won't be at the end row for any src
358			 * col! */
359			if (indexInSrc != RF_EO_MATRIX_DIM - 1)
360				destShortBuf[j] = (srcShortBuf2[j]) ^ temp1;
361			/* if indexInSrc is at the end row, ie.
362			 * RF_EO_MATRIX_DIM -1, then all elements are zero! */
363			else
364				destShortBuf[j] = temp1;
365		}
366
367#elif RF_EO_MATRIX_DIM == 17
368		destLongBuf = (long *) (destSecbuf + indexInDest * bytesPerEU);
369		srcLongBuf2 = (long *) (srcSecbuf + indexInSrc * bytesPerEU);
370		for (j = 0; j < longsPerEU; j++) {
371			temp1 = destLongBuf[j] ^ srcLongBuf1[j];
372			if (indexInSrc != RF_EO_MATRIX_DIM - 1)
373				destLongBuf[j] = (srcLongBuf2[j]) ^ temp1;
374			else
375				destLongBuf[j] = temp1;
376		}
377#endif
378	}
379}
380
381void
382rf_e_encToBuf(
383    RF_Raid_t * raidPtr,
384    RF_RowCol_t srcLogicCol,
385    char *srcbuf,
386    RF_RowCol_t destLogicCol,
387    char *destbuf,
388    int numSector)
389{
390	int     i, bytesPerSector = rf_RaidAddressToByte(raidPtr, 1);
391
392	for (i = 0; i < numSector; i++) {
393		rf_e_EncOneSect(srcLogicCol, srcbuf, destLogicCol, destbuf, bytesPerSector);
394		srcbuf += bytesPerSector;
395		destbuf += bytesPerSector;
396	}
397}
398/**************************************************************************************
399 * when parity die and one data die, We use second redundant information, 'E',
400 * to recover the data in dead disk. This function is used in the recovery node of
401 * for EO_110_CreateReadDAG
402 **************************************************************************************/
403int
404rf_RecoveryEFunc(node)
405	RF_DagNode_t *node;
406{
407	RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[node->numParams - 1].p;
408	RF_RaidLayout_t *layoutPtr = (RF_RaidLayout_t *) & raidPtr->Layout;
409	RF_PhysDiskAddr_t *failedPDA = (RF_PhysDiskAddr_t *) node->params[node->numParams - 2].p;
410	RF_RowCol_t scol,	/* source logical column */
411	        fcol = rf_EUCol(layoutPtr, failedPDA->raidAddress);	/* logical column of
412									 * failed SU */
413	int     i;
414	RF_PhysDiskAddr_t *pda;
415	int     suoffset, failedSUOffset = rf_StripeUnitOffset(layoutPtr, failedPDA->startSector);
416	char   *srcbuf, *destbuf;
417	RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
418	RF_Etimer_t timer;
419
420	memset((char *) node->results[0], 0,
421	    rf_RaidAddressToByte(raidPtr, failedPDA->numSector));
422	if (node->dagHdr->status == rf_enable) {
423		RF_ETIMER_START(timer);
424		for (i = 0; i < node->numParams - 2; i += 2)
425			if (node->params[i + 1].p != node->results[0]) {
426				pda = (RF_PhysDiskAddr_t *) node->params[i].p;
427				if (i == node->numParams - 4)
428					scol = RF_EO_MATRIX_DIM - 2;	/* the colume of
429									 * redundant E */
430				else
431					scol = rf_EUCol(layoutPtr, pda->raidAddress);
432				srcbuf = (char *) node->params[i + 1].p;
433				suoffset = rf_StripeUnitOffset(layoutPtr, pda->startSector);
434				destbuf = ((char *) node->results[0]) + rf_RaidAddressToByte(raidPtr, suoffset - failedSUOffset);
435				rf_e_encToBuf(raidPtr, scol, srcbuf, fcol, destbuf, pda->numSector);
436			}
437		RF_ETIMER_STOP(timer);
438		RF_ETIMER_EVAL(timer);
439		tracerec->xor_us += RF_ETIMER_VAL_US(timer);
440	}
441	return (rf_GenericWakeupFunc(node, 0));	/* node execute successfully */
442}
443/**************************************************************************************
444 * This function is used in the case where one data and the parity have filed.
445 * (in EO_110_CreateWriteDAG )
446 **************************************************************************************/
447int
448rf_EO_DegradedWriteEFunc(RF_DagNode_t * node)
449{
450	rf_DegrESubroutine(node, node->results[0]);
451	rf_GenericWakeupFunc(node, 0);
452#if 1
453	return (0);		/* XXX Yet another one!! GO */
454#endif
455}
456
457
458
459/**************************************************************************************
460 *  		THE FUNCTION IS FOR DOUBLE DEGRADED READ AND WRITE CASES
461 **************************************************************************************/
462
463void
464rf_doubleEOdecode(
465    RF_Raid_t * raidPtr,
466    char **rrdbuf,
467    char **dest,
468    RF_RowCol_t * fcol,
469    char *pbuf,
470    char *ebuf)
471{
472	RF_RaidLayout_t *layoutPtr = (RF_RaidLayout_t *) & (raidPtr->Layout);
473	int     i, j, k, f1, f2, row;
474	int     rrdrow, erow, count = 0;
475	int     bytesPerSector = rf_RaidAddressToByte(raidPtr, 1);
476	int     numRowInEncMatix = (RF_EO_MATRIX_DIM) - 1;
477#if 0
478	int     pcol = (RF_EO_MATRIX_DIM) - 1;
479#endif
480	int     ecol = (RF_EO_MATRIX_DIM) - 2;
481	int     bytesPerEU = bytesPerSector / numRowInEncMatix;
482	int     numDataCol = layoutPtr->numDataCol;
483#if RF_EO_MATRIX_DIM > 17
484	int     shortsPerEU = bytesPerEU / sizeof(short);
485	short  *rrdbuf_current, *pbuf_current, *ebuf_current;
486	short  *dest_smaller, *dest_smaller_current, *dest_larger, *dest_larger_current;
487	short *temp;
488	short  *P;
489
490	RF_ASSERT(bytesPerEU % sizeof(short) == 0);
491	RF_Malloc(P, bytesPerEU, (short *));
492	RF_Malloc(temp, bytesPerEU, (short *));
493#elif RF_EO_MATRIX_DIM == 17
494	int     longsPerEU = bytesPerEU / sizeof(long);
495	long   *rrdbuf_current, *pbuf_current, *ebuf_current;
496	long   *dest_smaller, *dest_smaller_current, *dest_larger, *dest_larger_current;
497	long *temp;
498	long   *P;
499
500	RF_ASSERT(bytesPerEU % sizeof(long) == 0);
501	RF_Malloc(P, bytesPerEU, (long *));
502	RF_Malloc(temp, bytesPerEU, (long *));
503#endif
504	RF_ASSERT(*((long *) dest[0]) == 0);
505	RF_ASSERT(*((long *) dest[1]) == 0);
506	memset((char *) P, 0, bytesPerEU);
507	memset((char *) temp, 0, bytesPerEU);
508	RF_ASSERT(*P == 0);
509	/* calculate the 'P' parameter, which, not parity, is the Xor of all
510	 * elements in the last two column, ie. 'E' and 'parity' colume, see
511	 * the Ref. paper by Blaum, et al 1993  */
512	for (i = 0; i < numRowInEncMatix; i++)
513		for (k = 0; k < longsPerEU; k++) {
514#if RF_EO_MATRIX_DIM > 17
515			ebuf_current = ((short *) ebuf) + i * shortsPerEU + k;
516			pbuf_current = ((short *) pbuf) + i * shortsPerEU + k;
517#elif RF_EO_MATRIX_DIM == 17
518			ebuf_current = ((long *) ebuf) + i * longsPerEU + k;
519			pbuf_current = ((long *) pbuf) + i * longsPerEU + k;
520#endif
521			P[k] ^= *ebuf_current;
522			P[k] ^= *pbuf_current;
523		}
524	RF_ASSERT(fcol[0] != fcol[1]);
525	if (fcol[0] < fcol[1]) {
526#if RF_EO_MATRIX_DIM > 17
527		dest_smaller = (short *) (dest[0]);
528		dest_larger = (short *) (dest[1]);
529#elif RF_EO_MATRIX_DIM == 17
530		dest_smaller = (long *) (dest[0]);
531		dest_larger = (long *) (dest[1]);
532#endif
533		f1 = fcol[0];
534		f2 = fcol[1];
535	} else {
536#if RF_EO_MATRIX_DIM > 17
537		dest_smaller = (short *) (dest[1]);
538		dest_larger = (short *) (dest[0]);
539#elif RF_EO_MATRIX_DIM == 17
540		dest_smaller = (long *) (dest[1]);
541		dest_larger = (long *) (dest[0]);
542#endif
543		f1 = fcol[1];
544		f2 = fcol[0];
545	}
546	row = (RF_EO_MATRIX_DIM) - 1;
547	while ((row = rf_EO_Mod((row + f1 - f2), RF_EO_MATRIX_DIM)) != ((RF_EO_MATRIX_DIM) - 1)) {
548#if RF_EO_MATRIX_DIM > 17
549		dest_larger_current = dest_larger + row * shortsPerEU;
550		dest_smaller_current = dest_smaller + row * shortsPerEU;
551#elif RF_EO_MATRIX_DIM == 17
552		dest_larger_current = dest_larger + row * longsPerEU;
553		dest_smaller_current = dest_smaller + row * longsPerEU;
554#endif
555		/**    Do the diagonal recovery. Initially, temp[k] = (failed 1),
556		       which is the failed data in the colume which has smaller col index. **/
557		/* step 1:  ^(SUM of nonfailed in-diagonal A(rrdrow,0..m-3))         */
558		for (j = 0; j < numDataCol; j++) {
559			if (j == f1 || j == f2)
560				continue;
561			rrdrow = rf_EO_Mod((row + f2 - j), RF_EO_MATRIX_DIM);
562			if (rrdrow != (RF_EO_MATRIX_DIM) - 1) {
563#if RF_EO_MATRIX_DIM > 17
564				rrdbuf_current = (short *) (rrdbuf[j]) + rrdrow * shortsPerEU;
565				for (k = 0; k < shortsPerEU; k++)
566					temp[k] ^= *(rrdbuf_current + k);
567#elif RF_EO_MATRIX_DIM == 17
568				rrdbuf_current = (long *) (rrdbuf[j]) + rrdrow * longsPerEU;
569				for (k = 0; k < longsPerEU; k++)
570					temp[k] ^= *(rrdbuf_current + k);
571#endif
572			}
573		}
574		/* step 2:  ^E(erow,m-2), If erow is at the buttom row, don't
575		 * Xor into it  E(erow,m-2) = (principle diagonal) ^ (failed
576		 * 1) ^ (failed 2) ^ ( SUM of nonfailed in-diagonal
577		 * A(rrdrow,0..m-3) ) After this step, temp[k] = (principle
578		 * diagonal) ^ (failed 2)       */
579
580		erow = rf_EO_Mod((row + f2 - ecol), (RF_EO_MATRIX_DIM));
581		if (erow != (RF_EO_MATRIX_DIM) - 1) {
582#if RF_EO_MATRIX_DIM > 17
583			ebuf_current = (short *) ebuf + shortsPerEU * erow;
584			for (k = 0; k < shortsPerEU; k++)
585				temp[k] ^= *(ebuf_current + k);
586#elif RF_EO_MATRIX_DIM == 17
587			ebuf_current = (long *) ebuf + longsPerEU * erow;
588			for (k = 0; k < longsPerEU; k++)
589				temp[k] ^= *(ebuf_current + k);
590#endif
591		}
592		/* step 3: ^P to obtain the failed data (failed 2).  P can be
593		 * proved to be actually  (principle diagonal)  After this
594		 * step, temp[k] = (failed 2), the failed data to be recovered */
595#if RF_EO_MATRIX_DIM > 17
596		for (k = 0; k < shortsPerEU; k++)
597			temp[k] ^= P[k];
598		/* Put the data to the destination buffer                              */
599		for (k = 0; k < shortsPerEU; k++)
600			dest_larger_current[k] = temp[k];
601#elif RF_EO_MATRIX_DIM == 17
602		for (k = 0; k < longsPerEU; k++)
603			temp[k] ^= P[k];
604		/* Put the data to the destination buffer                              */
605		for (k = 0; k < longsPerEU; k++)
606			dest_larger_current[k] = temp[k];
607#endif
608
609		/**          THE FOLLOWING DO THE HORIZONTAL XOR                **/
610		/* step 1:  ^(SUM of A(row,0..m-3)), ie. all nonfailed data
611		 * columes    */
612		for (j = 0; j < numDataCol; j++) {
613			if (j == f1 || j == f2)
614				continue;
615#if RF_EO_MATRIX_DIM > 17
616			rrdbuf_current = (short *) (rrdbuf[j]) + row * shortsPerEU;
617			for (k = 0; k < shortsPerEU; k++)
618				temp[k] ^= *(rrdbuf_current + k);
619#elif RF_EO_MATRIX_DIM == 17
620			rrdbuf_current = (long *) (rrdbuf[j]) + row * longsPerEU;
621			for (k = 0; k < longsPerEU; k++)
622				temp[k] ^= *(rrdbuf_current + k);
623#endif
624		}
625		/* step 2: ^A(row,m-1) */
626		/* step 3: Put the data to the destination buffer                             	 */
627#if RF_EO_MATRIX_DIM > 17
628		pbuf_current = (short *) pbuf + shortsPerEU * row;
629		for (k = 0; k < shortsPerEU; k++)
630			temp[k] ^= *(pbuf_current + k);
631		for (k = 0; k < shortsPerEU; k++)
632			dest_smaller_current[k] = temp[k];
633#elif RF_EO_MATRIX_DIM == 17
634		pbuf_current = (long *) pbuf + longsPerEU * row;
635		for (k = 0; k < longsPerEU; k++)
636			temp[k] ^= *(pbuf_current + k);
637		for (k = 0; k < longsPerEU; k++)
638			dest_smaller_current[k] = temp[k];
639#endif
640		count++;
641	}
642	/* Check if all Encoding Unit in the data buffer have been decoded,
643	 * according EvenOdd theory, if "RF_EO_MATRIX_DIM" is a prime number,
644	 * this algorithm will covered all buffer 				 */
645	RF_ASSERT(count == numRowInEncMatix);
646	RF_Free((char *) P, bytesPerEU);
647	RF_Free((char *) temp, bytesPerEU);
648}
649
650
651/***************************************************************************************
652* 	This function is called by double degragded read
653* 	EO_200_CreateReadDAG
654*
655***************************************************************************************/
656int
657rf_EvenOddDoubleRecoveryFunc(node)
658	RF_DagNode_t *node;
659{
660	int     ndataParam = 0;
661	int     np = node->numParams;
662	RF_AccessStripeMap_t *asmap = (RF_AccessStripeMap_t *) node->params[np - 1].p;
663	RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[np - 2].p;
664	RF_RaidLayout_t *layoutPtr = (RF_RaidLayout_t *) & (raidPtr->Layout);
665	int     i, prm, sector, nresults = node->numResults;
666	RF_SectorCount_t secPerSU = layoutPtr->sectorsPerStripeUnit;
667	unsigned sosAddr;
668	int     two = 0, mallc_one = 0, mallc_two = 0;	/* flags to indicate if
669							 * memory is allocated */
670	int     bytesPerSector = rf_RaidAddressToByte(raidPtr, 1);
671	RF_PhysDiskAddr_t *ppda, *ppda2, *epda, *epda2, *pda, *pda0, *pda1,
672	        npda;
673	RF_RowCol_t fcol[2], fsuoff[2], fsuend[2], numDataCol = layoutPtr->numDataCol;
674	char  **buf, *ebuf, *pbuf, *dest[2];
675	long   *suoff = NULL, *suend = NULL, *prmToCol = NULL, psuoff, esuoff;
676	RF_SectorNum_t startSector, endSector;
677	RF_Etimer_t timer;
678	RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
679
680	RF_ETIMER_START(timer);
681
682	/* Find out the number of parameters which are pdas for data
683	 * information */
684	for (i = 0; i <= np; i++)
685		if (((RF_PhysDiskAddr_t *) node->params[i].p)->type != RF_PDA_TYPE_DATA) {
686			ndataParam = i;
687			break;
688		}
689	RF_Malloc(buf, numDataCol * sizeof(char *), (char **));
690	if (ndataParam != 0) {
691		RF_Malloc(suoff, ndataParam * sizeof(long), (long *));
692		RF_Malloc(suend, ndataParam * sizeof(long), (long *));
693		RF_Malloc(prmToCol, ndataParam * sizeof(long), (long *));
694	}
695	if (asmap->failedPDAs[1] &&
696	    (asmap->failedPDAs[1]->numSector + asmap->failedPDAs[0]->numSector < secPerSU)) {
697		RF_ASSERT(0);	/* currently, no support for this situation */
698		ppda = node->params[np - 6].p;
699		ppda2 = node->params[np - 5].p;
700		RF_ASSERT(ppda2->type == RF_PDA_TYPE_PARITY);
701		epda = node->params[np - 4].p;
702		epda2 = node->params[np - 3].p;
703		RF_ASSERT(epda2->type == RF_PDA_TYPE_Q);
704		two = 1;
705	} else {
706		ppda = node->params[np - 4].p;
707		epda = node->params[np - 3].p;
708		psuoff = rf_StripeUnitOffset(layoutPtr, ppda->startSector);
709		esuoff = rf_StripeUnitOffset(layoutPtr, epda->startSector);
710		RF_ASSERT(psuoff == esuoff);
711	}
712	/*
713            the followings have three goals:
714            1. determine the startSector to begin decoding and endSector to end decoding.
715            2. determine the colume numbers of the two failed disks.
716            3. determine the offset and end offset of the access within each failed stripe unit.
717         */
718	if (nresults == 1) {
719		/* find the startSector to begin decoding */
720		pda = node->results[0];
721		memset(pda->bufPtr, 0, bytesPerSector * pda->numSector);
722		fsuoff[0] = rf_StripeUnitOffset(layoutPtr, pda->startSector);
723		fsuend[0] = fsuoff[0] + pda->numSector;
724		startSector = fsuoff[0];
725		endSector = fsuend[0];
726
727		/* find out the column of failed disk being accessed */
728		fcol[0] = rf_EUCol(layoutPtr, pda->raidAddress);
729
730		/* find out the other failed colume not accessed */
731		sosAddr = rf_RaidAddressOfPrevStripeBoundary(layoutPtr, asmap->raidAddress);
732		for (i = 0; i < numDataCol; i++) {
733			npda.raidAddress = sosAddr + (i * secPerSU);
734			(raidPtr->Layout.map->MapSector) (raidPtr, npda.raidAddress, &(npda.row), &(npda.col), &(npda.startSector), 0);
735			/* skip over dead disks */
736			if (RF_DEAD_DISK(raidPtr->Disks[npda.row][npda.col].status))
737				if (i != fcol[0])
738					break;
739		}
740		RF_ASSERT(i < numDataCol);
741		fcol[1] = i;
742	} else {
743		RF_ASSERT(nresults == 2);
744		pda0 = node->results[0];
745		memset(pda0->bufPtr, 0, bytesPerSector * pda0->numSector);
746		pda1 = node->results[1];
747		memset(pda1->bufPtr, 0, bytesPerSector * pda1->numSector);
748		/* determine the failed colume numbers of the two failed
749		 * disks. */
750		fcol[0] = rf_EUCol(layoutPtr, pda0->raidAddress);
751		fcol[1] = rf_EUCol(layoutPtr, pda1->raidAddress);
752		/* determine the offset and end offset of the access within
753		 * each failed stripe unit. */
754		fsuoff[0] = rf_StripeUnitOffset(layoutPtr, pda0->startSector);
755		fsuend[0] = fsuoff[0] + pda0->numSector;
756		fsuoff[1] = rf_StripeUnitOffset(layoutPtr, pda1->startSector);
757		fsuend[1] = fsuoff[1] + pda1->numSector;
758		/* determine the startSector to begin decoding */
759		startSector = RF_MIN(pda0->startSector, pda1->startSector);
760		/* determine the endSector to end decoding */
761		endSector = RF_MAX(fsuend[0], fsuend[1]);
762	}
763	/*
764	      assign the beginning sector and the end sector for each parameter
765	      find out the corresponding colume # for each parameter
766        */
767	for (prm = 0; prm < ndataParam; prm++) {
768		pda = node->params[prm].p;
769		suoff[prm] = rf_StripeUnitOffset(layoutPtr, pda->startSector);
770		suend[prm] = suoff[prm] + pda->numSector;
771		prmToCol[prm] = rf_EUCol(layoutPtr, pda->raidAddress);
772	}
773	/* 'sector' is the sector for the current decoding algorithm. For each
774	 * sector in the failed SU, find out the corresponding parameters that
775	 * cover the current sector and that are needed for decoding of this
776	 * sector in failed SU. 2.  Find out if sector is in the shadow of any
777	 * accessed failed SU. If not, malloc a temporary space of a sector in
778	 * size. */
779	for (sector = startSector; sector < endSector; sector++) {
780		if (nresults == 2)
781			if (!(fsuoff[0] <= sector && sector < fsuend[0]) && !(fsuoff[1] <= sector && sector < fsuend[1]))
782				continue;
783		for (prm = 0; prm < ndataParam; prm++)
784			if (suoff[prm] <= sector && sector < suend[prm])
785				buf[(prmToCol[prm])] = ((RF_PhysDiskAddr_t *) node->params[prm].p)->bufPtr +
786				    rf_RaidAddressToByte(raidPtr, sector - suoff[prm]);
787		/* find out if sector is in the shadow of any accessed failed
788		 * SU. If yes, assign dest[0], dest[1] to point at suitable
789		 * position of the buffer corresponding to failed SUs. if no,
790		 * malloc a temporary space of a sector in size for
791		 * destination of decoding. */
792		RF_ASSERT(nresults == 1 || nresults == 2);
793		if (nresults == 1) {
794			dest[0] = ((RF_PhysDiskAddr_t *) node->results[0])->bufPtr + rf_RaidAddressToByte(raidPtr, sector - fsuoff[0]);
795			/* Always malloc temp buffer to dest[1]  */
796			RF_Malloc(dest[1], bytesPerSector, (char *));
797			memset(dest[1], 0, bytesPerSector);
798			mallc_two = 1;
799		} else {
800			if (fsuoff[0] <= sector && sector < fsuend[0])
801				dest[0] = ((RF_PhysDiskAddr_t *) node->results[0])->bufPtr + rf_RaidAddressToByte(raidPtr, sector - fsuoff[0]);
802			else {
803				RF_Malloc(dest[0], bytesPerSector, (char *));
804				memset(dest[0], 0, bytesPerSector);
805				mallc_one = 1;
806			}
807			if (fsuoff[1] <= sector && sector < fsuend[1])
808				dest[1] = ((RF_PhysDiskAddr_t *) node->results[1])->bufPtr + rf_RaidAddressToByte(raidPtr, sector - fsuoff[1]);
809			else {
810				RF_Malloc(dest[1], bytesPerSector, (char *));
811				memset(dest[1], 0, bytesPerSector);
812				mallc_two = 1;
813			}
814			RF_ASSERT(mallc_one == 0 || mallc_two == 0);
815		}
816		pbuf = ppda->bufPtr + rf_RaidAddressToByte(raidPtr, sector - psuoff);
817		ebuf = epda->bufPtr + rf_RaidAddressToByte(raidPtr, sector - esuoff);
818		/*
819	         * After finish finding all needed sectors, call doubleEOdecode function for decoding
820	         * one sector to destination.
821	         */
822		rf_doubleEOdecode(raidPtr, buf, dest, fcol, pbuf, ebuf);
823		/* free all allocated memory, and mark flag to indicate no
824		 * memory is being allocated */
825		if (mallc_one == 1)
826			RF_Free(dest[0], bytesPerSector);
827		if (mallc_two == 1)
828			RF_Free(dest[1], bytesPerSector);
829		mallc_one = mallc_two = 0;
830	}
831	RF_Free(buf, numDataCol * sizeof(char *));
832	if (ndataParam != 0) {
833		RF_Free(suoff, ndataParam * sizeof(long));
834		RF_Free(suend, ndataParam * sizeof(long));
835		RF_Free(prmToCol, ndataParam * sizeof(long));
836	}
837	RF_ETIMER_STOP(timer);
838	RF_ETIMER_EVAL(timer);
839	if (tracerec) {
840		tracerec->q_us += RF_ETIMER_VAL_US(timer);
841	}
842	rf_GenericWakeupFunc(node, 0);
843#if 1
844	return (0);		/* XXX is this even close!!?!?!!? GO */
845#endif
846}
847
848
849/* currently, only access of one of the two failed SU is allowed in this function.
850 * also, asmap->numStripeUnitsAccessed is limited to be one, the RaidFrame will break large access into
851 * many accesses of single stripe unit.
852 */
853
854int
855rf_EOWriteDoubleRecoveryFunc(node)
856	RF_DagNode_t *node;
857{
858	int     np = node->numParams;
859	RF_AccessStripeMap_t *asmap = (RF_AccessStripeMap_t *) node->params[np - 1].p;
860	RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[np - 2].p;
861	RF_RaidLayout_t *layoutPtr = (RF_RaidLayout_t *) & (raidPtr->Layout);
862	RF_SectorNum_t sector;
863	RF_RowCol_t col, scol;
864	int     prm, i, j;
865	RF_SectorCount_t secPerSU = layoutPtr->sectorsPerStripeUnit;
866	unsigned sosAddr;
867	unsigned bytesPerSector = rf_RaidAddressToByte(raidPtr, 1);
868	RF_int64 numbytes;
869	RF_SectorNum_t startSector, endSector;
870	RF_PhysDiskAddr_t *ppda, *epda, *pda, *fpda, npda;
871	RF_RowCol_t fcol[2], numDataCol = layoutPtr->numDataCol;
872	char  **buf;		/* buf[0], buf[1], buf[2], ...etc. point to
873				 * buffer storing data read from col0, col1,
874				 * col2 */
875	char   *ebuf, *pbuf, *dest[2], *olddata[2];
876	RF_Etimer_t timer;
877	RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec;
878
879	RF_ASSERT(asmap->numDataFailed == 1);	/* currently only support this
880						 * case, the other failed SU
881						 * is not being accessed */
882	RF_ETIMER_START(timer);
883	RF_Malloc(buf, numDataCol * sizeof(char *), (char **));
884
885	ppda = node->results[0];/* Instead of being buffers, node->results[0]
886				 * and [1] are Ppda and Epda  */
887	epda = node->results[1];
888	fpda = asmap->failedPDAs[0];
889
890	/* First, recovery the failed old SU using EvenOdd double decoding      */
891	/* determine the startSector and endSector for decoding */
892	startSector = rf_StripeUnitOffset(layoutPtr, fpda->startSector);
893	endSector = startSector + fpda->numSector;
894	/* Assign buf[col] pointers to point to each non-failed colume  and
895	 * initialize the pbuf and ebuf to point at the beginning of each
896	 * source buffers and destination buffers */
897	for (prm = 0; prm < numDataCol - 2; prm++) {
898		pda = (RF_PhysDiskAddr_t *) node->params[prm].p;
899		col = rf_EUCol(layoutPtr, pda->raidAddress);
900		buf[col] = pda->bufPtr;
901	}
902	/* pbuf and ebuf:  they will change values as double recovery decoding
903	 * goes on */
904	pbuf = ppda->bufPtr;
905	ebuf = epda->bufPtr;
906	/* find out the logical colume numbers in the encoding matrix of the
907	 * two failed columes */
908	fcol[0] = rf_EUCol(layoutPtr, fpda->raidAddress);
909
910	/* find out the other failed colume not accessed this time */
911	sosAddr = rf_RaidAddressOfPrevStripeBoundary(layoutPtr, asmap->raidAddress);
912	for (i = 0; i < numDataCol; i++) {
913		npda.raidAddress = sosAddr + (i * secPerSU);
914		(raidPtr->Layout.map->MapSector) (raidPtr, npda.raidAddress, &(npda.row), &(npda.col), &(npda.startSector), 0);
915		/* skip over dead disks */
916		if (RF_DEAD_DISK(raidPtr->Disks[npda.row][npda.col].status))
917			if (i != fcol[0])
918				break;
919	}
920	RF_ASSERT(i < numDataCol);
921	fcol[1] = i;
922	/* assign temporary space to put recovered failed SU */
923	numbytes = fpda->numSector * bytesPerSector;
924	RF_Malloc(olddata[0], numbytes, (char *));
925	RF_Malloc(olddata[1], numbytes, (char *));
926	dest[0] = olddata[0];
927	dest[1] = olddata[1];
928	memset(olddata[0], 0, numbytes);
929	memset(olddata[1], 0, numbytes);
930	/* Begin the recovery decoding, initially buf[j],  ebuf, pbuf, dest[j]
931	 * have already pointed at the beginning of each source buffers and
932	 * destination buffers */
933	for (sector = startSector, i = 0; sector < endSector; sector++, i++) {
934		rf_doubleEOdecode(raidPtr, buf, dest, fcol, pbuf, ebuf);
935		for (j = 0; j < numDataCol; j++)
936			if ((j != fcol[0]) && (j != fcol[1]))
937				buf[j] += bytesPerSector;
938		dest[0] += bytesPerSector;
939		dest[1] += bytesPerSector;
940		ebuf += bytesPerSector;
941		pbuf += bytesPerSector;
942	}
943	/* after recovery, the buffer pointed by olddata[0] is the old failed
944	 * data. With new writing data and this old data, use small write to
945	 * calculate the new redundant informations */
946	/* node->params[ 0, ... PDAPerDisk * (numDataCol - 2)-1 ] are Pdas of
947	 * Rrd; params[ PDAPerDisk*(numDataCol - 2), ... PDAPerDisk*numDataCol
948	 * -1 ] are Pdas of Rp, ( Rp2 ), Re, ( Re2 ) ; params[
949	 * PDAPerDisk*numDataCol, ... PDAPerDisk*numDataCol
950	 * +asmap->numStripeUnitsAccessed -asmap->numDataFailed-1] are Pdas of
951	 * wudNodes; For current implementation, we assume the simplest case:
952	 * asmap->numStripeUnitsAccessed == 1 and asmap->numDataFailed == 1
953	 * ie. PDAPerDisk = 1 then node->params[numDataCol] must be the new
954	 * data to be writen to the failed disk. We first bxor the new data
955	 * into the old recovered data, then do the same things as small
956	 * write. */
957
958	rf_bxor(((RF_PhysDiskAddr_t *) node->params[numDataCol].p)->bufPtr, olddata[0], numbytes, node->dagHdr->bp);
959	/* do new 'E' calculation  */
960	/* find out the corresponding colume in encoding matrix for write
961	 * colume to be encoded into redundant disk 'E' */
962	scol = rf_EUCol(layoutPtr, fpda->raidAddress);
963	/* olddata[0] now is source buffer pointer; epda->bufPtr is the dest
964	 * buffer pointer               */
965	rf_e_encToBuf(raidPtr, scol, olddata[0], RF_EO_MATRIX_DIM - 2, epda->bufPtr, fpda->numSector);
966
967	/* do new 'P' calculation  */
968	rf_bxor(olddata[0], ppda->bufPtr, numbytes, node->dagHdr->bp);
969	/* Free the allocated buffer  */
970	RF_Free(olddata[0], numbytes);
971	RF_Free(olddata[1], numbytes);
972	RF_Free(buf, numDataCol * sizeof(char *));
973
974	RF_ETIMER_STOP(timer);
975	RF_ETIMER_EVAL(timer);
976	if (tracerec) {
977		tracerec->q_us += RF_ETIMER_VAL_US(timer);
978	}
979	rf_GenericWakeupFunc(node, 0);
980	return (0);
981}
982#endif				/* RF_INCLUDE_EVENODD > 0 */
983