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