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