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