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