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