2 * Copyright (c) 2008, 2009 Yahoo!, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
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
29 * $FreeBSD: src/usr.sbin/mfiutil/mfi_config.c,v 1.4 2010/11/19 15:39:59 jhb Exp $
32 #include <sys/param.h>
34 #include <sys/sysctl.h>
49 static void dump_config(int fd, struct mfi_config_data *config);
52 static int add_spare(int ac, char **av);
53 static int remove_spare(int ac, char **av);
56 dehumanize(const char *value)
63 iv = strtoq(value, &vtp, 0);
64 if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
84 mfi_config_read(int fd, struct mfi_config_data **configp)
86 struct mfi_config_data *config;
90 * Keep fetching the config in a loop until we have a large enough
91 * buffer to hold the entire configuration.
96 config = reallocf(config, config_size);
99 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_READ, config,
100 config_size, NULL, 0, NULL) < 0)
103 if (config->size > config_size) {
104 config_size = config->size;
112 static struct mfi_array *
113 mfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
115 struct mfi_array *ar;
119 p = (char *)config->array;
120 for (i = 0; i < config->array_count; i++) {
121 ar = (struct mfi_array *)p;
122 if (ar->array_ref == array_ref)
124 p += config->array_size;
130 static struct mfi_ld_config *
131 mfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
133 struct mfi_ld_config *ld;
137 p = (char *)config->array + config->array_count * config->array_size;
138 for (i = 0; i < config->log_drv_count; i++) {
139 ld = (struct mfi_ld_config *)p;
140 if (ld->properties.ld.v.target_id == target_id)
142 p += config->log_drv_size;
149 clear_config(int ac, char **av)
151 struct mfi_ld_list list;
155 fd = mfi_open(mfi_unit);
162 if (!mfi_reconfig_supported()) {
163 warnx("The current mfi(4) driver does not support "
164 "configuration changes.");
168 if (mfi_ld_get_list(fd, &list, NULL) < 0) {
170 warn("Failed to get volume list");
174 for (i = 0; i < list.ld_count; i++) {
175 if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) {
176 warnx("Volume %s is busy and cannot be deleted",
177 mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
183 "Are you sure you wish to clear the configuration on mfi%u? [y/N] ",
186 if (ch != 'y' && ch != 'Y') {
187 printf("\nAborting\n");
191 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) {
193 warn("Failed to clear configuration");
197 printf("mfi%d: Configuration cleared\n", mfi_unit);
202 MFI_COMMAND(top, clear, clear_config);
204 #define MFI_ARRAY_SIZE 288
205 #define MAX_DRIVES_PER_ARRAY \
206 ((MFI_ARRAY_SIZE - sizeof(struct mfi_array)) / 8)
219 compare_int(const void *one, const void *two)
223 first = *(const int *)one;
224 second = *(const int *)two;
226 return (first - second);
229 static struct raid_type_entry {
232 } raid_type_table[] = {
233 { "raid0", RT_RAID0 },
234 { "raid-0", RT_RAID0 },
235 { "raid1", RT_RAID1 },
236 { "raid-1", RT_RAID1 },
237 { "mirror", RT_RAID1 },
238 { "raid5", RT_RAID5 },
239 { "raid-5", RT_RAID5 },
240 { "raid6", RT_RAID6 },
241 { "raid-6", RT_RAID6 },
243 { "concat", RT_CONCAT },
244 { "raid10", RT_RAID10 },
245 { "raid1+0", RT_RAID10 },
246 { "raid-10", RT_RAID10 },
247 { "raid-1+0", RT_RAID10 },
248 { "raid50", RT_RAID50 },
249 { "raid5+0", RT_RAID50 },
250 { "raid-50", RT_RAID50 },
251 { "raid-5+0", RT_RAID50 },
252 { "raid60", RT_RAID60 },
253 { "raid6+0", RT_RAID60 },
254 { "raid-60", RT_RAID60 },
255 { "raid-6+0", RT_RAID60 },
259 struct config_id_state {
270 struct mfi_pd_info *drives;
271 struct mfi_array *array;
274 /* Parse a comma-separated list of drives for an array. */
276 parse_array(int fd, int raid_type, char *array_str, struct array_info *info)
278 struct mfi_pd_info *pinfo;
285 for (count = 0; cp != NULL; count++) {
286 cp = strchr(cp, ',');
290 warnx("Invalid drive list '%s'", array_str);
296 /* Validate the number of drives for this array. */
297 if (count >= MAX_DRIVES_PER_ARRAY) {
298 warnx("Too many drives for a single array: max is %zu",
299 MAX_DRIVES_PER_ARRAY);
305 if (count % 2 != 0) {
306 warnx("RAID1 and RAID10 require an even number of "
307 "drives in each array");
314 warnx("RAID5 and RAID50 require at least 3 drives in "
322 warnx("RAID6 and RAID60 require at least 4 drives in "
329 /* Validate each drive. */
330 info->drives = calloc(count, sizeof(struct mfi_pd_info));
331 if (info->drives == NULL) {
332 warnx("malloc failed");
335 info->drive_count = count;
336 for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL;
338 error = mfi_lookup_drive(fd, cp, &device_id);
342 if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) {
344 warn("Failed to fetch drive info for drive %s", cp);
348 if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
349 warnx("Drive %u is not available", device_id);
358 * Find the next free array ref assuming that 'array_ref' is the last
359 * one used. 'array_ref' should be 0xffff for the initial test.
362 find_next_array(struct config_id_state *state)
366 /* Assume the current one is used. */
369 /* Find the next free one. */
370 for (i = 0; i < state->array_count; i++)
371 if (state->arrays[i] == state->array_ref)
373 return (state->array_ref);
377 * Find the next free volume ID assuming that 'target_id' is the last
378 * one used. 'target_id' should be 0xff for the initial test.
381 find_next_volume(struct config_id_state *state)
385 /* Assume the current one is used. */
388 /* Find the next free one. */
389 for (i = 0; i < state->log_drv_count; i++)
390 if (state->volumes[i] == state->target_id)
392 return (state->target_id);
395 /* Populate an array with drives. */
397 build_array(int fd, char *arrayp, struct array_info *array_info,
398 struct config_id_state *state, int verbose)
400 struct mfi_array *ar = (struct mfi_array *)arrayp;
403 ar->size = array_info->drives[0].coerced_size;
404 ar->num_drives = array_info->drive_count;
405 ar->array_ref = find_next_array(state);
406 for (i = 0; i < array_info->drive_count; i++) {
408 printf("Adding drive %u to array %u\n",
409 array_info->drives[i].ref.v.device_id,
411 if (ar->size > array_info->drives[i].coerced_size)
412 ar->size = array_info->drives[i].coerced_size;
413 ar->pd[i].ref = array_info->drives[i].ref;
414 ar->pd[i].fw_state = MFI_PD_STATE_ONLINE;
416 array_info->array = ar;
420 * Create a volume that spans one or more arrays.
423 build_volume(char *volumep, int narrays, struct array_info *arrays,
424 int raid_type, long stripe_size, struct config_id_state *state, int verbose)
426 struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
427 struct mfi_array *ar;
431 ld->properties.ld.v.target_id = find_next_volume(state);
432 ld->properties.ld.v.seq = 0;
433 ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
434 MR_LD_CACHE_WRITE_BACK;
435 ld->properties.access_policy = MFI_LD_ACCESS_RW;
436 ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED;
437 ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
438 MR_LD_CACHE_WRITE_BACK;
439 ld->properties.no_bgi = 0;
445 ld->params.primary_raid_level = DDF_RAID0;
446 ld->params.raid_level_qualifier = 0;
447 ld->params.secondary_raid_level = 0;
450 ld->params.primary_raid_level = DDF_RAID1;
451 ld->params.raid_level_qualifier = 0;
452 ld->params.secondary_raid_level = 0;
455 ld->params.primary_raid_level = DDF_RAID5;
456 ld->params.raid_level_qualifier = 3;
457 ld->params.secondary_raid_level = 0;
460 ld->params.primary_raid_level = DDF_RAID6;
461 ld->params.raid_level_qualifier = 3;
462 ld->params.secondary_raid_level = 0;
465 ld->params.primary_raid_level = DDF_CONCAT;
466 ld->params.raid_level_qualifier = 0;
467 ld->params.secondary_raid_level = 0;
470 ld->params.primary_raid_level = DDF_RAID1;
471 ld->params.raid_level_qualifier = 0;
472 ld->params.secondary_raid_level = 3; /* XXX? */
476 * XXX: This appears to work though the card's BIOS
477 * complains that the configuration is foreign. The
478 * BIOS setup does not allow for creation of RAID-50
479 * or RAID-60 arrays. The only nested array
480 * configuration it allows for is RAID-10.
482 ld->params.primary_raid_level = DDF_RAID5;
483 ld->params.raid_level_qualifier = 3;
484 ld->params.secondary_raid_level = 3; /* XXX? */
487 ld->params.primary_raid_level = DDF_RAID6;
488 ld->params.raid_level_qualifier = 3;
489 ld->params.secondary_raid_level = 3; /* XXX? */
494 * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use
495 * ffs() to simulate log2(stripe_size).
497 ld->params.stripe_size = ffs(stripe_size) - 1 - 9;
498 ld->params.num_drives = arrays[0].array->num_drives;
499 ld->params.span_depth = narrays;
500 ld->params.state = MFI_LD_STATE_OPTIMAL;
501 ld->params.init_state = MFI_LD_PARAMS_INIT_NO;
502 ld->params.is_consistent = 0;
505 for (i = 0; i < narrays; i++) {
506 ar = arrays[i].array;
508 printf("Adding array %u to volume %u\n", ar->array_ref,
509 ld->properties.ld.v.target_id);
510 ld->span[i].start_block = 0;
511 ld->span[i].num_blocks = ar->size;
512 ld->span[i].array_ref = ar->array_ref;
517 create_volume(int ac, char **av)
519 struct mfi_config_data *config;
520 struct mfi_array *ar;
521 struct mfi_ld_config *ld;
522 struct config_id_state state;
524 char *p, *cfg_arrays, *cfg_volumes;
525 int error, fd, i, raid_type;
526 int narrays, nvolumes, arrays_per_volume;
527 struct array_info *arrays;
535 * Backwards compat. Map 'create volume' to 'create' and
536 * 'create spare' to 'add'.
539 if (strcmp(av[1], "volume") == 0) {
542 } else if (strcmp(av[1], "spare") == 0) {
545 return (add_spare(ac, av));
550 warnx("create volume: volume type required");
555 fd = mfi_open(mfi_unit);
562 if (!mfi_reconfig_supported()) {
563 warnx("The current mfi(4) driver does not support "
564 "configuration changes.");
568 /* Lookup the RAID type first. */
570 for (i = 0; raid_type_table[i].name != NULL; i++)
571 if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
572 raid_type = raid_type_table[i].raid_type;
576 if (raid_type == -1) {
577 warnx("Unknown or unsupported volume type %s", av[1]);
581 /* Parse any options. */
587 stripe_size = 64 * 1024;
589 while ((ch = getopt(ac, av, "ds:v")) != -1) {
597 stripe_size = dehumanize(optarg);
598 if ((stripe_size < 512) || (!powerof2(stripe_size)))
599 stripe_size = 64 * 1024;
612 /* Parse all the arrays. */
615 warnx("At least one drive list is required");
625 warnx("Only one drive list can be specified");
633 warnx("RAID10, RAID50, and RAID60 require at least "
637 if (narrays > MFI_MAX_SPAN_DEPTH) {
638 warnx("Volume spans more than %d arrays",
644 arrays = calloc(narrays, sizeof(*arrays));
645 if (arrays == NULL) {
646 warnx("malloc failed");
649 for (i = 0; i < narrays; i++) {
650 error = parse_array(fd, raid_type, av[i], &arrays[i]);
659 for (i = 1; i < narrays; i++) {
660 if (arrays[i].drive_count != arrays[0].drive_count) {
661 warnx("All arrays must contain the same "
670 * Fetch the current config and build sorted lists of existing
671 * array and volume identifiers.
673 if (mfi_config_read(fd, &config) < 0) {
675 warn("Failed to read configuration");
678 p = (char *)config->array;
679 state.array_ref = 0xffff;
680 state.target_id = 0xff;
681 state.array_count = config->array_count;
682 if (config->array_count > 0) {
683 state.arrays = calloc(config->array_count, sizeof(int));
684 if (state.arrays == NULL) {
685 warnx("malloc failed");
688 for (i = 0; i < config->array_count; i++) {
689 ar = (struct mfi_array *)p;
690 state.arrays[i] = ar->array_ref;
691 p += config->array_size;
693 qsort(state.arrays, config->array_count, sizeof(int),
697 state.log_drv_count = config->log_drv_count;
698 if (config->log_drv_count) {
699 state.volumes = calloc(config->log_drv_count, sizeof(int));
700 if (state.volumes == NULL) {
701 warnx("malloc failed");
704 for (i = 0; i < config->log_drv_count; i++) {
705 ld = (struct mfi_ld_config *)p;
706 state.volumes[i] = ld->properties.ld.v.target_id;
707 p += config->log_drv_size;
709 qsort(state.volumes, config->log_drv_count, sizeof(int),
712 state.volumes = NULL;
715 /* Determine the size of the configuration we will build. */
723 /* Each volume spans a single array. */
729 /* A single volume spans multiple arrays. */
737 config_size = sizeof(struct mfi_config_data) +
738 sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays;
739 config = calloc(1, config_size);
740 if (config == NULL) {
741 warnx("malloc failed");
744 config->size = config_size;
745 config->array_count = narrays;
746 config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */
747 config->log_drv_count = nvolumes;
748 config->log_drv_size = sizeof(struct mfi_ld_config);
749 config->spares_count = 0;
750 config->spares_size = 40; /* XXX: Firmware hardcode */
751 cfg_arrays = (char *)config->array;
752 cfg_volumes = cfg_arrays + config->array_size * narrays;
754 /* Build the arrays. */
755 for (i = 0; i < narrays; i++) {
756 build_array(fd, cfg_arrays, &arrays[i], &state, verbose);
757 cfg_arrays += config->array_size;
760 /* Now build the volume(s). */
761 arrays_per_volume = narrays / nvolumes;
762 for (i = 0; i < nvolumes; i++) {
763 build_volume(cfg_volumes, arrays_per_volume,
764 &arrays[i * arrays_per_volume], raid_type, stripe_size,
766 cfg_volumes += config->log_drv_size;
771 dump_config(fd, config);
774 /* Send the new config to the controller. */
775 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size,
776 NULL, 0, NULL) < 0) {
778 warn("Failed to add volume");
784 if (state.log_drv_count > 0)
786 if (state.array_count > 0)
788 for (i = 0; i < narrays; i++)
789 free(arrays[i].drives);
795 MFI_COMMAND(top, create, create_volume);
798 delete_volume(int ac, char **av)
800 struct mfi_ld_info info;
802 uint8_t target_id, mbox[4];
805 * Backwards compat. Map 'delete volume' to 'delete' and
806 * 'delete spare' to 'remove'.
809 if (strcmp(av[1], "volume") == 0) {
812 } else if (strcmp(av[1], "spare") == 0) {
815 return (remove_spare(ac, av));
820 warnx("delete volume: volume required");
824 fd = mfi_open(mfi_unit);
831 if (!mfi_reconfig_supported()) {
832 warnx("The current mfi(4) driver does not support "
833 "configuration changes.");
837 if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
839 warn("Invalid volume %s", av[1]);
843 if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
845 warn("Failed to get info for volume %d", target_id);
849 if (mfi_volume_busy(fd, target_id)) {
850 warnx("Volume %s is busy and cannot be deleted",
851 mfi_volume_name(fd, target_id));
855 mbox_store_ldref(mbox, &info.ld_config.properties.ld);
856 if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox,
857 sizeof(mbox), NULL) < 0) {
859 warn("Failed to delete volume");
867 MFI_COMMAND(top, delete, delete_volume);
870 add_spare(int ac, char **av)
872 struct mfi_pd_info info;
873 struct mfi_config_data *config;
874 struct mfi_array *ar;
875 struct mfi_ld_config *ld;
876 struct mfi_spare *spare;
883 warnx("add spare: drive required");
887 fd = mfi_open(mfi_unit);
894 error = mfi_lookup_drive(fd, av[1], &device_id);
898 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
900 warn("Failed to fetch drive info");
904 if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
905 warnx("Drive %u is not available", device_id);
910 if (mfi_lookup_volume(fd, av[2], &target_id) < 0) {
912 warn("Invalid volume %s", av[2]);
917 if (mfi_config_read(fd, &config) < 0) {
919 warn("Failed to read configuration");
923 spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) *
924 config->array_count);
926 warnx("malloc failed");
929 bzero(spare, sizeof(struct mfi_spare));
930 spare->ref = info.ref;
933 /* Global spare backs all arrays. */
934 p = (char *)config->array;
935 for (i = 0; i < config->array_count; i++) {
936 ar = (struct mfi_array *)p;
937 if (ar->size > info.coerced_size) {
938 warnx("Spare isn't large enough for array %u",
942 p += config->array_size;
944 spare->array_count = 0;
947 * Dedicated spares only back the arrays for a
950 ld = mfi_config_lookup_volume(config, target_id);
952 warnx("Did not find volume %d", target_id);
956 spare->spare_type |= MFI_SPARE_DEDICATED;
957 spare->array_count = ld->params.span_depth;
958 for (i = 0; i < ld->params.span_depth; i++) {
959 ar = mfi_config_lookup_array(config,
960 ld->span[i].array_ref);
962 warnx("Missing array; inconsistent config?");
965 if (ar->size > info.coerced_size) {
966 warnx("Spare isn't large enough for array %u",
970 spare->array_ref[i] = ar->array_ref;
975 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare,
976 sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count,
977 NULL, 0, NULL) < 0) {
979 warn("Failed to assign spare");
987 MFI_COMMAND(top, add, add_spare);
990 remove_spare(int ac, char **av)
992 struct mfi_pd_info info;
998 warnx("remove spare: drive required");
1002 fd = mfi_open(mfi_unit);
1009 error = mfi_lookup_drive(fd, av[1], &device_id);
1013 /* Get the info for this drive. */
1014 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
1016 warn("Failed to fetch info for drive %u", device_id);
1020 if (info.fw_state != MFI_PD_STATE_HOT_SPARE) {
1021 warnx("Drive %u is not a hot spare", device_id);
1025 mbox_store_pdref(mbox, &info.ref);
1026 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox,
1027 sizeof(mbox), NULL) < 0) {
1029 warn("Failed to delete spare");
1037 MFI_COMMAND(top, remove, remove_spare);
1040 /* Display raw data about a config. */
1042 dump_config(int fd, struct mfi_config_data *config)
1044 struct mfi_array *ar;
1045 struct mfi_ld_config *ld;
1046 struct mfi_spare *sp;
1047 struct mfi_pd_info pinfo;
1053 "mfi%d Configuration (Debug): %d arrays, %d volumes, %d spares\n",
1054 mfi_unit, config->array_count, config->log_drv_count,
1055 config->spares_count);
1056 printf(" array size: %u\n", config->array_size);
1057 printf(" volume size: %u\n", config->log_drv_size);
1058 printf(" spare size: %u\n", config->spares_size);
1059 p = (char *)config->array;
1061 for (i = 0; i < config->array_count; i++) {
1062 ar = (struct mfi_array *)p;
1063 printf(" array %u of %u drives:\n", ar->array_ref,
1065 printf(" size = %ju\n", (uintmax_t)ar->size);
1066 for (j = 0; j < ar->num_drives; j++) {
1067 device_id = ar->pd[j].ref.v.device_id;
1068 if (device_id == 0xffff)
1069 printf(" drive MISSING\n");
1071 printf(" drive %u %s\n", device_id,
1072 mfi_pdstate(ar->pd[j].fw_state));
1073 if (mfi_pd_get_info(fd, device_id, &pinfo,
1075 printf(" raw size: %ju\n",
1076 (uintmax_t)pinfo.raw_size);
1077 printf(" non-coerced size: %ju\n",
1078 (uintmax_t)pinfo.non_coerced_size);
1079 printf(" coerced size: %ju\n",
1080 (uintmax_t)pinfo.coerced_size);
1084 p += config->array_size;
1087 for (i = 0; i < config->log_drv_count; i++) {
1088 ld = (struct mfi_ld_config *)p;
1089 printf(" volume %s ",
1090 mfi_volume_name(fd, ld->properties.ld.v.target_id));
1092 mfi_raid_level(ld->params.primary_raid_level,
1093 ld->params.secondary_raid_level),
1094 mfi_ldstate(ld->params.state));
1095 if (ld->properties.name[0] != '\0')
1096 printf(" <%s>", ld->properties.name);
1098 printf(" primary raid level: %u\n",
1099 ld->params.primary_raid_level);
1100 printf(" raid level qualifier: %u\n",
1101 ld->params.raid_level_qualifier);
1102 printf(" secondary raid level: %u\n",
1103 ld->params.secondary_raid_level);
1104 printf(" stripe size: %u\n", ld->params.stripe_size);
1105 printf(" num drives: %u\n", ld->params.num_drives);
1106 printf(" init state: %u\n", ld->params.init_state);
1107 printf(" consistent: %u\n", ld->params.is_consistent);
1108 printf(" no bgi: %u\n", ld->properties.no_bgi);
1109 printf(" spans:\n");
1110 for (j = 0; j < ld->params.span_depth; j++) {
1111 printf(" array %u @ ", ld->span[j].array_ref);
1112 printf("%ju : %ju\n",
1113 (uintmax_t)ld->span[j].start_block,
1114 (uintmax_t)ld->span[j].num_blocks);
1116 p += config->log_drv_size;
1119 for (i = 0; i < config->spares_count; i++) {
1120 sp = (struct mfi_spare *)p;
1121 printf(" %s spare %u ",
1122 sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" :
1123 "global", sp->ref.v.device_id);
1124 printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE));
1125 printf(" backs:\n");
1126 for (j = 0; j < sp->array_count; j++)
1127 printf(" array %u\n", sp->array_ref[j]);
1128 p += config->spares_size;
1133 debug_config(int ac, char **av)
1135 struct mfi_config_data *config;
1139 warnx("debug: extra arguments");
1143 fd = mfi_open(mfi_unit);
1150 /* Get the config from the controller. */
1151 if (mfi_config_read(fd, &config) < 0) {
1153 warn("Failed to get config");
1157 /* Dump out the configuration. */
1158 dump_config(fd, config);
1164 MFI_COMMAND(top, debug, debug_config);
1167 dump(int ac, char **av)
1169 struct mfi_config_data *config;
1175 warnx("dump: extra arguments");
1179 fd = mfi_open(mfi_unit);
1186 /* Get the stashed copy of the last dcmd from the driver. */
1187 snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit);
1188 if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) {
1190 warn("Failed to read debug command");
1191 if (error == ENOENT)
1196 config = malloc(len);
1197 if (config == NULL) {
1198 warnx("malloc failed");
1201 if (sysctlbyname(buf, config, &len, NULL, 0) < 0) {
1203 warn("Failed to read debug command");
1206 dump_config(fd, config);
1212 MFI_COMMAND(top, dump, dump);