]> git.neil.brown.name Git - mdadm.git/blob - raid6check.c
Merge branch 'master' into devel-3.2
[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 }