2009-11-21 Samuel Thibault <samuel.thibault@ens-lyon.org>
[grub2.git] / disk / raid.c
blobc720fb36c417efce255ddf20f8ec9d8af5ec354c
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, const char *buf2, int size)
177 grub_size_t *p1;
178 const grub_size_t *p2;
180 p1 = (grub_size_t *) buf1;
181 p2 = (const grub_size_t *) buf2;
182 size /= GRUB_CPU_SIZEOF_VOID_P;
184 while (size)
186 *(p1++) ^= *(p2++);
187 size--;
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)
195 struct grub_raid_array *array = disk->data;
196 grub_err_t err = 0;
198 switch (array->level)
200 case 0:
201 case 1:
202 case 10:
204 grub_disk_addr_t read_sector, far_ofs;
205 grub_uint32_t disknr, b, near, far, ofs;
207 read_sector = grub_divmod64 (sector, array->chunk_size, &b);
208 far = ofs = near = 1;
209 far_ofs = 0;
211 if (array->level == 1)
212 near = array->total_devs;
213 else if (array->level == 10)
215 near = array->layout & 0xFF;
216 far = (array->layout >> 8) & 0xFF;
217 if (array->layout >> 16)
219 ofs = far;
220 far_ofs = 1;
222 else
223 far_ofs = grub_divmod64 (array->disk_size,
224 far * array->chunk_size, 0);
226 far_ofs *= array->chunk_size;
229 read_sector = grub_divmod64 (read_sector * near, array->total_devs,
230 &disknr);
232 ofs *= array->chunk_size;
233 read_sector *= ofs;
235 while (1)
237 grub_size_t read_size;
238 unsigned int i, j;
240 read_size = array->chunk_size - b;
241 if (read_size > size)
242 read_size = size;
244 for (i = 0; i < near; i++)
246 unsigned int k;
248 k = disknr;
249 for (j = 0; j < far; j++)
251 if (array->device[k])
253 if (grub_errno == GRUB_ERR_READ_ERROR)
254 grub_errno = GRUB_ERR_NONE;
256 err = grub_disk_read (array->device[k],
257 read_sector + j * far_ofs + b,
259 read_size << GRUB_DISK_SECTOR_BITS,
260 buf);
261 if (! err)
262 break;
263 else if (err != GRUB_ERR_READ_ERROR)
264 return err;
266 else
267 err = grub_error (GRUB_ERR_READ_ERROR,
268 "disk missing.");
270 k++;
271 if (k == array->total_devs)
272 k = 0;
275 if (! err)
276 break;
278 disknr++;
279 if (disknr == array->total_devs)
281 disknr = 0;
282 read_sector += ofs;
286 if (err)
287 return err;
289 buf += read_size << GRUB_DISK_SECTOR_BITS;
290 size -= read_size;
291 if (! size)
292 break;
294 b = 0;
295 disknr += (near - i);
296 while (disknr >= array->total_devs)
298 disknr -= array->total_devs;
299 read_sector += ofs;
302 break;
305 case 4:
306 case 5:
307 case 6:
309 grub_disk_addr_t read_sector;
310 grub_uint32_t b, p, n, disknr, e;
312 /* n = 1 for level 4 and 5, 2 for level 6. */
313 n = array->level / 3;
315 /* Find the first sector to read. */
316 read_sector = grub_divmod64 (sector, array->chunk_size, &b);
317 read_sector = grub_divmod64 (read_sector, array->total_devs - n,
318 &disknr);
319 if (array->level >= 5)
321 grub_divmod64 (read_sector, array->total_devs, &p);
323 if (! (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK))
324 p = array->total_devs - 1 - p;
326 if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
328 disknr += p + n;
330 else
332 grub_uint32_t q;
334 q = p + (n - 1);
335 if (q >= array->total_devs)
336 q -= array->total_devs;
338 if (disknr >= p)
339 disknr += n;
340 else if (disknr >= q)
341 disknr += q + 1;
344 if (disknr >= array->total_devs)
345 disknr -= array->total_devs;
347 else
348 p = array->total_devs - n;
350 read_sector *= array->chunk_size;
352 while (1)
354 grub_size_t read_size;
355 int next_level;
357 read_size = array->chunk_size - b;
358 if (read_size > size)
359 read_size = size;
361 e = 0;
362 if (array->device[disknr])
364 /* Reset read error. */
365 if (grub_errno == GRUB_ERR_READ_ERROR)
366 grub_errno = GRUB_ERR_NONE;
368 err = grub_disk_read (array->device[disknr],
369 read_sector + b, 0,
370 read_size << GRUB_DISK_SECTOR_BITS,
371 buf);
373 if ((err) && (err != GRUB_ERR_READ_ERROR))
374 break;
375 e++;
377 else
378 err = GRUB_ERR_READ_ERROR;
380 if (err)
382 if (array->nr_devs < array->total_devs - n + e)
383 break;
385 grub_errno = GRUB_ERR_NONE;
386 if (array->level == 6)
388 err = ((grub_raid6_recover_func) ?
389 (*grub_raid6_recover_func) (array, disknr, p,
390 buf, read_sector + b,
391 read_size) :
392 grub_error (GRUB_ERR_BAD_DEVICE,
393 "raid6rec is not loaded"));
395 else
397 err = ((grub_raid5_recover_func) ?
398 (*grub_raid5_recover_func) (array, disknr,
399 buf, read_sector + b,
400 read_size) :
401 grub_error (GRUB_ERR_BAD_DEVICE,
402 "raid5rec is not loaded"));
405 if (err)
406 break;
409 buf += read_size << GRUB_DISK_SECTOR_BITS;
410 size -= read_size;
411 if (! size)
412 break;
414 b = 0;
415 disknr++;
417 if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
419 if (disknr == array->total_devs)
420 disknr = 0;
422 next_level = (disknr == p);
424 else
426 if (disknr == p)
427 disknr += n;
429 next_level = (disknr >= array->total_devs);
432 if (next_level)
434 read_sector += array->chunk_size;
436 if (array->level >= 5)
438 if (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK)
439 p = (p == array->total_devs - 1) ? 0 : p + 1;
440 else
441 p = (p == 0) ? array->total_devs - 1 : p - 1;
443 if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK)
445 disknr = p + n;
446 if (disknr >= array->total_devs)
447 disknr -= array->total_devs;
449 else
451 disknr -= array->total_devs;
452 if (disknr == p)
453 disknr += n;
456 else
457 disknr = 0;
461 break;
464 return err;
467 static grub_err_t
468 grub_raid_write (grub_disk_t disk __attribute ((unused)),
469 grub_disk_addr_t sector __attribute ((unused)),
470 grub_size_t size __attribute ((unused)),
471 const char *buf __attribute ((unused)))
473 return GRUB_ERR_NOT_IMPLEMENTED_YET;
476 static grub_err_t
477 insert_array (grub_disk_t disk, struct grub_raid_array *new_array,
478 const char *scanner_name)
480 struct grub_raid_array *array = 0, *p;
482 /* See whether the device is part of an array we have already seen a
483 device from. */
484 for (p = array_list; p != NULL; p = p->next)
485 if ((p->uuid_len == new_array->uuid_len) &&
486 (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len)))
488 grub_free (new_array->uuid);
489 array = p;
491 /* Do some checks before adding the device to the array. */
493 /* FIXME: Check whether the update time of the superblocks are
494 the same. */
496 if (array->total_devs == array->nr_devs)
497 /* We found more members of the array than the array
498 actually has according to its superblock. This shouldn't
499 happen normally. */
500 grub_dprintf ("raid", "array->nr_devs > array->total_devs (%d)?!?",
501 array->total_devs);
503 if (array->device[new_array->index] != NULL)
504 /* We found multiple devices with the same number. Again,
505 this shouldn't happen.*/
506 grub_dprintf ("raid", "Found two disks with the number %d?!?",
507 new_array->number);
509 if (new_array->disk_size < array->disk_size)
510 array->disk_size = new_array->disk_size;
511 break;
514 /* Add an array to the list if we didn't find any. */
515 if (!array)
517 array = grub_malloc (sizeof (*array));
518 if (!array)
520 grub_free (new_array->uuid);
521 return grub_errno;
524 *array = *new_array;
525 array->nr_devs = 0;
526 grub_memset (&array->device, 0, sizeof (array->device));
528 /* Check whether we don't have multiple arrays with the same number. */
529 for (p = array_list; p != NULL; p = p->next)
531 if (p->number == array->number)
532 break;
535 if (p)
537 /* The number is already in use, so we need to find an new number. */
538 int i = 0;
540 while (1)
542 for (p = array_list; p != NULL; p = p->next)
544 if (p->number == i)
545 break;
548 if (!p)
550 /* We found an unused number. */
551 array->number = i;
552 break;
555 i++;
559 array->name = grub_malloc (13);
560 if (! array->name)
562 grub_free (array->uuid);
563 grub_free (array);
565 return grub_errno;
568 grub_sprintf (array->name, "md%d", array->number);
570 grub_dprintf ("raid", "Found array %s (%s)\n", array->name,
571 scanner_name);
573 /* Add our new array to the list. */
574 array->next = array_list;
575 array_list = array;
577 /* RAID 1 doesn't use a chunksize but code assumes one so set
578 one. */
579 if (array->level == 1)
580 array->chunk_size = 64;
583 /* Add the device to the array. */
584 array->device[new_array->index] = disk;
585 array->nr_devs++;
587 return 0;
590 static grub_raid_t grub_raid_list;
592 static void
593 free_array (void)
595 struct grub_raid_array *array;
597 array = array_list;
598 while (array)
600 struct grub_raid_array *p;
601 int i;
603 p = array;
604 array = array->next;
606 for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++)
607 if (p->device[i])
608 grub_disk_close (p->device[i]);
610 grub_free (p->uuid);
611 grub_free (p->name);
612 grub_free (p);
615 array_list = 0;
618 void
619 grub_raid_register (grub_raid_t raid)
621 auto int hook (const char *name);
622 int hook (const char *name)
624 grub_disk_t disk;
625 struct grub_raid_array array;
627 grub_dprintf ("raid", "Scanning for RAID devices on disk %s\n", name);
629 disk = grub_disk_open (name);
630 if (!disk)
631 return 0;
633 if ((disk->total_sectors != GRUB_ULONG_MAX) &&
634 (! grub_raid_list->detect (disk, &array)) &&
635 (! insert_array (disk, &array, grub_raid_list->name)))
636 return 0;
638 /* This error usually means it's not raid, no need to display
639 it. */
640 if (grub_errno != GRUB_ERR_OUT_OF_RANGE)
641 grub_print_error ();
643 grub_errno = GRUB_ERR_NONE;
645 grub_disk_close (disk);
647 return 0;
650 raid->next = grub_raid_list;
651 grub_raid_list = raid;
652 grub_device_iterate (&hook);
655 void
656 grub_raid_unregister (grub_raid_t raid)
658 grub_raid_t *p, q;
660 for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next)
661 if (q == raid)
663 *p = q->next;
664 break;
668 static struct grub_disk_dev grub_raid_dev =
670 .name = "raid",
671 .id = GRUB_DISK_DEVICE_RAID_ID,
672 .iterate = grub_raid_iterate,
673 .open = grub_raid_open,
674 .close = grub_raid_close,
675 .read = grub_raid_read,
676 .write = grub_raid_write,
677 #ifdef GRUB_UTIL
678 .memberlist = grub_raid_memberlist,
679 #endif
680 .next = 0
684 GRUB_MOD_INIT(raid)
686 grub_disk_dev_register (&grub_raid_dev);
689 GRUB_MOD_FINI(raid)
691 grub_disk_dev_unregister (&grub_raid_dev);
692 free_array ();