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