]> git.proxmox.com Git - grub2.git/blob - grub-core/disk/raid.c
merge with mainline
[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->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 {
533 for (p = array_list; p != NULL; p = p->next)
534 {
535 if (! p->name && p->number == array->number)
536 break;
537 }
538 }
539
540 if (array->name || p)
541 {
542 /* The number is already in use, so we need to find a new one.
543 (Or, in the case of named arrays, the array doesn't have its
544 own number, but we need one that doesn't clash for use as a key
545 in the disk cache. */
546 int i = array->name ? 0x40000000 : 0;
547
548 while (1)
549 {
550 for (p = array_list; p != NULL; p = p->next)
551 {
552 if (p->number == i)
553 break;
554 }
555
556 if (! p)
557 {
558 /* We found an unused number. */
559 array->number = i;
560 break;
561 }
562
563 i++;
564 }
565 }
566
567 /* mdraid 1.x superblocks have only a name stored not a number.
568 Use it directly as GRUB device. */
569 if (! array->name)
570 {
571 array->name = grub_xasprintf ("md%d", array->number);
572 if (! array->name)
573 {
574 grub_free (array->uuid);
575 grub_free (array);
576
577 return grub_errno;
578 }
579 }
580 else
581 {
582 /* Strip off the homehost if present. */
583 char *colon = grub_strchr (array->name, ':');
584 char *new_name = grub_xasprintf ("md/%s",
585 colon ? colon + 1 : array->name);
586
587 if (! new_name)
588 {
589 grub_free (array->uuid);
590 grub_free (array);
591
592 return grub_errno;
593 }
594
595 grub_free (array->name);
596 array->name = new_name;
597 }
598
599 grub_dprintf ("raid", "Found array %s (%s)\n", array->name,
600 scanner_name);
601
602 /* Add our new array to the list. */
603 array->next = array_list;
604 array_list = array;
605
606 /* RAID 1 doesn't use a chunksize but code assumes one so set
607 one. */
608 if (array->level == 1)
609 array->chunk_size = 64;
610 }
611
612 /* Add the device to the array. */
613 array->device[new_array->index] = disk;
614 array->start_sector[new_array->index] = start_sector;
615 array->nr_devs++;
616
617 return 0;
618 }
619
620 static grub_raid_t grub_raid_list;
621
622 static void
623 free_array (void)
624 {
625 struct grub_raid_array *array;
626
627 array = array_list;
628 while (array)
629 {
630 struct grub_raid_array *p;
631 int i;
632
633 p = array;
634 array = array->next;
635
636 for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++)
637 if (p->device[i])
638 grub_disk_close (p->device[i]);
639
640 grub_free (p->uuid);
641 grub_free (p->name);
642 grub_free (p);
643 }
644
645 array_list = 0;
646 }
647
648 void
649 grub_raid_register (grub_raid_t raid)
650 {
651 auto int hook (const char *name);
652 int hook (const char *name)
653 {
654 grub_disk_t disk;
655 struct grub_raid_array array;
656 grub_disk_addr_t start_sector;
657
658 grub_dprintf ("raid", "Scanning for RAID devices on disk %s\n", name);
659
660 disk = grub_disk_open (name);
661 if (!disk)
662 return 0;
663
664 if ((disk->total_sectors != GRUB_ULONG_MAX) &&
665 (! grub_raid_list->detect (disk, &array, &start_sector)) &&
666 (! insert_array (disk, &array, start_sector, grub_raid_list->name)))
667 return 0;
668
669 /* This error usually means it's not raid, no need to display
670 it. */
671 if (grub_errno != GRUB_ERR_OUT_OF_RANGE)
672 grub_print_error ();
673
674 grub_errno = GRUB_ERR_NONE;
675
676 grub_disk_close (disk);
677
678 return 0;
679 }
680
681 raid->next = grub_raid_list;
682 grub_raid_list = raid;
683 grub_device_iterate (&hook);
684 }
685
686 void
687 grub_raid_unregister (grub_raid_t raid)
688 {
689 grub_raid_t *p, q;
690
691 for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next)
692 if (q == raid)
693 {
694 *p = q->next;
695 break;
696 }
697 }
698
699 static struct grub_disk_dev grub_raid_dev =
700 {
701 .name = "raid",
702 .id = GRUB_DISK_DEVICE_RAID_ID,
703 .iterate = grub_raid_iterate,
704 .open = grub_raid_open,
705 .close = grub_raid_close,
706 .read = grub_raid_read,
707 .write = grub_raid_write,
708 #ifdef GRUB_UTIL
709 .memberlist = grub_raid_memberlist,
710 #endif
711 .next = 0
712 };
713
714 \f
715 GRUB_MOD_INIT(raid)
716 {
717 grub_disk_dev_register (&grub_raid_dev);
718 }
719
720 GRUB_MOD_FINI(raid)
721 {
722 grub_disk_dev_unregister (&grub_raid_dev);
723 free_array ();
724 }