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