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