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