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