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.8 2011/11/29 08:16:14 delphij 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 mfi_config_read(int fd
, struct mfi_config_data
**configp
)
58 struct mfi_config_data
*config
;
63 * Keep fetching the config in a loop until we have a large enough
64 * buffer to hold the entire configuration.
69 config
= reallocf(config
, config_size
);
72 if (mfi_dcmd_command(fd
, MFI_DCMD_CFG_READ
, config
,
73 config_size
, NULL
, 0, NULL
) < 0) {
80 if (config
->size
> config_size
) {
81 config_size
= config
->size
;
89 static struct mfi_array
*
90 mfi_config_lookup_array(struct mfi_config_data
*config
, uint16_t array_ref
)
96 p
= (char *)config
->array
;
97 for (i
= 0; i
< config
->array_count
; i
++) {
98 ar
= (struct mfi_array
*)p
;
99 if (ar
->array_ref
== array_ref
)
101 p
+= config
->array_size
;
107 static struct mfi_ld_config
*
108 mfi_config_lookup_volume(struct mfi_config_data
*config
, uint8_t target_id
)
110 struct mfi_ld_config
*ld
;
114 p
= (char *)config
->array
+ config
->array_count
* config
->array_size
;
115 for (i
= 0; i
< config
->log_drv_count
; i
++) {
116 ld
= (struct mfi_ld_config
*)p
;
117 if (ld
->properties
.ld
.v
.target_id
== target_id
)
119 p
+= config
->log_drv_size
;
126 clear_config(int ac
, char **av
)
128 struct mfi_ld_list list
;
132 fd
= mfi_open(mfi_unit
);
139 if (!mfi_reconfig_supported()) {
140 warnx("The current mfi(4) driver does not support "
141 "configuration changes.");
146 if (mfi_ld_get_list(fd
, &list
, NULL
) < 0) {
148 warn("Failed to get volume list");
153 for (i
= 0; i
< list
.ld_count
; i
++) {
154 if (mfi_volume_busy(fd
, list
.ld_list
[i
].ld
.v
.target_id
)) {
155 warnx("Volume %s is busy and cannot be deleted",
156 mfi_volume_name(fd
, list
.ld_list
[i
].ld
.v
.target_id
));
163 "Are you sure you wish to clear the configuration on mfi%u? [y/N] ",
166 if (ch
!= 'y' && ch
!= 'Y') {
167 printf("\nAborting\n");
172 if (mfi_dcmd_command(fd
, MFI_DCMD_CFG_CLEAR
, NULL
, 0, NULL
, 0, NULL
) < 0) {
174 warn("Failed to clear configuration");
179 printf("mfi%d: Configuration cleared\n", mfi_unit
);
184 MFI_COMMAND(top
, clear
, clear_config
);
186 #define MFI_ARRAY_SIZE 288
187 #define MAX_DRIVES_PER_ARRAY \
188 ((MFI_ARRAY_SIZE - sizeof(struct mfi_array)) / 8)
201 compare_int(const void *one
, const void *two
)
205 first
= *(const int *)one
;
206 second
= *(const int *)two
;
208 return (first
- second
);
211 static struct raid_type_entry
{
214 } raid_type_table
[] = {
215 { "raid0", RT_RAID0
},
216 { "raid-0", RT_RAID0
},
217 { "raid1", RT_RAID1
},
218 { "raid-1", RT_RAID1
},
219 { "mirror", RT_RAID1
},
220 { "raid5", RT_RAID5
},
221 { "raid-5", RT_RAID5
},
222 { "raid6", RT_RAID6
},
223 { "raid-6", RT_RAID6
},
225 { "concat", RT_CONCAT
},
226 { "raid10", RT_RAID10
},
227 { "raid1+0", RT_RAID10
},
228 { "raid-10", RT_RAID10
},
229 { "raid-1+0", RT_RAID10
},
230 { "raid50", RT_RAID50
},
231 { "raid5+0", RT_RAID50
},
232 { "raid-50", RT_RAID50
},
233 { "raid-5+0", RT_RAID50
},
234 { "raid60", RT_RAID60
},
235 { "raid6+0", RT_RAID60
},
236 { "raid-60", RT_RAID60
},
237 { "raid-6+0", RT_RAID60
},
241 struct config_id_state
{
252 struct mfi_pd_info
*drives
;
253 struct mfi_array
*array
;
256 /* Parse a comma-separated list of drives for an array. */
258 parse_array(int fd
, int raid_type
, char *array_str
, struct array_info
*info
)
260 struct mfi_pd_info
*pinfo
;
267 for (count
= 0; cp
!= NULL
; count
++) {
268 cp
= strchr(cp
, ',');
272 warnx("Invalid drive list '%s'", array_str
);
278 /* Validate the number of drives for this array. */
279 if (count
>= MAX_DRIVES_PER_ARRAY
) {
280 warnx("Too many drives for a single array: max is %zu",
281 MAX_DRIVES_PER_ARRAY
);
287 if (count
% 2 != 0) {
288 warnx("RAID1 and RAID10 require an even number of "
289 "drives in each array");
296 warnx("RAID5 and RAID50 require at least 3 drives in "
304 warnx("RAID6 and RAID60 require at least 4 drives in "
311 /* Validate each drive. */
312 info
->drives
= calloc(count
, sizeof(struct mfi_pd_info
));
313 if (info
->drives
== NULL
) {
314 warnx("malloc failed");
317 info
->drive_count
= count
;
318 for (pinfo
= info
->drives
; (cp
= strsep(&array_str
, ",")) != NULL
;
320 error
= mfi_lookup_drive(fd
, cp
, &device_id
);
327 if (mfi_pd_get_info(fd
, device_id
, pinfo
, NULL
) < 0) {
329 warn("Failed to fetch drive info for drive %s", cp
);
335 if (pinfo
->fw_state
!= MFI_PD_STATE_UNCONFIGURED_GOOD
) {
336 warnx("Drive %u is not available", device_id
);
347 * Find the next free array ref assuming that 'array_ref' is the last
348 * one used. 'array_ref' should be 0xffff for the initial test.
351 find_next_array(struct config_id_state
*state
)
355 /* Assume the current one is used. */
358 /* Find the next free one. */
359 for (i
= 0; i
< state
->array_count
; i
++)
360 if (state
->arrays
[i
] == state
->array_ref
)
362 return (state
->array_ref
);
366 * Find the next free volume ID assuming that 'target_id' is the last
367 * one used. 'target_id' should be 0xff for the initial test.
370 find_next_volume(struct config_id_state
*state
)
374 /* Assume the current one is used. */
377 /* Find the next free one. */
378 for (i
= 0; i
< state
->log_drv_count
; i
++)
379 if (state
->volumes
[i
] == state
->target_id
)
381 return (state
->target_id
);
384 /* Populate an array with drives. */
386 build_array(int fd
, char *arrayp
, struct array_info
*array_info
,
387 struct config_id_state
*state
, int verbose
)
389 struct mfi_array
*ar
= (struct mfi_array
*)arrayp
;
392 ar
->size
= array_info
->drives
[0].coerced_size
;
393 ar
->num_drives
= array_info
->drive_count
;
394 ar
->array_ref
= find_next_array(state
);
395 for (i
= 0; i
< array_info
->drive_count
; i
++) {
397 printf("Adding drive %s to array %u\n",
399 array_info
->drives
[i
].ref
.v
.device_id
,
400 MFI_DNAME_DEVICE_ID
|MFI_DNAME_HONOR_OPTS
),
402 if (ar
->size
> array_info
->drives
[i
].coerced_size
)
403 ar
->size
= array_info
->drives
[i
].coerced_size
;
404 ar
->pd
[i
].ref
= array_info
->drives
[i
].ref
;
405 ar
->pd
[i
].fw_state
= MFI_PD_STATE_ONLINE
;
407 array_info
->array
= ar
;
411 * Create a volume that spans one or more arrays.
414 build_volume(char *volumep
, int narrays
, struct array_info
*arrays
,
415 int raid_type
, long stripe_size
, struct config_id_state
*state
, int verbose
)
417 struct mfi_ld_config
*ld
= (struct mfi_ld_config
*)volumep
;
418 struct mfi_array
*ar
;
422 ld
->properties
.ld
.v
.target_id
= find_next_volume(state
);
423 ld
->properties
.ld
.v
.seq
= 0;
424 ld
->properties
.default_cache_policy
= MR_LD_CACHE_ALLOW_WRITE_CACHE
|
425 MR_LD_CACHE_WRITE_BACK
;
426 ld
->properties
.access_policy
= MFI_LD_ACCESS_RW
;
427 ld
->properties
.disk_cache_policy
= MR_PD_CACHE_UNCHANGED
;
428 ld
->properties
.current_cache_policy
= MR_LD_CACHE_ALLOW_WRITE_CACHE
|
429 MR_LD_CACHE_WRITE_BACK
;
430 ld
->properties
.no_bgi
= 0;
436 ld
->params
.primary_raid_level
= DDF_RAID0
;
437 ld
->params
.raid_level_qualifier
= 0;
438 ld
->params
.secondary_raid_level
= 0;
441 ld
->params
.primary_raid_level
= DDF_RAID1
;
442 ld
->params
.raid_level_qualifier
= 0;
443 ld
->params
.secondary_raid_level
= 0;
446 ld
->params
.primary_raid_level
= DDF_RAID5
;
447 ld
->params
.raid_level_qualifier
= 3;
448 ld
->params
.secondary_raid_level
= 0;
451 ld
->params
.primary_raid_level
= DDF_RAID6
;
452 ld
->params
.raid_level_qualifier
= 3;
453 ld
->params
.secondary_raid_level
= 0;
456 ld
->params
.primary_raid_level
= DDF_CONCAT
;
457 ld
->params
.raid_level_qualifier
= 0;
458 ld
->params
.secondary_raid_level
= 0;
461 ld
->params
.primary_raid_level
= DDF_RAID1
;
462 ld
->params
.raid_level_qualifier
= 0;
463 ld
->params
.secondary_raid_level
= 3; /* XXX? */
467 * XXX: This appears to work though the card's BIOS
468 * complains that the configuration is foreign. The
469 * BIOS setup does not allow for creation of RAID-50
470 * or RAID-60 arrays. The only nested array
471 * configuration it allows for is RAID-10.
473 ld
->params
.primary_raid_level
= DDF_RAID5
;
474 ld
->params
.raid_level_qualifier
= 3;
475 ld
->params
.secondary_raid_level
= 3; /* XXX? */
478 ld
->params
.primary_raid_level
= DDF_RAID6
;
479 ld
->params
.raid_level_qualifier
= 3;
480 ld
->params
.secondary_raid_level
= 3; /* XXX? */
485 * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use
486 * ffs() to simulate log2(stripe_size).
488 ld
->params
.stripe_size
= ffs(stripe_size
) - 1 - 9;
489 ld
->params
.num_drives
= arrays
[0].array
->num_drives
;
490 ld
->params
.span_depth
= narrays
;
491 ld
->params
.state
= MFI_LD_STATE_OPTIMAL
;
492 ld
->params
.init_state
= MFI_LD_PARAMS_INIT_NO
;
493 ld
->params
.is_consistent
= 0;
496 for (i
= 0; i
< narrays
; i
++) {
497 ar
= arrays
[i
].array
;
499 printf("Adding array %u to volume %u\n", ar
->array_ref
,
500 ld
->properties
.ld
.v
.target_id
);
501 ld
->span
[i
].start_block
= 0;
502 ld
->span
[i
].num_blocks
= ar
->size
;
503 ld
->span
[i
].array_ref
= ar
->array_ref
;
508 create_volume(int ac
, char **av
)
510 struct mfi_config_data
*config
;
511 struct mfi_array
*ar
;
512 struct mfi_ld_config
*ld
;
513 struct config_id_state state
;
515 char *p
, *cfg_arrays
, *cfg_volumes
;
516 int error
, fd
, i
, raid_type
;
517 int narrays
, nvolumes
, arrays_per_volume
;
518 struct array_info
*arrays
;
526 * Backwards compat. Map 'create volume' to 'create' and
527 * 'create spare' to 'add'.
530 if (strcmp(av
[1], "volume") == 0) {
533 } else if (strcmp(av
[1], "spare") == 0) {
536 return (add_spare(ac
, av
));
541 warnx("create volume: volume type required");
545 bzero(&state
, sizeof(state
));
551 fd
= mfi_open(mfi_unit
);
558 if (!mfi_reconfig_supported()) {
559 warnx("The current mfi(4) driver does not support "
560 "configuration changes.");
565 /* Lookup the RAID type first. */
567 for (i
= 0; raid_type_table
[i
].name
!= NULL
; i
++)
568 if (strcasecmp(raid_type_table
[i
].name
, av
[1]) == 0) {
569 raid_type
= raid_type_table
[i
].raid_type
;
573 if (raid_type
== -1) {
574 warnx("Unknown or unsupported volume type %s", av
[1]);
579 /* Parse any options. */
585 stripe_size
= 64 * 1024;
587 while ((ch
= getopt(ac
, av
, "ds:v")) != -1) {
595 error
= dehumanize_number(optarg
, &stripe_size
);
597 warnx("Illegal stripe size");
600 if ((stripe_size
< 512) || (!powerof2(stripe_size
))) {
601 warnx("Illegal stripe size, using 64K");
602 stripe_size
= 64 * 1024;
617 /* Parse all the arrays. */
620 warnx("At least one drive list is required");
631 warnx("Only one drive list can be specified");
640 warnx("RAID10, RAID50, and RAID60 require at least "
645 if (narrays
> MFI_MAX_SPAN_DEPTH
) {
646 warnx("Volume spans more than %d arrays",
653 arrays
= calloc(narrays
, sizeof(*arrays
));
654 if (arrays
== NULL
) {
655 warnx("malloc failed");
659 for (i
= 0; i
< narrays
; i
++) {
660 error
= parse_array(fd
, raid_type
, av
[i
], &arrays
[i
]);
669 for (i
= 1; i
< narrays
; i
++) {
670 if (arrays
[i
].drive_count
!= arrays
[0].drive_count
) {
671 warnx("All arrays must contain the same "
681 * Fetch the current config and build sorted lists of existing
682 * array and volume identifiers.
684 if (mfi_config_read(fd
, &config
) < 0) {
686 warn("Failed to read configuration");
689 p
= (char *)config
->array
;
690 state
.array_ref
= 0xffff;
691 state
.target_id
= 0xff;
692 state
.array_count
= config
->array_count
;
693 if (config
->array_count
> 0) {
694 state
.arrays
= calloc(config
->array_count
, sizeof(int));
695 if (state
.arrays
== NULL
) {
696 warnx("malloc failed");
700 for (i
= 0; i
< config
->array_count
; i
++) {
701 ar
= (struct mfi_array
*)p
;
702 state
.arrays
[i
] = ar
->array_ref
;
703 p
+= config
->array_size
;
705 qsort(state
.arrays
, config
->array_count
, sizeof(int),
709 state
.log_drv_count
= config
->log_drv_count
;
710 if (config
->log_drv_count
) {
711 state
.volumes
= calloc(config
->log_drv_count
, sizeof(int));
712 if (state
.volumes
== NULL
) {
713 warnx("malloc failed");
717 for (i
= 0; i
< config
->log_drv_count
; i
++) {
718 ld
= (struct mfi_ld_config
*)p
;
719 state
.volumes
[i
] = ld
->properties
.ld
.v
.target_id
;
720 p
+= config
->log_drv_size
;
722 qsort(state
.volumes
, config
->log_drv_count
, sizeof(int),
725 state
.volumes
= NULL
;
728 /* Determine the size of the configuration we will build. */
736 /* Each volume spans a single array. */
742 /* A single volume spans multiple arrays. */
750 config_size
= sizeof(struct mfi_config_data
) +
751 sizeof(struct mfi_ld_config
) * nvolumes
+ MFI_ARRAY_SIZE
* narrays
;
752 config
= calloc(1, config_size
);
753 if (config
== NULL
) {
754 warnx("malloc failed");
758 config
->size
= config_size
;
759 config
->array_count
= narrays
;
760 config
->array_size
= MFI_ARRAY_SIZE
; /* XXX: Firmware hardcode */
761 config
->log_drv_count
= nvolumes
;
762 config
->log_drv_size
= sizeof(struct mfi_ld_config
);
763 config
->spares_count
= 0;
764 config
->spares_size
= 40; /* XXX: Firmware hardcode */
765 cfg_arrays
= (char *)config
->array
;
766 cfg_volumes
= cfg_arrays
+ config
->array_size
* narrays
;
768 /* Build the arrays. */
769 for (i
= 0; i
< narrays
; i
++) {
770 build_array(fd
, cfg_arrays
, &arrays
[i
], &state
, verbose
);
771 cfg_arrays
+= config
->array_size
;
774 /* Now build the volume(s). */
775 arrays_per_volume
= narrays
/ nvolumes
;
776 for (i
= 0; i
< nvolumes
; i
++) {
777 build_volume(cfg_volumes
, arrays_per_volume
,
778 &arrays
[i
* arrays_per_volume
], raid_type
, stripe_size
,
780 cfg_volumes
+= config
->log_drv_size
;
785 dump_config(fd
, config
);
788 /* Send the new config to the controller. */
789 if (mfi_dcmd_command(fd
, MFI_DCMD_CFG_ADD
, config
, config_size
,
790 NULL
, 0, NULL
) < 0) {
792 warn("Failed to add volume");
801 if (arrays
!= NULL
) {
802 for (i
= 0; i
< narrays
; i
++)
803 free(arrays
[i
].drives
);
810 MFI_COMMAND(top
, create
, create_volume
);
813 delete_volume(int ac
, char **av
)
815 struct mfi_ld_info info
;
817 uint8_t target_id
, mbox
[4];
820 * Backwards compat. Map 'delete volume' to 'delete' and
821 * 'delete spare' to 'remove'.
824 if (strcmp(av
[1], "volume") == 0) {
827 } else if (strcmp(av
[1], "spare") == 0) {
830 return (remove_spare(ac
, av
));
835 warnx("delete volume: volume required");
839 fd
= mfi_open(mfi_unit
);
846 if (!mfi_reconfig_supported()) {
847 warnx("The current mfi(4) driver does not support "
848 "configuration changes.");
853 if (mfi_lookup_volume(fd
, av
[1], &target_id
) < 0) {
855 warn("Invalid volume %s", av
[1]);
860 if (mfi_ld_get_info(fd
, target_id
, &info
, NULL
) < 0) {
862 warn("Failed to get info for volume %d", target_id
);
867 if (mfi_volume_busy(fd
, target_id
)) {
868 warnx("Volume %s is busy and cannot be deleted",
869 mfi_volume_name(fd
, target_id
));
874 mbox_store_ldref(mbox
, &info
.ld_config
.properties
.ld
);
875 if (mfi_dcmd_command(fd
, MFI_DCMD_LD_DELETE
, NULL
, 0, mbox
,
876 sizeof(mbox
), NULL
) < 0) {
878 warn("Failed to delete volume");
887 MFI_COMMAND(top
, delete, delete_volume
);
890 add_spare(int ac
, char **av
)
892 struct mfi_pd_info info
;
893 struct mfi_config_data
*config
;
894 struct mfi_array
*ar
;
895 struct mfi_ld_config
*ld
;
896 struct mfi_spare
*spare
;
903 warnx("add spare: drive required");
907 fd
= mfi_open(mfi_unit
);
916 error
= mfi_lookup_drive(fd
, av
[1], &device_id
);
920 if (mfi_pd_get_info(fd
, device_id
, &info
, NULL
) < 0) {
922 warn("Failed to fetch drive info");
926 if (info
.fw_state
!= MFI_PD_STATE_UNCONFIGURED_GOOD
) {
927 warnx("Drive %u is not available", device_id
);
933 if (mfi_lookup_volume(fd
, av
[2], &target_id
) < 0) {
935 warn("Invalid volume %s", av
[2]);
940 if (mfi_config_read(fd
, &config
) < 0) {
942 warn("Failed to read configuration");
946 spare
= malloc(sizeof(struct mfi_spare
) + sizeof(uint16_t) *
947 config
->array_count
);
949 warnx("malloc failed");
953 bzero(spare
, sizeof(struct mfi_spare
));
954 spare
->ref
= info
.ref
;
957 /* Global spare backs all arrays. */
958 p
= (char *)config
->array
;
959 for (i
= 0; i
< config
->array_count
; i
++) {
960 ar
= (struct mfi_array
*)p
;
961 if (ar
->size
> info
.coerced_size
) {
962 warnx("Spare isn't large enough for array %u",
967 p
+= config
->array_size
;
969 spare
->array_count
= 0;
972 * Dedicated spares only back the arrays for a
975 ld
= mfi_config_lookup_volume(config
, target_id
);
977 warnx("Did not find volume %d", target_id
);
982 spare
->spare_type
|= MFI_SPARE_DEDICATED
;
983 spare
->array_count
= ld
->params
.span_depth
;
984 for (i
= 0; i
< ld
->params
.span_depth
; i
++) {
985 ar
= mfi_config_lookup_array(config
,
986 ld
->span
[i
].array_ref
);
988 warnx("Missing array; inconsistent config?");
992 if (ar
->size
> info
.coerced_size
) {
993 warnx("Spare isn't large enough for array %u",
998 spare
->array_ref
[i
] = ar
->array_ref
;
1002 if (mfi_dcmd_command(fd
, MFI_DCMD_CFG_MAKE_SPARE
, spare
,
1003 sizeof(struct mfi_spare
) + sizeof(uint16_t) * spare
->array_count
,
1004 NULL
, 0, NULL
) < 0) {
1006 warn("Failed to assign spare");
1017 MFI_COMMAND(top
, add
, add_spare
);
1020 remove_spare(int ac
, char **av
)
1022 struct mfi_pd_info info
;
1028 warnx("remove spare: drive required");
1032 fd
= mfi_open(mfi_unit
);
1039 error
= mfi_lookup_drive(fd
, av
[1], &device_id
);
1045 /* Get the info for this drive. */
1046 if (mfi_pd_get_info(fd
, device_id
, &info
, NULL
) < 0) {
1048 warn("Failed to fetch info for drive %u", device_id
);
1053 if (info
.fw_state
!= MFI_PD_STATE_HOT_SPARE
) {
1054 warnx("Drive %u is not a hot spare", device_id
);
1059 mbox_store_pdref(mbox
, &info
.ref
);
1060 if (mfi_dcmd_command(fd
, MFI_DCMD_CFG_REMOVE_SPARE
, NULL
, 0, mbox
,
1061 sizeof(mbox
), NULL
) < 0) {
1063 warn("Failed to delete spare");
1072 MFI_COMMAND(top
, remove
, remove_spare
);
1075 /* Display raw data about a config. */
1077 dump_config(int fd
, struct mfi_config_data
*config
)
1079 struct mfi_array
*ar
;
1080 struct mfi_ld_config
*ld
;
1081 struct mfi_spare
*sp
;
1082 struct mfi_pd_info pinfo
;
1088 "mfi%d Configuration (Debug): %d arrays, %d volumes, %d spares\n",
1089 mfi_unit
, config
->array_count
, config
->log_drv_count
,
1090 config
->spares_count
);
1091 printf(" array size: %u\n", config
->array_size
);
1092 printf(" volume size: %u\n", config
->log_drv_size
);
1093 printf(" spare size: %u\n", config
->spares_size
);
1094 p
= (char *)config
->array
;
1096 for (i
= 0; i
< config
->array_count
; i
++) {
1097 ar
= (struct mfi_array
*)p
;
1098 printf(" array %u of %u drives:\n", ar
->array_ref
,
1100 printf(" size = %ju\n", (uintmax_t)ar
->size
);
1101 for (j
= 0; j
< ar
->num_drives
; j
++) {
1102 device_id
= ar
->pd
[j
].ref
.v
.device_id
;
1103 if (device_id
== 0xffff)
1104 printf(" drive MISSING\n");
1106 printf(" drive %u %s\n", device_id
,
1107 mfi_pdstate(ar
->pd
[j
].fw_state
));
1108 if (mfi_pd_get_info(fd
, device_id
, &pinfo
,
1110 printf(" raw size: %ju\n",
1111 (uintmax_t)pinfo
.raw_size
);
1112 printf(" non-coerced size: %ju\n",
1113 (uintmax_t)pinfo
.non_coerced_size
);
1114 printf(" coerced size: %ju\n",
1115 (uintmax_t)pinfo
.coerced_size
);
1119 p
+= config
->array_size
;
1122 for (i
= 0; i
< config
->log_drv_count
; i
++) {
1123 ld
= (struct mfi_ld_config
*)p
;
1124 printf(" volume %s ",
1125 mfi_volume_name(fd
, ld
->properties
.ld
.v
.target_id
));
1127 mfi_raid_level(ld
->params
.primary_raid_level
,
1128 ld
->params
.secondary_raid_level
),
1129 mfi_ldstate(ld
->params
.state
));
1130 if (ld
->properties
.name
[0] != '\0')
1131 printf(" <%s>", ld
->properties
.name
);
1133 printf(" primary raid level: %u\n",
1134 ld
->params
.primary_raid_level
);
1135 printf(" raid level qualifier: %u\n",
1136 ld
->params
.raid_level_qualifier
);
1137 printf(" secondary raid level: %u\n",
1138 ld
->params
.secondary_raid_level
);
1139 printf(" stripe size: %u\n", ld
->params
.stripe_size
);
1140 printf(" num drives: %u\n", ld
->params
.num_drives
);
1141 printf(" init state: %u\n", ld
->params
.init_state
);
1142 printf(" consistent: %u\n", ld
->params
.is_consistent
);
1143 printf(" no bgi: %u\n", ld
->properties
.no_bgi
);
1144 printf(" spans:\n");
1145 for (j
= 0; j
< ld
->params
.span_depth
; j
++) {
1146 printf(" array %u @ ", ld
->span
[j
].array_ref
);
1147 printf("%ju : %ju\n",
1148 (uintmax_t)ld
->span
[j
].start_block
,
1149 (uintmax_t)ld
->span
[j
].num_blocks
);
1151 p
+= config
->log_drv_size
;
1154 for (i
= 0; i
< config
->spares_count
; i
++) {
1155 sp
= (struct mfi_spare
*)p
;
1156 printf(" %s spare %u ",
1157 sp
->spare_type
& MFI_SPARE_DEDICATED
? "dedicated" :
1158 "global", sp
->ref
.v
.device_id
);
1159 printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE
));
1160 printf(" backs:\n");
1161 for (j
= 0; j
< sp
->array_count
; j
++)
1162 printf(" array %u\n", sp
->array_ref
[j
]);
1163 p
+= config
->spares_size
;
1168 debug_config(int ac
, char **av
)
1170 struct mfi_config_data
*config
;
1174 warnx("debug: extra arguments");
1178 fd
= mfi_open(mfi_unit
);
1185 /* Get the config from the controller. */
1186 if (mfi_config_read(fd
, &config
) < 0) {
1188 warn("Failed to get config");
1193 /* Dump out the configuration. */
1194 dump_config(fd
, config
);
1200 MFI_COMMAND(top
, debug
, debug_config
);
1203 dump(int ac
, char **av
)
1205 struct mfi_config_data
*config
;
1211 warnx("dump: extra arguments");
1215 fd
= mfi_open(mfi_unit
);
1222 /* Get the stashed copy of the last dcmd from the driver. */
1223 snprintf(buf
, sizeof(buf
), "hw.mfi%d.debug_command", mfi_unit
);
1224 if (sysctlbyname(buf
, NULL
, &len
, NULL
, 0) < 0) {
1226 warn("Failed to read debug command");
1227 if (error
== ENOENT
)
1233 config
= malloc(len
);
1234 if (config
== NULL
) {
1235 warnx("malloc failed");
1239 if (sysctlbyname(buf
, config
, &len
, NULL
, 0) < 0) {
1241 warn("Failed to read debug command");
1246 dump_config(fd
, config
);
1252 MFI_COMMAND(top
, dump
, dump
);