]> git.ipfire.org Git - thirdparty/mdadm.git/blob - raid6check.c
97e3f1c8e482230e95f4673f8405f836b6b6c67b
[thirdparty/mdadm.git] / raid6check.c
1 /*
2 * raid6check - extended consistency check for RAID-6
3 *
4 * Copyright (C) 2011 Piergiorgio Sartor
5 *
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 * Author: Piergiorgio Sartor
22 * Based on "restripe.c" from "mdadm" codebase
23 */
24
25 #include "mdadm.h"
26 #include <stdint.h>
27
28 int geo_map(int block, unsigned long long stripe, int raid_disks,
29 int level, int layout);
30 void qsyndrome(uint8_t *p, uint8_t *q, uint8_t **sources, int disks, int size);
31 void make_tables(void);
32
33 /* Collect per stripe consistency information */
34 void raid6_collect(int chunk_size, uint8_t *p, uint8_t *q,
35 char *chunkP, char *chunkQ, int *results)
36 {
37 int i;
38 int data_id;
39 uint8_t Px, Qx;
40 extern uint8_t raid6_gflog[];
41
42 for(i = 0; i < chunk_size; i++) {
43 Px = (uint8_t)chunkP[i] ^ (uint8_t)p[i];
44 Qx = (uint8_t)chunkQ[i] ^ (uint8_t)q[i];
45
46 if((Px != 0) && (Qx == 0))
47 results[i] = -1;
48
49 if((Px == 0) && (Qx != 0))
50 results[i] = -2;
51
52 if((Px != 0) && (Qx != 0)) {
53 data_id = (raid6_gflog[Qx] - raid6_gflog[Px]);
54 if(data_id < 0) data_id += 255;
55 results[i] = data_id;
56 }
57
58 if((Px == 0) && (Qx == 0))
59 results[i] = -255;
60 }
61 }
62
63 /* Try to find out if a specific disk has problems */
64 int raid6_stats(int *results, int raid_disks, int chunk_size)
65 {
66 int i;
67 int curr_broken_disk = -255;
68 int prev_broken_disk = -255;
69 int broken_status = 0;
70
71 for(i = 0; i < chunk_size; i++) {
72
73 if(results[i] != -255)
74 curr_broken_disk = results[i];
75
76 if(curr_broken_disk >= raid_disks)
77 broken_status = 2;
78
79 switch(broken_status) {
80 case 0:
81 if(curr_broken_disk != -255) {
82 prev_broken_disk = curr_broken_disk;
83 broken_status = 1;
84 }
85 break;
86
87 case 1:
88 if(curr_broken_disk != prev_broken_disk)
89 broken_status = 2;
90 break;
91
92 case 2:
93 default:
94 curr_broken_disk = prev_broken_disk = -65535;
95 break;
96 }
97 }
98
99 return curr_broken_disk;
100 }
101
102 int check_stripes(int *source, unsigned long long *offsets,
103 int raid_disks, int chunk_size, int level, int layout,
104 unsigned long long start, unsigned long long length, char *name[])
105 {
106 /* read the data and p and q blocks, and check we got them right */
107 char *stripe_buf = malloc(raid_disks * chunk_size);
108 char **stripes = malloc(raid_disks * sizeof(char*));
109 char **blocks = malloc(raid_disks * sizeof(char*));
110 uint8_t *p = malloc(chunk_size);
111 uint8_t *q = malloc(chunk_size);
112 int *results = malloc(chunk_size * sizeof(int));
113
114 int i;
115 int diskP, diskQ;
116 int data_disks = raid_disks - 2;
117
118 extern int tables_ready;
119
120 if (!tables_ready)
121 make_tables();
122
123 for ( i = 0 ; i < raid_disks ; i++)
124 stripes[i] = stripe_buf + i * chunk_size;
125
126 while (length > 0) {
127 int disk;
128
129 for (i = 0 ; i < raid_disks ; i++) {
130 lseek64(source[i], offsets[i]+start, 0);
131 read(source[i], stripes[i], chunk_size);
132 }
133 for (i = 0 ; i < data_disks ; i++) {
134 int disk = geo_map(i, start/chunk_size, raid_disks,
135 level, layout);
136 blocks[i] = stripes[disk];
137 printf("%d->%d\n", i, disk);
138 }
139
140 qsyndrome(p, q, (uint8_t**)blocks, data_disks, chunk_size);
141 diskP = geo_map(-1, start/chunk_size, raid_disks,
142 level, layout);
143 if (memcmp(p, stripes[diskP], chunk_size) != 0) {
144 printf("P(%d) wrong at %llu\n", diskP,
145 start / chunk_size);
146 }
147 diskQ = geo_map(-2, start/chunk_size, raid_disks,
148 level, layout);
149 if (memcmp(q, stripes[diskQ], chunk_size) != 0) {
150 printf("Q(%d) wrong at %llu\n", diskQ,
151 start / chunk_size);
152 }
153 raid6_collect(chunk_size, p, q,
154 stripes[diskP], stripes[diskQ], results);
155 disk = raid6_stats(results, raid_disks, chunk_size);
156
157 if(disk >= -2) {
158 disk = geo_map(disk, start/chunk_size, raid_disks,
159 level, layout);
160 }
161 if(disk >= 0) {
162 printf("Possible failed disk: %d --> %s\n", disk, name[disk]);
163 }
164 if(disk == -65535) {
165 printf("Failure detected, but disk unknown\n");
166 }
167
168 length -= chunk_size;
169 start += chunk_size;
170 }
171
172 free(stripe_buf);
173 free(stripes);
174 free(blocks);
175 free(p);
176 free(q);
177 free(results);
178
179 return 0;
180 }
181
182 unsigned long long getnum(char *str, char **err)
183 {
184 char *e;
185 unsigned long long rv = strtoull(str, &e, 10);
186 if (e==str || *e) {
187 *err = str;
188 return 0;
189 }
190 return rv;
191 }
192
193 int main(int argc, char *argv[])
194 {
195 /* raid_disks chunk_size layout start length devices...
196 */
197 int *fds;
198 char *buf;
199 unsigned long long *offsets;
200 int raid_disks, chunk_size, layout;
201 int level = 6;
202 unsigned long long start, length;
203 int i;
204
205 char *err = NULL;
206 if (argc < 8) {
207 fprintf(stderr, "Usage: raid6check raid_disks"
208 " chunk_size layout start length devices...\n");
209 exit(1);
210 }
211
212 raid_disks = getnum(argv[1], &err);
213 chunk_size = getnum(argv[2], &err);
214 layout = getnum(argv[3], &err);
215 start = getnum(argv[4], &err);
216 length = getnum(argv[5], &err);
217 if (err) {
218 fprintf(stderr, "test_stripe: Bad number: %s\n", err);
219 exit(2);
220 }
221 if (argc != raid_disks + 6) {
222 fprintf(stderr, "test_stripe: wrong number of devices: want %d found %d\n",
223 raid_disks, argc-6);
224 exit(2);
225 }
226 fds = malloc(raid_disks * sizeof(*fds));
227 offsets = malloc(raid_disks * sizeof(*offsets));
228 memset(offsets, 0, raid_disks * sizeof(*offsets));
229
230 for (i=0; i<raid_disks; i++) {
231 char *p;
232 p = strchr(argv[6+i], ':');
233
234 if(p != NULL) {
235 *p++ = '\0';
236 offsets[i] = atoll(p) * 512;
237 }
238 fds[i] = open(argv[6+i], O_RDWR);
239 if (fds[i] < 0) {
240 perror(argv[6+i]);
241 fprintf(stderr,"test_stripe: cannot open %s.\n", argv[6+i]);
242 exit(3);
243 }
244 }
245
246 buf = malloc(raid_disks * chunk_size);
247
248 int rv = check_stripes(fds, offsets,
249 raid_disks, chunk_size, level, layout,
250 start, length, &argv[6]);
251 if (rv != 0) {
252 fprintf(stderr,
253 "test_stripe: test_stripes returned %d\n", rv);
254 exit(1);
255 }
256
257 free(fds);
258 free(offsets);
259 free(buf);
260
261 exit(0);
262 }