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