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