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