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