xref: /dragonfly/usr.sbin/mfiutil/mfi_config.c (revision 650094e1)
1 /*-
2  * Copyright (c) 2008, 2009 Yahoo!, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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
15  *    permission.
16  *
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
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD: src/usr.sbin/mfiutil/mfi_config.c,v 1.8 2011/11/29 08:16:14 delphij Exp $
30  */
31 
32 #include <sys/param.h>
33 #ifdef DEBUG
34 #include <sys/sysctl.h>
35 #endif
36 #include <err.h>
37 #include <errno.h>
38 #include <libutil.h>
39 #ifdef DEBUG
40 #include <stdint.h>
41 #endif
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include "mfiutil.h"
47 
48 #ifdef DEBUG
49 static void	dump_config(int fd, struct mfi_config_data *config);
50 #endif
51 
52 static int	add_spare(int ac, char **av);
53 static int	remove_spare(int ac, char **av);
54 
55 int
56 mfi_config_read(int fd, struct mfi_config_data **configp)
57 {
58 	struct mfi_config_data *config;
59 	uint32_t config_size;
60 	int error;
61 
62 	/*
63 	 * Keep fetching the config in a loop until we have a large enough
64 	 * buffer to hold the entire configuration.
65 	 */
66 	config = NULL;
67 	config_size = 1024;
68 fetch:
69 	config = reallocf(config, config_size);
70 	if (config == NULL)
71 		return (-1);
72 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_READ, config,
73 	    config_size, NULL, 0, NULL) < 0) {
74 		error = errno;
75 		free(config);
76 		errno = error;
77 		return (-1);
78 	}
79 
80 	if (config->size > config_size) {
81 		config_size = config->size;
82 		goto fetch;
83 	}
84 
85 	*configp = config;
86 	return (0);
87 }
88 
89 static struct mfi_array *
90 mfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
91 {
92 	struct mfi_array *ar;
93 	char *p;
94 	int i;
95 
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)
100 			return (ar);
101 		p += config->array_size;
102 	}
103 
104 	return (NULL);
105 }
106 
107 static struct mfi_ld_config *
108 mfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
109 {
110 	struct mfi_ld_config *ld;
111 	char *p;
112 	int i;
113 
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)
118 			return (ld);
119 		p += config->log_drv_size;
120 	}
121 
122 	return (NULL);
123 }
124 
125 static int
126 clear_config(int ac, char **av)
127 {
128 	struct mfi_ld_list list;
129 	int ch, error, fd;
130 	u_int i;
131 
132 	fd = mfi_open(mfi_unit);
133 	if (fd < 0) {
134 		error = errno;
135 		warn("mfi_open");
136 		return (error);
137 	}
138 
139 	if (!mfi_reconfig_supported()) {
140 		warnx("The current mfi(4) driver does not support "
141 		    "configuration changes.");
142 		close(fd);
143 		return (EOPNOTSUPP);
144 	}
145 
146 	if (mfi_ld_get_list(fd, &list, NULL) < 0) {
147 		error = errno;
148 		warn("Failed to get volume list");
149 		close(fd);
150 		return (error);
151 	}
152 
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));
157 			close(fd);
158 			return (EBUSY);
159 		}
160 	}
161 
162 	printf(
163 	    "Are you sure you wish to clear the configuration on mfi%u? [y/N] ",
164 	    mfi_unit);
165 	ch = getchar();
166 	if (ch != 'y' && ch != 'Y') {
167 		printf("\nAborting\n");
168 		close(fd);
169 		return (0);
170 	}
171 
172 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) {
173 		error = errno;
174 		warn("Failed to clear configuration");
175 		close(fd);
176 		return (error);
177 	}
178 
179 	printf("mfi%d: Configuration cleared\n", mfi_unit);
180 	close(fd);
181 
182 	return (0);
183 }
184 MFI_COMMAND(top, clear, clear_config);
185 
186 #define	MFI_ARRAY_SIZE		288
187 #define	MAX_DRIVES_PER_ARRAY						\
188 	((MFI_ARRAY_SIZE - sizeof(struct mfi_array)) / 8)
189 
190 #define	RT_RAID0	0
191 #define	RT_RAID1	1
192 #define	RT_RAID5	2
193 #define	RT_RAID6	3
194 #define	RT_JBOD		4
195 #define	RT_CONCAT	5
196 #define	RT_RAID10	6
197 #define	RT_RAID50	7
198 #define	RT_RAID60	8
199 
200 static int
201 compare_int(const void *one, const void *two)
202 {
203 	int first, second;
204 
205 	first = *(const int *)one;
206 	second = *(const int *)two;
207 
208 	return (first - second);
209 }
210 
211 static struct raid_type_entry {
212 	const char *name;
213 	int	raid_type;
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 },
224 	{ "jbod",	RT_JBOD },
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 },
238 	{ NULL,		0 },
239 };
240 
241 struct config_id_state {
242 	int	array_count;
243 	int	log_drv_count;
244 	int	*arrays;
245 	int	*volumes;
246 	uint16_t array_ref;
247 	uint8_t	target_id;
248 };
249 
250 struct array_info {
251 	int	drive_count;
252 	struct mfi_pd_info *drives;
253 	struct mfi_array *array;
254 };
255 
256 /* Parse a comma-separated list of drives for an array. */
257 static int
258 parse_array(int fd, int raid_type, char *array_str, struct array_info *info)
259 {
260 	struct mfi_pd_info *pinfo;
261 	uint16_t device_id;
262 	char *cp;
263 	u_int count;
264 	int error;
265 
266 	cp = array_str;
267 	for (count = 0; cp != NULL; count++) {
268 		cp = strchr(cp, ',');
269 		if (cp != NULL) {
270 			cp++;
271 			if (*cp == ',') {
272 				warnx("Invalid drive list '%s'", array_str);
273 				return (EINVAL);
274 			}
275 		}
276 	}
277 
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);
282 		return (EINVAL);
283 	}
284 	switch (raid_type) {
285 	case RT_RAID1:
286 	case RT_RAID10:
287 		if (count % 2 != 0) {
288 			warnx("RAID1 and RAID10 require an even number of "
289 			    "drives in each array");
290 			return (EINVAL);
291 		}
292 		break;
293 	case RT_RAID5:
294 	case RT_RAID50:
295 		if (count < 3) {
296 			warnx("RAID5 and RAID50 require at least 3 drives in "
297 			    "each array");
298 			return (EINVAL);
299 		}
300 		break;
301 	case RT_RAID6:
302 	case RT_RAID60:
303 		if (count < 4) {
304 			warnx("RAID6 and RAID60 require at least 4 drives in "
305 			    "each array");
306 			return (EINVAL);
307 		}
308 		break;
309 	}
310 
311 	/* Validate each drive. */
312 	info->drives = calloc(count, sizeof(struct mfi_pd_info));
313 	if (info->drives == NULL) {
314 		warnx("malloc failed");
315 		return (ENOMEM);
316 	}
317 	info->drive_count = count;
318 	for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL;
319 	     pinfo++) {
320 		error = mfi_lookup_drive(fd, cp, &device_id);
321 		if (error) {
322 			free(info->drives);
323 			info->drives = NULL;
324 			return (error);
325 		}
326 
327 		if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) {
328 			error = errno;
329 			warn("Failed to fetch drive info for drive %s", cp);
330 			free(info->drives);
331 			info->drives = NULL;
332 			return (error);
333 		}
334 
335 		if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
336 			warnx("Drive %u is not available", device_id);
337 			free(info->drives);
338 			info->drives = NULL;
339 			return (EINVAL);
340 		}
341 	}
342 
343 	return (0);
344 }
345 
346 /*
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.
349  */
350 static uint16_t
351 find_next_array(struct config_id_state *state)
352 {
353 	int i;
354 
355 	/* Assume the current one is used. */
356 	state->array_ref++;
357 
358 	/* Find the next free one. */
359 	for (i = 0; i < state->array_count; i++)
360 		if (state->arrays[i] == state->array_ref)
361 			state->array_ref++;
362 	return (state->array_ref);
363 }
364 
365 /*
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.
368  */
369 static uint8_t
370 find_next_volume(struct config_id_state *state)
371 {
372 	int i;
373 
374 	/* Assume the current one is used. */
375 	state->target_id++;
376 
377 	/* Find the next free one. */
378 	for (i = 0; i < state->log_drv_count; i++)
379 		if (state->volumes[i] == state->target_id)
380 			state->target_id++;
381 	return (state->target_id);
382 }
383 
384 /* Populate an array with drives. */
385 static void
386 build_array(int fd, char *arrayp, struct array_info *array_info,
387     struct config_id_state *state, int verbose)
388 {
389 	struct mfi_array *ar = (struct mfi_array *)arrayp;
390 	int i;
391 
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++) {
396 		if (verbose)
397 			printf("Adding drive %s to array %u\n",
398 			    mfi_drive_name(NULL,
399 			    array_info->drives[i].ref.v.device_id,
400 			    MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS),
401 			    ar->array_ref);
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;
406 	}
407 	array_info->array = ar;
408 }
409 
410 /*
411  * Create a volume that spans one or more arrays.
412  */
413 static void
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)
416 {
417 	struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
418 	struct mfi_array *ar;
419 	int i;
420 
421 	/* properties */
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;
431 
432 	/* params */
433 	switch (raid_type) {
434 	case RT_RAID0:
435 	case RT_JBOD:
436 		ld->params.primary_raid_level = DDF_RAID0;
437 		ld->params.raid_level_qualifier = 0;
438 		ld->params.secondary_raid_level = 0;
439 		break;
440 	case RT_RAID1:
441 		ld->params.primary_raid_level = DDF_RAID1;
442 		ld->params.raid_level_qualifier = 0;
443 		ld->params.secondary_raid_level = 0;
444 		break;
445 	case RT_RAID5:
446 		ld->params.primary_raid_level = DDF_RAID5;
447 		ld->params.raid_level_qualifier = 3;
448 		ld->params.secondary_raid_level = 0;
449 		break;
450 	case RT_RAID6:
451 		ld->params.primary_raid_level = DDF_RAID6;
452 		ld->params.raid_level_qualifier = 3;
453 		ld->params.secondary_raid_level = 0;
454 		break;
455 	case RT_CONCAT:
456 		ld->params.primary_raid_level = DDF_CONCAT;
457 		ld->params.raid_level_qualifier = 0;
458 		ld->params.secondary_raid_level = 0;
459 		break;
460 	case RT_RAID10:
461 		ld->params.primary_raid_level = DDF_RAID1;
462 		ld->params.raid_level_qualifier = 0;
463 		ld->params.secondary_raid_level = 3; /* XXX? */
464 		break;
465 	case RT_RAID50:
466 		/*
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.
472 		 */
473 		ld->params.primary_raid_level = DDF_RAID5;
474 		ld->params.raid_level_qualifier = 3;
475 		ld->params.secondary_raid_level = 3; /* XXX? */
476 		break;
477 	case RT_RAID60:
478 		ld->params.primary_raid_level = DDF_RAID6;
479 		ld->params.raid_level_qualifier = 3;
480 		ld->params.secondary_raid_level = 3; /* XXX? */
481 		break;
482 	}
483 
484 	/*
485 	 * Stripe size is encoded as (2 ^ N) * 512 = stripe_size.  Use
486 	 * ffs() to simulate log2(stripe_size).
487 	 */
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;
494 
495 	/* spans */
496 	for (i = 0; i < narrays; i++) {
497 		ar = arrays[i].array;
498 		if (verbose)
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;
504 	}
505 }
506 
507 static int
508 create_volume(int ac, char **av)
509 {
510 	struct mfi_config_data *config;
511 	struct mfi_array *ar;
512 	struct mfi_ld_config *ld;
513 	struct config_id_state state;
514 	size_t config_size;
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;
519 	int64_t stripe_size;
520 #ifdef DEBUG
521 	int dump;
522 #endif
523 	int ch, verbose;
524 
525 	/*
526 	 * Backwards compat.  Map 'create volume' to 'create' and
527 	 * 'create spare' to 'add'.
528 	 */
529 	if (ac > 1) {
530 		if (strcmp(av[1], "volume") == 0) {
531 			av++;
532 			ac--;
533 		} else if (strcmp(av[1], "spare") == 0) {
534 			av++;
535 			ac--;
536 			return (add_spare(ac, av));
537 		}
538 	}
539 
540 	if (ac < 2) {
541 		warnx("create volume: volume type required");
542 		return (EINVAL);
543 	}
544 
545 	bzero(&state, sizeof(state));
546 	config = NULL;
547 	arrays = NULL;
548 	narrays = 0;
549 	error = 0;
550 
551 	fd = mfi_open(mfi_unit);
552 	if (fd < 0) {
553 		error = errno;
554 		warn("mfi_open");
555 		return (error);
556 	}
557 
558 	if (!mfi_reconfig_supported()) {
559 		warnx("The current mfi(4) driver does not support "
560 		    "configuration changes.");
561 		error = EOPNOTSUPP;
562 		goto error;
563 	}
564 
565 	/* Lookup the RAID type first. */
566 	raid_type = -1;
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;
570 			break;
571 		}
572 
573 	if (raid_type == -1) {
574 		warnx("Unknown or unsupported volume type %s", av[1]);
575 		error = EINVAL;
576 		goto error;
577 	}
578 
579 	/* Parse any options. */
580 	optind = 2;
581 #ifdef DEBUG
582 	dump = 0;
583 #endif
584 	verbose = 0;
585 	stripe_size = 64 * 1024;
586 
587 	while ((ch = getopt(ac, av, "ds:v")) != -1) {
588 		switch (ch) {
589 #ifdef DEBUG
590 		case 'd':
591 			dump = 1;
592 			break;
593 #endif
594 		case 's':
595 			error = dehumanize_number(optarg, &stripe_size);
596 			if (error != 0) {
597 				warnx("Illegal stripe size");
598 				goto error;
599 			}
600 			if ((stripe_size < 512) || (!powerof2(stripe_size))) {
601 				warnx("Illegal stripe size, using 64K");
602 				stripe_size = 64 * 1024;
603 			}
604 			break;
605 		case 'v':
606 			verbose = 1;
607 			break;
608 		case '?':
609 		default:
610 			error = EINVAL;
611 			goto error;
612 		}
613 	}
614 	ac -= optind;
615 	av += optind;
616 
617 	/* Parse all the arrays. */
618 	narrays = ac;
619 	if (narrays == 0) {
620 		warnx("At least one drive list is required");
621 		error = EINVAL;
622 		goto error;
623 	}
624 	switch (raid_type) {
625 	case RT_RAID0:
626 	case RT_RAID1:
627 	case RT_RAID5:
628 	case RT_RAID6:
629 	case RT_CONCAT:
630 		if (narrays != 1) {
631 			warnx("Only one drive list can be specified");
632 			error = EINVAL;
633 			goto error;
634 		}
635 		break;
636 	case RT_RAID10:
637 	case RT_RAID50:
638 	case RT_RAID60:
639 		if (narrays < 1) {
640 			warnx("RAID10, RAID50, and RAID60 require at least "
641 			    "two drive lists");
642 			error = EINVAL;
643 			goto error;
644 		}
645 		if (narrays > MFI_MAX_SPAN_DEPTH) {
646 			warnx("Volume spans more than %d arrays",
647 			    MFI_MAX_SPAN_DEPTH);
648 			error = EINVAL;
649 			goto error;
650 		}
651 		break;
652 	}
653 	arrays = calloc(narrays, sizeof(*arrays));
654 	if (arrays == NULL) {
655 		warnx("malloc failed");
656 		error = ENOMEM;
657 		goto error;
658 	}
659 	for (i = 0; i < narrays; i++) {
660 		error = parse_array(fd, raid_type, av[i], &arrays[i]);
661 		if (error)
662 			goto error;
663 	}
664 
665 	switch (raid_type) {
666 	case RT_RAID10:
667 	case RT_RAID50:
668 	case RT_RAID60:
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 "
672 				    "number of drives");
673 				error = EINVAL;
674 				goto error;
675 			}
676 		}
677 		break;
678 	}
679 
680 	/*
681 	 * Fetch the current config and build sorted lists of existing
682 	 * array and volume identifiers.
683 	 */
684 	if (mfi_config_read(fd, &config) < 0) {
685 		error = errno;
686 		warn("Failed to read configuration");
687 		goto error;
688 	}
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");
697 			error = ENOMEM;
698 			goto error;
699 		}
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;
704 		}
705 		qsort(state.arrays, config->array_count, sizeof(int),
706 		    compare_int);
707 	} else
708 		state.arrays = NULL;
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");
714 			error = ENOMEM;
715 			goto error;
716 		}
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;
721 		}
722 		qsort(state.volumes, config->log_drv_count, sizeof(int),
723 		    compare_int);
724 	} else
725 		state.volumes = NULL;
726 	free(config);
727 
728 	/* Determine the size of the configuration we will build. */
729 	switch (raid_type) {
730 	case RT_RAID0:
731 	case RT_RAID1:
732 	case RT_RAID5:
733 	case RT_RAID6:
734 	case RT_CONCAT:
735 	case RT_JBOD:
736 		/* Each volume spans a single array. */
737 		nvolumes = narrays;
738 		break;
739 	case RT_RAID10:
740 	case RT_RAID50:
741 	case RT_RAID60:
742 		/* A single volume spans multiple arrays. */
743 		nvolumes = 1;
744 		break;
745 	default:
746 		/* Pacify gcc. */
747 		abort();
748 	}
749 
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");
755 		error = ENOMEM;
756 		goto error;
757 	}
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;
767 
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;
772 	}
773 
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,
779 		    &state, verbose);
780 		cfg_volumes += config->log_drv_size;
781 	}
782 
783 #ifdef DEBUG
784 	if (dump)
785 		dump_config(fd, config);
786 #endif
787 
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) {
791 		error = errno;
792 		warn("Failed to add volume");
793 		/* FALLTHROUGH */
794 	}
795 
796 error:
797 	/* Clean up. */
798 	free(config);
799 	free(state.volumes);
800 	free(state.arrays);
801 	if (arrays != NULL) {
802 		for (i = 0; i < narrays; i++)
803 			free(arrays[i].drives);
804 		free(arrays);
805 	}
806 	close(fd);
807 
808 	return (error);
809 }
810 MFI_COMMAND(top, create, create_volume);
811 
812 static int
813 delete_volume(int ac, char **av)
814 {
815 	struct mfi_ld_info info;
816 	int error, fd;
817 	uint8_t target_id, mbox[4];
818 
819 	/*
820 	 * Backwards compat.  Map 'delete volume' to 'delete' and
821 	 * 'delete spare' to 'remove'.
822 	 */
823 	if (ac > 1) {
824 		if (strcmp(av[1], "volume") == 0) {
825 			av++;
826 			ac--;
827 		} else if (strcmp(av[1], "spare") == 0) {
828 			av++;
829 			ac--;
830 			return (remove_spare(ac, av));
831 		}
832 	}
833 
834 	if (ac != 2) {
835 		warnx("delete volume: volume required");
836 		return (EINVAL);
837 	}
838 
839 	fd = mfi_open(mfi_unit);
840 	if (fd < 0) {
841 		error = errno;
842 		warn("mfi_open");
843 		return (error);
844 	}
845 
846 	if (!mfi_reconfig_supported()) {
847 		warnx("The current mfi(4) driver does not support "
848 		    "configuration changes.");
849 		close(fd);
850 		return (EOPNOTSUPP);
851 	}
852 
853 	if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
854 		error = errno;
855 		warn("Invalid volume %s", av[1]);
856 		close(fd);
857 		return (error);
858 	}
859 
860 	if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
861 		error = errno;
862 		warn("Failed to get info for volume %d", target_id);
863 		close(fd);
864 		return (error);
865 	}
866 
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));
870 		close(fd);
871 		return (EBUSY);
872 	}
873 
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) {
877 		error = errno;
878 		warn("Failed to delete volume");
879 		close(fd);
880 		return (error);
881 	}
882 
883 	close(fd);
884 
885 	return (0);
886 }
887 MFI_COMMAND(top, delete, delete_volume);
888 
889 static int
890 add_spare(int ac, char **av)
891 {
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;
897 	uint16_t device_id;
898 	uint8_t target_id;
899 	char *p;
900 	int error, fd, i;
901 
902 	if (ac < 2) {
903 		warnx("add spare: drive required");
904 		return (EINVAL);
905 	}
906 
907 	fd = mfi_open(mfi_unit);
908 	if (fd < 0) {
909 		error = errno;
910 		warn("mfi_open");
911 		return (error);
912 	}
913 
914 	config = NULL;
915 	spare = NULL;
916 	error = mfi_lookup_drive(fd, av[1], &device_id);
917 	if (error)
918 		goto error;
919 
920 	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
921 		error = errno;
922 		warn("Failed to fetch drive info");
923 		goto error;
924 	}
925 
926 	if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
927 		warnx("Drive %u is not available", device_id);
928 		error = EINVAL;
929 		goto error;
930 	}
931 
932 	if (ac > 2) {
933 		if (mfi_lookup_volume(fd, av[2], &target_id) < 0) {
934 			error = errno;
935 			warn("Invalid volume %s", av[2]);
936 			goto error;
937 		}
938 	}
939 
940 	if (mfi_config_read(fd, &config) < 0) {
941 		error = errno;
942 		warn("Failed to read configuration");
943 		goto error;
944 	}
945 
946 	spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) *
947 	    config->array_count);
948 	if (spare == NULL) {
949 		warnx("malloc failed");
950 		error = ENOMEM;
951 		goto error;
952 	}
953 	bzero(spare, sizeof(struct mfi_spare));
954 	spare->ref = info.ref;
955 
956 	if (ac == 2) {
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",
963 				    ar->array_ref);
964 				error = EINVAL;
965 				goto error;
966 			}
967 			p += config->array_size;
968 		}
969 		spare->array_count = 0;
970 	} else  {
971 		/*
972 		 * Dedicated spares only back the arrays for a
973 		 * specific volume.
974 		 */
975 		ld = mfi_config_lookup_volume(config, target_id);
976 		if (ld == NULL) {
977 			warnx("Did not find volume %d", target_id);
978 			error = EINVAL;
979 			goto error;
980 		}
981 
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);
987 			if (ar == NULL) {
988 				warnx("Missing array; inconsistent config?");
989 				error = ENXIO;
990 				goto error;
991 			}
992 			if (ar->size > info.coerced_size) {
993 				warnx("Spare isn't large enough for array %u",
994 				    ar->array_ref);
995 				error = EINVAL;
996 				goto error;
997 			}
998 			spare->array_ref[i] = ar->array_ref;
999 		}
1000 	}
1001 
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) {
1005 		error = errno;
1006 		warn("Failed to assign spare");
1007 		/* FALLTHROUGH. */
1008 	}
1009 
1010 error:
1011 	free(spare);
1012 	free(config);
1013 	close(fd);
1014 
1015 	return (error);
1016 }
1017 MFI_COMMAND(top, add, add_spare);
1018 
1019 static int
1020 remove_spare(int ac, char **av)
1021 {
1022 	struct mfi_pd_info info;
1023 	int error, fd;
1024 	uint16_t device_id;
1025 	uint8_t mbox[4];
1026 
1027 	if (ac != 2) {
1028 		warnx("remove spare: drive required");
1029 		return (EINVAL);
1030 	}
1031 
1032 	fd = mfi_open(mfi_unit);
1033 	if (fd < 0) {
1034 		error = errno;
1035 		warn("mfi_open");
1036 		return (error);
1037 	}
1038 
1039 	error = mfi_lookup_drive(fd, av[1], &device_id);
1040 	if (error) {
1041 		close(fd);
1042 		return (error);
1043 	}
1044 
1045 	/* Get the info for this drive. */
1046 	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
1047 		error = errno;
1048 		warn("Failed to fetch info for drive %u", device_id);
1049 		close(fd);
1050 		return (error);
1051 	}
1052 
1053 	if (info.fw_state != MFI_PD_STATE_HOT_SPARE) {
1054 		warnx("Drive %u is not a hot spare", device_id);
1055 		close(fd);
1056 		return (EINVAL);
1057 	}
1058 
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) {
1062 		error = errno;
1063 		warn("Failed to delete spare");
1064 		close(fd);
1065 		return (error);
1066 	}
1067 
1068 	close(fd);
1069 
1070 	return (0);
1071 }
1072 MFI_COMMAND(top, remove, remove_spare);
1073 
1074 #ifdef DEBUG
1075 /* Display raw data about a config. */
1076 static void
1077 dump_config(int fd, struct mfi_config_data *config)
1078 {
1079 	struct mfi_array *ar;
1080 	struct mfi_ld_config *ld;
1081 	struct mfi_spare *sp;
1082 	struct mfi_pd_info pinfo;
1083 	uint16_t device_id;
1084 	char *p;
1085 	int i, j;
1086 
1087 	printf(
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;
1095 
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,
1099 		    ar->num_drives);
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");
1105 			else {
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,
1109 				    NULL) >= 0) {
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);
1116 				}
1117 			}
1118 		}
1119 		p += config->array_size;
1120 	}
1121 
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));
1126 		printf("%s %s",
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);
1132 		printf("\n");
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);
1150 		}
1151 		p += config->log_drv_size;
1152 	}
1153 
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;
1164 	}
1165 }
1166 
1167 static int
1168 debug_config(int ac, char **av)
1169 {
1170 	struct mfi_config_data *config;
1171 	int error, fd;
1172 
1173 	if (ac != 1) {
1174 		warnx("debug: extra arguments");
1175 		return (EINVAL);
1176 	}
1177 
1178 	fd = mfi_open(mfi_unit);
1179 	if (fd < 0) {
1180 		error = errno;
1181 		warn("mfi_open");
1182 		return (error);
1183 	}
1184 
1185 	/* Get the config from the controller. */
1186 	if (mfi_config_read(fd, &config) < 0) {
1187 		error = errno;
1188 		warn("Failed to get config");
1189 		close(fd);
1190 		return (error);
1191 	}
1192 
1193 	/* Dump out the configuration. */
1194 	dump_config(fd, config);
1195 	free(config);
1196 	close(fd);
1197 
1198 	return (0);
1199 }
1200 MFI_COMMAND(top, debug, debug_config);
1201 
1202 static int
1203 dump(int ac, char **av)
1204 {
1205 	struct mfi_config_data *config;
1206 	char buf[64];
1207 	size_t len;
1208 	int error, fd;
1209 
1210 	if (ac != 1) {
1211 		warnx("dump: extra arguments");
1212 		return (EINVAL);
1213 	}
1214 
1215 	fd = mfi_open(mfi_unit);
1216 	if (fd < 0) {
1217 		error = errno;
1218 		warn("mfi_open");
1219 		return (error);
1220 	}
1221 
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) {
1225 		error = errno;
1226 		warn("Failed to read debug command");
1227 		if (error == ENOENT)
1228 			error = EOPNOTSUPP;
1229 		close(fd);
1230 		return (error);
1231 	}
1232 
1233 	config = malloc(len);
1234 	if (config == NULL) {
1235 		warnx("malloc failed");
1236 		close(fd);
1237 		return (ENOMEM);
1238 	}
1239 	if (sysctlbyname(buf, config, &len, NULL, 0) < 0) {
1240 		error = errno;
1241 		warn("Failed to read debug command");
1242 		free(config);
1243 		close(fd);
1244 		return (error);
1245 	}
1246 	dump_config(fd, config);
1247 	free(config);
1248 	close(fd);
1249 
1250 	return (0);
1251 }
1252 MFI_COMMAND(top, dump, dump);
1253 #endif
1254