*
*/
-static int geo_map(int block, unsigned long long stripe, int raid_disks,
+int geo_map(int block, unsigned long long stripe, int raid_disks,
int level, int layout)
{
/* On the given stripe, find which disk in the array will have
*/
int pd;
+ /* layout is not relevant for raid0 and raid4 */
+ if ((level == 0) ||
+ (level == 4))
+ layout = 0;
+
switch(level*100 + layout) {
case 000:
case 400:
}
}
-static void qsyndrome(uint8_t *p, uint8_t *q, uint8_t **sources, int disks, int size)
+void qsyndrome(uint8_t *p, uint8_t *q, uint8_t **sources, int disks, int size)
{
int d, z;
uint8_t wq0, wp0, wd0, w10, w20;
uint8_t raid6_gfexp[256];
uint8_t raid6_gfinv[256];
uint8_t raid6_gfexi[256];
+uint8_t raid6_gflog[256];
+uint8_t raid6_gfilog[256];
void make_tables(void)
{
int i, j;
uint8_t v;
+ uint32_t b, log;
/* Compute multiplication table */
for (i = 0; i < 256; i++)
for (i = 0; i < 256; i ++)
raid6_gfexi[i] = raid6_gfinv[raid6_gfexp[i] ^ 1];
+ /* Compute log and inverse log */
+ /* Modified code from:
+ * http://web.eecs.utk.edu/~plank/plank/papers/CS-96-332.html
+ */
+ b = 1;
+ raid6_gflog[0] = 0;
+ raid6_gfilog[255] = 0;
+
+ for (log = 0; log < 255; log++) {
+ raid6_gflog[b] = (uint8_t) log;
+ raid6_gfilog[log] = (uint8_t) b;
+ b = b << 1;
+ if (b & 256) b = b ^ 0435;
+ }
+
tables_ready = 1;
}
uint8_t *zero;
+int zero_size;
/* Following was taken from linux/drivers/md/raid6recov.c */
/* Recover two failed data blocks. */
}
}
+/* Try to find out if a specific disk has a problem */
+int raid6_check_disks(int data_disks, int start, int chunk_size,
+ int level, int layout, int diskP, int diskQ,
+ char *p, char *q, char **stripes)
+{
+ int i;
+ int data_id, diskD;
+ uint8_t Px, Qx;
+ int curr_broken_disk = -1;
+ int prev_broken_disk = -1;
+ int broken_status = 0;
+
+ for(i = 0; i < chunk_size; i++) {
+ Px = (uint8_t)stripes[diskP][i] ^ (uint8_t)p[i];
+ Qx = (uint8_t)stripes[diskQ][i] ^ (uint8_t)q[i];
+
+ if((Px != 0) && (Qx == 0))
+ curr_broken_disk = diskP;
+
+
+ if((Px == 0) && (Qx != 0))
+ curr_broken_disk = diskQ;
+
+
+ if((Px != 0) && (Qx != 0)) {
+ data_id = (raid6_gflog[Qx] - raid6_gflog[Px]);
+ if(data_id < 0) data_id += 255;
+ diskD = geo_map(data_id, start/chunk_size,
+ data_disks + 2, level, layout);
+ curr_broken_disk = diskD;
+ }
+
+ if((Px == 0) && (Qx == 0))
+ curr_broken_disk = curr_broken_disk;
+
+ if(curr_broken_disk >= data_disks + 2)
+ broken_status = 2;
+
+ switch(broken_status) {
+ case 0:
+ if(curr_broken_disk != -1) {
+ prev_broken_disk = curr_broken_disk;
+ broken_status = 1;
+ }
+ break;
+
+ case 1:
+ if(curr_broken_disk != prev_broken_disk)
+ broken_status = 2;
+ break;
+
+ case 2:
+ default:
+ curr_broken_disk = prev_broken_disk = -2;
+ break;
+ }
+ }
+
+ return curr_broken_disk;
+}
+
/* Save data:
* We are given:
* A list of 'fds' of the active disks. Some may be absent.
if (!tables_ready)
make_tables();
- if (zero == NULL) {
+ if (zero == NULL || chunk_size > zero_size) {
+ if (zero)
+ free(zero);
zero = malloc(chunk_size);
- memset(zero, 0, chunk_size);
+ if (zero)
+ memset(zero, 0, chunk_size);
+ zero_size = chunk_size;
}
len = data_disks * chunk_size;
if (posix_memalign((void**)&stripe_buf, 4096, raid_disks * chunk_size))
stripe_buf = NULL;
- if (zero == NULL) {
+
+ if (zero == NULL || chunk_size > zero_size) {
+ if (zero)
+ free(zero);
zero = malloc(chunk_size);
if (zero)
memset(zero, 0, chunk_size);
+ zero_size = chunk_size;
}
+
if (stripe_buf == NULL || stripes == NULL || blocks == NULL
|| zero == NULL) {
free(stripe_buf);
char *q = malloc(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;
switch(level) {
case 6:
qsyndrome(p, q, (uint8_t**)blocks, data_disks, chunk_size);
- disk = geo_map(-1, start/chunk_size, raid_disks,
+ diskP = geo_map(-1, start/chunk_size, raid_disks,
level, layout);
- if (memcmp(p, stripes[disk], chunk_size) != 0) {
- printf("P(%d) wrong at %llu\n", disk,
+ if (memcmp(p, stripes[diskP], chunk_size) != 0) {
+ printf("P(%d) wrong at %llu\n", diskP,
start / chunk_size);
}
- disk = geo_map(-2, start/chunk_size, raid_disks,
+ diskQ = geo_map(-2, start/chunk_size, raid_disks,
level, layout);
- if (memcmp(q, stripes[disk], chunk_size) != 0) {
- printf("Q(%d) wrong at %llu\n", disk,
+ 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;
exit(3);
}
for (i=0; i<raid_disks; i++) {
+ char *p;
+ p = strchr(argv[9+i], ':');
+
+ if(p != NULL) {
+ *p++ = '\0';
+ offsets[i] = atoll(p) * 512;
+ }
+
fds[i] = open(argv[9+i], O_RDWR);
if (fds[i] < 0) {
perror(argv[9+i]);