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