xref: /freebsd/usr.sbin/mfiutil/mfi_config.c (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2008, 2009 Yahoo!, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
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
17  *    permission.
18  *
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
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/param.h>
33 #ifdef DEBUG
34 #include <sys/sysctl.h>
35 #endif
36 #include <ctype.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <libutil.h>
41 #include <stdint.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include "mfiutil.h"
47 
48 static int	add_spare(int ac, char **av);
49 static int	remove_spare(int ac, char **av);
50 
51 static long
52 dehumanize(const char *value)
53 {
54 	char    *vtp;
55 	long    iv;
56 
57 	if (value == NULL)
58 		return (0);
59 	iv = strtoq(value, &vtp, 0);
60 	if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
61 		return (0);
62 	}
63 	switch (vtp[0]) {
64 	case 't': case 'T':
65 		iv *= 1024;
66 	case 'g': case 'G':
67 		iv *= 1024;
68 	case 'm': case 'M':
69 		iv *= 1024;
70 	case 'k': case 'K':
71 		iv *= 1024;
72 	case '\0':
73 		break;
74 	default:
75 		return (0);
76 	}
77 	return (iv);
78 }
79 
80 int
81 mfi_config_read(int fd, struct mfi_config_data **configp)
82 {
83 	return mfi_config_read_opcode(fd, MFI_DCMD_CFG_READ, configp, NULL, 0);
84 }
85 
86 int
87 mfi_config_read_opcode(int fd, uint32_t opcode, struct mfi_config_data **configp,
88 	uint8_t *mbox, size_t mboxlen)
89 {
90 	struct mfi_config_data *config;
91 	uint32_t config_size;
92 	int error;
93 
94 	/*
95 	 * Keep fetching the config in a loop until we have a large enough
96 	 * buffer to hold the entire configuration.
97 	 */
98 	config = NULL;
99 	config_size = 1024;
100 fetch:
101 	config = reallocf(config, config_size);
102 	if (config == NULL)
103 		return (-1);
104 	if (mfi_dcmd_command(fd, opcode, config,
105 	    config_size, mbox, mboxlen, NULL) < 0) {
106 		error = errno;
107 		free(config);
108 		errno = error;
109 		return (-1);
110 	}
111 
112 	if (config->size > config_size) {
113 		config_size = config->size;
114 		goto fetch;
115 	}
116 
117 	*configp = config;
118 	return (0);
119 }
120 
121 static struct mfi_array *
122 mfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
123 {
124 	struct mfi_array *ar;
125 	char *p;
126 	int i;
127 
128 	p = (char *)config->array;
129 	for (i = 0; i < config->array_count; i++) {
130 		ar = (struct mfi_array *)p;
131 		if (ar->array_ref == array_ref)
132 			return (ar);
133 		p += config->array_size;
134 	}
135 
136 	return (NULL);
137 }
138 
139 static struct mfi_ld_config *
140 mfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
141 {
142 	struct mfi_ld_config *ld;
143 	char *p;
144 	int i;
145 
146 	p = (char *)config->array + config->array_count * config->array_size;
147 	for (i = 0; i < config->log_drv_count; i++) {
148 		ld = (struct mfi_ld_config *)p;
149 		if (ld->properties.ld.v.target_id == target_id)
150 			return (ld);
151 		p += config->log_drv_size;
152 	}
153 
154 	return (NULL);
155 }
156 
157 static int
158 clear_config(int ac __unused, char **av __unused)
159 {
160 	struct mfi_ld_list list;
161 	int ch, error, fd;
162 	u_int i;
163 
164 	fd = mfi_open(mfi_device, O_RDWR);
165 	if (fd < 0) {
166 		error = errno;
167 		warn("mfi_open");
168 		return (error);
169 	}
170 
171 	if (!mfi_reconfig_supported(mfi_device)) {
172 		warnx("The current %s driver does not support "
173 		    "configuration changes.", mfi_device);
174 		close(fd);
175 		return (EOPNOTSUPP);
176 	}
177 
178 	if (mfi_ld_get_list(fd, &list, NULL) < 0) {
179 		error = errno;
180 		warn("Failed to get volume list");
181 		close(fd);
182 		return (error);
183 	}
184 
185 	for (i = 0; i < list.ld_count; i++) {
186 		if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) {
187 			warnx("Volume %s is busy and cannot be deleted",
188 			    mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
189 			close(fd);
190 			return (EBUSY);
191 		}
192 	}
193 
194 	printf(
195 	    "Are you sure you wish to clear the configuration on %s? [y/N] ",
196 	    mfi_device);
197 	ch = getchar();
198 	if (ch != 'y' && ch != 'Y') {
199 		printf("\nAborting\n");
200 		close(fd);
201 		return (0);
202 	}
203 
204 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) {
205 		error = errno;
206 		warn("Failed to clear configuration");
207 		close(fd);
208 		return (error);
209 	}
210 
211 	printf("%s: Configuration cleared\n", mfi_device);
212 	close(fd);
213 
214 	return (0);
215 }
216 MFI_COMMAND(top, clear, clear_config);
217 
218 #define MAX_DRIVES_PER_ARRAY MFI_MAX_ROW_SIZE
219 #define MFI_ARRAY_SIZE sizeof(struct mfi_array)
220 
221 #define	RT_RAID0	0
222 #define	RT_RAID1	1
223 #define	RT_RAID5	2
224 #define	RT_RAID6	3
225 #define	RT_JBOD		4
226 #define	RT_CONCAT	5
227 #define	RT_RAID10	6
228 #define	RT_RAID50	7
229 #define	RT_RAID60	8
230 
231 static int
232 compare_int(const void *one, const void *two)
233 {
234 	int first, second;
235 
236 	first = *(const int *)one;
237 	second = *(const int *)two;
238 
239 	return (first - second);
240 }
241 
242 static struct raid_type_entry {
243 	const char *name;
244 	int	raid_type;
245 } raid_type_table[] = {
246 	{ "raid0",	RT_RAID0 },
247 	{ "raid-0",	RT_RAID0 },
248 	{ "raid1",	RT_RAID1 },
249 	{ "raid-1",	RT_RAID1 },
250 	{ "mirror",	RT_RAID1 },
251 	{ "raid5",	RT_RAID5 },
252 	{ "raid-5",	RT_RAID5 },
253 	{ "raid6",	RT_RAID6 },
254 	{ "raid-6",	RT_RAID6 },
255 	{ "jbod",	RT_JBOD },
256 	{ "concat",	RT_CONCAT },
257 	{ "raid10",	RT_RAID10 },
258 	{ "raid1+0",	RT_RAID10 },
259 	{ "raid-10",	RT_RAID10 },
260 	{ "raid-1+0",	RT_RAID10 },
261 	{ "raid50",	RT_RAID50 },
262 	{ "raid5+0",	RT_RAID50 },
263 	{ "raid-50",	RT_RAID50 },
264 	{ "raid-5+0",	RT_RAID50 },
265 	{ "raid60",	RT_RAID60 },
266 	{ "raid6+0",	RT_RAID60 },
267 	{ "raid-60",	RT_RAID60 },
268 	{ "raid-6+0",	RT_RAID60 },
269 	{ NULL,		0 },
270 };
271 
272 struct config_id_state {
273 	int	array_count;
274 	int	log_drv_count;
275 	int	*arrays;
276 	int	*volumes;
277 	uint16_t array_ref;
278 	uint8_t	target_id;
279 };
280 
281 struct array_info {
282 	int	drive_count;
283 	struct mfi_pd_info *drives;
284 	struct mfi_array *array;
285 };
286 
287 /* Parse a comma-separated list of drives for an array. */
288 static int
289 parse_array(int fd, int raid_type, char *array_str, struct array_info *info)
290 {
291 	struct mfi_pd_info *pinfo;
292 	uint16_t device_id;
293 	char *cp;
294 	u_int count;
295 	int error;
296 
297 	cp = array_str;
298 	for (count = 0; cp != NULL; count++) {
299 		cp = strchr(cp, ',');
300 		if (cp != NULL) {
301 			cp++;
302 			if (*cp == ',') {
303 				warnx("Invalid drive list '%s'", array_str);
304 				return (EINVAL);
305 			}
306 		}
307 	}
308 
309 	/* Validate the number of drives for this array. */
310 	if (count >= MAX_DRIVES_PER_ARRAY) {
311 		warnx("Too many drives for a single array: max is %d",
312 		    MAX_DRIVES_PER_ARRAY);
313 		return (EINVAL);
314 	}
315 	switch (raid_type) {
316 	case RT_RAID1:
317 	case RT_RAID10:
318 		if (count % 2 != 0) {
319 			warnx("RAID1 and RAID10 require an even number of "
320 			    "drives in each array");
321 			return (EINVAL);
322 		}
323 		break;
324 	case RT_RAID5:
325 	case RT_RAID50:
326 		if (count < 3) {
327 			warnx("RAID5 and RAID50 require at least 3 drives in "
328 			    "each array");
329 			return (EINVAL);
330 		}
331 		break;
332 	case RT_RAID6:
333 	case RT_RAID60:
334 		if (count < 4) {
335 			warnx("RAID6 and RAID60 require at least 4 drives in "
336 			    "each array");
337 			return (EINVAL);
338 		}
339 		break;
340 	}
341 
342 	/* Validate each drive. */
343 	info->drives = calloc(count, sizeof(struct mfi_pd_info));
344 	if (info->drives == NULL) {
345 		warnx("malloc failed");
346 		return (ENOMEM);
347 	}
348 	info->drive_count = count;
349 	for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL;
350 	     pinfo++) {
351 		error = mfi_lookup_drive(fd, cp, &device_id);
352 		if (error) {
353 			free(info->drives);
354 			info->drives = NULL;
355 			return (error);
356 		}
357 
358 		if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) {
359 			error = errno;
360 			warn("Failed to fetch drive info for drive %s", cp);
361 			free(info->drives);
362 			info->drives = NULL;
363 			return (error);
364 		}
365 
366 		if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
367 			warnx("Drive %u is not available", device_id);
368 			free(info->drives);
369 			info->drives = NULL;
370 			return (EINVAL);
371 		}
372 
373 		if (pinfo->state.ddf.v.pd_type.is_foreign) {
374 			warnx("Drive %u is foreign", device_id);
375 			free(info->drives);
376 			info->drives = NULL;
377 			return (EINVAL);
378 		}
379 	}
380 
381 	return (0);
382 }
383 
384 /*
385  * Find the next free array ref assuming that 'array_ref' is the last
386  * one used.  'array_ref' should be 0xffff for the initial test.
387  */
388 static uint16_t
389 find_next_array(struct config_id_state *state)
390 {
391 	int i;
392 
393 	/* Assume the current one is used. */
394 	state->array_ref++;
395 
396 	/* Find the next free one. */
397 	for (i = 0; i < state->array_count; i++)
398 		if (state->arrays[i] == state->array_ref)
399 			state->array_ref++;
400 	return (state->array_ref);
401 }
402 
403 /*
404  * Find the next free volume ID assuming that 'target_id' is the last
405  * one used.  'target_id' should be 0xff for the initial test.
406  */
407 static uint8_t
408 find_next_volume(struct config_id_state *state)
409 {
410 	int i;
411 
412 	/* Assume the current one is used. */
413 	state->target_id++;
414 
415 	/* Find the next free one. */
416 	for (i = 0; i < state->log_drv_count; i++)
417 		if (state->volumes[i] == state->target_id)
418 			state->target_id++;
419 	return (state->target_id);
420 }
421 
422 /* Populate an array with drives. */
423 static void
424 build_array(int fd __unused, char *arrayp, struct array_info *array_info,
425     struct config_id_state *state, int verbose)
426 {
427 	struct mfi_array *ar = (struct mfi_array *)arrayp;
428 	int i;
429 
430 	ar->size = array_info->drives[0].coerced_size;
431 	ar->num_drives = array_info->drive_count;
432 	ar->array_ref = find_next_array(state);
433 	for (i = 0; i < array_info->drive_count; i++) {
434 		if (verbose)
435 			printf("Adding drive %s to array %u\n",
436 			    mfi_drive_name(NULL,
437 			    array_info->drives[i].ref.v.device_id,
438 			    MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS),
439 			    ar->array_ref);
440 		if (ar->size > array_info->drives[i].coerced_size)
441 			ar->size = array_info->drives[i].coerced_size;
442 		ar->pd[i].ref = array_info->drives[i].ref;
443 		ar->pd[i].fw_state = MFI_PD_STATE_ONLINE;
444 	}
445 	array_info->array = ar;
446 }
447 
448 /*
449  * Create a volume that spans one or more arrays.
450  */
451 static void
452 build_volume(char *volumep, int narrays, struct array_info *arrays,
453     int raid_type, long stripe_size, struct config_id_state *state, int verbose)
454 {
455 	struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
456 	struct mfi_array *ar;
457 	int i;
458 
459 	/* properties */
460 	ld->properties.ld.v.target_id = find_next_volume(state);
461 	ld->properties.ld.v.seq = 0;
462 	ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
463 	    MR_LD_CACHE_WRITE_BACK;
464 	ld->properties.access_policy = MFI_LD_ACCESS_RW;
465 	ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED;
466 	ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
467 	    MR_LD_CACHE_WRITE_BACK;
468 	ld->properties.no_bgi = 0;
469 
470 	/* params */
471 	switch (raid_type) {
472 	case RT_RAID0:
473 	case RT_JBOD:
474 		ld->params.primary_raid_level = DDF_RAID0;
475 		ld->params.raid_level_qualifier = 0;
476 		ld->params.secondary_raid_level = 0;
477 		break;
478 	case RT_RAID1:
479 		ld->params.primary_raid_level = DDF_RAID1;
480 		ld->params.raid_level_qualifier = 0;
481 		ld->params.secondary_raid_level = 0;
482 		break;
483 	case RT_RAID5:
484 		ld->params.primary_raid_level = DDF_RAID5;
485 		ld->params.raid_level_qualifier = 3;
486 		ld->params.secondary_raid_level = 0;
487 		break;
488 	case RT_RAID6:
489 		ld->params.primary_raid_level = DDF_RAID6;
490 		ld->params.raid_level_qualifier = 3;
491 		ld->params.secondary_raid_level = 0;
492 		break;
493 	case RT_CONCAT:
494 		ld->params.primary_raid_level = DDF_CONCAT;
495 		ld->params.raid_level_qualifier = 0;
496 		ld->params.secondary_raid_level = 0;
497 		break;
498 	case RT_RAID10:
499 		ld->params.primary_raid_level = DDF_RAID1;
500 		ld->params.raid_level_qualifier = 0;
501 		ld->params.secondary_raid_level = 3; /* XXX? */
502 		break;
503 	case RT_RAID50:
504 		/*
505 		 * XXX: This appears to work though the card's BIOS
506 		 * complains that the configuration is foreign.  The
507 		 * BIOS setup does not allow for creation of RAID-50
508 		 * or RAID-60 arrays.  The only nested array
509 		 * configuration it allows for is RAID-10.
510 		 */
511 		ld->params.primary_raid_level = DDF_RAID5;
512 		ld->params.raid_level_qualifier = 3;
513 		ld->params.secondary_raid_level = 3; /* XXX? */
514 		break;
515 	case RT_RAID60:
516 		ld->params.primary_raid_level = DDF_RAID6;
517 		ld->params.raid_level_qualifier = 3;
518 		ld->params.secondary_raid_level = 3; /* XXX? */
519 		break;
520 	}
521 
522 	/*
523 	 * Stripe size is encoded as (2 ^ N) * 512 = stripe_size.  Use
524 	 * ffs() to simulate log2(stripe_size).
525 	 */
526 	ld->params.stripe_size = ffs(stripe_size) - 1 - 9;
527 	ld->params.num_drives = arrays[0].array->num_drives;
528 	ld->params.span_depth = narrays;
529 	ld->params.state = MFI_LD_STATE_OPTIMAL;
530 	ld->params.init_state = MFI_LD_PARAMS_INIT_NO;
531 	ld->params.is_consistent = 0;
532 
533 	/* spans */
534 	for (i = 0; i < narrays; i++) {
535 		ar = arrays[i].array;
536 		if (verbose)
537 			printf("Adding array %u to volume %u\n", ar->array_ref,
538 			    ld->properties.ld.v.target_id);
539 		ld->span[i].start_block = 0;
540 		ld->span[i].num_blocks = ar->size;
541 		ld->span[i].array_ref = ar->array_ref;
542 	}
543 }
544 
545 static int
546 create_volume(int ac, char **av)
547 {
548 	struct mfi_config_data *config;
549 	struct mfi_array *ar;
550 	struct mfi_ld_config *ld;
551 	struct config_id_state state;
552 	size_t config_size;
553 	char *p, *cfg_arrays, *cfg_volumes;
554 	int error, fd, i, raid_type;
555 	int narrays, nvolumes, arrays_per_volume;
556 	struct array_info *arrays;
557 	long stripe_size;
558 #ifdef DEBUG
559 	int dump;
560 #endif
561 	int ch, verbose;
562 
563 	/*
564 	 * Backwards compat.  Map 'create volume' to 'create' and
565 	 * 'create spare' to 'add'.
566 	 */
567 	if (ac > 1) {
568 		if (strcmp(av[1], "volume") == 0) {
569 			av++;
570 			ac--;
571 		} else if (strcmp(av[1], "spare") == 0) {
572 			av++;
573 			ac--;
574 			return (add_spare(ac, av));
575 		}
576 	}
577 
578 	if (ac < 2) {
579 		warnx("create volume: volume type required");
580 		return (EINVAL);
581 	}
582 
583 	bzero(&state, sizeof(state));
584 	config = NULL;
585 	arrays = NULL;
586 	narrays = 0;
587 	error = 0;
588 
589 	fd = mfi_open(mfi_device, O_RDWR);
590 	if (fd < 0) {
591 		error = errno;
592 		warn("mfi_open");
593 		return (error);
594 	}
595 
596 	if (!mfi_reconfig_supported(mfi_device)) {
597 		warnx("The current %s(4) driver does not support "
598 		    "configuration changes.", mfi_device);
599 		error = EOPNOTSUPP;
600 		goto error;
601 	}
602 
603 	/* Lookup the RAID type first. */
604 	raid_type = -1;
605 	for (i = 0; raid_type_table[i].name != NULL; i++)
606 		if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
607 			raid_type = raid_type_table[i].raid_type;
608 			break;
609 		}
610 
611 	if (raid_type == -1) {
612 		warnx("Unknown or unsupported volume type %s", av[1]);
613 		error = EINVAL;
614 		goto error;
615 	}
616 
617 	/* Parse any options. */
618 	optind = 2;
619 #ifdef DEBUG
620 	dump = 0;
621 #endif
622 	verbose = 0;
623 	stripe_size = 64 * 1024;
624 
625 	while ((ch = getopt(ac, av, "ds:v")) != -1) {
626 		switch (ch) {
627 #ifdef DEBUG
628 		case 'd':
629 			dump = 1;
630 			break;
631 #endif
632 		case 's':
633 			stripe_size = dehumanize(optarg);
634 			if ((stripe_size < 512) || (!powerof2(stripe_size)))
635 				stripe_size = 64 * 1024;
636 			break;
637 		case 'v':
638 			verbose = 1;
639 			break;
640 		case '?':
641 		default:
642 			error = EINVAL;
643 			goto error;
644 		}
645 	}
646 	ac -= optind;
647 	av += optind;
648 
649 	/* Parse all the arrays. */
650 	narrays = ac;
651 	if (narrays == 0) {
652 		warnx("At least one drive list is required");
653 		error = EINVAL;
654 		goto error;
655 	}
656 	switch (raid_type) {
657 	case RT_RAID0:
658 	case RT_RAID1:
659 	case RT_RAID5:
660 	case RT_RAID6:
661 	case RT_CONCAT:
662 		if (narrays != 1) {
663 			warnx("Only one drive list can be specified");
664 			error = EINVAL;
665 			goto error;
666 		}
667 		break;
668 	case RT_RAID10:
669 	case RT_RAID50:
670 	case RT_RAID60:
671 		if (narrays < 1) {
672 			warnx("RAID10, RAID50, and RAID60 require at least "
673 			    "two drive lists");
674 			error = EINVAL;
675 			goto error;
676 		}
677 		if (narrays > MFI_MAX_SPAN_DEPTH) {
678 			warnx("Volume spans more than %d arrays",
679 			    MFI_MAX_SPAN_DEPTH);
680 			error = EINVAL;
681 			goto error;
682 		}
683 		break;
684 	}
685 	arrays = calloc(narrays, sizeof(*arrays));
686 	if (arrays == NULL) {
687 		warnx("malloc failed");
688 		error = ENOMEM;
689 		goto error;
690 	}
691 	for (i = 0; i < narrays; i++) {
692 		error = parse_array(fd, raid_type, av[i], &arrays[i]);
693 		if (error)
694 			goto error;
695 	}
696 
697 	switch (raid_type) {
698 	case RT_RAID10:
699 	case RT_RAID50:
700 	case RT_RAID60:
701 		for (i = 1; i < narrays; i++) {
702 			if (arrays[i].drive_count != arrays[0].drive_count) {
703 				warnx("All arrays must contain the same "
704 				    "number of drives");
705 				error = EINVAL;
706 				goto error;
707 			}
708 		}
709 		break;
710 	}
711 
712 	/*
713 	 * Fetch the current config and build sorted lists of existing
714 	 * array and volume identifiers.
715 	 */
716 	if (mfi_config_read(fd, &config) < 0) {
717 		error = errno;
718 		warn("Failed to read configuration");
719 		goto error;
720 	}
721 	p = (char *)config->array;
722 	state.array_ref = 0xffff;
723 	state.target_id = 0xff;
724 	state.array_count = config->array_count;
725 	if (config->array_count > 0) {
726 		state.arrays = calloc(config->array_count, sizeof(int));
727 		if (state.arrays == NULL) {
728 			warnx("malloc failed");
729 			error = ENOMEM;
730 			goto error;
731 		}
732 		for (i = 0; i < config->array_count; i++) {
733 			ar = (struct mfi_array *)p;
734 			state.arrays[i] = ar->array_ref;
735 			p += config->array_size;
736 		}
737 		qsort(state.arrays, config->array_count, sizeof(int),
738 		    compare_int);
739 	} else
740 		state.arrays = NULL;
741 	state.log_drv_count = config->log_drv_count;
742 	if (config->log_drv_count) {
743 		state.volumes = calloc(config->log_drv_count, sizeof(int));
744 		if (state.volumes == NULL) {
745 			warnx("malloc failed");
746 			error = ENOMEM;
747 			goto error;
748 		}
749 		for (i = 0; i < config->log_drv_count; i++) {
750 			ld = (struct mfi_ld_config *)p;
751 			state.volumes[i] = ld->properties.ld.v.target_id;
752 			p += config->log_drv_size;
753 		}
754 		qsort(state.volumes, config->log_drv_count, sizeof(int),
755 		    compare_int);
756 	} else
757 		state.volumes = NULL;
758 	free(config);
759 
760 	/* Determine the size of the configuration we will build. */
761 	switch (raid_type) {
762 	case RT_RAID0:
763 	case RT_RAID1:
764 	case RT_RAID5:
765 	case RT_RAID6:
766 	case RT_CONCAT:
767 	case RT_JBOD:
768 		/* Each volume spans a single array. */
769 		nvolumes = narrays;
770 		break;
771 	case RT_RAID10:
772 	case RT_RAID50:
773 	case RT_RAID60:
774 		/* A single volume spans multiple arrays. */
775 		nvolumes = 1;
776 		break;
777 	default:
778 		/* Pacify gcc. */
779 		abort();
780 	}
781 
782 	config_size = sizeof(struct mfi_config_data) +
783 	    sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays;
784 	config = calloc(1, config_size);
785 	if (config == NULL) {
786 		warnx("malloc failed");
787 		error = ENOMEM;
788 		goto error;
789 	}
790 	config->size = config_size;
791 	config->array_count = narrays;
792 	config->array_size = MFI_ARRAY_SIZE;	/* XXX: Firmware hardcode */
793 	config->log_drv_count = nvolumes;
794 	config->log_drv_size = sizeof(struct mfi_ld_config);
795 	config->spares_count = 0;
796 	config->spares_size = 40;		/* XXX: Firmware hardcode */
797 	cfg_arrays = (char *)config->array;
798 	cfg_volumes = cfg_arrays + config->array_size * narrays;
799 
800 	/* Build the arrays. */
801 	for (i = 0; i < narrays; i++) {
802 		build_array(fd, cfg_arrays, &arrays[i], &state, verbose);
803 		cfg_arrays += config->array_size;
804 	}
805 
806 	/* Now build the volume(s). */
807 	arrays_per_volume = narrays / nvolumes;
808 	for (i = 0; i < nvolumes; i++) {
809 		build_volume(cfg_volumes, arrays_per_volume,
810 		    &arrays[i * arrays_per_volume], raid_type, stripe_size,
811 		    &state, verbose);
812 		cfg_volumes += config->log_drv_size;
813 	}
814 
815 #ifdef DEBUG
816 	if (dump)
817 		dump_config(fd, config, NULL);
818 #endif
819 
820 	/* Send the new config to the controller. */
821 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size,
822 	    NULL, 0, NULL) < 0) {
823 		error = errno;
824 		warn("Failed to add volume");
825 		/* FALLTHROUGH */
826 	}
827 
828 error:
829 	/* Clean up. */
830 	free(config);
831 	free(state.volumes);
832 	free(state.arrays);
833 	if (arrays != NULL) {
834 		for (i = 0; i < narrays; i++)
835 			free(arrays[i].drives);
836 		free(arrays);
837 	}
838 	close(fd);
839 
840 	return (error);
841 }
842 MFI_COMMAND(top, create, create_volume);
843 
844 static int
845 delete_volume(int ac, char **av)
846 {
847 	struct mfi_ld_info info;
848 	int error, fd;
849 	uint8_t target_id, mbox[4];
850 
851 	/*
852 	 * Backwards compat.  Map 'delete volume' to 'delete' and
853 	 * 'delete spare' to 'remove'.
854 	 */
855 	if (ac > 1) {
856 		if (strcmp(av[1], "volume") == 0) {
857 			av++;
858 			ac--;
859 		} else if (strcmp(av[1], "spare") == 0) {
860 			av++;
861 			ac--;
862 			return (remove_spare(ac, av));
863 		}
864 	}
865 
866 	if (ac != 2) {
867 		warnx("delete volume: volume required");
868 		return (EINVAL);
869 	}
870 
871 	fd = mfi_open(mfi_device, O_RDWR);
872 	if (fd < 0) {
873 		error = errno;
874 		warn("mfi_open");
875 		return (error);
876 	}
877 
878 	if (!mfi_reconfig_supported(mfi_device)) {
879 		warnx("The current %s(4) driver does not support "
880 		    "configuration changes.", mfi_device);
881 		close(fd);
882 		return (EOPNOTSUPP);
883 	}
884 
885 	if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
886 		error = errno;
887 		warn("Invalid volume %s", av[1]);
888 		close(fd);
889 		return (error);
890 	}
891 
892 	if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
893 		error = errno;
894 		warn("Failed to get info for volume %d", target_id);
895 		close(fd);
896 		return (error);
897 	}
898 
899 	if (mfi_volume_busy(fd, target_id)) {
900 		warnx("Volume %s is busy and cannot be deleted",
901 		    mfi_volume_name(fd, target_id));
902 		close(fd);
903 		return (EBUSY);
904 	}
905 
906 	mbox_store_ldref(mbox, &info.ld_config.properties.ld);
907 	if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox,
908 	    sizeof(mbox), NULL) < 0) {
909 		error = errno;
910 		warn("Failed to delete volume");
911 		close(fd);
912 		return (error);
913 	}
914 
915 	close(fd);
916 
917 	return (0);
918 }
919 MFI_COMMAND(top, delete, delete_volume);
920 
921 static int
922 add_spare(int ac, char **av)
923 {
924 	struct mfi_pd_info info;
925 	struct mfi_config_data *config;
926 	struct mfi_array *ar;
927 	struct mfi_ld_config *ld;
928 	struct mfi_spare *spare;
929 	uint16_t device_id;
930 	uint8_t target_id;
931 	char *p;
932 	int error, fd, i;
933 
934 	if (ac < 2) {
935 		warnx("add spare: drive required");
936 		return (EINVAL);
937 	}
938 
939 	fd = mfi_open(mfi_device, O_RDWR);
940 	if (fd < 0) {
941 		error = errno;
942 		warn("mfi_open");
943 		return (error);
944 	}
945 
946 	config = NULL;
947 	spare = NULL;
948 	error = mfi_lookup_drive(fd, av[1], &device_id);
949 	if (error)
950 		goto error;
951 
952 	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
953 		error = errno;
954 		warn("Failed to fetch drive info");
955 		goto error;
956 	}
957 
958 	if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
959 		warnx("Drive %u is not available", device_id);
960 		error = EINVAL;
961 		goto error;
962 	}
963 
964 	if (ac > 2) {
965 		if (mfi_lookup_volume(fd, av[2], &target_id) < 0) {
966 			error = errno;
967 			warn("Invalid volume %s", av[2]);
968 			goto error;
969 		}
970 	}
971 
972 	if (mfi_config_read(fd, &config) < 0) {
973 		error = errno;
974 		warn("Failed to read configuration");
975 		goto error;
976 	}
977 
978 	spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) *
979 	    config->array_count);
980 	if (spare == NULL) {
981 		warnx("malloc failed");
982 		error = ENOMEM;
983 		goto error;
984 	}
985 	bzero(spare, sizeof(struct mfi_spare));
986 	spare->ref = info.ref;
987 
988 	if (ac == 2) {
989 		/* Global spare backs all arrays. */
990 		p = (char *)config->array;
991 		for (i = 0; i < config->array_count; i++) {
992 			ar = (struct mfi_array *)p;
993 			if (ar->size > info.coerced_size) {
994 				warnx("Spare isn't large enough for array %u",
995 				    ar->array_ref);
996 				error = EINVAL;
997 				goto error;
998 			}
999 			p += config->array_size;
1000 		}
1001 		spare->array_count = 0;
1002 	} else  {
1003 		/*
1004 		 * Dedicated spares only back the arrays for a
1005 		 * specific volume.
1006 		 */
1007 		ld = mfi_config_lookup_volume(config, target_id);
1008 		if (ld == NULL) {
1009 			warnx("Did not find volume %d", target_id);
1010 			error = EINVAL;
1011 			goto error;
1012 		}
1013 
1014 		spare->spare_type |= MFI_SPARE_DEDICATED;
1015 		spare->array_count = ld->params.span_depth;
1016 		for (i = 0; i < ld->params.span_depth; i++) {
1017 			ar = mfi_config_lookup_array(config,
1018 			    ld->span[i].array_ref);
1019 			if (ar == NULL) {
1020 				warnx("Missing array; inconsistent config?");
1021 				error = ENXIO;
1022 				goto error;
1023 			}
1024 			if (ar->size > info.coerced_size) {
1025 				warnx("Spare isn't large enough for array %u",
1026 				    ar->array_ref);
1027 				error = EINVAL;
1028 				goto error;
1029 			}
1030 			spare->array_ref[i] = ar->array_ref;
1031 		}
1032 	}
1033 
1034 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare,
1035 	    sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count,
1036 	    NULL, 0, NULL) < 0) {
1037 		error = errno;
1038 		warn("Failed to assign spare");
1039 		/* FALLTHROUGH. */
1040 	}
1041 
1042 error:
1043 	free(spare);
1044 	free(config);
1045 	close(fd);
1046 
1047 	return (error);
1048 }
1049 MFI_COMMAND(top, add, add_spare);
1050 
1051 static int
1052 remove_spare(int ac, char **av)
1053 {
1054 	struct mfi_pd_info info;
1055 	int error, fd;
1056 	uint16_t device_id;
1057 	uint8_t mbox[4];
1058 
1059 	if (ac != 2) {
1060 		warnx("remove spare: drive required");
1061 		return (EINVAL);
1062 	}
1063 
1064 	fd = mfi_open(mfi_device, O_RDWR);
1065 	if (fd < 0) {
1066 		error = errno;
1067 		warn("mfi_open");
1068 		return (error);
1069 	}
1070 
1071 	error = mfi_lookup_drive(fd, av[1], &device_id);
1072 	if (error) {
1073 		close(fd);
1074 		return (error);
1075 	}
1076 
1077 	/* Get the info for this drive. */
1078 	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
1079 		error = errno;
1080 		warn("Failed to fetch info for drive %u", device_id);
1081 		close(fd);
1082 		return (error);
1083 	}
1084 
1085 	if (info.fw_state != MFI_PD_STATE_HOT_SPARE) {
1086 		warnx("Drive %u is not a hot spare", device_id);
1087 		close(fd);
1088 		return (EINVAL);
1089 	}
1090 
1091 	mbox_store_pdref(mbox, &info.ref);
1092 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox,
1093 	    sizeof(mbox), NULL) < 0) {
1094 		error = errno;
1095 		warn("Failed to delete spare");
1096 		close(fd);
1097 		return (error);
1098 	}
1099 
1100 	close(fd);
1101 
1102 	return (0);
1103 }
1104 MFI_COMMAND(top, remove, remove_spare);
1105 
1106 /* Display raw data about a config. */
1107 void
1108 dump_config(int fd, struct mfi_config_data *config, const char *msg_prefix)
1109 {
1110 	struct mfi_array *ar;
1111 	struct mfi_ld_config *ld;
1112 	struct mfi_spare *sp;
1113 	struct mfi_pd_info pinfo;
1114 	uint16_t device_id;
1115 	char *p;
1116 	int i, j;
1117 
1118 	if (NULL == msg_prefix)
1119 		msg_prefix = "Configuration (Debug)";
1120 
1121 	printf(
1122 	    "%s %s: %d arrays, %d volumes, %d spares\n", mfi_device,
1123 	    msg_prefix, config->array_count, config->log_drv_count,
1124 	    config->spares_count);
1125 	printf("  array size: %u\n", config->array_size);
1126 	printf("  volume size: %u\n", config->log_drv_size);
1127 	printf("  spare size: %u\n", config->spares_size);
1128 	p = (char *)config->array;
1129 
1130 	for (i = 0; i < config->array_count; i++) {
1131 		ar = (struct mfi_array *)p;
1132 		printf("    array %u of %u drives:\n", ar->array_ref,
1133 		    ar->num_drives);
1134 		printf("      size = %ju\n", (uintmax_t)ar->size);
1135 		for (j = 0; j < ar->num_drives; j++) {
1136 			device_id = ar->pd[j].ref.v.device_id;
1137 			if (device_id == 0xffff)
1138 				printf("        drive MISSING\n");
1139 			else {
1140 				printf("        drive %u %s\n", device_id,
1141 				    mfi_pdstate(ar->pd[j].fw_state));
1142 				if (mfi_pd_get_info(fd, device_id, &pinfo,
1143 				    NULL) >= 0) {
1144 					printf("          raw size: %ju\n",
1145 					    (uintmax_t)pinfo.raw_size);
1146 					printf("          non-coerced size: %ju\n",
1147 					    (uintmax_t)pinfo.non_coerced_size);
1148 					printf("          coerced size: %ju\n",
1149 					    (uintmax_t)pinfo.coerced_size);
1150 				}
1151 			}
1152 		}
1153 		p += config->array_size;
1154 	}
1155 
1156 	for (i = 0; i < config->log_drv_count; i++) {
1157 		ld = (struct mfi_ld_config *)p;
1158 		printf("    volume %s ",
1159 		    mfi_volume_name(fd, ld->properties.ld.v.target_id));
1160 		printf("%s %s",
1161 		    mfi_raid_level(ld->params.primary_raid_level,
1162 			ld->params.secondary_raid_level),
1163 		    mfi_ldstate(ld->params.state));
1164 		if (ld->properties.name[0] != '\0')
1165 			printf(" <%s>", ld->properties.name);
1166 		printf("\n");
1167 		printf("      primary raid level: %u\n",
1168 		    ld->params.primary_raid_level);
1169 		printf("      raid level qualifier: %u\n",
1170 		    ld->params.raid_level_qualifier);
1171 		printf("      secondary raid level: %u\n",
1172 		    ld->params.secondary_raid_level);
1173 		printf("      stripe size: %u\n", ld->params.stripe_size);
1174 		printf("      num drives: %u\n", ld->params.num_drives);
1175 		printf("      init state: %u\n", ld->params.init_state);
1176 		printf("      consistent: %u\n", ld->params.is_consistent);
1177 		printf("      no bgi: %u\n", ld->properties.no_bgi);
1178 		printf("      spans:\n");
1179 		for (j = 0; j < ld->params.span_depth; j++) {
1180 			printf("        array %u @ ", ld->span[j].array_ref);
1181 			printf("%ju : %ju\n",
1182 			    (uintmax_t)ld->span[j].start_block,
1183 			    (uintmax_t)ld->span[j].num_blocks);
1184 		}
1185 		p += config->log_drv_size;
1186 	}
1187 
1188 	for (i = 0; i < config->spares_count; i++) {
1189 		sp = (struct mfi_spare *)p;
1190 		printf("    %s spare %u ",
1191 		    sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" :
1192 		    "global", sp->ref.v.device_id);
1193 		printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE));
1194 		printf(" backs:\n");
1195 		for (j = 0; j < sp->array_count; j++)
1196 			printf("        array %u\n", sp->array_ref[j]);
1197 		p += config->spares_size;
1198 	}
1199 }
1200 
1201 #ifdef DEBUG
1202 static int
1203 debug_config(int ac, char **av)
1204 {
1205 	struct mfi_config_data *config;
1206 	int error, fd;
1207 
1208 	if (ac != 1) {
1209 		warnx("debug: extra arguments");
1210 		return (EINVAL);
1211 	}
1212 
1213 	fd = mfi_open(mfi_device, O_RDWR);
1214 	if (fd < 0) {
1215 		error = errno;
1216 		warn("mfi_open");
1217 		return (error);
1218 	}
1219 
1220 	/* Get the config from the controller. */
1221 	if (mfi_config_read(fd, &config) < 0) {
1222 		error = errno;
1223 		warn("Failed to get config");
1224 		close(fd);
1225 		return (error);
1226 	}
1227 
1228 	/* Dump out the configuration. */
1229 	dump_config(fd, config, NULL);
1230 	free(config);
1231 	close(fd);
1232 
1233 	return (0);
1234 }
1235 MFI_COMMAND(top, debug, debug_config);
1236 
1237 static int
1238 dump(int ac, char **av)
1239 {
1240 	struct mfi_config_data *config;
1241 	char buf[64];
1242 	size_t len;
1243 	int error, fd;
1244 
1245 	if (ac != 1) {
1246 		warnx("dump: extra arguments");
1247 		return (EINVAL);
1248 	}
1249 
1250 	fd = mfi_open(mfi_device, O_RDWR);
1251 	if (fd < 0) {
1252 		error = errno;
1253 		warn("mfi_open");
1254 		return (error);
1255 	}
1256 
1257 	/* Get the stashed copy of the last dcmd from the driver. */
1258 	snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit);
1259 	if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) {
1260 		error = errno;
1261 		warn("Failed to read debug command");
1262 		if (error == ENOENT)
1263 			error = EOPNOTSUPP;
1264 		close(fd);
1265 		return (error);
1266 	}
1267 
1268 	config = malloc(len);
1269 	if (config == NULL) {
1270 		warnx("malloc failed");
1271 		close(fd);
1272 		return (ENOMEM);
1273 	}
1274 	if (sysctlbyname(buf, config, &len, NULL, 0) < 0) {
1275 		error = errno;
1276 		warn("Failed to read debug command");
1277 		free(config);
1278 		close(fd);
1279 		return (error);
1280 	}
1281 	dump_config(fd, config, NULL);
1282 	free(config);
1283 	close(fd);
1284 
1285 	return (0);
1286 }
1287 MFI_COMMAND(top, dump, dump);
1288 #endif
1289