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>
50 static int add_spare(int ac, char **av);
51 static int remove_spare(int ac, char **av);
54 dehumanize(const char *value)
61 iv = strtoq(value, &vtp, 0);
62 if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
83 mfi_config_read(int fd, struct mfi_config_data **configp)
85 return mfi_config_read_opcode(fd, MFI_DCMD_CFG_READ, configp, NULL, 0);
89 mfi_config_read_opcode(int fd, uint32_t opcode, struct mfi_config_data **configp,
90 uint8_t *mbox, size_t mboxlen)
92 struct mfi_config_data *config;
97 * Keep fetching the config in a loop until we have a large enough
98 * buffer to hold the entire configuration.
103 config = reallocf(config, config_size);
106 if (mfi_dcmd_command(fd, opcode, config,
107 config_size, mbox, mboxlen, NULL) < 0) {
114 if (config->size > config_size) {
115 config_size = config->size;
123 static struct mfi_array *
124 mfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
126 struct mfi_array *ar;
130 p = (char *)config->array;
131 for (i = 0; i < config->array_count; i++) {
132 ar = (struct mfi_array *)p;
133 if (ar->array_ref == array_ref)
135 p += config->array_size;
141 static struct mfi_ld_config *
142 mfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
144 struct mfi_ld_config *ld;
148 p = (char *)config->array + config->array_count * config->array_size;
149 for (i = 0; i < config->log_drv_count; i++) {
150 ld = (struct mfi_ld_config *)p;
151 if (ld->properties.ld.v.target_id == target_id)
153 p += config->log_drv_size;
160 clear_config(int ac __unused, char **av __unused)
162 struct mfi_ld_list list;
166 fd = mfi_open(mfi_device, O_RDWR);
173 if (!mfi_reconfig_supported(mfi_device)) {
174 warnx("The current %s driver does not support "
175 "configuration changes.", mfi_device);
180 if (mfi_ld_get_list(fd, &list, NULL) < 0) {
182 warn("Failed to get volume list");
187 for (i = 0; i < list.ld_count; i++) {
188 if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) {
189 warnx("Volume %s is busy and cannot be deleted",
190 mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
197 "Are you sure you wish to clear the configuration on %s? [y/N] ",
200 if (ch != 'y' && ch != 'Y') {
201 printf("\nAborting\n");
206 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) {
208 warn("Failed to clear configuration");
213 printf("%s: Configuration cleared\n", mfi_device);
218 MFI_COMMAND(top, clear, clear_config);
220 #define MAX_DRIVES_PER_ARRAY MFI_MAX_ROW_SIZE
221 #define MFI_ARRAY_SIZE sizeof(struct mfi_array)
234 compare_int(const void *one, const void *two)
238 first = *(const int *)one;
239 second = *(const int *)two;
241 return (first - second);
244 static struct raid_type_entry {
247 } raid_type_table[] = {
248 { "raid0", RT_RAID0 },
249 { "raid-0", RT_RAID0 },
250 { "raid1", RT_RAID1 },
251 { "raid-1", RT_RAID1 },
252 { "mirror", RT_RAID1 },
253 { "raid5", RT_RAID5 },
254 { "raid-5", RT_RAID5 },
255 { "raid6", RT_RAID6 },
256 { "raid-6", RT_RAID6 },
258 { "concat", RT_CONCAT },
259 { "raid10", RT_RAID10 },
260 { "raid1+0", RT_RAID10 },
261 { "raid-10", RT_RAID10 },
262 { "raid-1+0", RT_RAID10 },
263 { "raid50", RT_RAID50 },
264 { "raid5+0", RT_RAID50 },
265 { "raid-50", RT_RAID50 },
266 { "raid-5+0", RT_RAID50 },
267 { "raid60", RT_RAID60 },
268 { "raid6+0", RT_RAID60 },
269 { "raid-60", RT_RAID60 },
270 { "raid-6+0", RT_RAID60 },
274 struct config_id_state {
285 struct mfi_pd_info *drives;
286 struct mfi_array *array;
289 /* Parse a comma-separated list of drives for an array. */
291 parse_array(int fd, int raid_type, char *array_str, struct array_info *info)
293 struct mfi_pd_info *pinfo;
300 for (count = 0; cp != NULL; count++) {
301 cp = strchr(cp, ',');
305 warnx("Invalid drive list '%s'", array_str);
311 /* Validate the number of drives for this array. */
312 if (count >= MAX_DRIVES_PER_ARRAY) {
313 warnx("Too many drives for a single array: max is %d",
314 MAX_DRIVES_PER_ARRAY);
320 if (count % 2 != 0) {
321 warnx("RAID1 and RAID10 require an even number of "
322 "drives in each array");
329 warnx("RAID5 and RAID50 require at least 3 drives in "
337 warnx("RAID6 and RAID60 require at least 4 drives in "
344 /* Validate each drive. */
345 info->drives = calloc(count, sizeof(struct mfi_pd_info));
346 if (info->drives == NULL) {
347 warnx("malloc failed");
350 info->drive_count = count;
351 for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL;
353 error = mfi_lookup_drive(fd, cp, &device_id);
360 if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) {
362 warn("Failed to fetch drive info for drive %s", cp);
368 if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
369 warnx("Drive %u is not available", device_id);
375 if (pinfo->state.ddf.v.pd_type.is_foreign) {
376 warnx("Drive %u is foreign", device_id);
387 * Find the next free array ref assuming that 'array_ref' is the last
388 * one used. 'array_ref' should be 0xffff for the initial test.
391 find_next_array(struct config_id_state *state)
395 /* Assume the current one is used. */
398 /* Find the next free one. */
399 for (i = 0; i < state->array_count; i++)
400 if (state->arrays[i] == state->array_ref)
402 return (state->array_ref);
406 * Find the next free volume ID assuming that 'target_id' is the last
407 * one used. 'target_id' should be 0xff for the initial test.
410 find_next_volume(struct config_id_state *state)
414 /* Assume the current one is used. */
417 /* Find the next free one. */
418 for (i = 0; i < state->log_drv_count; i++)
419 if (state->volumes[i] == state->target_id)
421 return (state->target_id);
424 /* Populate an array with drives. */
426 build_array(int fd __unused, char *arrayp, struct array_info *array_info,
427 struct config_id_state *state, int verbose)
429 struct mfi_array *ar = (struct mfi_array *)arrayp;
432 ar->size = array_info->drives[0].coerced_size;
433 ar->num_drives = array_info->drive_count;
434 ar->array_ref = find_next_array(state);
435 for (i = 0; i < array_info->drive_count; i++) {
437 printf("Adding drive %s to array %u\n",
439 array_info->drives[i].ref.v.device_id,
440 MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS),
442 if (ar->size > array_info->drives[i].coerced_size)
443 ar->size = array_info->drives[i].coerced_size;
444 ar->pd[i].ref = array_info->drives[i].ref;
445 ar->pd[i].fw_state = MFI_PD_STATE_ONLINE;
447 array_info->array = ar;
451 * Create a volume that spans one or more arrays.
454 build_volume(char *volumep, int narrays, struct array_info *arrays,
455 int raid_type, long stripe_size, struct config_id_state *state, int verbose)
457 struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
458 struct mfi_array *ar;
462 ld->properties.ld.v.target_id = find_next_volume(state);
463 ld->properties.ld.v.seq = 0;
464 ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
465 MR_LD_CACHE_WRITE_BACK;
466 ld->properties.access_policy = MFI_LD_ACCESS_RW;
467 ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED;
468 ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
469 MR_LD_CACHE_WRITE_BACK;
470 ld->properties.no_bgi = 0;
476 ld->params.primary_raid_level = DDF_RAID0;
477 ld->params.raid_level_qualifier = 0;
478 ld->params.secondary_raid_level = 0;
481 ld->params.primary_raid_level = DDF_RAID1;
482 ld->params.raid_level_qualifier = 0;
483 ld->params.secondary_raid_level = 0;
486 ld->params.primary_raid_level = DDF_RAID5;
487 ld->params.raid_level_qualifier = 3;
488 ld->params.secondary_raid_level = 0;
491 ld->params.primary_raid_level = DDF_RAID6;
492 ld->params.raid_level_qualifier = 3;
493 ld->params.secondary_raid_level = 0;
496 ld->params.primary_raid_level = DDF_CONCAT;
497 ld->params.raid_level_qualifier = 0;
498 ld->params.secondary_raid_level = 0;
501 ld->params.primary_raid_level = DDF_RAID1;
502 ld->params.raid_level_qualifier = 0;
503 ld->params.secondary_raid_level = 3; /* XXX? */
507 * XXX: This appears to work though the card's BIOS
508 * complains that the configuration is foreign. The
509 * BIOS setup does not allow for creation of RAID-50
510 * or RAID-60 arrays. The only nested array
511 * configuration it allows for is RAID-10.
513 ld->params.primary_raid_level = DDF_RAID5;
514 ld->params.raid_level_qualifier = 3;
515 ld->params.secondary_raid_level = 3; /* XXX? */
518 ld->params.primary_raid_level = DDF_RAID6;
519 ld->params.raid_level_qualifier = 3;
520 ld->params.secondary_raid_level = 3; /* XXX? */
525 * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use
526 * ffs() to simulate log2(stripe_size).
528 ld->params.stripe_size = ffs(stripe_size) - 1 - 9;
529 ld->params.num_drives = arrays[0].array->num_drives;
530 ld->params.span_depth = narrays;
531 ld->params.state = MFI_LD_STATE_OPTIMAL;
532 ld->params.init_state = MFI_LD_PARAMS_INIT_NO;
533 ld->params.is_consistent = 0;
536 for (i = 0; i < narrays; i++) {
537 ar = arrays[i].array;
539 printf("Adding array %u to volume %u\n", ar->array_ref,
540 ld->properties.ld.v.target_id);
541 ld->span[i].start_block = 0;
542 ld->span[i].num_blocks = ar->size;
543 ld->span[i].array_ref = ar->array_ref;
548 create_volume(int ac, char **av)
550 struct mfi_config_data *config;
551 struct mfi_array *ar;
552 struct mfi_ld_config *ld;
553 struct config_id_state state;
555 char *p, *cfg_arrays, *cfg_volumes;
556 int error, fd, i, raid_type;
557 int narrays, nvolumes, arrays_per_volume;
558 struct array_info *arrays;
566 * Backwards compat. Map 'create volume' to 'create' and
567 * 'create spare' to 'add'.
570 if (strcmp(av[1], "volume") == 0) {
573 } else if (strcmp(av[1], "spare") == 0) {
576 return (add_spare(ac, av));
581 warnx("create volume: volume type required");
585 bzero(&state, sizeof(state));
591 fd = mfi_open(mfi_device, O_RDWR);
598 if (!mfi_reconfig_supported(mfi_device)) {
599 warnx("The current %s(4) driver does not support "
600 "configuration changes.", mfi_device);
605 /* Lookup the RAID type first. */
607 for (i = 0; raid_type_table[i].name != NULL; i++)
608 if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
609 raid_type = raid_type_table[i].raid_type;
613 if (raid_type == -1) {
614 warnx("Unknown or unsupported volume type %s", av[1]);
619 /* Parse any options. */
625 stripe_size = 64 * 1024;
627 while ((ch = getopt(ac, av, "ds:v")) != -1) {
635 stripe_size = dehumanize(optarg);
636 if ((stripe_size < 512) || (!powerof2(stripe_size)))
637 stripe_size = 64 * 1024;
651 /* Parse all the arrays. */
654 warnx("At least one drive list is required");
665 warnx("Only one drive list can be specified");
674 warnx("RAID10, RAID50, and RAID60 require at least "
679 if (narrays > MFI_MAX_SPAN_DEPTH) {
680 warnx("Volume spans more than %d arrays",
687 arrays = calloc(narrays, sizeof(*arrays));
688 if (arrays == NULL) {
689 warnx("malloc failed");
693 for (i = 0; i < narrays; i++) {
694 error = parse_array(fd, raid_type, av[i], &arrays[i]);
703 for (i = 1; i < narrays; i++) {
704 if (arrays[i].drive_count != arrays[0].drive_count) {
705 warnx("All arrays must contain the same "
715 * Fetch the current config and build sorted lists of existing
716 * array and volume identifiers.
718 if (mfi_config_read(fd, &config) < 0) {
720 warn("Failed to read configuration");
723 p = (char *)config->array;
724 state.array_ref = 0xffff;
725 state.target_id = 0xff;
726 state.array_count = config->array_count;
727 if (config->array_count > 0) {
728 state.arrays = calloc(config->array_count, sizeof(int));
729 if (state.arrays == NULL) {
730 warnx("malloc failed");
734 for (i = 0; i < config->array_count; i++) {
735 ar = (struct mfi_array *)p;
736 state.arrays[i] = ar->array_ref;
737 p += config->array_size;
739 qsort(state.arrays, config->array_count, sizeof(int),
743 state.log_drv_count = config->log_drv_count;
744 if (config->log_drv_count) {
745 state.volumes = calloc(config->log_drv_count, sizeof(int));
746 if (state.volumes == NULL) {
747 warnx("malloc failed");
751 for (i = 0; i < config->log_drv_count; i++) {
752 ld = (struct mfi_ld_config *)p;
753 state.volumes[i] = ld->properties.ld.v.target_id;
754 p += config->log_drv_size;
756 qsort(state.volumes, config->log_drv_count, sizeof(int),
759 state.volumes = NULL;
762 /* Determine the size of the configuration we will build. */
770 /* Each volume spans a single array. */
776 /* A single volume spans multiple arrays. */
784 config_size = sizeof(struct mfi_config_data) +
785 sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays;
786 config = calloc(1, config_size);
787 if (config == NULL) {
788 warnx("malloc failed");
792 config->size = config_size;
793 config->array_count = narrays;
794 config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */
795 config->log_drv_count = nvolumes;
796 config->log_drv_size = sizeof(struct mfi_ld_config);
797 config->spares_count = 0;
798 config->spares_size = 40; /* XXX: Firmware hardcode */
799 cfg_arrays = (char *)config->array;
800 cfg_volumes = cfg_arrays + config->array_size * narrays;
802 /* Build the arrays. */
803 for (i = 0; i < narrays; i++) {
804 build_array(fd, cfg_arrays, &arrays[i], &state, verbose);
805 cfg_arrays += config->array_size;
808 /* Now build the volume(s). */
809 arrays_per_volume = narrays / nvolumes;
810 for (i = 0; i < nvolumes; i++) {
811 build_volume(cfg_volumes, arrays_per_volume,
812 &arrays[i * arrays_per_volume], raid_type, stripe_size,
814 cfg_volumes += config->log_drv_size;
819 dump_config(fd, config, NULL);
822 /* Send the new config to the controller. */
823 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size,
824 NULL, 0, NULL) < 0) {
826 warn("Failed to add volume");
835 if (arrays != NULL) {
836 for (i = 0; i < narrays; i++)
837 free(arrays[i].drives);
844 MFI_COMMAND(top, create, create_volume);
847 delete_volume(int ac, char **av)
849 struct mfi_ld_info info;
851 uint8_t target_id, mbox[4];
854 * Backwards compat. Map 'delete volume' to 'delete' and
855 * 'delete spare' to 'remove'.
858 if (strcmp(av[1], "volume") == 0) {
861 } else if (strcmp(av[1], "spare") == 0) {
864 return (remove_spare(ac, av));
869 warnx("delete volume: volume required");
873 fd = mfi_open(mfi_device, O_RDWR);
880 if (!mfi_reconfig_supported(mfi_device)) {
881 warnx("The current %s(4) driver does not support "
882 "configuration changes.", mfi_device);
887 if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
889 warn("Invalid volume %s", av[1]);
894 if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
896 warn("Failed to get info for volume %d", target_id);
901 if (mfi_volume_busy(fd, target_id)) {
902 warnx("Volume %s is busy and cannot be deleted",
903 mfi_volume_name(fd, target_id));
908 mbox_store_ldref(mbox, &info.ld_config.properties.ld);
909 if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox,
910 sizeof(mbox), NULL) < 0) {
912 warn("Failed to delete volume");
921 MFI_COMMAND(top, delete, delete_volume);
924 add_spare(int ac, char **av)
926 struct mfi_pd_info info;
927 struct mfi_config_data *config;
928 struct mfi_array *ar;
929 struct mfi_ld_config *ld;
930 struct mfi_spare *spare;
937 warnx("add spare: drive required");
941 fd = mfi_open(mfi_device, O_RDWR);
950 error = mfi_lookup_drive(fd, av[1], &device_id);
954 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
956 warn("Failed to fetch drive info");
960 if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
961 warnx("Drive %u is not available", device_id);
967 if (mfi_lookup_volume(fd, av[2], &target_id) < 0) {
969 warn("Invalid volume %s", av[2]);
974 if (mfi_config_read(fd, &config) < 0) {
976 warn("Failed to read configuration");
980 spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) *
981 config->array_count);
983 warnx("malloc failed");
987 bzero(spare, sizeof(struct mfi_spare));
988 spare->ref = info.ref;
991 /* Global spare backs all arrays. */
992 p = (char *)config->array;
993 for (i = 0; i < config->array_count; i++) {
994 ar = (struct mfi_array *)p;
995 if (ar->size > info.coerced_size) {
996 warnx("Spare isn't large enough for array %u",
1001 p += config->array_size;
1003 spare->array_count = 0;
1006 * Dedicated spares only back the arrays for a
1009 ld = mfi_config_lookup_volume(config, target_id);
1011 warnx("Did not find volume %d", target_id);
1016 spare->spare_type |= MFI_SPARE_DEDICATED;
1017 spare->array_count = ld->params.span_depth;
1018 for (i = 0; i < ld->params.span_depth; i++) {
1019 ar = mfi_config_lookup_array(config,
1020 ld->span[i].array_ref);
1022 warnx("Missing array; inconsistent config?");
1026 if (ar->size > info.coerced_size) {
1027 warnx("Spare isn't large enough for array %u",
1032 spare->array_ref[i] = ar->array_ref;
1036 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare,
1037 sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count,
1038 NULL, 0, NULL) < 0) {
1040 warn("Failed to assign spare");
1051 MFI_COMMAND(top, add, add_spare);
1054 remove_spare(int ac, char **av)
1056 struct mfi_pd_info info;
1062 warnx("remove spare: drive required");
1066 fd = mfi_open(mfi_device, O_RDWR);
1073 error = mfi_lookup_drive(fd, av[1], &device_id);
1079 /* Get the info for this drive. */
1080 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
1082 warn("Failed to fetch info for drive %u", device_id);
1087 if (info.fw_state != MFI_PD_STATE_HOT_SPARE) {
1088 warnx("Drive %u is not a hot spare", device_id);
1093 mbox_store_pdref(mbox, &info.ref);
1094 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox,
1095 sizeof(mbox), NULL) < 0) {
1097 warn("Failed to delete spare");
1106 MFI_COMMAND(top, remove, remove_spare);
1108 /* Display raw data about a config. */
1110 dump_config(int fd, struct mfi_config_data *config, const char *msg_prefix)
1112 struct mfi_array *ar;
1113 struct mfi_ld_config *ld;
1114 struct mfi_spare *sp;
1115 struct mfi_pd_info pinfo;
1120 if (NULL == msg_prefix)
1121 msg_prefix = "Configuration (Debug)";
1124 "%s %s: %d arrays, %d volumes, %d spares\n", mfi_device,
1125 msg_prefix, config->array_count, config->log_drv_count,
1126 config->spares_count);
1127 printf(" array size: %u\n", config->array_size);
1128 printf(" volume size: %u\n", config->log_drv_size);
1129 printf(" spare size: %u\n", config->spares_size);
1130 p = (char *)config->array;
1132 for (i = 0; i < config->array_count; i++) {
1133 ar = (struct mfi_array *)p;
1134 printf(" array %u of %u drives:\n", ar->array_ref,
1136 printf(" size = %ju\n", (uintmax_t)ar->size);
1137 for (j = 0; j < ar->num_drives; j++) {
1138 device_id = ar->pd[j].ref.v.device_id;
1139 if (device_id == 0xffff)
1140 printf(" drive MISSING\n");
1142 printf(" drive %u %s\n", device_id,
1143 mfi_pdstate(ar->pd[j].fw_state));
1144 if (mfi_pd_get_info(fd, device_id, &pinfo,
1146 printf(" raw size: %ju\n",
1147 (uintmax_t)pinfo.raw_size);
1148 printf(" non-coerced size: %ju\n",
1149 (uintmax_t)pinfo.non_coerced_size);
1150 printf(" coerced size: %ju\n",
1151 (uintmax_t)pinfo.coerced_size);
1155 p += config->array_size;
1158 for (i = 0; i < config->log_drv_count; i++) {
1159 ld = (struct mfi_ld_config *)p;
1160 printf(" volume %s ",
1161 mfi_volume_name(fd, ld->properties.ld.v.target_id));
1163 mfi_raid_level(ld->params.primary_raid_level,
1164 ld->params.secondary_raid_level),
1165 mfi_ldstate(ld->params.state));
1166 if (ld->properties.name[0] != '\0')
1167 printf(" <%s>", ld->properties.name);
1169 printf(" primary raid level: %u\n",
1170 ld->params.primary_raid_level);
1171 printf(" raid level qualifier: %u\n",
1172 ld->params.raid_level_qualifier);
1173 printf(" secondary raid level: %u\n",
1174 ld->params.secondary_raid_level);
1175 printf(" stripe size: %u\n", ld->params.stripe_size);
1176 printf(" num drives: %u\n", ld->params.num_drives);
1177 printf(" init state: %u\n", ld->params.init_state);
1178 printf(" consistent: %u\n", ld->params.is_consistent);
1179 printf(" no bgi: %u\n", ld->properties.no_bgi);
1180 printf(" spans:\n");
1181 for (j = 0; j < ld->params.span_depth; j++) {
1182 printf(" array %u @ ", ld->span[j].array_ref);
1183 printf("%ju : %ju\n",
1184 (uintmax_t)ld->span[j].start_block,
1185 (uintmax_t)ld->span[j].num_blocks);
1187 p += config->log_drv_size;
1190 for (i = 0; i < config->spares_count; i++) {
1191 sp = (struct mfi_spare *)p;
1192 printf(" %s spare %u ",
1193 sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" :
1194 "global", sp->ref.v.device_id);
1195 printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE));
1196 printf(" backs:\n");
1197 for (j = 0; j < sp->array_count; j++)
1198 printf(" array %u\n", sp->array_ref[j]);
1199 p += config->spares_size;
1205 debug_config(int ac, char **av)
1207 struct mfi_config_data *config;
1211 warnx("debug: extra arguments");
1215 fd = mfi_open(mfi_device, O_RDWR);
1222 /* Get the config from the controller. */
1223 if (mfi_config_read(fd, &config) < 0) {
1225 warn("Failed to get config");
1230 /* Dump out the configuration. */
1231 dump_config(fd, config, NULL);
1237 MFI_COMMAND(top, debug, debug_config);
1240 dump(int ac, char **av)
1242 struct mfi_config_data *config;
1248 warnx("dump: extra arguments");
1252 fd = mfi_open(mfi_device, O_RDWR);
1259 /* Get the stashed copy of the last dcmd from the driver. */
1260 snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit);
1261 if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) {
1263 warn("Failed to read debug command");
1264 if (error == ENOENT)
1270 config = malloc(len);
1271 if (config == NULL) {
1272 warnx("malloc failed");
1276 if (sysctlbyname(buf, config, &len, NULL, 0) < 0) {
1278 warn("Failed to read debug command");
1283 dump_config(fd, config, NULL);
1289 MFI_COMMAND(top, dump, dump);