]> git.proxmox.com Git - grub2.git/blob - disk/raid.c
* disk/mdraid_linux.c (struct grub_raid_super_1x): Remove
[grub2.git] / disk / raid.c
1 /* raid.c - module to read RAID arrays. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc.
5 *
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <grub/dl.h>
21 #include <grub/disk.h>
22 #include <grub/mm.h>
23 #include <grub/err.h>
24 #include <grub/misc.h>
25 #include <grub/raid.h>
26
27 /* Linked list of RAID arrays. */
28 static struct grub_raid_array *array_list;
29 grub_raid5_recover_func_t grub_raid5_recover_func;
30 grub_raid6_recover_func_t grub_raid6_recover_func;
31
32 \f
33 static char
34 grub_is_array_readable (struct grub_raid_array *array)
35 {
36 switch (array->level)
37 {
38 case 0:
39 if (array->nr_devs == array->total_devs)
40 return 1;
41 break;
42
43 case 1:
44 if (array->nr_devs >= 1)
45 return 1;
46 break;
47
48 case 4:
49 case 5:
50 case 6:
51 case 10:
52 {
53 unsigned int n;
54
55 if (array->level == 10)
56 {
57 n = array->layout & 0xFF;
58 if (n == 1)
59 n = (array->layout >> 8) & 0xFF;
60
61 n--;
62 }
63 else
64 n = array->level / 3;
65
66 if (array->nr_devs >= array->total_devs - n)
67 return 1;
68
69 break;
70 }
71 }
72
73 return 0;
74 }
75
76 static int
77 grub_raid_iterate (int (*hook) (const char *name))
78 {
79 struct grub_raid_array *array;
80
81 for (array = array_list; array != NULL; array = array->next)
82 {
83 if (grub_is_array_readable (array))
84 if (hook (array->name))
85 return 1;
86 }
87
88 return 0;
89 }
90
91 #ifdef GRUB_UTIL
92 static grub_disk_memberlist_t
93 grub_raid_memberlist (grub_disk_t disk)
94 {
95 struct grub_raid_array *array = disk->data;
96 grub_disk_memberlist_t list = NULL, tmp;
97 unsigned int i;
98
99 for (i = 0; i < array->total_devs; i++)
100 if (array->device[i])
101 {
102 tmp = grub_malloc (sizeof (*tmp));
103 tmp->disk = array->device[i];
104 tmp->next = list;
105 list = tmp;
106 }
107
108 return list;
109 }
110 #endif
111
112 static grub_err_t
113 grub_raid_open (const char *name, grub_disk_t disk)
114 {
115 struct grub_raid_array *array;
116 unsigned n;
117
118 for (array = array_list; array != NULL; array = array->next)
119 {
120 if (!grub_strcmp (array->name, name))
121 if (grub_is_array_readable (array))
122 break;
123 }
124
125 if (!array)
126 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown RAID device %s",
127 name);
128
129 disk->has_partitions = 1;
130 disk->id = array->number;
131 disk->data = array;
132
133 grub_dprintf ("raid", "%s: total_devs=%d, disk_size=%lld\n", name,
134 array->total_devs, (unsigned long long) array->disk_size);
135
136 switch (array->level)
137 {
138 case 1:
139 disk->total_sectors = array->disk_size;
140 break;
141
142 case 10:
143 n = array->layout & 0xFF;
144 if (n == 1)
145 n = (array->layout >> 8) & 0xFF;
146
147 disk->total_sectors = grub_divmod64 (array->total_devs *
148 array->disk_size,
149 n, 0);
150 break;
151
152 case 0:
153 case 4:
154 case 5:
155 case 6:
156 n = array->level / 3;
157
158 disk->total_sectors = (array->total_devs - n) * array->disk_size;
159 break;
160 }
161
162 grub_dprintf ("raid", "%s: level=%d, total_sectors=%lld\n", name,
163 array->level, (unsigned long long) disk->total_sectors);
164
165 return 0;
166 }
167
168 static void
169 grub_raid_close (grub_disk_t disk __attribute ((unused)))
170 {
171 return;
172 }
173
174 void
175 grub_raid_block_xor (char *buf1, const char *buf2, int size)
176 {
177 grub_size_t *p1;
178 const grub_size_t *p2;
179
180 p1 = (grub_size_t *) buf1;
181 p2 = (const grub_size_t *) buf2;
182 size /= GRUB_CPU_SIZEOF_VOID_P;
183
184 while (size)
185 {
186 *(p1++) ^= *(p2++);
187 size--;
188 }
189 }
190
191 static grub_err_t
192 grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector,
193 grub_size_t size, char *buf)
194 {
195 struct grub_raid_array *array = disk->data;
196 grub_err_t err = 0;
197
198 switch (array->level)
199 {
200 case 0:
201 case 1:
202 case 10:
203 {
204 grub_disk_addr_t read_sector, far_ofs;
205 grub_uint32_t disknr, b, near, far, ofs;
206
207 read_sector = grub_divmod64 (sector, array->chunk_size, &b);
208 far = ofs = near = 1;
209 far_ofs = 0;
210
211 if (array->level == 1)
212 near = array->total_devs;
213 else if (array->level == 10)
214 {
215 near = array->layout & 0xFF;
216 far = (array->layout >> 8) & 0xFF;
217 if (array->layout >> 16)
218 {
219 ofs = far;
220 far_ofs = 1;
221 }
222 else
223 far_ofs = grub_divmod64 (array->disk_size,
224 far * array->chunk_size, 0);
225
226 far_ofs *= array->chunk_size;
227 }
228
229 read_sector = grub_divmod64 (read_sector * near, array->total_devs,
230 &disknr);
231
232 ofs *= array->chunk_size;
233 read_sector *= ofs;
234
235 while (1)
236 {
237 grub_size_t read_size;
238 unsigned int i, j;
239
240 read_size = array->chunk_size - b;
241 if (read_size > size)
242 read_size = size;
243
244 for (i = 0; i < near; i++)
245 {
246 unsigned int k;
247
248 k = disknr;
249 for (j = 0; j < far; j++)
250 {
251 if (array->device[k])
252 {
253 if (grub_errno == GRUB_ERR_READ_ERROR)
254 grub_errno = GRUB_ERR_NONE;
255
256 err = grub_disk_read (array->device[k],
257 array->start_sector[k] +
258 read_sector + j * far_ofs + b,
259 0,
260 read_size << GRUB_DISK_SECTOR_BITS,
261 buf);
262 if (! err)
263 break;
264 else if (err != GRUB_ERR_READ_ERROR)
265 return err;
266 }
267 else
268 err = grub_error (GRUB_ERR_READ_ERROR,
269 "disk missing");
270
271 k++;
272 if (k == array->total_devs)
273 k = 0;
274 }
275
276 if (! err)
277 break;
278
279 disknr++;
280 if (disknr == array->total_devs)
281 {
282 disknr = 0;
283 read_sector += ofs;
284 }
285 }
286
287 if (err)
288 return err;
289
290 buf += read_size << GRUB_DISK_SECTOR_BITS;
291 size -= read_size;
292 if (! size)
293 break;
294
295 b = 0;
296 disknr += (near - i);
297 while (disknr >= array->total_devs)
298 {
299 disknr -= array->total_devs;
300 read_sector += ofs;
301 }
302 }
303 break;
304 }
305
306 case 4:
307 case 5:
308 case 6:
309 {
310 grub_disk_addr_t read_sector;
311 grub_uint32_t b, p, n, disknr, e;
312
313 /* n = 1 for level 4 and 5, 2 for level 6. */
314 n = array->level / 3;
315
316 /* Find the first sector to read. */
317 read_sector = grub_divmod64 (sector, array->chunk_size, &b);
318 read_sector = grub_divmod64 (read_sector, array->total_devs - n,
319 &disknr);
320 if (array->level >= 5)
321 {
322 grub_divmod64 (read_sector, array->total_devs, &p);
323
324 if (! (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK))
325 p = array->total_devs - 1 - p;
326
327 if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
328 {
329 disknr += p + n;
330 }
331 else
332 {
333 grub_uint32_t q;
334
335 q = p + (n - 1);
336 if (q >= array->total_devs)
337 q -= array->total_devs;
338
339 if (disknr >= p)
340 disknr += n;
341 else if (disknr >= q)
342 disknr += q + 1;
343 }
344
345 if (disknr >= array->total_devs)
346 disknr -= array->total_devs;
347 }
348 else
349 p = array->total_devs - n;
350
351 read_sector *= array->chunk_size;
352
353 while (1)
354 {
355 grub_size_t read_size;
356 int next_level;
357
358 read_size = array->chunk_size - b;
359 if (read_size > size)
360 read_size = size;
361
362 e = 0;
363 if (array->device[disknr])
364 {
365 /* Reset read error. */
366 if (grub_errno == GRUB_ERR_READ_ERROR)
367 grub_errno = GRUB_ERR_NONE;
368
369 err = grub_disk_read (array->device[disknr],
370 array->start_sector[disknr] +
371 read_sector + b, 0,
372 read_size << GRUB_DISK_SECTOR_BITS,
373 buf);
374
375 if ((err) && (err != GRUB_ERR_READ_ERROR))
376 break;
377 e++;
378 }
379 else
380 err = GRUB_ERR_READ_ERROR;
381
382 if (err)
383 {
384 if (array->nr_devs < array->total_devs - n + e)
385 break;
386
387 grub_errno = GRUB_ERR_NONE;
388 if (array->level == 6)
389 {
390 err = ((grub_raid6_recover_func) ?
391 (*grub_raid6_recover_func) (array, disknr, p,
392 buf, read_sector + b,
393 read_size) :
394 grub_error (GRUB_ERR_BAD_DEVICE,
395 "raid6rec is not loaded"));
396 }
397 else
398 {
399 err = ((grub_raid5_recover_func) ?
400 (*grub_raid5_recover_func) (array, disknr,
401 buf, read_sector + b,
402 read_size) :
403 grub_error (GRUB_ERR_BAD_DEVICE,
404 "raid5rec is not loaded"));
405 }
406
407 if (err)
408 break;
409 }
410
411 buf += read_size << GRUB_DISK_SECTOR_BITS;
412 size -= read_size;
413 if (! size)
414 break;
415
416 b = 0;
417 disknr++;
418
419 if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
420 {
421 if (disknr == array->total_devs)
422 disknr = 0;
423
424 next_level = (disknr == p);
425 }
426 else
427 {
428 if (disknr == p)
429 disknr += n;
430
431 next_level = (disknr >= array->total_devs);
432 }
433
434 if (next_level)
435 {
436 read_sector += array->chunk_size;
437
438 if (array->level >= 5)
439 {
440 if (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK)
441 p = (p == array->total_devs - 1) ? 0 : p + 1;
442 else
443 p = (p == 0) ? array->total_devs - 1 : p - 1;
444
445 if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
446 {
447 disknr = p + n;
448 if (disknr >= array->total_devs)
449 disknr -= array->total_devs;
450 }
451 else
452 {
453 disknr -= array->total_devs;
454 if (disknr == p)
455 disknr += n;
456 }
457 }
458 else
459 disknr = 0;
460 }
461 }
462 }
463 break;
464 }
465
466 return err;
467 }
468
469 static grub_err_t
470 grub_raid_write (grub_disk_t disk __attribute ((unused)),
471 grub_disk_addr_t sector __attribute ((unused)),
472 grub_size_t size __attribute ((unused)),
473 const char *buf __attribute ((unused)))
474 {
475 return GRUB_ERR_NOT_IMPLEMENTED_YET;
476 }
477
478 static grub_err_t
479 insert_array (grub_disk_t disk, struct grub_raid_array *new_array,
480 grub_disk_addr_t start_sector, const char *scanner_name)
481 {
482 struct grub_raid_array *array = 0, *p;
483
484 /* See whether the device is part of an array we have already seen a
485 device from. */
486 for (p = array_list; p != NULL; p = p->next)
487 if ((p->uuid_len == new_array->uuid_len) &&
488 (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len)))
489 {
490 grub_free (new_array->uuid);
491 array = p;
492
493 /* Do some checks before adding the device to the array. */
494
495 /* FIXME: Check whether the update time of the superblocks are
496 the same. */
497
498 if (array->total_devs == array->nr_devs)
499 /* We found more members of the array than the array
500 actually has according to its superblock. This shouldn't
501 happen normally. */
502 grub_dprintf ("raid", "array->nr_devs > array->total_devs (%d)?!?",
503 array->total_devs);
504
505 if (array->device[new_array->index] != NULL)
506 /* We found multiple devices with the same number. Again,
507 this shouldn't happen. */
508 grub_dprintf ("raid", "Found two disks with the number %d?!?",
509 new_array->number);
510
511 if (new_array->disk_size < array->disk_size)
512 array->disk_size = new_array->disk_size;
513 break;
514 }
515
516 /* Add an array to the list if we didn't find any. */
517 if (!array)
518 {
519 array = grub_malloc (sizeof (*array));
520 if (!array)
521 {
522 grub_free (new_array->uuid);
523 return grub_errno;
524 }
525
526 *array = *new_array;
527 array->nr_devs = 0;
528 grub_memset (&array->device, 0, sizeof (array->device));
529 grub_memset (&array->start_sector, 0, sizeof (array->start_sector));
530
531 if (array->name)
532 goto skip_duplicate_check;
533 /* Check whether we don't have multiple arrays with the same number. */
534 for (p = array_list; p != NULL; p = p->next)
535 {
536 if (p->number == array->number)
537 break;
538 }
539
540 if (p)
541 {
542 /* The number is already in use, so we need to find a new one. */
543 int i = 0;
544
545 while (1)
546 {
547 for (p = array_list; p != NULL; p = p->next)
548 {
549 if (p->number == i)
550 break;
551 }
552
553 if (! p)
554 {
555 /* We found an unused number. */
556 array->number = i;
557 break;
558 }
559
560 i++;
561 }
562 }
563 skip_duplicate_check:
564 /* mdraid 1.x superblocks have only a name stored not a number.
565 Use it directly as GRUB device. */
566 if (! array->name)
567 {
568 array->name = grub_xasprintf ("md%d", array->number);
569 if (! array->name)
570 {
571 grub_free (array->uuid);
572 grub_free (array);
573
574 return grub_errno;
575 }
576 }
577 else
578 {
579 /* Strip off the homehost if present. */
580 char *colon = grub_strchr (array->name, ':');
581 char *new_name = grub_xasprintf ("md/%s",
582 colon ? colon + 1 : array->name);
583
584 if (! new_name)
585 {
586 grub_free (array->uuid);
587 grub_free (array);
588
589 return grub_errno;
590 }
591
592 grub_free (array->name);
593 array->name = new_name;
594 }
595
596 grub_dprintf ("raid", "Found array %s (%s)\n", array->name,
597 scanner_name);
598
599 /* Add our new array to the list. */
600 array->next = array_list;
601 array_list = array;
602
603 /* RAID 1 doesn't use a chunksize but code assumes one so set
604 one. */
605 if (array->level == 1)
606 array->chunk_size = 64;
607 }
608
609 /* Add the device to the array. */
610 array->device[new_array->index] = disk;
611 array->start_sector[new_array->index] = start_sector;
612 array->nr_devs++;
613
614 return 0;
615 }
616
617 static grub_raid_t grub_raid_list;
618
619 static void
620 free_array (void)
621 {
622 struct grub_raid_array *array;
623
624 array = array_list;
625 while (array)
626 {
627 struct grub_raid_array *p;
628 int i;
629
630 p = array;
631 array = array->next;
632
633 for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++)
634 if (p->device[i])
635 grub_disk_close (p->device[i]);
636
637 grub_free (p->uuid);
638 grub_free (p->name);
639 grub_free (p);
640 }
641
642 array_list = 0;
643 }
644
645 void
646 grub_raid_register (grub_raid_t raid)
647 {
648 auto int hook (const char *name);
649 int hook (const char *name)
650 {
651 grub_disk_t disk;
652 struct grub_raid_array array;
653 grub_disk_addr_t start_sector;
654
655 grub_dprintf ("raid", "Scanning for RAID devices on disk %s\n", name);
656
657 disk = grub_disk_open (name);
658 if (!disk)
659 return 0;
660
661 if ((disk->total_sectors != GRUB_ULONG_MAX) &&
662 (! grub_raid_list->detect (disk, &array, &start_sector)) &&
663 (! insert_array (disk, &array, start_sector, grub_raid_list->name)))
664 return 0;
665
666 /* This error usually means it's not raid, no need to display
667 it. */
668 if (grub_errno != GRUB_ERR_OUT_OF_RANGE)
669 grub_print_error ();
670
671 grub_errno = GRUB_ERR_NONE;
672
673 grub_disk_close (disk);
674
675 return 0;
676 }
677
678 raid->next = grub_raid_list;
679 grub_raid_list = raid;
680 grub_device_iterate (&hook);
681 }
682
683 void
684 grub_raid_unregister (grub_raid_t raid)
685 {
686 grub_raid_t *p, q;
687
688 for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next)
689 if (q == raid)
690 {
691 *p = q->next;
692 break;
693 }
694 }
695
696 static struct grub_disk_dev grub_raid_dev =
697 {
698 .name = "raid",
699 .id = GRUB_DISK_DEVICE_RAID_ID,
700 .iterate = grub_raid_iterate,
701 .open = grub_raid_open,
702 .close = grub_raid_close,
703 .read = grub_raid_read,
704 .write = grub_raid_write,
705 #ifdef GRUB_UTIL
706 .memberlist = grub_raid_memberlist,
707 #endif
708 .next = 0
709 };
710
711 \f
712 GRUB_MOD_INIT(raid)
713 {
714 grub_disk_dev_register (&grub_raid_dev);
715 }
716
717 GRUB_MOD_FINI(raid)
718 {
719 grub_disk_dev_unregister (&grub_raid_dev);
720 free_array ();
721 }