2008-09-08 Robert Millan <rmh@aybabtu.com>
[grub2/phcoder/solaris.git] / disk / raid.c
blob62cbcb5bac28e31d3d1a7049194f852a31317551
1 /* raid.c - module to read RAID arrays. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc.
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.
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.
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/>.
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>
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;
33 static char
34 grub_is_array_readable (struct grub_raid_array *array)
36 switch (array->level)
38 case 0:
39 if (array->nr_devs == array->total_devs)
40 return 1;
41 break;
43 case 1:
44 if (array->nr_devs >= 1)
45 return 1;
46 break;
48 case 4:
49 case 5:
50 case 6:
51 case 10:
53 unsigned int n;
55 if (array->level == 10)
57 n = array->layout & 0xFF;
58 if (n == 1)
59 n = (array->layout >> 8) & 0xFF;
61 n--;
63 else
64 n = array->level / 3;
66 if (array->nr_devs >= array->total_devs - n)
67 return 1;
69 break;
73 return 0;
76 static int
77 grub_raid_iterate (int (*hook) (const char *name))
79 struct grub_raid_array *array;
81 for (array = array_list; array != NULL; array = array->next)
83 if (grub_is_array_readable (array))
84 if (hook (array->name))
85 return 1;
88 return 0;
91 #ifdef GRUB_UTIL
92 static grub_disk_memberlist_t
93 grub_raid_memberlist (grub_disk_t disk)
95 struct grub_raid_array *array = disk->data;
96 grub_disk_memberlist_t list = NULL, tmp;
97 unsigned int i;
99 for (i = 0; i < array->total_devs; i++)
100 if (array->device[i])
102 tmp = grub_malloc (sizeof (*tmp));
103 tmp->disk = array->device[i];
104 tmp->next = list;
105 list = tmp;
108 return list;
110 #endif
112 static grub_err_t
113 grub_raid_open (const char *name, grub_disk_t disk)
115 struct grub_raid_array *array;
116 unsigned n;
118 for (array = array_list; array != NULL; array = array->next)
120 if (!grub_strcmp (array->name, name))
121 if (grub_is_array_readable (array))
122 break;
125 if (!array)
126 return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Unknown RAID device %s",
127 name);
129 disk->has_partitions = 1;
130 disk->id = array->number;
131 disk->data = array;
133 grub_dprintf ("raid", "%s: total_devs=%d, disk_size=%lld\n", name,
134 array->total_devs, (unsigned long long) array->disk_size);
136 switch (array->level)
138 case 1:
139 disk->total_sectors = array->disk_size;
140 break;
142 case 10:
143 n = array->layout & 0xFF;
144 if (n == 1)
145 n = (array->layout >> 8) & 0xFF;
147 disk->total_sectors = grub_divmod64 (array->total_devs *
148 array->disk_size,
149 n, 0);
150 break;
152 case 0:
153 case 4:
154 case 5:
155 case 6:
156 n = array->level / 3;
158 disk->total_sectors = (array->total_devs - n) * array->disk_size;
159 break;
162 grub_dprintf ("raid", "%s: level=%d, total_sectors=%lld\n", name,
163 array->level, (unsigned long long) disk->total_sectors);
165 return 0;
168 static void
169 grub_raid_close (grub_disk_t disk __attribute ((unused)))
171 return;
174 void
175 grub_raid_block_xor (char *buf1, char *buf2, int size)
177 grub_size_t *p1, *p2;
179 p1 = (grub_size_t *) buf1;
180 p2 = (grub_size_t *) buf2;
181 size /= GRUB_CPU_SIZEOF_VOID_P;
183 while (size)
185 *(p1++) ^= *(p2++);
186 size--;
190 static grub_err_t
191 grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector,
192 grub_size_t size, char *buf)
194 struct grub_raid_array *array = disk->data;
195 grub_err_t err = 0;
197 switch (array->level)
199 case 0:
200 case 1:
201 case 10:
203 grub_disk_addr_t read_sector, far_ofs;
204 grub_uint32_t disknr, b, near, far, ofs;
206 read_sector = grub_divmod64 (sector, array->chunk_size, &b);
207 far = ofs = near = 1;
208 far_ofs = 0;
210 if (array->level == 1)
211 near = array->total_devs;
212 else if (array->level == 10)
214 near = array->layout & 0xFF;
215 far = (array->layout >> 8) & 0xFF;
216 if (array->layout >> 16)
218 ofs = far;
219 far_ofs = 1;
221 else
222 far_ofs = grub_divmod64 (array->disk_size,
223 far * array->chunk_size, 0);
225 far_ofs *= array->chunk_size;
228 read_sector = grub_divmod64 (read_sector * near, array->total_devs,
229 &disknr);
231 ofs *= array->chunk_size;
232 read_sector *= ofs;
234 while (1)
236 grub_size_t read_size;
237 unsigned int i, j;
239 read_size = array->chunk_size - b;
240 if (read_size > size)
241 read_size = size;
243 for (i = 0; i < near; i++)
245 unsigned int k;
247 k = disknr;
248 for (j = 0; j < far; j++)
250 if (array->device[k])
252 if (grub_errno == GRUB_ERR_READ_ERROR)
253 grub_errno = GRUB_ERR_NONE;
255 err = grub_disk_read (array->device[k],
256 read_sector + j * far_ofs + b,
258 read_size << GRUB_DISK_SECTOR_BITS,
259 buf);
260 if (! err)
261 break;
262 else if (err != GRUB_ERR_READ_ERROR)
263 return err;
265 else
266 err = grub_error (GRUB_ERR_READ_ERROR,
267 "disk missing.");
269 k++;
270 if (k == array->total_devs)
271 k = 0;
274 if (! err)
275 break;
277 disknr++;
278 if (disknr == array->total_devs)
280 disknr = 0;
281 read_sector += ofs;
285 if (err)
286 return err;
288 buf += read_size << GRUB_DISK_SECTOR_BITS;
289 size -= read_size;
290 if (! size)
291 break;
293 b = 0;
294 disknr += (near - i);
295 while (disknr >= array->total_devs)
297 disknr -= array->total_devs;
298 read_sector += ofs;
301 break;
304 case 4:
305 case 5:
306 case 6:
308 grub_disk_addr_t read_sector;
309 grub_uint32_t b, p, n, disknr, e;
311 /* n = 1 for level 4 and 5, 2 for level 6. */
312 n = array->level / 3;
314 /* Find the first sector to read. */
315 read_sector = grub_divmod64 (sector, array->chunk_size, &b);
316 read_sector = grub_divmod64 (read_sector, array->total_devs - n,
317 &disknr);
318 if (array->level >= 5)
320 grub_divmod64 (read_sector, array->total_devs, &p);
322 if (! (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK))
323 p = array->total_devs - 1 - p;
325 if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
327 disknr += p + n;
329 else
331 grub_uint32_t q;
333 q = p + (n - 1);
334 if (q >= array->total_devs)
335 q -= array->total_devs;
337 if (disknr >= p)
338 disknr += n;
339 else if (disknr >= q)
340 disknr += q + 1;
343 if (disknr >= array->total_devs)
344 disknr -= array->total_devs;
346 else
347 p = array->total_devs - n;
349 read_sector *= array->chunk_size;
351 while (1)
353 grub_size_t read_size;
354 int next_level;
356 read_size = array->chunk_size - b;
357 if (read_size > size)
358 read_size = size;
360 e = 0;
361 if (array->device[disknr])
363 /* Reset read error. */
364 if (grub_errno == GRUB_ERR_READ_ERROR)
365 grub_errno = GRUB_ERR_NONE;
367 err = grub_disk_read (array->device[disknr],
368 read_sector + b, 0,
369 read_size << GRUB_DISK_SECTOR_BITS,
370 buf);
372 if ((err) && (err != GRUB_ERR_READ_ERROR))
373 break;
374 e++;
376 else
377 err = GRUB_ERR_READ_ERROR;
379 if (err)
381 if (array->nr_devs < array->total_devs - n + e)
382 break;
384 grub_errno = GRUB_ERR_NONE;
385 if (array->level == 6)
387 err = ((grub_raid6_recover_func) ?
388 (*grub_raid6_recover_func) (array, disknr, p,
389 buf, read_sector + b,
390 read_size) :
391 grub_error (GRUB_ERR_BAD_DEVICE,
392 "raid6rec is not loaded"));
394 else
396 err = ((grub_raid5_recover_func) ?
397 (*grub_raid5_recover_func) (array, disknr,
398 buf, read_sector + b,
399 read_size) :
400 grub_error (GRUB_ERR_BAD_DEVICE,
401 "raid5rec is not loaded"));
404 if (err)
405 break;
408 buf += read_size << GRUB_DISK_SECTOR_BITS;
409 size -= read_size;
410 if (! size)
411 break;
413 b = 0;
414 disknr++;
416 if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
418 if (disknr == array->total_devs)
419 disknr = 0;
421 next_level = (disknr == p);
423 else
425 if (disknr == p)
426 disknr += n;
428 next_level = (disknr >= array->total_devs);
431 if (next_level)
433 read_sector += array->chunk_size;
435 if (array->level >= 5)
437 if (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK)
438 p = (p == array->total_devs - 1) ? 0 : p + 1;
439 else
440 p = (p == 0) ? array->total_devs - 1 : p - 1;
442 if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
444 disknr = p + n;
445 if (disknr >= array->total_devs)
446 disknr -= array->total_devs;
448 else
450 disknr -= array->total_devs;
451 if (disknr == p)
452 disknr += n;
455 else
456 disknr = 0;
460 break;
463 return err;
466 static grub_err_t
467 grub_raid_write (grub_disk_t disk __attribute ((unused)),
468 grub_disk_addr_t sector __attribute ((unused)),
469 grub_size_t size __attribute ((unused)),
470 const char *buf __attribute ((unused)))
472 return GRUB_ERR_NOT_IMPLEMENTED_YET;
475 static grub_err_t
476 insert_array (grub_disk_t disk, struct grub_raid_array *new_array,
477 const char *scanner_name)
479 struct grub_raid_array *array = 0, *p;
481 /* See whether the device is part of an array we have already seen a
482 device from. */
483 for (p = array_list; p != NULL; p = p->next)
484 if ((p->uuid_len == new_array->uuid_len) &&
485 (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len)))
487 grub_free (new_array->uuid);
488 array = p;
490 /* Do some checks before adding the device to the array. */
492 /* FIXME: Check whether the update time of the superblocks are
493 the same. */
495 if (array->total_devs == array->nr_devs)
496 /* We found more members of the array than the array
497 actually has according to its superblock. This shouldn't
498 happen normally, but what is the sanest things to do in such
499 a case? */
500 return grub_error (GRUB_ERR_BAD_NUMBER,
501 "array->nr_devs > array->total_devs (%d)?!?",
502 array->total_devs);
504 if (array->device[new_array->index] != NULL)
505 /* We found multiple devices with the same number. Again,
506 this shouldn't happen.*/
507 return grub_error (GRUB_ERR_BAD_NUMBER,
508 "Found two disks with the number %d?!?",
509 new_array->number);
511 if (new_array->disk_size < array->disk_size)
512 array->disk_size = new_array->disk_size;
513 break;
516 /* Add an array to the list if we didn't find any. */
517 if (!array)
519 array = grub_malloc (sizeof (*array));
520 if (!array)
522 grub_free (new_array->uuid);
523 return grub_errno;
526 *array = *new_array;
527 array->nr_devs = 0;
528 grub_memset (&array->device, 0, sizeof (array->device));
530 /* Check whether we don't have multiple arrays with the same number. */
531 for (p = array_list; p != NULL; p = p->next)
533 if (p->number == array->number)
534 break;
537 if (p)
539 /* The number is already in use, so we need to find an new number. */
540 int i = 0;
542 while (1)
544 for (p = array_list; p != NULL; p = p->next)
546 if (p->number == i)
547 break;
550 if (!p)
552 /* We found an unused number. */
553 array->number = i;
554 break;
557 i++;
561 array->name = grub_malloc (13);
562 if (! array->name)
564 grub_free (array->uuid);
565 grub_free (array);
567 return grub_errno;
570 grub_sprintf (array->name, "md%d", array->number);
572 grub_dprintf ("raid", "Found array %s (%s)\n", array->name,
573 scanner_name);
575 /* Add our new array to the list. */
576 array->next = array_list;
577 array_list = array;
579 /* RAID 1 doestn't use a chunksize but code assumes one so set
580 one. */
581 if (array->level == 1)
582 array->chunk_size = 64;
585 /* Add the device to the array. */
586 array->device[new_array->index] = disk;
587 array->nr_devs++;
589 return 0;
592 static grub_raid_t grub_raid_list;
594 static void
595 grub_raid_scan_device (int head_only)
597 auto int hook (const char *name);
598 int hook (const char *name)
600 grub_disk_t disk;
601 struct grub_raid_array array;
602 struct grub_raid *p;
604 grub_dprintf ("raid", "Scanning for RAID devices\n");
606 disk = grub_disk_open (name);
607 if (!disk)
608 return 0;
610 if (disk->total_sectors == ULONG_MAX)
612 grub_disk_close (disk);
613 return 0;
616 for (p = grub_raid_list; p; p = p->next)
618 if (! p->detect (disk, &array))
620 if (! insert_array (disk, &array, p->name))
621 return 0;
623 break;
626 /* This error usually means it's not raid, no need to display
627 it. */
628 if (grub_errno != GRUB_ERR_OUT_OF_RANGE)
629 grub_print_error ();
631 grub_errno = GRUB_ERR_NONE;
632 if (head_only)
633 break;
636 grub_disk_close (disk);
638 return 0;
641 grub_device_iterate (&hook);
644 static void
645 free_array (void)
647 struct grub_raid_array *array;
649 array = array_list;
650 while (array)
652 struct grub_raid_array *p;
653 int i;
655 p = array;
656 array = array->next;
658 for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++)
659 if (p->device[i])
660 grub_disk_close (p->device[i]);
662 grub_free (p->uuid);
663 grub_free (p->name);
664 grub_free (p);
667 array_list = 0;
670 void
671 grub_raid_register (grub_raid_t raid)
673 raid->next = grub_raid_list;
674 grub_raid_list = raid;
675 grub_raid_scan_device (1);
678 void
679 grub_raid_unregister (grub_raid_t raid)
681 grub_raid_t *p, q;
683 for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next)
684 if (q == raid)
686 *p = q->next;
687 break;
691 void
692 grub_raid_rescan (void)
694 free_array ();
695 grub_raid_scan_device (0);
698 static struct grub_disk_dev grub_raid_dev =
700 .name = "raid",
701 .id = GRUB_DISK_DEVICE_RAID_ID,
702 .iterate = grub_raid_iterate,
703 .open = grub_raid_open,
704 .close = grub_raid_close,
705 .read = grub_raid_read,
706 .write = grub_raid_write,
707 #ifdef GRUB_UTIL
708 .memberlist = grub_raid_memberlist,
709 #endif
710 .next = 0
714 GRUB_MOD_INIT(raid)
716 grub_disk_dev_register (&grub_raid_dev);
719 GRUB_MOD_FINI(raid)
721 grub_disk_dev_unregister (&grub_raid_dev);
722 free_array ();