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