2 * SPDX-License-Identifier: BSD-3-Clause
4 * Copyright (c) 2008, 2009 Yahoo!, Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The names of the authors may not be used to endorse or promote
16 * products derived from this software without specific prior written
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include <sys/param.h>
36 #include <sys/sysctl.h>
49 static int add_spare(int ac, char **av);
50 static int remove_spare(int ac, char **av);
53 dehumanize(const char *value)
60 iv = strtoq(value, &vtp, 0);
61 if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
82 mfi_config_read(int fd, struct mfi_config_data **configp)
84 return mfi_config_read_opcode(fd, MFI_DCMD_CFG_READ, configp, NULL, 0);
88 mfi_config_read_opcode(int fd, uint32_t opcode, struct mfi_config_data **configp,
89 uint8_t *mbox, size_t mboxlen)
91 struct mfi_config_data *config;
96 * Keep fetching the config in a loop until we have a large enough
97 * buffer to hold the entire configuration.
102 config = reallocf(config, config_size);
105 if (mfi_dcmd_command(fd, opcode, config,
106 config_size, mbox, mboxlen, NULL) < 0) {
113 if (config->size > config_size) {
114 config_size = config->size;
122 static struct mfi_array *
123 mfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
125 struct mfi_array *ar;
129 p = (char *)config->array;
130 for (i = 0; i < config->array_count; i++) {
131 ar = (struct mfi_array *)p;
132 if (ar->array_ref == array_ref)
134 p += config->array_size;
140 static struct mfi_ld_config *
141 mfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
143 struct mfi_ld_config *ld;
147 p = (char *)config->array + config->array_count * config->array_size;
148 for (i = 0; i < config->log_drv_count; i++) {
149 ld = (struct mfi_ld_config *)p;
150 if (ld->properties.ld.v.target_id == target_id)
152 p += config->log_drv_size;
159 clear_config(int ac __unused, char **av __unused)
161 struct mfi_ld_list list;
165 fd = mfi_open(mfi_unit, O_RDWR);
172 if (!mfi_reconfig_supported()) {
173 warnx("The current mfi(4) driver does not support "
174 "configuration changes.");
179 if (mfi_ld_get_list(fd, &list, NULL) < 0) {
181 warn("Failed to get volume list");
186 for (i = 0; i < list.ld_count; i++) {
187 if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) {
188 warnx("Volume %s is busy and cannot be deleted",
189 mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
196 "Are you sure you wish to clear the configuration on mfi%u? [y/N] ",
199 if (ch != 'y' && ch != 'Y') {
200 printf("\nAborting\n");
205 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) {
207 warn("Failed to clear configuration");
212 printf("mfi%d: Configuration cleared\n", mfi_unit);
217 MFI_COMMAND(top, clear, clear_config);
219 #define MAX_DRIVES_PER_ARRAY MFI_MAX_ROW_SIZE
220 #define MFI_ARRAY_SIZE sizeof(struct mfi_array)
233 compare_int(const void *one, const void *two)
237 first = *(const int *)one;
238 second = *(const int *)two;
240 return (first - second);
243 static struct raid_type_entry {
246 } raid_type_table[] = {
247 { "raid0", RT_RAID0 },
248 { "raid-0", RT_RAID0 },
249 { "raid1", RT_RAID1 },
250 { "raid-1", RT_RAID1 },
251 { "mirror", RT_RAID1 },
252 { "raid5", RT_RAID5 },
253 { "raid-5", RT_RAID5 },
254 { "raid6", RT_RAID6 },
255 { "raid-6", RT_RAID6 },
257 { "concat", RT_CONCAT },
258 { "raid10", RT_RAID10 },
259 { "raid1+0", RT_RAID10 },
260 { "raid-10", RT_RAID10 },
261 { "raid-1+0", RT_RAID10 },
262 { "raid50", RT_RAID50 },
263 { "raid5+0", RT_RAID50 },
264 { "raid-50", RT_RAID50 },
265 { "raid-5+0", RT_RAID50 },
266 { "raid60", RT_RAID60 },
267 { "raid6+0", RT_RAID60 },
268 { "raid-60", RT_RAID60 },
269 { "raid-6+0", RT_RAID60 },
273 struct config_id_state {
284 struct mfi_pd_info *drives;
285 struct mfi_array *array;
288 /* Parse a comma-separated list of drives for an array. */
290 parse_array(int fd, int raid_type, char *array_str, struct array_info *info)
292 struct mfi_pd_info *pinfo;
299 for (count = 0; cp != NULL; count++) {
300 cp = strchr(cp, ',');
304 warnx("Invalid drive list '%s'", array_str);
310 /* Validate the number of drives for this array. */
311 if (count >= MAX_DRIVES_PER_ARRAY) {
312 warnx("Too many drives for a single array: max is %d",
313 MAX_DRIVES_PER_ARRAY);
319 if (count % 2 != 0) {
320 warnx("RAID1 and RAID10 require an even number of "
321 "drives in each array");
328 warnx("RAID5 and RAID50 require at least 3 drives in "
336 warnx("RAID6 and RAID60 require at least 4 drives in "
343 /* Validate each drive. */
344 info->drives = calloc(count, sizeof(struct mfi_pd_info));
345 if (info->drives == NULL) {
346 warnx("malloc failed");
349 info->drive_count = count;
350 for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL;
352 error = mfi_lookup_drive(fd, cp, &device_id);
359 if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) {
361 warn("Failed to fetch drive info for drive %s", cp);
367 if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
368 warnx("Drive %u is not available", device_id);
374 if (pinfo->state.ddf.v.pd_type.is_foreign) {
375 warnx("Drive %u is foreign", device_id);
386 * Find the next free array ref assuming that 'array_ref' is the last
387 * one used. 'array_ref' should be 0xffff for the initial test.
390 find_next_array(struct config_id_state *state)
394 /* Assume the current one is used. */
397 /* Find the next free one. */
398 for (i = 0; i < state->array_count; i++)
399 if (state->arrays[i] == state->array_ref)
401 return (state->array_ref);
405 * Find the next free volume ID assuming that 'target_id' is the last
406 * one used. 'target_id' should be 0xff for the initial test.
409 find_next_volume(struct config_id_state *state)
413 /* Assume the current one is used. */
416 /* Find the next free one. */
417 for (i = 0; i < state->log_drv_count; i++)
418 if (state->volumes[i] == state->target_id)
420 return (state->target_id);
423 /* Populate an array with drives. */
425 build_array(int fd __unused, char *arrayp, struct array_info *array_info,
426 struct config_id_state *state, int verbose)
428 struct mfi_array *ar = (struct mfi_array *)arrayp;
431 ar->size = array_info->drives[0].coerced_size;
432 ar->num_drives = array_info->drive_count;
433 ar->array_ref = find_next_array(state);
434 for (i = 0; i < array_info->drive_count; i++) {
436 printf("Adding drive %s to array %u\n",
438 array_info->drives[i].ref.v.device_id,
439 MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS),
441 if (ar->size > array_info->drives[i].coerced_size)
442 ar->size = array_info->drives[i].coerced_size;
443 ar->pd[i].ref = array_info->drives[i].ref;
444 ar->pd[i].fw_state = MFI_PD_STATE_ONLINE;
446 array_info->array = ar;
450 * Create a volume that spans one or more arrays.
453 build_volume(char *volumep, int narrays, struct array_info *arrays,
454 int raid_type, long stripe_size, struct config_id_state *state, int verbose)
456 struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
457 struct mfi_array *ar;
461 ld->properties.ld.v.target_id = find_next_volume(state);
462 ld->properties.ld.v.seq = 0;
463 ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
464 MR_LD_CACHE_WRITE_BACK;
465 ld->properties.access_policy = MFI_LD_ACCESS_RW;
466 ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED;
467 ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
468 MR_LD_CACHE_WRITE_BACK;
469 ld->properties.no_bgi = 0;
475 ld->params.primary_raid_level = DDF_RAID0;
476 ld->params.raid_level_qualifier = 0;
477 ld->params.secondary_raid_level = 0;
480 ld->params.primary_raid_level = DDF_RAID1;
481 ld->params.raid_level_qualifier = 0;
482 ld->params.secondary_raid_level = 0;
485 ld->params.primary_raid_level = DDF_RAID5;
486 ld->params.raid_level_qualifier = 3;
487 ld->params.secondary_raid_level = 0;
490 ld->params.primary_raid_level = DDF_RAID6;
491 ld->params.raid_level_qualifier = 3;
492 ld->params.secondary_raid_level = 0;
495 ld->params.primary_raid_level = DDF_CONCAT;
496 ld->params.raid_level_qualifier = 0;
497 ld->params.secondary_raid_level = 0;
500 ld->params.primary_raid_level = DDF_RAID1;
501 ld->params.raid_level_qualifier = 0;
502 ld->params.secondary_raid_level = 3; /* XXX? */
506 * XXX: This appears to work though the card's BIOS
507 * complains that the configuration is foreign. The
508 * BIOS setup does not allow for creation of RAID-50
509 * or RAID-60 arrays. The only nested array
510 * configuration it allows for is RAID-10.
512 ld->params.primary_raid_level = DDF_RAID5;
513 ld->params.raid_level_qualifier = 3;
514 ld->params.secondary_raid_level = 3; /* XXX? */
517 ld->params.primary_raid_level = DDF_RAID6;
518 ld->params.raid_level_qualifier = 3;
519 ld->params.secondary_raid_level = 3; /* XXX? */
524 * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use
525 * ffs() to simulate log2(stripe_size).
527 ld->params.stripe_size = ffs(stripe_size) - 1 - 9;
528 ld->params.num_drives = arrays[0].array->num_drives;
529 ld->params.span_depth = narrays;
530 ld->params.state = MFI_LD_STATE_OPTIMAL;
531 ld->params.init_state = MFI_LD_PARAMS_INIT_NO;
532 ld->params.is_consistent = 0;
535 for (i = 0; i < narrays; i++) {
536 ar = arrays[i].array;
538 printf("Adding array %u to volume %u\n", ar->array_ref,
539 ld->properties.ld.v.target_id);
540 ld->span[i].start_block = 0;
541 ld->span[i].num_blocks = ar->size;
542 ld->span[i].array_ref = ar->array_ref;
547 create_volume(int ac, char **av)
549 struct mfi_config_data *config;
550 struct mfi_array *ar;
551 struct mfi_ld_config *ld;
552 struct config_id_state state;
554 char *p, *cfg_arrays, *cfg_volumes;
555 int error, fd, i, raid_type;
556 int narrays, nvolumes, arrays_per_volume;
557 struct array_info *arrays;
565 * Backwards compat. Map 'create volume' to 'create' and
566 * 'create spare' to 'add'.
569 if (strcmp(av[1], "volume") == 0) {
572 } else if (strcmp(av[1], "spare") == 0) {
575 return (add_spare(ac, av));
580 warnx("create volume: volume type required");
584 bzero(&state, sizeof(state));
590 fd = mfi_open(mfi_unit, O_RDWR);
597 if (!mfi_reconfig_supported()) {
598 warnx("The current mfi(4) driver does not support "
599 "configuration changes.");
604 /* Lookup the RAID type first. */
606 for (i = 0; raid_type_table[i].name != NULL; i++)
607 if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
608 raid_type = raid_type_table[i].raid_type;
612 if (raid_type == -1) {
613 warnx("Unknown or unsupported volume type %s", av[1]);
618 /* Parse any options. */
624 stripe_size = 64 * 1024;
626 while ((ch = getopt(ac, av, "ds:v")) != -1) {
634 stripe_size = dehumanize(optarg);
635 if ((stripe_size < 512) || (!powerof2(stripe_size)))
636 stripe_size = 64 * 1024;
650 /* Parse all the arrays. */
653 warnx("At least one drive list is required");
664 warnx("Only one drive list can be specified");
673 warnx("RAID10, RAID50, and RAID60 require at least "
678 if (narrays > MFI_MAX_SPAN_DEPTH) {
679 warnx("Volume spans more than %d arrays",
686 arrays = calloc(narrays, sizeof(*arrays));
687 if (arrays == NULL) {
688 warnx("malloc failed");
692 for (i = 0; i < narrays; i++) {
693 error = parse_array(fd, raid_type, av[i], &arrays[i]);
702 for (i = 1; i < narrays; i++) {
703 if (arrays[i].drive_count != arrays[0].drive_count) {
704 warnx("All arrays must contain the same "
714 * Fetch the current config and build sorted lists of existing
715 * array and volume identifiers.
717 if (mfi_config_read(fd, &config) < 0) {
719 warn("Failed to read configuration");
722 p = (char *)config->array;
723 state.array_ref = 0xffff;
724 state.target_id = 0xff;
725 state.array_count = config->array_count;
726 if (config->array_count > 0) {
727 state.arrays = calloc(config->array_count, sizeof(int));
728 if (state.arrays == NULL) {
729 warnx("malloc failed");
733 for (i = 0; i < config->array_count; i++) {
734 ar = (struct mfi_array *)p;
735 state.arrays[i] = ar->array_ref;
736 p += config->array_size;
738 qsort(state.arrays, config->array_count, sizeof(int),
742 state.log_drv_count = config->log_drv_count;
743 if (config->log_drv_count) {
744 state.volumes = calloc(config->log_drv_count, sizeof(int));
745 if (state.volumes == NULL) {
746 warnx("malloc failed");
750 for (i = 0; i < config->log_drv_count; i++) {
751 ld = (struct mfi_ld_config *)p;
752 state.volumes[i] = ld->properties.ld.v.target_id;
753 p += config->log_drv_size;
755 qsort(state.volumes, config->log_drv_count, sizeof(int),
758 state.volumes = NULL;
761 /* Determine the size of the configuration we will build. */
769 /* Each volume spans a single array. */
775 /* A single volume spans multiple arrays. */
783 config_size = sizeof(struct mfi_config_data) +
784 sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays;
785 config = calloc(1, config_size);
786 if (config == NULL) {
787 warnx("malloc failed");
791 config->size = config_size;
792 config->array_count = narrays;
793 config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */
794 config->log_drv_count = nvolumes;
795 config->log_drv_size = sizeof(struct mfi_ld_config);
796 config->spares_count = 0;
797 config->spares_size = 40; /* XXX: Firmware hardcode */
798 cfg_arrays = (char *)config->array;
799 cfg_volumes = cfg_arrays + config->array_size * narrays;
801 /* Build the arrays. */
802 for (i = 0; i < narrays; i++) {
803 build_array(fd, cfg_arrays, &arrays[i], &state, verbose);
804 cfg_arrays += config->array_size;
807 /* Now build the volume(s). */
808 arrays_per_volume = narrays / nvolumes;
809 for (i = 0; i < nvolumes; i++) {
810 build_volume(cfg_volumes, arrays_per_volume,
811 &arrays[i * arrays_per_volume], raid_type, stripe_size,
813 cfg_volumes += config->log_drv_size;
818 dump_config(fd, config, NULL);
821 /* Send the new config to the controller. */
822 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size,
823 NULL, 0, NULL) < 0) {
825 warn("Failed to add volume");
834 if (arrays != NULL) {
835 for (i = 0; i < narrays; i++)
836 free(arrays[i].drives);
843 MFI_COMMAND(top, create, create_volume);
846 delete_volume(int ac, char **av)
848 struct mfi_ld_info info;
850 uint8_t target_id, mbox[4];
853 * Backwards compat. Map 'delete volume' to 'delete' and
854 * 'delete spare' to 'remove'.
857 if (strcmp(av[1], "volume") == 0) {
860 } else if (strcmp(av[1], "spare") == 0) {
863 return (remove_spare(ac, av));
868 warnx("delete volume: volume required");
872 fd = mfi_open(mfi_unit, O_RDWR);
879 if (!mfi_reconfig_supported()) {
880 warnx("The current mfi(4) driver does not support "
881 "configuration changes.");
886 if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
888 warn("Invalid volume %s", av[1]);
893 if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
895 warn("Failed to get info for volume %d", target_id);
900 if (mfi_volume_busy(fd, target_id)) {
901 warnx("Volume %s is busy and cannot be deleted",
902 mfi_volume_name(fd, target_id));
907 mbox_store_ldref(mbox, &info.ld_config.properties.ld);
908 if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox,
909 sizeof(mbox), NULL) < 0) {
911 warn("Failed to delete volume");
920 MFI_COMMAND(top, delete, delete_volume);
923 add_spare(int ac, char **av)
925 struct mfi_pd_info info;
926 struct mfi_config_data *config;
927 struct mfi_array *ar;
928 struct mfi_ld_config *ld;
929 struct mfi_spare *spare;
936 warnx("add spare: drive required");
940 fd = mfi_open(mfi_unit, O_RDWR);
949 error = mfi_lookup_drive(fd, av[1], &device_id);
953 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
955 warn("Failed to fetch drive info");
959 if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
960 warnx("Drive %u is not available", device_id);
966 if (mfi_lookup_volume(fd, av[2], &target_id) < 0) {
968 warn("Invalid volume %s", av[2]);
973 if (mfi_config_read(fd, &config) < 0) {
975 warn("Failed to read configuration");
979 spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) *
980 config->array_count);
982 warnx("malloc failed");
986 bzero(spare, sizeof(struct mfi_spare));
987 spare->ref = info.ref;
990 /* Global spare backs all arrays. */
991 p = (char *)config->array;
992 for (i = 0; i < config->array_count; i++) {
993 ar = (struct mfi_array *)p;
994 if (ar->size > info.coerced_size) {
995 warnx("Spare isn't large enough for array %u",
1000 p += config->array_size;
1002 spare->array_count = 0;
1005 * Dedicated spares only back the arrays for a
1008 ld = mfi_config_lookup_volume(config, target_id);
1010 warnx("Did not find volume %d", target_id);
1015 spare->spare_type |= MFI_SPARE_DEDICATED;
1016 spare->array_count = ld->params.span_depth;
1017 for (i = 0; i < ld->params.span_depth; i++) {
1018 ar = mfi_config_lookup_array(config,
1019 ld->span[i].array_ref);
1021 warnx("Missing array; inconsistent config?");
1025 if (ar->size > info.coerced_size) {
1026 warnx("Spare isn't large enough for array %u",
1031 spare->array_ref[i] = ar->array_ref;
1035 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare,
1036 sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count,
1037 NULL, 0, NULL) < 0) {
1039 warn("Failed to assign spare");
1050 MFI_COMMAND(top, add, add_spare);
1053 remove_spare(int ac, char **av)
1055 struct mfi_pd_info info;
1061 warnx("remove spare: drive required");
1065 fd = mfi_open(mfi_unit, O_RDWR);
1072 error = mfi_lookup_drive(fd, av[1], &device_id);
1078 /* Get the info for this drive. */
1079 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
1081 warn("Failed to fetch info for drive %u", device_id);
1086 if (info.fw_state != MFI_PD_STATE_HOT_SPARE) {
1087 warnx("Drive %u is not a hot spare", device_id);
1092 mbox_store_pdref(mbox, &info.ref);
1093 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox,
1094 sizeof(mbox), NULL) < 0) {
1096 warn("Failed to delete spare");
1105 MFI_COMMAND(top, remove, remove_spare);
1107 /* Display raw data about a config. */
1109 dump_config(int fd, struct mfi_config_data *config, const char *msg_prefix)
1111 struct mfi_array *ar;
1112 struct mfi_ld_config *ld;
1113 struct mfi_spare *sp;
1114 struct mfi_pd_info pinfo;
1119 if (NULL == msg_prefix)
1120 msg_prefix = "Configuration (Debug)";
1123 "mfi%d %s: %d arrays, %d volumes, %d spares\n", mfi_unit,
1124 msg_prefix, config->array_count, config->log_drv_count,
1125 config->spares_count);
1126 printf(" array size: %u\n", config->array_size);
1127 printf(" volume size: %u\n", config->log_drv_size);
1128 printf(" spare size: %u\n", config->spares_size);
1129 p = (char *)config->array;
1131 for (i = 0; i < config->array_count; i++) {
1132 ar = (struct mfi_array *)p;
1133 printf(" array %u of %u drives:\n", ar->array_ref,
1135 printf(" size = %ju\n", (uintmax_t)ar->size);
1136 for (j = 0; j < ar->num_drives; j++) {
1137 device_id = ar->pd[j].ref.v.device_id;
1138 if (device_id == 0xffff)
1139 printf(" drive MISSING\n");
1141 printf(" drive %u %s\n", device_id,
1142 mfi_pdstate(ar->pd[j].fw_state));
1143 if (mfi_pd_get_info(fd, device_id, &pinfo,
1145 printf(" raw size: %ju\n",
1146 (uintmax_t)pinfo.raw_size);
1147 printf(" non-coerced size: %ju\n",
1148 (uintmax_t)pinfo.non_coerced_size);
1149 printf(" coerced size: %ju\n",
1150 (uintmax_t)pinfo.coerced_size);
1154 p += config->array_size;
1157 for (i = 0; i < config->log_drv_count; i++) {
1158 ld = (struct mfi_ld_config *)p;
1159 printf(" volume %s ",
1160 mfi_volume_name(fd, ld->properties.ld.v.target_id));
1162 mfi_raid_level(ld->params.primary_raid_level,
1163 ld->params.secondary_raid_level),
1164 mfi_ldstate(ld->params.state));
1165 if (ld->properties.name[0] != '\0')
1166 printf(" <%s>", ld->properties.name);
1168 printf(" primary raid level: %u\n",
1169 ld->params.primary_raid_level);
1170 printf(" raid level qualifier: %u\n",
1171 ld->params.raid_level_qualifier);
1172 printf(" secondary raid level: %u\n",
1173 ld->params.secondary_raid_level);
1174 printf(" stripe size: %u\n", ld->params.stripe_size);
1175 printf(" num drives: %u\n", ld->params.num_drives);
1176 printf(" init state: %u\n", ld->params.init_state);
1177 printf(" consistent: %u\n", ld->params.is_consistent);
1178 printf(" no bgi: %u\n", ld->properties.no_bgi);
1179 printf(" spans:\n");
1180 for (j = 0; j < ld->params.span_depth; j++) {
1181 printf(" array %u @ ", ld->span[j].array_ref);
1182 printf("%ju : %ju\n",
1183 (uintmax_t)ld->span[j].start_block,
1184 (uintmax_t)ld->span[j].num_blocks);
1186 p += config->log_drv_size;
1189 for (i = 0; i < config->spares_count; i++) {
1190 sp = (struct mfi_spare *)p;
1191 printf(" %s spare %u ",
1192 sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" :
1193 "global", sp->ref.v.device_id);
1194 printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE));
1195 printf(" backs:\n");
1196 for (j = 0; j < sp->array_count; j++)
1197 printf(" array %u\n", sp->array_ref[j]);
1198 p += config->spares_size;
1204 debug_config(int ac, char **av)
1206 struct mfi_config_data *config;
1210 warnx("debug: extra arguments");
1214 fd = mfi_open(mfi_unit, O_RDWR);
1221 /* Get the config from the controller. */
1222 if (mfi_config_read(fd, &config) < 0) {
1224 warn("Failed to get config");
1229 /* Dump out the configuration. */
1230 dump_config(fd, config, NULL);
1236 MFI_COMMAND(top, debug, debug_config);
1239 dump(int ac, char **av)
1241 struct mfi_config_data *config;
1247 warnx("dump: extra arguments");
1251 fd = mfi_open(mfi_unit, O_RDWR);
1258 /* Get the stashed copy of the last dcmd from the driver. */
1259 snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit);
1260 if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) {
1262 warn("Failed to read debug command");
1263 if (error == ENOENT)
1269 config = malloc(len);
1270 if (config == NULL) {
1271 warnx("malloc failed");
1275 if (sysctlbyname(buf, config, &len, NULL, 0) < 0) {
1277 warn("Failed to read debug command");
1282 dump_config(fd, config, NULL);
1288 MFI_COMMAND(top, dump, dump);