rf_evenodd_dagfuncs.c revision 1.7
1/* $NetBSD: rf_evenodd_dagfuncs.c,v 1.7 2001/01/26 03:50: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 "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 */ 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, *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 148int 149rf_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 ********/ 189void 190rf_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 ******************************************************************************************/ 222int 223rf_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 *******************************************************************************************/ 245void 246rf_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 **************************************************************************************/ 283int 284rf_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 **************************************************************************************/ 299void 300rf_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 374void 375rf_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 **************************************************************************************/ 396int 397rf_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 bzero((char *) node->results[0], rf_RaidAddressToByte(raidPtr, failedPDA->numSector)); 414 if (node->dagHdr->status == rf_enable) { 415 RF_ETIMER_START(timer); 416 for (i = 0; i < node->numParams - 2; i += 2) 417 if (node->params[i + 1].p != node->results[0]) { 418 pda = (RF_PhysDiskAddr_t *) node->params[i].p; 419 if (i == node->numParams - 4) 420 scol = RF_EO_MATRIX_DIM - 2; /* the colume of 421 * redundant E */ 422 else 423 scol = rf_EUCol(layoutPtr, pda->raidAddress); 424 srcbuf = (char *) node->params[i + 1].p; 425 suoffset = rf_StripeUnitOffset(layoutPtr, pda->startSector); 426 destbuf = ((char *) node->results[0]) + rf_RaidAddressToByte(raidPtr, suoffset - failedSUOffset); 427 rf_e_encToBuf(raidPtr, scol, srcbuf, fcol, destbuf, pda->numSector); 428 } 429 RF_ETIMER_STOP(timer); 430 RF_ETIMER_EVAL(timer); 431 tracerec->xor_us += RF_ETIMER_VAL_US(timer); 432 } 433 return (rf_GenericWakeupFunc(node, 0)); /* node execute successfully */ 434} 435/************************************************************************************** 436 * This function is used in the case where one data and the parity have filed. 437 * (in EO_110_CreateWriteDAG ) 438 **************************************************************************************/ 439int 440rf_EO_DegradedWriteEFunc(RF_DagNode_t * node) 441{ 442 rf_DegrESubroutine(node, node->results[0]); 443 rf_GenericWakeupFunc(node, 0); 444#if 1 445 return (0); /* XXX Yet another one!! GO */ 446#endif 447} 448 449 450 451/************************************************************************************** 452 * THE FUNCTION IS FOR DOUBLE DEGRADED READ AND WRITE CASES 453 **************************************************************************************/ 454 455void 456rf_doubleEOdecode( 457 RF_Raid_t * raidPtr, 458 char **rrdbuf, 459 char **dest, 460 RF_RowCol_t * fcol, 461 char *pbuf, 462 char *ebuf) 463{ 464 RF_RaidLayout_t *layoutPtr = (RF_RaidLayout_t *) & (raidPtr->Layout); 465 int i, j, k, f1, f2, row; 466 int rrdrow, erow, count = 0; 467 int bytesPerSector = rf_RaidAddressToByte(raidPtr, 1); 468 int numRowInEncMatix = (RF_EO_MATRIX_DIM) - 1; 469#if 0 470 int pcol = (RF_EO_MATRIX_DIM) - 1; 471#endif 472 int ecol = (RF_EO_MATRIX_DIM) - 2; 473 int bytesPerEU = bytesPerSector / numRowInEncMatix; 474 int numDataCol = layoutPtr->numDataCol; 475#if RF_EO_MATRIX_DIM > 17 476 int shortsPerEU = bytesPerEU / sizeof(short); 477 short *rrdbuf_current, *pbuf_current, *ebuf_current; 478 short *dest_smaller, *dest_smaller_current, *dest_larger, *dest_larger_current; 479 short *temp; 480 short *P; 481 482 RF_ASSERT(bytesPerEU % sizeof(short) == 0); 483 RF_Malloc(P, bytesPerEU, (short *)); 484 RF_Malloc(temp, bytesPerEU, (short *)); 485#elif RF_EO_MATRIX_DIM == 17 486 int longsPerEU = bytesPerEU / sizeof(long); 487 long *rrdbuf_current, *pbuf_current, *ebuf_current; 488 long *dest_smaller, *dest_smaller_current, *dest_larger, *dest_larger_current; 489 long *temp; 490 long *P; 491 492 RF_ASSERT(bytesPerEU % sizeof(long) == 0); 493 RF_Malloc(P, bytesPerEU, (long *)); 494 RF_Malloc(temp, bytesPerEU, (long *)); 495#endif 496 RF_ASSERT(*((long *) dest[0]) == 0); 497 RF_ASSERT(*((long *) dest[1]) == 0); 498 bzero((char *) P, bytesPerEU); 499 bzero((char *) temp, bytesPerEU); 500 RF_ASSERT(*P == 0); 501 /* calculate the 'P' parameter, which, not parity, is the Xor of all 502 * elements in the last two column, ie. 'E' and 'parity' colume, see 503 * the Ref. paper by Blaum, et al 1993 */ 504 for (i = 0; i < numRowInEncMatix; i++) 505 for (k = 0; k < longsPerEU; k++) { 506#if RF_EO_MATRIX_DIM > 17 507 ebuf_current = ((short *) ebuf) + i * shortsPerEU + k; 508 pbuf_current = ((short *) pbuf) + i * shortsPerEU + k; 509#elif RF_EO_MATRIX_DIM == 17 510 ebuf_current = ((long *) ebuf) + i * longsPerEU + k; 511 pbuf_current = ((long *) pbuf) + i * longsPerEU + k; 512#endif 513 P[k] ^= *ebuf_current; 514 P[k] ^= *pbuf_current; 515 } 516 RF_ASSERT(fcol[0] != fcol[1]); 517 if (fcol[0] < fcol[1]) { 518#if RF_EO_MATRIX_DIM > 17 519 dest_smaller = (short *) (dest[0]); 520 dest_larger = (short *) (dest[1]); 521#elif RF_EO_MATRIX_DIM == 17 522 dest_smaller = (long *) (dest[0]); 523 dest_larger = (long *) (dest[1]); 524#endif 525 f1 = fcol[0]; 526 f2 = fcol[1]; 527 } else { 528#if RF_EO_MATRIX_DIM > 17 529 dest_smaller = (short *) (dest[1]); 530 dest_larger = (short *) (dest[0]); 531#elif RF_EO_MATRIX_DIM == 17 532 dest_smaller = (long *) (dest[1]); 533 dest_larger = (long *) (dest[0]); 534#endif 535 f1 = fcol[1]; 536 f2 = fcol[0]; 537 } 538 row = (RF_EO_MATRIX_DIM) - 1; 539 while ((row = rf_EO_Mod((row + f1 - f2), RF_EO_MATRIX_DIM)) != ((RF_EO_MATRIX_DIM) - 1)) { 540#if RF_EO_MATRIX_DIM > 17 541 dest_larger_current = dest_larger + row * shortsPerEU; 542 dest_smaller_current = dest_smaller + row * shortsPerEU; 543#elif RF_EO_MATRIX_DIM == 17 544 dest_larger_current = dest_larger + row * longsPerEU; 545 dest_smaller_current = dest_smaller + row * longsPerEU; 546#endif 547 /** Do the diagonal recovery. Initially, temp[k] = (failed 1), 548 which is the failed data in the colume which has smaller col index. **/ 549 /* step 1: ^(SUM of nonfailed in-diagonal A(rrdrow,0..m-3)) */ 550 for (j = 0; j < numDataCol; j++) { 551 if (j == f1 || j == f2) 552 continue; 553 rrdrow = rf_EO_Mod((row + f2 - j), RF_EO_MATRIX_DIM); 554 if (rrdrow != (RF_EO_MATRIX_DIM) - 1) { 555#if RF_EO_MATRIX_DIM > 17 556 rrdbuf_current = (short *) (rrdbuf[j]) + rrdrow * shortsPerEU; 557 for (k = 0; k < shortsPerEU; k++) 558 temp[k] ^= *(rrdbuf_current + k); 559#elif RF_EO_MATRIX_DIM == 17 560 rrdbuf_current = (long *) (rrdbuf[j]) + rrdrow * longsPerEU; 561 for (k = 0; k < longsPerEU; k++) 562 temp[k] ^= *(rrdbuf_current + k); 563#endif 564 } 565 } 566 /* step 2: ^E(erow,m-2), If erow is at the buttom row, don't 567 * Xor into it E(erow,m-2) = (principle diagonal) ^ (failed 568 * 1) ^ (failed 2) ^ ( SUM of nonfailed in-diagonal 569 * A(rrdrow,0..m-3) ) After this step, temp[k] = (principle 570 * diagonal) ^ (failed 2) */ 571 572 erow = rf_EO_Mod((row + f2 - ecol), (RF_EO_MATRIX_DIM)); 573 if (erow != (RF_EO_MATRIX_DIM) - 1) { 574#if RF_EO_MATRIX_DIM > 17 575 ebuf_current = (short *) ebuf + shortsPerEU * erow; 576 for (k = 0; k < shortsPerEU; k++) 577 temp[k] ^= *(ebuf_current + k); 578#elif RF_EO_MATRIX_DIM == 17 579 ebuf_current = (long *) ebuf + longsPerEU * erow; 580 for (k = 0; k < longsPerEU; k++) 581 temp[k] ^= *(ebuf_current + k); 582#endif 583 } 584 /* step 3: ^P to obtain the failed data (failed 2). P can be 585 * proved to be actually (principle diagonal) After this 586 * step, temp[k] = (failed 2), the failed data to be recovered */ 587#if RF_EO_MATRIX_DIM > 17 588 for (k = 0; k < shortsPerEU; k++) 589 temp[k] ^= P[k]; 590 /* Put the data to the destination buffer */ 591 for (k = 0; k < shortsPerEU; k++) 592 dest_larger_current[k] = temp[k]; 593#elif RF_EO_MATRIX_DIM == 17 594 for (k = 0; k < longsPerEU; k++) 595 temp[k] ^= P[k]; 596 /* Put the data to the destination buffer */ 597 for (k = 0; k < longsPerEU; k++) 598 dest_larger_current[k] = temp[k]; 599#endif 600 601 /** THE FOLLOWING DO THE HORIZONTAL XOR **/ 602 /* step 1: ^(SUM of A(row,0..m-3)), ie. all nonfailed data 603 * columes */ 604 for (j = 0; j < numDataCol; j++) { 605 if (j == f1 || j == f2) 606 continue; 607#if RF_EO_MATRIX_DIM > 17 608 rrdbuf_current = (short *) (rrdbuf[j]) + row * shortsPerEU; 609 for (k = 0; k < shortsPerEU; k++) 610 temp[k] ^= *(rrdbuf_current + k); 611#elif RF_EO_MATRIX_DIM == 17 612 rrdbuf_current = (long *) (rrdbuf[j]) + row * longsPerEU; 613 for (k = 0; k < longsPerEU; k++) 614 temp[k] ^= *(rrdbuf_current + k); 615#endif 616 } 617 /* step 2: ^A(row,m-1) */ 618 /* step 3: Put the data to the destination buffer */ 619#if RF_EO_MATRIX_DIM > 17 620 pbuf_current = (short *) pbuf + shortsPerEU * row; 621 for (k = 0; k < shortsPerEU; k++) 622 temp[k] ^= *(pbuf_current + k); 623 for (k = 0; k < shortsPerEU; k++) 624 dest_smaller_current[k] = temp[k]; 625#elif RF_EO_MATRIX_DIM == 17 626 pbuf_current = (long *) pbuf + longsPerEU * row; 627 for (k = 0; k < longsPerEU; k++) 628 temp[k] ^= *(pbuf_current + k); 629 for (k = 0; k < longsPerEU; k++) 630 dest_smaller_current[k] = temp[k]; 631#endif 632 count++; 633 } 634 /* Check if all Encoding Unit in the data buffer have been decoded, 635 * according EvenOdd theory, if "RF_EO_MATRIX_DIM" is a prime number, 636 * this algorithm will covered all buffer */ 637 RF_ASSERT(count == numRowInEncMatix); 638 RF_Free((char *) P, bytesPerEU); 639 RF_Free((char *) temp, bytesPerEU); 640} 641 642 643/*************************************************************************************** 644* This function is called by double degragded read 645* EO_200_CreateReadDAG 646* 647***************************************************************************************/ 648int 649rf_EvenOddDoubleRecoveryFunc(node) 650 RF_DagNode_t *node; 651{ 652 int ndataParam = 0; 653 int np = node->numParams; 654 RF_AccessStripeMap_t *asmap = (RF_AccessStripeMap_t *) node->params[np - 1].p; 655 RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[np - 2].p; 656 RF_RaidLayout_t *layoutPtr = (RF_RaidLayout_t *) & (raidPtr->Layout); 657 int i, prm, sector, nresults = node->numResults; 658 RF_SectorCount_t secPerSU = layoutPtr->sectorsPerStripeUnit; 659 unsigned sosAddr; 660 int two = 0, mallc_one = 0, mallc_two = 0; /* flags to indicate if 661 * memory is allocated */ 662 int bytesPerSector = rf_RaidAddressToByte(raidPtr, 1); 663 RF_PhysDiskAddr_t *ppda, *ppda2, *epda, *epda2, *pda, *pda0, *pda1, 664 npda; 665 RF_RowCol_t fcol[2], fsuoff[2], fsuend[2], numDataCol = layoutPtr->numDataCol; 666 char **buf, *ebuf, *pbuf, *dest[2]; 667 long *suoff = NULL, *suend = NULL, *prmToCol = NULL, psuoff, esuoff; 668 RF_SectorNum_t startSector, endSector; 669 RF_Etimer_t timer; 670 RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec; 671 672 RF_ETIMER_START(timer); 673 674 /* Find out the number of parameters which are pdas for data 675 * information */ 676 for (i = 0; i <= np; i++) 677 if (((RF_PhysDiskAddr_t *) node->params[i].p)->type != RF_PDA_TYPE_DATA) { 678 ndataParam = i; 679 break; 680 } 681 RF_Malloc(buf, numDataCol * sizeof(char *), (char **)); 682 if (ndataParam != 0) { 683 RF_Malloc(suoff, ndataParam * sizeof(long), (long *)); 684 RF_Malloc(suend, ndataParam * sizeof(long), (long *)); 685 RF_Malloc(prmToCol, ndataParam * sizeof(long), (long *)); 686 } 687 if (asmap->failedPDAs[1] && 688 (asmap->failedPDAs[1]->numSector + asmap->failedPDAs[0]->numSector < secPerSU)) { 689 RF_ASSERT(0); /* currently, no support for this situation */ 690 ppda = node->params[np - 6].p; 691 ppda2 = node->params[np - 5].p; 692 RF_ASSERT(ppda2->type == RF_PDA_TYPE_PARITY); 693 epda = node->params[np - 4].p; 694 epda2 = node->params[np - 3].p; 695 RF_ASSERT(epda2->type == RF_PDA_TYPE_Q); 696 two = 1; 697 } else { 698 ppda = node->params[np - 4].p; 699 epda = node->params[np - 3].p; 700 psuoff = rf_StripeUnitOffset(layoutPtr, ppda->startSector); 701 esuoff = rf_StripeUnitOffset(layoutPtr, epda->startSector); 702 RF_ASSERT(psuoff == esuoff); 703 } 704 /* 705 the followings have three goals: 706 1. determine the startSector to begin decoding and endSector to end decoding. 707 2. determine the colume numbers of the two failed disks. 708 3. determine the offset and end offset of the access within each failed stripe unit. 709 */ 710 if (nresults == 1) { 711 /* find the startSector to begin decoding */ 712 pda = node->results[0]; 713 bzero(pda->bufPtr, bytesPerSector * pda->numSector); 714 fsuoff[0] = rf_StripeUnitOffset(layoutPtr, pda->startSector); 715 fsuend[0] = fsuoff[0] + pda->numSector; 716 startSector = fsuoff[0]; 717 endSector = fsuend[0]; 718 719 /* find out the column of failed disk being accessed */ 720 fcol[0] = rf_EUCol(layoutPtr, pda->raidAddress); 721 722 /* find out the other failed colume not accessed */ 723 sosAddr = rf_RaidAddressOfPrevStripeBoundary(layoutPtr, asmap->raidAddress); 724 for (i = 0; i < numDataCol; i++) { 725 npda.raidAddress = sosAddr + (i * secPerSU); 726 (raidPtr->Layout.map->MapSector) (raidPtr, npda.raidAddress, &(npda.row), &(npda.col), &(npda.startSector), 0); 727 /* skip over dead disks */ 728 if (RF_DEAD_DISK(raidPtr->Disks[npda.row][npda.col].status)) 729 if (i != fcol[0]) 730 break; 731 } 732 RF_ASSERT(i < numDataCol); 733 fcol[1] = i; 734 } else { 735 RF_ASSERT(nresults == 2); 736 pda0 = node->results[0]; 737 bzero(pda0->bufPtr, bytesPerSector * pda0->numSector); 738 pda1 = node->results[1]; 739 bzero(pda1->bufPtr, bytesPerSector * pda1->numSector); 740 /* determine the failed colume numbers of the two failed 741 * disks. */ 742 fcol[0] = rf_EUCol(layoutPtr, pda0->raidAddress); 743 fcol[1] = rf_EUCol(layoutPtr, pda1->raidAddress); 744 /* determine the offset and end offset of the access within 745 * each failed stripe unit. */ 746 fsuoff[0] = rf_StripeUnitOffset(layoutPtr, pda0->startSector); 747 fsuend[0] = fsuoff[0] + pda0->numSector; 748 fsuoff[1] = rf_StripeUnitOffset(layoutPtr, pda1->startSector); 749 fsuend[1] = fsuoff[1] + pda1->numSector; 750 /* determine the startSector to begin decoding */ 751 startSector = RF_MIN(pda0->startSector, pda1->startSector); 752 /* determine the endSector to end decoding */ 753 endSector = RF_MAX(fsuend[0], fsuend[1]); 754 } 755 /* 756 assign the beginning sector and the end sector for each parameter 757 find out the corresponding colume # for each parameter 758 */ 759 for (prm = 0; prm < ndataParam; prm++) { 760 pda = node->params[prm].p; 761 suoff[prm] = rf_StripeUnitOffset(layoutPtr, pda->startSector); 762 suend[prm] = suoff[prm] + pda->numSector; 763 prmToCol[prm] = rf_EUCol(layoutPtr, pda->raidAddress); 764 } 765 /* 'sector' is the sector for the current decoding algorithm. For each 766 * sector in the failed SU, find out the corresponding parameters that 767 * cover the current sector and that are needed for decoding of this 768 * sector in failed SU. 2. Find out if sector is in the shadow of any 769 * accessed failed SU. If not, malloc a temporary space of a sector in 770 * size. */ 771 for (sector = startSector; sector < endSector; sector++) { 772 if (nresults == 2) 773 if (!(fsuoff[0] <= sector && sector < fsuend[0]) && !(fsuoff[1] <= sector && sector < fsuend[1])) 774 continue; 775 for (prm = 0; prm < ndataParam; prm++) 776 if (suoff[prm] <= sector && sector < suend[prm]) 777 buf[(prmToCol[prm])] = ((RF_PhysDiskAddr_t *) node->params[prm].p)->bufPtr + 778 rf_RaidAddressToByte(raidPtr, sector - suoff[prm]); 779 /* find out if sector is in the shadow of any accessed failed 780 * SU. If yes, assign dest[0], dest[1] to point at suitable 781 * position of the buffer corresponding to failed SUs. if no, 782 * malloc a temporary space of a sector in size for 783 * destination of decoding. */ 784 RF_ASSERT(nresults == 1 || nresults == 2); 785 if (nresults == 1) { 786 dest[0] = ((RF_PhysDiskAddr_t *) node->results[0])->bufPtr + rf_RaidAddressToByte(raidPtr, sector - fsuoff[0]); 787 /* Always malloc temp buffer to dest[1] */ 788 RF_Malloc(dest[1], bytesPerSector, (char *)); 789 bzero(dest[1], bytesPerSector); 790 mallc_two = 1; 791 } else { 792 if (fsuoff[0] <= sector && sector < fsuend[0]) 793 dest[0] = ((RF_PhysDiskAddr_t *) node->results[0])->bufPtr + rf_RaidAddressToByte(raidPtr, sector - fsuoff[0]); 794 else { 795 RF_Malloc(dest[0], bytesPerSector, (char *)); 796 bzero(dest[0], bytesPerSector); 797 mallc_one = 1; 798 } 799 if (fsuoff[1] <= sector && sector < fsuend[1]) 800 dest[1] = ((RF_PhysDiskAddr_t *) node->results[1])->bufPtr + rf_RaidAddressToByte(raidPtr, sector - fsuoff[1]); 801 else { 802 RF_Malloc(dest[1], bytesPerSector, (char *)); 803 bzero(dest[1], bytesPerSector); 804 mallc_two = 1; 805 } 806 RF_ASSERT(mallc_one == 0 || mallc_two == 0); 807 } 808 pbuf = ppda->bufPtr + rf_RaidAddressToByte(raidPtr, sector - psuoff); 809 ebuf = epda->bufPtr + rf_RaidAddressToByte(raidPtr, sector - esuoff); 810 /* 811 * After finish finding all needed sectors, call doubleEOdecode function for decoding 812 * one sector to destination. 813 */ 814 rf_doubleEOdecode(raidPtr, buf, dest, fcol, pbuf, ebuf); 815 /* free all allocated memory, and mark flag to indicate no 816 * memory is being allocated */ 817 if (mallc_one == 1) 818 RF_Free(dest[0], bytesPerSector); 819 if (mallc_two == 1) 820 RF_Free(dest[1], bytesPerSector); 821 mallc_one = mallc_two = 0; 822 } 823 RF_Free(buf, numDataCol * sizeof(char *)); 824 if (ndataParam != 0) { 825 RF_Free(suoff, ndataParam * sizeof(long)); 826 RF_Free(suend, ndataParam * sizeof(long)); 827 RF_Free(prmToCol, ndataParam * sizeof(long)); 828 } 829 RF_ETIMER_STOP(timer); 830 RF_ETIMER_EVAL(timer); 831 if (tracerec) { 832 tracerec->q_us += RF_ETIMER_VAL_US(timer); 833 } 834 rf_GenericWakeupFunc(node, 0); 835#if 1 836 return (0); /* XXX is this even close!!?!?!!? GO */ 837#endif 838} 839 840 841/* currently, only access of one of the two failed SU is allowed in this function. 842 * also, asmap->numStripeUnitsAccessed is limited to be one, the RaidFrame will break large access into 843 * many accesses of single stripe unit. 844 */ 845 846int 847rf_EOWriteDoubleRecoveryFunc(node) 848 RF_DagNode_t *node; 849{ 850 int np = node->numParams; 851 RF_AccessStripeMap_t *asmap = (RF_AccessStripeMap_t *) node->params[np - 1].p; 852 RF_Raid_t *raidPtr = (RF_Raid_t *) node->params[np - 2].p; 853 RF_RaidLayout_t *layoutPtr = (RF_RaidLayout_t *) & (raidPtr->Layout); 854 RF_SectorNum_t sector; 855 RF_RowCol_t col, scol; 856 int prm, i, j; 857 RF_SectorCount_t secPerSU = layoutPtr->sectorsPerStripeUnit; 858 unsigned sosAddr; 859 unsigned bytesPerSector = rf_RaidAddressToByte(raidPtr, 1); 860 RF_int64 numbytes; 861 RF_SectorNum_t startSector, endSector; 862 RF_PhysDiskAddr_t *ppda, *epda, *pda, *fpda, npda; 863 RF_RowCol_t fcol[2], numDataCol = layoutPtr->numDataCol; 864 char **buf; /* buf[0], buf[1], buf[2], ...etc. point to 865 * buffer storing data read from col0, col1, 866 * col2 */ 867 char *ebuf, *pbuf, *dest[2], *olddata[2]; 868 RF_Etimer_t timer; 869 RF_AccTraceEntry_t *tracerec = node->dagHdr->tracerec; 870 871 RF_ASSERT(asmap->numDataFailed == 1); /* currently only support this 872 * case, the other failed SU 873 * is not being accessed */ 874 RF_ETIMER_START(timer); 875 RF_Malloc(buf, numDataCol * sizeof(char *), (char **)); 876 877 ppda = node->results[0];/* Instead of being buffers, node->results[0] 878 * and [1] are Ppda and Epda */ 879 epda = node->results[1]; 880 fpda = asmap->failedPDAs[0]; 881 882 /* First, recovery the failed old SU using EvenOdd double decoding */ 883 /* determine the startSector and endSector for decoding */ 884 startSector = rf_StripeUnitOffset(layoutPtr, fpda->startSector); 885 endSector = startSector + fpda->numSector; 886 /* Assign buf[col] pointers to point to each non-failed colume and 887 * initialize the pbuf and ebuf to point at the beginning of each 888 * source buffers and destination buffers */ 889 for (prm = 0; prm < numDataCol - 2; prm++) { 890 pda = (RF_PhysDiskAddr_t *) node->params[prm].p; 891 col = rf_EUCol(layoutPtr, pda->raidAddress); 892 buf[col] = pda->bufPtr; 893 } 894 /* pbuf and ebuf: they will change values as double recovery decoding 895 * goes on */ 896 pbuf = ppda->bufPtr; 897 ebuf = epda->bufPtr; 898 /* find out the logical colume numbers in the encoding matrix of the 899 * two failed columes */ 900 fcol[0] = rf_EUCol(layoutPtr, fpda->raidAddress); 901 902 /* find out the other failed colume not accessed this time */ 903 sosAddr = rf_RaidAddressOfPrevStripeBoundary(layoutPtr, asmap->raidAddress); 904 for (i = 0; i < numDataCol; i++) { 905 npda.raidAddress = sosAddr + (i * secPerSU); 906 (raidPtr->Layout.map->MapSector) (raidPtr, npda.raidAddress, &(npda.row), &(npda.col), &(npda.startSector), 0); 907 /* skip over dead disks */ 908 if (RF_DEAD_DISK(raidPtr->Disks[npda.row][npda.col].status)) 909 if (i != fcol[0]) 910 break; 911 } 912 RF_ASSERT(i < numDataCol); 913 fcol[1] = i; 914 /* assign temporary space to put recovered failed SU */ 915 numbytes = fpda->numSector * bytesPerSector; 916 RF_Malloc(olddata[0], numbytes, (char *)); 917 RF_Malloc(olddata[1], numbytes, (char *)); 918 dest[0] = olddata[0]; 919 dest[1] = olddata[1]; 920 bzero(olddata[0], numbytes); 921 bzero(olddata[1], numbytes); 922 /* Begin the recovery decoding, initially buf[j], ebuf, pbuf, dest[j] 923 * have already pointed at the beginning of each source buffers and 924 * destination buffers */ 925 for (sector = startSector, i = 0; sector < endSector; sector++, i++) { 926 rf_doubleEOdecode(raidPtr, buf, dest, fcol, pbuf, ebuf); 927 for (j = 0; j < numDataCol; j++) 928 if ((j != fcol[0]) && (j != fcol[1])) 929 buf[j] += bytesPerSector; 930 dest[0] += bytesPerSector; 931 dest[1] += bytesPerSector; 932 ebuf += bytesPerSector; 933 pbuf += bytesPerSector; 934 } 935 /* after recovery, the buffer pointed by olddata[0] is the old failed 936 * data. With new writing data and this old data, use small write to 937 * calculate the new redundant informations */ 938 /* node->params[ 0, ... PDAPerDisk * (numDataCol - 2)-1 ] are Pdas of 939 * Rrd; params[ PDAPerDisk*(numDataCol - 2), ... PDAPerDisk*numDataCol 940 * -1 ] are Pdas of Rp, ( Rp2 ), Re, ( Re2 ) ; params[ 941 * PDAPerDisk*numDataCol, ... PDAPerDisk*numDataCol 942 * +asmap->numStripeUnitsAccessed -asmap->numDataFailed-1] are Pdas of 943 * wudNodes; For current implementation, we assume the simplest case: 944 * asmap->numStripeUnitsAccessed == 1 and asmap->numDataFailed == 1 945 * ie. PDAPerDisk = 1 then node->params[numDataCol] must be the new 946 * data to be writen to the failed disk. We first bxor the new data 947 * into the old recovered data, then do the same things as small 948 * write. */ 949 950 rf_bxor(((RF_PhysDiskAddr_t *) node->params[numDataCol].p)->bufPtr, olddata[0], numbytes, node->dagHdr->bp); 951 /* do new 'E' calculation */ 952 /* find out the corresponding colume in encoding matrix for write 953 * colume to be encoded into redundant disk 'E' */ 954 scol = rf_EUCol(layoutPtr, fpda->raidAddress); 955 /* olddata[0] now is source buffer pointer; epda->bufPtr is the dest 956 * buffer pointer */ 957 rf_e_encToBuf(raidPtr, scol, olddata[0], RF_EO_MATRIX_DIM - 2, epda->bufPtr, fpda->numSector); 958 959 /* do new 'P' calculation */ 960 rf_bxor(olddata[0], ppda->bufPtr, numbytes, node->dagHdr->bp); 961 /* Free the allocated buffer */ 962 RF_Free(olddata[0], numbytes); 963 RF_Free(olddata[1], numbytes); 964 RF_Free(buf, numDataCol * sizeof(char *)); 965 966 RF_ETIMER_STOP(timer); 967 RF_ETIMER_EVAL(timer); 968 if (tracerec) { 969 tracerec->q_us += RF_ETIMER_VAL_US(timer); 970 } 971 rf_GenericWakeupFunc(node, 0); 972 return (0); 973} 974#endif /* RF_INCLUDE_EVENODD > 0 */ 975