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