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