Change to DragonFly 3.5.
[dragonfly.git] / usr.sbin / mfiutil / mfi_config.c
blobb4d9a54d73bb57cdb0c021f631fccd93541a7e8d
1 /*-
2 * Copyright (c) 2008, 2009 Yahoo!, Inc.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The names of the authors may not be used to endorse or promote
14 * products derived from this software without specific prior written
15 * permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
29 * $FreeBSD: src/usr.sbin/mfiutil/mfi_config.c,v 1.8 2011/11/29 08:16:14 delphij Exp $
32 #include <sys/param.h>
33 #ifdef DEBUG
34 #include <sys/sysctl.h>
35 #endif
36 #include <err.h>
37 #include <errno.h>
38 #include <libutil.h>
39 #include <stdint.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include "mfiutil.h"
46 static int add_spare(int ac, char **av);
47 static int remove_spare(int ac, char **av);
49 int
50 mfi_config_read(int fd, struct mfi_config_data **configp)
52 return mfi_config_read_opcode(fd, MFI_DCMD_CFG_READ, configp, NULL, 0);
55 int
56 mfi_config_read_opcode(int fd, uint32_t opcode,
57 struct mfi_config_data **configp, uint8_t *mbox, size_t mboxlen)
59 struct mfi_config_data *config;
60 uint32_t config_size;
61 int error;
64 * Keep fetching the config in a loop until we have a large enough
65 * buffer to hold the entire configuration.
67 config = NULL;
68 config_size = 1024;
69 fetch:
70 config = reallocf(config, config_size);
71 if (config == NULL)
72 return (-1);
73 if (mfi_dcmd_command(fd, opcode, config,
74 config_size, mbox, mboxlen, NULL) < 0) {
75 error = errno;
76 free(config);
77 errno = error;
78 return (-1);
81 if (config->size > config_size) {
82 config_size = config->size;
83 goto fetch;
86 *configp = config;
87 return (0);
90 static struct mfi_array *
91 mfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
93 struct mfi_array *ar;
94 char *p;
95 int i;
97 p = (char *)config->array;
98 for (i = 0; i < config->array_count; i++) {
99 ar = (struct mfi_array *)p;
100 if (ar->array_ref == array_ref)
101 return (ar);
102 p += config->array_size;
105 return (NULL);
108 static struct mfi_ld_config *
109 mfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
111 struct mfi_ld_config *ld;
112 char *p;
113 int i;
115 p = (char *)config->array + config->array_count * config->array_size;
116 for (i = 0; i < config->log_drv_count; i++) {
117 ld = (struct mfi_ld_config *)p;
118 if (ld->properties.ld.v.target_id == target_id)
119 return (ld);
120 p += config->log_drv_size;
123 return (NULL);
126 static int
127 clear_config(__unused int ac, __unused char **av)
129 struct mfi_ld_list list;
130 int ch, error, fd;
131 u_int i;
133 fd = mfi_open(mfi_unit);
134 if (fd < 0) {
135 error = errno;
136 warn("mfi_open");
137 return (error);
140 if (!mfi_reconfig_supported()) {
141 warnx("The current mfi(4) driver does not support "
142 "configuration changes.");
143 close(fd);
144 return (EOPNOTSUPP);
147 if (mfi_ld_get_list(fd, &list, NULL) < 0) {
148 error = errno;
149 warn("Failed to get volume list");
150 close(fd);
151 return (error);
154 for (i = 0; i < list.ld_count; i++) {
155 if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) {
156 warnx("Volume %s is busy and cannot be deleted",
157 mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
158 close(fd);
159 return (EBUSY);
163 printf(
164 "Are you sure you wish to clear the configuration on mfi%u? [y/N] ",
165 mfi_unit);
166 ch = getchar();
167 if (ch != 'y' && ch != 'Y') {
168 printf("\nAborting\n");
169 close(fd);
170 return (0);
173 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) {
174 error = errno;
175 warn("Failed to clear configuration");
176 close(fd);
177 return (error);
180 printf("mfi%d: Configuration cleared\n", mfi_unit);
181 close(fd);
183 return (0);
185 MFI_COMMAND(top, clear, clear_config);
187 #define MFI_ARRAY_SIZE 288
188 #define MAX_DRIVES_PER_ARRAY \
189 ((MFI_ARRAY_SIZE - sizeof(struct mfi_array)) / 8)
191 #define RT_RAID0 0
192 #define RT_RAID1 1
193 #define RT_RAID5 2
194 #define RT_RAID6 3
195 #define RT_JBOD 4
196 #define RT_CONCAT 5
197 #define RT_RAID10 6
198 #define RT_RAID50 7
199 #define RT_RAID60 8
201 static int
202 compare_int(const void *one, const void *two)
204 int first, second;
206 first = *(const int *)one;
207 second = *(const int *)two;
209 return (first - second);
212 static struct raid_type_entry {
213 const char *name;
214 int raid_type;
215 } raid_type_table[] = {
216 { "raid0", RT_RAID0 },
217 { "raid-0", RT_RAID0 },
218 { "raid1", RT_RAID1 },
219 { "raid-1", RT_RAID1 },
220 { "mirror", RT_RAID1 },
221 { "raid5", RT_RAID5 },
222 { "raid-5", RT_RAID5 },
223 { "raid6", RT_RAID6 },
224 { "raid-6", RT_RAID6 },
225 { "jbod", RT_JBOD },
226 { "concat", RT_CONCAT },
227 { "raid10", RT_RAID10 },
228 { "raid1+0", RT_RAID10 },
229 { "raid-10", RT_RAID10 },
230 { "raid-1+0", RT_RAID10 },
231 { "raid50", RT_RAID50 },
232 { "raid5+0", RT_RAID50 },
233 { "raid-50", RT_RAID50 },
234 { "raid-5+0", RT_RAID50 },
235 { "raid60", RT_RAID60 },
236 { "raid6+0", RT_RAID60 },
237 { "raid-60", RT_RAID60 },
238 { "raid-6+0", RT_RAID60 },
239 { NULL, 0 },
242 struct config_id_state {
243 int array_count;
244 int log_drv_count;
245 int *arrays;
246 int *volumes;
247 uint16_t array_ref;
248 uint8_t target_id;
251 struct array_info {
252 int drive_count;
253 struct mfi_pd_info *drives;
254 struct mfi_array *array;
257 /* Parse a comma-separated list of drives for an array. */
258 static int
259 parse_array(int fd, int raid_type, char *array_str, struct array_info *info)
261 struct mfi_pd_info *pinfo;
262 uint16_t device_id;
263 char *cp;
264 u_int count;
265 int error;
267 cp = array_str;
268 for (count = 0; cp != NULL; count++) {
269 cp = strchr(cp, ',');
270 if (cp != NULL) {
271 cp++;
272 if (*cp == ',') {
273 warnx("Invalid drive list '%s'", array_str);
274 return (EINVAL);
279 /* Validate the number of drives for this array. */
280 if (count >= MAX_DRIVES_PER_ARRAY) {
281 warnx("Too many drives for a single array: max is %zu",
282 MAX_DRIVES_PER_ARRAY);
283 return (EINVAL);
285 switch (raid_type) {
286 case RT_RAID1:
287 case RT_RAID10:
288 if (count % 2 != 0) {
289 warnx("RAID1 and RAID10 require an even number of "
290 "drives in each array");
291 return (EINVAL);
293 break;
294 case RT_RAID5:
295 case RT_RAID50:
296 if (count < 3) {
297 warnx("RAID5 and RAID50 require at least 3 drives in "
298 "each array");
299 return (EINVAL);
301 break;
302 case RT_RAID6:
303 case RT_RAID60:
304 if (count < 4) {
305 warnx("RAID6 and RAID60 require at least 4 drives in "
306 "each array");
307 return (EINVAL);
309 break;
312 /* Validate each drive. */
313 info->drives = calloc(count, sizeof(struct mfi_pd_info));
314 if (info->drives == NULL) {
315 warnx("malloc failed");
316 return (ENOMEM);
318 info->drive_count = count;
319 for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL;
320 pinfo++) {
321 error = mfi_lookup_drive(fd, cp, &device_id);
322 if (error) {
323 free(info->drives);
324 info->drives = NULL;
325 return (error);
328 if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) {
329 error = errno;
330 warn("Failed to fetch drive info for drive %s", cp);
331 free(info->drives);
332 info->drives = NULL;
333 return (error);
336 if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
337 warnx("Drive %u is not available", device_id);
338 free(info->drives);
339 info->drives = NULL;
340 return (EINVAL);
343 if (pinfo->state.ddf.v.pd_type.is_foreign) {
344 warnx("Drive %u is foreign", device_id);
345 free(info->drives);
346 info->drives = NULL;
347 return (EINVAL);
351 return (0);
355 * Find the next free array ref assuming that 'array_ref' is the last
356 * one used. 'array_ref' should be 0xffff for the initial test.
358 static uint16_t
359 find_next_array(struct config_id_state *state)
361 int i;
363 /* Assume the current one is used. */
364 state->array_ref++;
366 /* Find the next free one. */
367 for (i = 0; i < state->array_count; i++)
368 if (state->arrays[i] == state->array_ref)
369 state->array_ref++;
370 return (state->array_ref);
374 * Find the next free volume ID assuming that 'target_id' is the last
375 * one used. 'target_id' should be 0xff for the initial test.
377 static uint8_t
378 find_next_volume(struct config_id_state *state)
380 int i;
382 /* Assume the current one is used. */
383 state->target_id++;
385 /* Find the next free one. */
386 for (i = 0; i < state->log_drv_count; i++)
387 if (state->volumes[i] == state->target_id)
388 state->target_id++;
389 return (state->target_id);
392 /* Populate an array with drives. */
393 static void
394 build_array(__unused int fd, char *arrayp, struct array_info *array_info,
395 struct config_id_state *state, int verbose)
397 struct mfi_array *ar = (struct mfi_array *)arrayp;
398 int i;
400 ar->size = array_info->drives[0].coerced_size;
401 ar->num_drives = array_info->drive_count;
402 ar->array_ref = find_next_array(state);
403 for (i = 0; i < array_info->drive_count; i++) {
404 if (verbose)
405 printf("Adding drive %s to array %u\n",
406 mfi_drive_name(NULL,
407 array_info->drives[i].ref.v.device_id,
408 MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS),
409 ar->array_ref);
410 if (ar->size > array_info->drives[i].coerced_size)
411 ar->size = array_info->drives[i].coerced_size;
412 ar->pd[i].ref = array_info->drives[i].ref;
413 ar->pd[i].fw_state = MFI_PD_STATE_ONLINE;
415 array_info->array = ar;
419 * Create a volume that spans one or more arrays.
421 static void
422 build_volume(char *volumep, int narrays, struct array_info *arrays,
423 int raid_type, long stripe_size, struct config_id_state *state, int verbose)
425 struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
426 struct mfi_array *ar;
427 int i;
429 /* properties */
430 ld->properties.ld.v.target_id = find_next_volume(state);
431 ld->properties.ld.v.seq = 0;
432 ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
433 MR_LD_CACHE_WRITE_BACK;
434 ld->properties.access_policy = MFI_LD_ACCESS_RW;
435 ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED;
436 ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
437 MR_LD_CACHE_WRITE_BACK;
438 ld->properties.no_bgi = 0;
440 /* params */
441 switch (raid_type) {
442 case RT_RAID0:
443 case RT_JBOD:
444 ld->params.primary_raid_level = DDF_RAID0;
445 ld->params.raid_level_qualifier = 0;
446 ld->params.secondary_raid_level = 0;
447 break;
448 case RT_RAID1:
449 ld->params.primary_raid_level = DDF_RAID1;
450 ld->params.raid_level_qualifier = 0;
451 ld->params.secondary_raid_level = 0;
452 break;
453 case RT_RAID5:
454 ld->params.primary_raid_level = DDF_RAID5;
455 ld->params.raid_level_qualifier = 3;
456 ld->params.secondary_raid_level = 0;
457 break;
458 case RT_RAID6:
459 ld->params.primary_raid_level = DDF_RAID6;
460 ld->params.raid_level_qualifier = 3;
461 ld->params.secondary_raid_level = 0;
462 break;
463 case RT_CONCAT:
464 ld->params.primary_raid_level = DDF_CONCAT;
465 ld->params.raid_level_qualifier = 0;
466 ld->params.secondary_raid_level = 0;
467 break;
468 case RT_RAID10:
469 ld->params.primary_raid_level = DDF_RAID1;
470 ld->params.raid_level_qualifier = 0;
471 ld->params.secondary_raid_level = 3; /* XXX? */
472 break;
473 case RT_RAID50:
475 * XXX: This appears to work though the card's BIOS
476 * complains that the configuration is foreign. The
477 * BIOS setup does not allow for creation of RAID-50
478 * or RAID-60 arrays. The only nested array
479 * configuration it allows for is RAID-10.
481 ld->params.primary_raid_level = DDF_RAID5;
482 ld->params.raid_level_qualifier = 3;
483 ld->params.secondary_raid_level = 3; /* XXX? */
484 break;
485 case RT_RAID60:
486 ld->params.primary_raid_level = DDF_RAID6;
487 ld->params.raid_level_qualifier = 3;
488 ld->params.secondary_raid_level = 3; /* XXX? */
489 break;
493 * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use
494 * ffs() to simulate log2(stripe_size).
496 ld->params.stripe_size = ffs(stripe_size) - 1 - 9;
497 ld->params.num_drives = arrays[0].array->num_drives;
498 ld->params.span_depth = narrays;
499 ld->params.state = MFI_LD_STATE_OPTIMAL;
500 ld->params.init_state = MFI_LD_PARAMS_INIT_NO;
501 ld->params.is_consistent = 0;
503 /* spans */
504 for (i = 0; i < narrays; i++) {
505 ar = arrays[i].array;
506 if (verbose)
507 printf("Adding array %u to volume %u\n", ar->array_ref,
508 ld->properties.ld.v.target_id);
509 ld->span[i].start_block = 0;
510 ld->span[i].num_blocks = ar->size;
511 ld->span[i].array_ref = ar->array_ref;
515 static int
516 create_volume(int ac, char **av)
518 struct mfi_config_data *config;
519 struct mfi_array *ar;
520 struct mfi_ld_config *ld;
521 struct config_id_state state;
522 size_t config_size;
523 char *p, *cfg_arrays, *cfg_volumes;
524 int error, fd, i, raid_type;
525 int narrays, nvolumes, arrays_per_volume;
526 struct array_info *arrays;
527 int64_t stripe_size;
528 #ifdef DEBUG
529 int dump;
530 #endif
531 int ch, verbose;
534 * Backwards compat. Map 'create volume' to 'create' and
535 * 'create spare' to 'add'.
537 if (ac > 1) {
538 if (strcmp(av[1], "volume") == 0) {
539 av++;
540 ac--;
541 } else if (strcmp(av[1], "spare") == 0) {
542 av++;
543 ac--;
544 return (add_spare(ac, av));
548 if (ac < 2) {
549 warnx("create volume: volume type required");
550 return (EINVAL);
553 bzero(&state, sizeof(state));
554 config = NULL;
555 arrays = NULL;
556 narrays = 0;
557 error = 0;
559 fd = mfi_open(mfi_unit);
560 if (fd < 0) {
561 error = errno;
562 warn("mfi_open");
563 return (error);
566 if (!mfi_reconfig_supported()) {
567 warnx("The current mfi(4) driver does not support "
568 "configuration changes.");
569 error = EOPNOTSUPP;
570 goto error;
573 /* Lookup the RAID type first. */
574 raid_type = -1;
575 for (i = 0; raid_type_table[i].name != NULL; i++)
576 if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
577 raid_type = raid_type_table[i].raid_type;
578 break;
581 if (raid_type == -1) {
582 warnx("Unknown or unsupported volume type %s", av[1]);
583 error = EINVAL;
584 goto error;
587 /* Parse any options. */
588 optind = 2;
589 #ifdef DEBUG
590 dump = 0;
591 #endif
592 verbose = 0;
593 stripe_size = 64 * 1024;
595 while ((ch = getopt(ac, av, "ds:v")) != -1) {
596 switch (ch) {
597 #ifdef DEBUG
598 case 'd':
599 dump = 1;
600 break;
601 #endif
602 case 's':
603 error = dehumanize_number(optarg, &stripe_size);
604 if (error != 0) {
605 warnx("Illegal stripe size");
606 goto error;
608 if ((stripe_size < 512) || (!powerof2(stripe_size))) {
609 warnx("Illegal stripe size, using 64K");
610 stripe_size = 64 * 1024;
612 break;
613 case 'v':
614 verbose = 1;
615 break;
616 case '?':
617 default:
618 error = EINVAL;
619 goto error;
622 ac -= optind;
623 av += optind;
625 /* Parse all the arrays. */
626 narrays = ac;
627 if (narrays == 0) {
628 warnx("At least one drive list is required");
629 error = EINVAL;
630 goto error;
632 switch (raid_type) {
633 case RT_RAID0:
634 case RT_RAID1:
635 case RT_RAID5:
636 case RT_RAID6:
637 case RT_CONCAT:
638 if (narrays != 1) {
639 warnx("Only one drive list can be specified");
640 error = EINVAL;
641 goto error;
643 break;
644 case RT_RAID10:
645 case RT_RAID50:
646 case RT_RAID60:
647 if (narrays < 1) {
648 warnx("RAID10, RAID50, and RAID60 require at least "
649 "two drive lists");
650 error = EINVAL;
651 goto error;
653 if (narrays > MFI_MAX_SPAN_DEPTH) {
654 warnx("Volume spans more than %d arrays",
655 MFI_MAX_SPAN_DEPTH);
656 error = EINVAL;
657 goto error;
659 break;
661 arrays = calloc(narrays, sizeof(*arrays));
662 if (arrays == NULL) {
663 warnx("malloc failed");
664 error = ENOMEM;
665 goto error;
667 for (i = 0; i < narrays; i++) {
668 error = parse_array(fd, raid_type, av[i], &arrays[i]);
669 if (error)
670 goto error;
673 switch (raid_type) {
674 case RT_RAID10:
675 case RT_RAID50:
676 case RT_RAID60:
677 for (i = 1; i < narrays; i++) {
678 if (arrays[i].drive_count != arrays[0].drive_count) {
679 warnx("All arrays must contain the same "
680 "number of drives");
681 error = EINVAL;
682 goto error;
685 break;
689 * Fetch the current config and build sorted lists of existing
690 * array and volume identifiers.
692 if (mfi_config_read(fd, &config) < 0) {
693 error = errno;
694 warn("Failed to read configuration");
695 goto error;
697 p = (char *)config->array;
698 state.array_ref = 0xffff;
699 state.target_id = 0xff;
700 state.array_count = config->array_count;
701 if (config->array_count > 0) {
702 state.arrays = calloc(config->array_count, sizeof(int));
703 if (state.arrays == NULL) {
704 warnx("malloc failed");
705 error = ENOMEM;
706 goto error;
708 for (i = 0; i < config->array_count; i++) {
709 ar = (struct mfi_array *)p;
710 state.arrays[i] = ar->array_ref;
711 p += config->array_size;
713 qsort(state.arrays, config->array_count, sizeof(int),
714 compare_int);
715 } else
716 state.arrays = NULL;
717 state.log_drv_count = config->log_drv_count;
718 if (config->log_drv_count) {
719 state.volumes = calloc(config->log_drv_count, sizeof(int));
720 if (state.volumes == NULL) {
721 warnx("malloc failed");
722 error = ENOMEM;
723 goto error;
725 for (i = 0; i < config->log_drv_count; i++) {
726 ld = (struct mfi_ld_config *)p;
727 state.volumes[i] = ld->properties.ld.v.target_id;
728 p += config->log_drv_size;
730 qsort(state.volumes, config->log_drv_count, sizeof(int),
731 compare_int);
732 } else
733 state.volumes = NULL;
734 free(config);
736 /* Determine the size of the configuration we will build. */
737 switch (raid_type) {
738 case RT_RAID0:
739 case RT_RAID1:
740 case RT_RAID5:
741 case RT_RAID6:
742 case RT_CONCAT:
743 case RT_JBOD:
744 /* Each volume spans a single array. */
745 nvolumes = narrays;
746 break;
747 case RT_RAID10:
748 case RT_RAID50:
749 case RT_RAID60:
750 /* A single volume spans multiple arrays. */
751 nvolumes = 1;
752 break;
753 default:
754 /* Pacify gcc. */
755 abort();
758 config_size = sizeof(struct mfi_config_data) +
759 sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays;
760 config = calloc(1, config_size);
761 if (config == NULL) {
762 warnx("malloc failed");
763 error = ENOMEM;
764 goto error;
766 config->size = config_size;
767 config->array_count = narrays;
768 config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */
769 config->log_drv_count = nvolumes;
770 config->log_drv_size = sizeof(struct mfi_ld_config);
771 config->spares_count = 0;
772 config->spares_size = 40; /* XXX: Firmware hardcode */
773 cfg_arrays = (char *)config->array;
774 cfg_volumes = cfg_arrays + config->array_size * narrays;
776 /* Build the arrays. */
777 for (i = 0; i < narrays; i++) {
778 build_array(fd, cfg_arrays, &arrays[i], &state, verbose);
779 cfg_arrays += config->array_size;
782 /* Now build the volume(s). */
783 arrays_per_volume = narrays / nvolumes;
784 for (i = 0; i < nvolumes; i++) {
785 build_volume(cfg_volumes, arrays_per_volume,
786 &arrays[i * arrays_per_volume], raid_type, stripe_size,
787 &state, verbose);
788 cfg_volumes += config->log_drv_size;
791 #ifdef DEBUG
792 if (dump)
793 dump_config(fd, config, NULL);
794 #endif
796 /* Send the new config to the controller. */
797 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size,
798 NULL, 0, NULL) < 0) {
799 error = errno;
800 warn("Failed to add volume");
801 /* FALLTHROUGH */
804 error:
805 /* Clean up. */
806 free(config);
807 free(state.volumes);
808 free(state.arrays);
809 if (arrays != NULL) {
810 for (i = 0; i < narrays; i++)
811 free(arrays[i].drives);
812 free(arrays);
814 close(fd);
816 return (error);
818 MFI_COMMAND(top, create, create_volume);
820 static int
821 delete_volume(int ac, char **av)
823 struct mfi_ld_info info;
824 int error, fd;
825 uint8_t target_id, mbox[4];
828 * Backwards compat. Map 'delete volume' to 'delete' and
829 * 'delete spare' to 'remove'.
831 if (ac > 1) {
832 if (strcmp(av[1], "volume") == 0) {
833 av++;
834 ac--;
835 } else if (strcmp(av[1], "spare") == 0) {
836 av++;
837 ac--;
838 return (remove_spare(ac, av));
842 if (ac != 2) {
843 warnx("delete volume: volume required");
844 return (EINVAL);
847 fd = mfi_open(mfi_unit);
848 if (fd < 0) {
849 error = errno;
850 warn("mfi_open");
851 return (error);
854 if (!mfi_reconfig_supported()) {
855 warnx("The current mfi(4) driver does not support "
856 "configuration changes.");
857 close(fd);
858 return (EOPNOTSUPP);
861 if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
862 error = errno;
863 warn("Invalid volume %s", av[1]);
864 close(fd);
865 return (error);
868 if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
869 error = errno;
870 warn("Failed to get info for volume %d", target_id);
871 close(fd);
872 return (error);
875 if (mfi_volume_busy(fd, target_id)) {
876 warnx("Volume %s is busy and cannot be deleted",
877 mfi_volume_name(fd, target_id));
878 close(fd);
879 return (EBUSY);
882 mbox_store_ldref(mbox, &info.ld_config.properties.ld);
883 if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox,
884 sizeof(mbox), NULL) < 0) {
885 error = errno;
886 warn("Failed to delete volume");
887 close(fd);
888 return (error);
891 close(fd);
893 return (0);
895 MFI_COMMAND(top, delete, delete_volume);
897 static int
898 add_spare(int ac, char **av)
900 struct mfi_pd_info info;
901 struct mfi_config_data *config;
902 struct mfi_array *ar;
903 struct mfi_ld_config *ld;
904 struct mfi_spare *spare;
905 uint16_t device_id;
906 uint8_t target_id;
907 char *p;
908 int error, fd, i;
910 if (ac < 2) {
911 warnx("add spare: drive required");
912 return (EINVAL);
915 fd = mfi_open(mfi_unit);
916 if (fd < 0) {
917 error = errno;
918 warn("mfi_open");
919 return (error);
922 config = NULL;
923 spare = NULL;
924 error = mfi_lookup_drive(fd, av[1], &device_id);
925 if (error)
926 goto error;
928 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
929 error = errno;
930 warn("Failed to fetch drive info");
931 goto error;
934 if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
935 warnx("Drive %u is not available", device_id);
936 error = EINVAL;
937 goto error;
940 if (ac > 2) {
941 if (mfi_lookup_volume(fd, av[2], &target_id) < 0) {
942 error = errno;
943 warn("Invalid volume %s", av[2]);
944 goto error;
948 if (mfi_config_read(fd, &config) < 0) {
949 error = errno;
950 warn("Failed to read configuration");
951 goto error;
954 spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) *
955 config->array_count);
956 if (spare == NULL) {
957 warnx("malloc failed");
958 error = ENOMEM;
959 goto error;
961 bzero(spare, sizeof(struct mfi_spare));
962 spare->ref = info.ref;
964 if (ac == 2) {
965 /* Global spare backs all arrays. */
966 p = (char *)config->array;
967 for (i = 0; i < config->array_count; i++) {
968 ar = (struct mfi_array *)p;
969 if (ar->size > info.coerced_size) {
970 warnx("Spare isn't large enough for array %u",
971 ar->array_ref);
972 error = EINVAL;
973 goto error;
975 p += config->array_size;
977 spare->array_count = 0;
978 } else {
980 * Dedicated spares only back the arrays for a
981 * specific volume.
983 ld = mfi_config_lookup_volume(config, target_id);
984 if (ld == NULL) {
985 warnx("Did not find volume %d", target_id);
986 error = EINVAL;
987 goto error;
990 spare->spare_type |= MFI_SPARE_DEDICATED;
991 spare->array_count = ld->params.span_depth;
992 for (i = 0; i < ld->params.span_depth; i++) {
993 ar = mfi_config_lookup_array(config,
994 ld->span[i].array_ref);
995 if (ar == NULL) {
996 warnx("Missing array; inconsistent config?");
997 error = ENXIO;
998 goto error;
1000 if (ar->size > info.coerced_size) {
1001 warnx("Spare isn't large enough for array %u",
1002 ar->array_ref);
1003 error = EINVAL;
1004 goto error;
1006 spare->array_ref[i] = ar->array_ref;
1010 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare,
1011 sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count,
1012 NULL, 0, NULL) < 0) {
1013 error = errno;
1014 warn("Failed to assign spare");
1015 /* FALLTHROUGH. */
1018 error:
1019 free(spare);
1020 free(config);
1021 close(fd);
1023 return (error);
1025 MFI_COMMAND(top, add, add_spare);
1027 static int
1028 remove_spare(int ac, char **av)
1030 struct mfi_pd_info info;
1031 int error, fd;
1032 uint16_t device_id;
1033 uint8_t mbox[4];
1035 if (ac != 2) {
1036 warnx("remove spare: drive required");
1037 return (EINVAL);
1040 fd = mfi_open(mfi_unit);
1041 if (fd < 0) {
1042 error = errno;
1043 warn("mfi_open");
1044 return (error);
1047 error = mfi_lookup_drive(fd, av[1], &device_id);
1048 if (error) {
1049 close(fd);
1050 return (error);
1053 /* Get the info for this drive. */
1054 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
1055 error = errno;
1056 warn("Failed to fetch info for drive %u", device_id);
1057 close(fd);
1058 return (error);
1061 if (info.fw_state != MFI_PD_STATE_HOT_SPARE) {
1062 warnx("Drive %u is not a hot spare", device_id);
1063 close(fd);
1064 return (EINVAL);
1067 mbox_store_pdref(mbox, &info.ref);
1068 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox,
1069 sizeof(mbox), NULL) < 0) {
1070 error = errno;
1071 warn("Failed to delete spare");
1072 close(fd);
1073 return (error);
1076 close(fd);
1078 return (0);
1080 MFI_COMMAND(top, remove, remove_spare);
1082 /* Display raw data about a config. */
1083 void
1084 dump_config(int fd, struct mfi_config_data *config, const char *msg_prefix)
1086 struct mfi_array *ar;
1087 struct mfi_ld_config *ld;
1088 struct mfi_spare *sp;
1089 struct mfi_pd_info pinfo;
1090 uint16_t device_id;
1091 char *p;
1092 int i, j;
1094 if (NULL == msg_prefix)
1095 msg_prefix = "Configuration (Debug)";
1097 printf(
1098 "mfi%d %s: %d arrays, %d volumes, %d spares\n", mfi_unit,
1099 msg_prefix, config->array_count, config->log_drv_count,
1100 config->spares_count);
1101 printf(" array size: %u\n", config->array_size);
1102 printf(" volume size: %u\n", config->log_drv_size);
1103 printf(" spare size: %u\n", config->spares_size);
1104 p = (char *)config->array;
1106 for (i = 0; i < config->array_count; i++) {
1107 ar = (struct mfi_array *)p;
1108 printf(" array %u of %u drives:\n", ar->array_ref,
1109 ar->num_drives);
1110 printf(" size = %ju\n", (uintmax_t)ar->size);
1111 for (j = 0; j < ar->num_drives; j++) {
1112 device_id = ar->pd[j].ref.v.device_id;
1113 if (device_id == 0xffff)
1114 printf(" drive MISSING\n");
1115 else {
1116 printf(" drive %u %s\n", device_id,
1117 mfi_pdstate(ar->pd[j].fw_state));
1118 if (mfi_pd_get_info(fd, device_id, &pinfo,
1119 NULL) >= 0) {
1120 printf(" raw size: %ju\n",
1121 (uintmax_t)pinfo.raw_size);
1122 printf(" non-coerced size: %ju\n",
1123 (uintmax_t)pinfo.non_coerced_size);
1124 printf(" coerced size: %ju\n",
1125 (uintmax_t)pinfo.coerced_size);
1129 p += config->array_size;
1132 for (i = 0; i < config->log_drv_count; i++) {
1133 ld = (struct mfi_ld_config *)p;
1134 printf(" volume %s ",
1135 mfi_volume_name(fd, ld->properties.ld.v.target_id));
1136 printf("%s %s",
1137 mfi_raid_level(ld->params.primary_raid_level,
1138 ld->params.secondary_raid_level),
1139 mfi_ldstate(ld->params.state));
1140 if (ld->properties.name[0] != '\0')
1141 printf(" <%s>", ld->properties.name);
1142 printf("\n");
1143 printf(" primary raid level: %u\n",
1144 ld->params.primary_raid_level);
1145 printf(" raid level qualifier: %u\n",
1146 ld->params.raid_level_qualifier);
1147 printf(" secondary raid level: %u\n",
1148 ld->params.secondary_raid_level);
1149 printf(" stripe size: %u\n", ld->params.stripe_size);
1150 printf(" num drives: %u\n", ld->params.num_drives);
1151 printf(" init state: %u\n", ld->params.init_state);
1152 printf(" consistent: %u\n", ld->params.is_consistent);
1153 printf(" no bgi: %u\n", ld->properties.no_bgi);
1154 printf(" spans:\n");
1155 for (j = 0; j < ld->params.span_depth; j++) {
1156 printf(" array %u @ ", ld->span[j].array_ref);
1157 printf("%ju : %ju\n",
1158 (uintmax_t)ld->span[j].start_block,
1159 (uintmax_t)ld->span[j].num_blocks);
1161 p += config->log_drv_size;
1164 for (i = 0; i < config->spares_count; i++) {
1165 sp = (struct mfi_spare *)p;
1166 printf(" %s spare %u ",
1167 sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" :
1168 "global", sp->ref.v.device_id);
1169 printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE));
1170 printf(" backs:\n");
1171 for (j = 0; j < sp->array_count; j++)
1172 printf(" array %u\n", sp->array_ref[j]);
1173 p += config->spares_size;
1177 #ifdef DEBUG
1178 static int
1179 debug_config(int ac, char **av)
1181 struct mfi_config_data *config;
1182 int error, fd;
1184 if (ac != 1) {
1185 warnx("debug: extra arguments");
1186 return (EINVAL);
1189 fd = mfi_open(mfi_unit);
1190 if (fd < 0) {
1191 error = errno;
1192 warn("mfi_open");
1193 return (error);
1196 /* Get the config from the controller. */
1197 if (mfi_config_read(fd, &config) < 0) {
1198 error = errno;
1199 warn("Failed to get config");
1200 close(fd);
1201 return (error);
1204 /* Dump out the configuration. */
1205 dump_config(fd, config, NULL);
1206 free(config);
1207 close(fd);
1209 return (0);
1211 MFI_COMMAND(top, debug, debug_config);
1213 static int
1214 dump(int ac, char **av)
1216 struct mfi_config_data *config;
1217 char buf[64];
1218 size_t len;
1219 int error, fd;
1221 if (ac != 1) {
1222 warnx("dump: extra arguments");
1223 return (EINVAL);
1226 fd = mfi_open(mfi_unit);
1227 if (fd < 0) {
1228 error = errno;
1229 warn("mfi_open");
1230 return (error);
1233 /* Get the stashed copy of the last dcmd from the driver. */
1234 snprintf(buf, sizeof(buf), "hw.mfi%d.debug_command", mfi_unit);
1235 if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) {
1236 error = errno;
1237 warn("Failed to read debug command");
1238 if (error == ENOENT)
1239 error = EOPNOTSUPP;
1240 close(fd);
1241 return (error);
1244 config = malloc(len);
1245 if (config == NULL) {
1246 warnx("malloc failed");
1247 close(fd);
1248 return (ENOMEM);
1250 if (sysctlbyname(buf, config, &len, NULL, 0) < 0) {
1251 error = errno;
1252 warn("Failed to read debug command");
1253 free(config);
1254 close(fd);
1255 return (error);
1257 dump_config(fd, config, NULL);
1258 free(config);
1259 close(fd);
1261 return (0);
1263 MFI_COMMAND(top, dump, dump);
1264 #endif