/* Recover failure of one data block plus the P block */ void raid6_datap_recov(int disks, size_t bytes, int faila, uint8_t **ptrs, int neg_offset) { uint8_t *p, *q, *dq; const uint8_t *qmul; /* Q multiplier table */ if (neg_offset) { p = ptrs[-1]; q = ptrs[-2]; } else { p = ptrs[disks-2]; q = ptrs[disks-1]; } /* Compute syndrome with zero for the missing data page Use the dead data page as temporary storage for delta q */ dq = ptrs[faila]; ptrs[faila] = zero; qsyndrome(p, dq, ptrs, disks-2, bytes); /* Restore pointer table */ ptrs[faila] = dq; /* Now, pick the proper data tables */ qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]]]; /* Now do it... */ while ( bytes-- ) { *p++ ^= *dq = qmul[*q ^ *dq]; q++; dq++; } }
void raid6_2data_recov(int disks, size_t bytes, int faila, int failb, uint8_t **ptrs, int neg_offset) { uint8_t *p, *q, *dp, *dq; uint8_t px, qx, db; const uint8_t *pbmul; /* P multiplier table for B data */ const uint8_t *qmul; /* Q multiplier table (for both) */ if (faila > failb) { int t = faila; faila = failb; failb = t; } if (neg_offset) { p = ptrs[-1]; q = ptrs[-2]; } else { p = ptrs[disks-2]; q = ptrs[disks-1]; } /* Compute syndrome with zero for the missing data pages Use the dead data pages as temporary storage for delta p and delta q */ dp = ptrs[faila]; ptrs[faila] = zero; dq = ptrs[failb]; ptrs[failb] = zero; qsyndrome(dp, dq, ptrs, disks-2, bytes); /* Restore pointer table */ ptrs[faila] = dp; ptrs[failb] = dq; /* Now, pick the proper data tables */ pbmul = raid6_gfmul[raid6_gfexi[failb-faila]]; qmul = raid6_gfmul[raid6_gfinv[raid6_gfexp[faila]^raid6_gfexp[failb]]]; /* Now do it... */ while ( bytes-- ) { px = *p ^ *dp; qx = qmul[*q ^ *dq]; *dq++ = db = pbmul[px] ^ qx; /* Reconstructed B */ *dp++ = db ^ px; /* Reconstructed A */ p++; q++; } }
int test_stripes(int *source, unsigned long long *offsets, int raid_disks, int chunk_size, int level, int layout, unsigned long long start, unsigned long long length) { /* ready the data and p (and q) blocks, and check we got them right */ char *stripe_buf = xmalloc(raid_disks * chunk_size); char **stripes = xmalloc(raid_disks * sizeof(char*)); char **blocks = xmalloc(raid_disks * sizeof(char*)); char *p = xmalloc(chunk_size); char *q = xmalloc(chunk_size); int i; int diskP, diskQ; int data_disks = raid_disks - (level == 5 ? 1: 2); if (!tables_ready) make_tables(); for ( i = 0 ; i < raid_disks ; i++) stripes[i] = stripe_buf + i * chunk_size; while (length > 0) { int disk; for (i = 0 ; i < raid_disks ; i++) { lseek64(source[i], offsets[i]+start, 0); read(source[i], stripes[i], chunk_size); } for (i = 0 ; i < data_disks ; i++) { int disk = geo_map(i, start/chunk_size, raid_disks, level, layout); blocks[i] = stripes[disk]; printf("%d->%d\n", i, disk); } switch(level) { case 6: qsyndrome(p, q, (uint8_t**)blocks, data_disks, chunk_size); diskP = geo_map(-1, start/chunk_size, raid_disks, level, layout); if (memcmp(p, stripes[diskP], chunk_size) != 0) { printf("P(%d) wrong at %llu\n", diskP, start / chunk_size); } diskQ = geo_map(-2, start/chunk_size, raid_disks, level, layout); if (memcmp(q, stripes[diskQ], chunk_size) != 0) { printf("Q(%d) wrong at %llu\n", diskQ, start / chunk_size); } disk = raid6_check_disks(data_disks, start, chunk_size, level, layout, diskP, diskQ, p, q, stripes); if(disk >= 0) { printf("Possible failed disk: %d\n", disk); } if(disk == -2) { printf("Failure detected, but disk unknown\n"); } break; } length -= chunk_size; start += chunk_size; } return 0; }
/* Restore data: * We are given: * A list of 'fds' of the active disks. Some may be '-1' for not-available. * A geometry: raid_disks, chunk_size, level, layout * An 'fd' to read from. It is already seeked to the right (Read) location. * A start and length. * The length must be a multiple of the stripe size. * * We build a full stripe in memory and then write it out. * We assume that there are enough working devices. */ int restore_stripes(int *dest, unsigned long long *offsets, int raid_disks, int chunk_size, int level, int layout, int source, unsigned long long read_offset, unsigned long long start, unsigned long long length, char *src_buf) { char *stripe_buf; char **stripes = xmalloc(raid_disks * sizeof(char*)); char **blocks = xmalloc(raid_disks * sizeof(char*)); int i; int rv; int data_disks = raid_disks - (level == 0 ? 0 : level <= 5 ? 1 : 2); if (posix_memalign((void**)&stripe_buf, 4096, raid_disks * chunk_size)) stripe_buf = NULL; if (zero == NULL || chunk_size > zero_size) { if (zero) free(zero); zero = xcalloc(1, chunk_size); zero_size = chunk_size; } if (stripe_buf == NULL || stripes == NULL || blocks == NULL || zero == NULL) { rv = -2; goto abort; } for (i = 0; i < raid_disks; i++) stripes[i] = stripe_buf + i * chunk_size; while (length > 0) { unsigned int len = data_disks * chunk_size; unsigned long long offset; int disk, qdisk; int syndrome_disks; if (length < len) { rv = -3; goto abort; } for (i = 0; i < data_disks; i++) { int disk = geo_map(i, start/chunk_size/data_disks, raid_disks, level, layout); if (src_buf == NULL) { /* read from file */ if (lseek64(source, read_offset, 0) != (off64_t)read_offset) { rv = -1; goto abort; } if (read(source, stripes[disk], chunk_size) != chunk_size) { rv = -1; goto abort; } } else { /* read from input buffer */ memcpy(stripes[disk], src_buf + read_offset, chunk_size); } read_offset += chunk_size; } /* We have the data, now do the parity */ offset = (start/chunk_size/data_disks) * chunk_size; switch (level) { case 4: case 5: disk = geo_map(-1, start/chunk_size/data_disks, raid_disks, level, layout); for (i = 0; i < data_disks; i++) blocks[i] = stripes[(disk+1+i) % raid_disks]; xor_blocks(stripes[disk], blocks, data_disks, chunk_size); break; case 6: disk = geo_map(-1, start/chunk_size/data_disks, raid_disks, level, layout); qdisk = geo_map(-2, start/chunk_size/data_disks, raid_disks, level, layout); if (is_ddf(layout)) { /* q over 'raid_disks' blocks, in device order. * 'p' and 'q' get to be all zero */ for (i = 0; i < raid_disks; i++) if (i == disk || i == qdisk) blocks[i] = (char*)zero; else blocks[i] = stripes[i]; syndrome_disks = raid_disks; } else { /* for md, q is over 'data_disks' blocks, * starting immediately after 'q' */ for (i = 0; i < data_disks; i++) blocks[i] = stripes[(qdisk+1+i) % raid_disks]; syndrome_disks = data_disks; } qsyndrome((uint8_t*)stripes[disk], (uint8_t*)stripes[qdisk], (uint8_t**)blocks, syndrome_disks, chunk_size); break; } for (i=0; i < raid_disks ; i++) if (dest[i] >= 0) { if (lseek64(dest[i], offsets[i]+offset, 0) < 0) { rv = -1; goto abort; } if (write(dest[i], stripes[i], chunk_size) != chunk_size) { rv = -1; goto abort; } } length -= len; start += len; } rv = 0; abort: free(stripe_buf); free(stripes); free(blocks); return rv; }
int check_stripes(struct mdinfo *info, int *source, unsigned long long *offsets, int raid_disks, int chunk_size, int level, int layout, unsigned long long start, unsigned long long length, char *name[], int repair, int failed_disk1, int failed_disk2) { /* read the data and p and q blocks, and check we got them right */ char *stripe_buf = xmalloc(raid_disks * chunk_size); char **stripes = xmalloc(raid_disks * sizeof(char*)); char **blocks = xmalloc(raid_disks * sizeof(char*)); int *block_index_for_slot = xmalloc(raid_disks * sizeof(int)); uint8_t *p = xmalloc(chunk_size); uint8_t *q = xmalloc(chunk_size); int *results = xmalloc(chunk_size * sizeof(int)); sighandler_t *sig = xmalloc(3 * sizeof(sighandler_t)); int i; int diskP, diskQ; int data_disks = raid_disks - 2; int err = 0; extern int tables_ready; if (!tables_ready) make_tables(); for ( i = 0 ; i < raid_disks ; i++) stripes[i] = stripe_buf + i * chunk_size; while (length > 0) { int disk; printf("pos --> %llu\n", start); err = lock_stripe(info, start, chunk_size, data_disks, sig); if(err != 0) { if (err != 2) unlock_all_stripes(info, sig); goto exitCheck; } for (i = 0 ; i < raid_disks ; i++) { lseek64(source[i], offsets[i] + start * chunk_size, 0); read(source[i], stripes[i], chunk_size); } err = unlock_all_stripes(info, sig); if(err != 0) goto exitCheck; for (i = 0 ; i < data_disks ; i++) { int disk = geo_map(i, start, raid_disks, level, layout); blocks[i] = stripes[disk]; block_index_for_slot[disk] = i; printf("%d->%d\n", i, disk); } qsyndrome(p, q, (uint8_t**)blocks, data_disks, chunk_size); diskP = geo_map(-1, start, raid_disks, level, layout); diskQ = geo_map(-2, start, raid_disks, level, layout); blocks[data_disks] = stripes[diskP]; block_index_for_slot[diskP] = data_disks; blocks[data_disks+1] = stripes[diskQ]; block_index_for_slot[diskQ] = data_disks+1; if (memcmp(p, stripes[diskP], chunk_size) != 0) { printf("P(%d) wrong at %llu\n", diskP, start); } if (memcmp(q, stripes[diskQ], chunk_size) != 0) { printf("Q(%d) wrong at %llu\n", diskQ, start); } raid6_collect(chunk_size, p, q, stripes[diskP], stripes[diskQ], results); disk = raid6_stats(results, raid_disks, chunk_size); if(disk >= -2) { disk = geo_map(disk, start, raid_disks, level, layout); } if(disk >= 0) { printf("Error detected at %llu: possible failed disk slot: %d --> %s\n", start, disk, name[disk]); } if(disk == -65535) { printf("Error detected at %llu: disk slot unknown\n", start); } if(repair == 1) { printf("Repairing stripe %llu\n", start); printf("Assuming slots %d (%s) and %d (%s) are incorrect\n", failed_disk1, name[failed_disk1], failed_disk2, name[failed_disk2]); if (failed_disk1 == diskQ || failed_disk2 == diskQ) { char *all_but_failed_blocks[data_disks]; int failed_data_or_p; int failed_block_index; if (failed_disk1 == diskQ) failed_data_or_p = failed_disk2; else failed_data_or_p = failed_disk1; printf("Repairing D/P(%d) and Q\n", failed_data_or_p); failed_block_index = block_index_for_slot[failed_data_or_p]; for (i=0; i < data_disks; i++) if (failed_block_index == i) all_but_failed_blocks[i] = stripes[diskP]; else all_but_failed_blocks[i] = blocks[i]; xor_blocks(stripes[failed_data_or_p], all_but_failed_blocks, data_disks, chunk_size); qsyndrome(p, (uint8_t*)stripes[diskQ], (uint8_t**)blocks, data_disks, chunk_size); } else { ensure_zero_has_size(chunk_size); if (failed_disk1 == diskP || failed_disk2 == diskP) { int failed_data, failed_block_index; if (failed_disk1 == diskP) failed_data = failed_disk2; else failed_data = failed_disk1; failed_block_index = block_index_for_slot[failed_data]; printf("Repairing D(%d) and P\n", failed_data); raid6_datap_recov(raid_disks, chunk_size, failed_block_index, (uint8_t**)blocks); } else { printf("Repairing D and D\n"); int failed_block_index1 = block_index_for_slot[failed_disk1]; int failed_block_index2 = block_index_for_slot[failed_disk2]; if (failed_block_index1 > failed_block_index2) { int t = failed_block_index1; failed_block_index1 = failed_block_index2; failed_block_index2 = t; } raid6_2data_recov(raid_disks, chunk_size, failed_block_index1, failed_block_index2, (uint8_t**)blocks); } } err = lock_stripe(info, start, chunk_size, data_disks, sig); if(err != 0) { if (err != 2) unlock_all_stripes(info, sig); goto exitCheck; } lseek64(source[failed_disk1], offsets[failed_disk1] + start * chunk_size, 0); write(source[failed_disk1], stripes[failed_disk1], chunk_size); lseek64(source[failed_disk2], offsets[failed_disk2] + start * chunk_size, 0); write(source[failed_disk2], stripes[failed_disk2], chunk_size); err = unlock_all_stripes(info, sig); if(err != 0) goto exitCheck; } else if (disk >= 0 && repair == 2) { printf("Auto-repairing slot %d (%s)\n", disk, name[disk]); if (disk == diskQ) { qsyndrome(p, (uint8_t*)stripes[diskQ], (uint8_t**)blocks, data_disks, chunk_size); } else { char *all_but_failed_blocks[data_disks]; int failed_block_index = block_index_for_slot[disk]; for (i=0; i < data_disks; i++) if (failed_block_index == i) all_but_failed_blocks[i] = stripes[diskP]; else all_but_failed_blocks[i] = blocks[i]; xor_blocks(stripes[disk], all_but_failed_blocks, data_disks, chunk_size); } err = lock_stripe(info, start, chunk_size, data_disks, sig); if(err != 0) { if (err != 2) unlock_all_stripes(info, sig); goto exitCheck; } lseek64(source[disk], offsets[disk] + start * chunk_size, 0); write(source[disk], stripes[disk], chunk_size); err = unlock_all_stripes(info, sig); if(err != 0) goto exitCheck; } length--; start++; } exitCheck: free(stripe_buf); free(stripes); free(blocks); free(p); free(q); free(results); return err; }