]>
Commit | Line | Data |
---|---|---|
979afcb8 PS |
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 | } |