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