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