1116743Ssam/*-
2186904Ssam * SPDX-License-Identifier: BSD-3-Clause
3116743Ssam *
4116743Ssam * Copyright (c) 2008, 2009 Yahoo!, Inc.
5116743Ssam * All rights reserved.
6116743Ssam *
7116743Ssam * Redistribution and use in source and binary forms, with or without
8116743Ssam * modification, are permitted provided that the following conditions
9116743Ssam * are met:
10116743Ssam * 1. Redistributions of source code must retain the above copyright
11116743Ssam *    notice, this list of conditions and the following disclaimer.
12116743Ssam * 2. Redistributions in binary form must reproduce the above copyright
13116743Ssam *    notice, this list of conditions and the following disclaimer in the
14116743Ssam *    documentation and/or other materials provided with the distribution.
15116743Ssam * 3. The names of the authors may not be used to endorse or promote
16116743Ssam *    products derived from this software without specific prior written
17116743Ssam *    permission.
18116743Ssam *
19116743Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20116743Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21116743Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22116743Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23116743Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24116743Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25116743Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26116743Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27116743Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28116743Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29116743Ssam * SUCH DAMAGE.
30116743Ssam */
31116743Ssam
32116743Ssam#include <sys/param.h>
33116743Ssam#ifdef DEBUG
34116743Ssam#include <sys/sysctl.h>
35116743Ssam#endif
36116743Ssam#include <ctype.h>
37116743Ssam#include <err.h>
38185522Ssam#include <errno.h>
39185522Ssam#include <fcntl.h>
40119783Ssam#include <libutil.h>
41116743Ssam#include <stdint.h>
42138570Ssam#include <stdio.h>
43116743Ssam#include <stdlib.h>
44116743Ssam#include <string.h>
45116743Ssam#include <unistd.h>
46155481Ssam#include "mfiutil.h"
47116743Ssam
48155481Ssamstatic int	add_spare(int ac, char **av);
49155481Ssamstatic int	remove_spare(int ac, char **av);
50170530Ssam
51155481Ssamstatic long
52178354Ssamdehumanize(const char *value)
53178354Ssam{
54140438Ssam	char    *vtp;
55138570Ssam	long    iv;
56155480Ssam
57138570Ssam	if (value == NULL)
58116743Ssam		return (0);
59147067Ssam	iv = strtoq(value, &vtp, 0);
60147067Ssam	if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
61147067Ssam		return (0);
62147067Ssam	}
63147057Ssam	switch (vtp[0]) {
64147057Ssam	case 't': case 'T':
65147057Ssam		iv *= 1024;
66147057Ssam	case 'g': case 'G':
67147057Ssam		iv *= 1024;
68147057Ssam	case 'm': case 'M':
69147057Ssam		iv *= 1024;
70147057Ssam	case 'k': case 'K':
71147057Ssam		iv *= 1024;
72147057Ssam	case '\0':
73147057Ssam		break;
74170530Ssam	default:
75170530Ssam		return (0);
76170530Ssam	}
77170530Ssam	return (iv);
78170530Ssam}
79170530Ssam
80170530Ssamint
81170530Ssammfi_config_read(int fd, struct mfi_config_data **configp)
82138570Ssam{
83116743Ssam	return mfi_config_read_opcode(fd, MFI_DCMD_CFG_READ, configp, NULL, 0);
84119150Ssam}
85178354Ssam
86178354Ssamint
87170530Ssammfi_config_read_opcode(int fd, uint32_t opcode, struct mfi_config_data **configp,
88138570Ssam	uint8_t *mbox, size_t mboxlen)
89116743Ssam{
90138570Ssam	struct mfi_config_data *config;
91138570Ssam	uint32_t config_size;
92116743Ssam	int error;
93138570Ssam
94138570Ssam	/*
95138570Ssam	 * Keep fetching the config in a loop until we have a large enough
96138570Ssam	 * buffer to hold the entire configuration.
97138570Ssam	 */
98138570Ssam	config = NULL;
99138570Ssam	config_size = 1024;
100138570Ssamfetch:
101138570Ssam	config = reallocf(config, config_size);
102138570Ssam	if (config == NULL)
103184358Ssam		return (-1);
104184358Ssam	if (mfi_dcmd_command(fd, opcode, config,
105184358Ssam	    config_size, mbox, mboxlen, NULL) < 0) {
106138570Ssam		error = errno;
107116743Ssam		free(config);
108138570Ssam		errno = error;
109170530Ssam		return (-1);
110170530Ssam	}
111116743Ssam
112186904Ssam	if (config->size > config_size) {
113186904Ssam		config_size = config->size;
114116743Ssam		goto fetch;
115165185Ssam	}
116116743Ssam
117138570Ssam	*configp = config;
118116743Ssam	return (0);
119116743Ssam}
120116743Ssam
121140438Ssamstatic struct mfi_array *
122116743Ssammfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
123116743Ssam{
124138570Ssam	struct mfi_array *ar;
125116743Ssam	char *p;
126186904Ssam	int i;
127186904Ssam
128138570Ssam	p = (char *)config->array;
129138570Ssam	for (i = 0; i < config->array_count; i++) {
130138570Ssam		ar = (struct mfi_array *)p;
131138570Ssam		if (ar->array_ref == array_ref)
132138570Ssam			return (ar);
133138570Ssam		p += config->array_size;
134138570Ssam	}
135158298Ssam
136138570Ssam	return (NULL);
137138570Ssam}
138138570Ssam
139138570Ssamstatic struct mfi_ld_config *
140138570Ssammfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
141138570Ssam{
142138570Ssam	struct mfi_ld_config *ld;
143138570Ssam	char *p;
144138570Ssam	int i;
145138570Ssam
146138570Ssam	p = (char *)config->array + config->array_count * config->array_size;
147138570Ssam	for (i = 0; i < config->log_drv_count; i++) {
148138570Ssam		ld = (struct mfi_ld_config *)p;
149138570Ssam		if (ld->properties.ld.v.target_id == target_id)
150138570Ssam			return (ld);
151138570Ssam		p += config->log_drv_size;
152138570Ssam	}
153178354Ssam
154186904Ssam	return (NULL);
155186904Ssam}
156156073Ssam
157138570Ssamstatic int
158138570Ssamclear_config(int ac __unused, char **av __unused)
159138570Ssam{
160138570Ssam	struct mfi_ld_list list;
161155482Ssam	int ch, error, fd;
162170530Ssam	u_int i;
163170530Ssam
164170530Ssam	fd = mfi_open(mfi_device, O_RDWR);
165170530Ssam	if (fd < 0) {
166170530Ssam		error = errno;
167170530Ssam		warn("mfi_open");
168170530Ssam		return (error);
169138570Ssam	}
170138570Ssam
171155482Ssam	if (!mfi_reconfig_supported(mfi_device)) {
172155482Ssam		warnx("The current %s driver does not support "
173155482Ssam		    "configuration changes.", mfi_device);
174167252Ssam		close(fd);
175161425Simp		return (EOPNOTSUPP);
176138570Ssam	}
177138570Ssam
178138570Ssam	if (mfi_ld_get_list(fd, &list, NULL) < 0) {
179138570Ssam		error = errno;
180138570Ssam		warn("Failed to get volume list");
181138570Ssam		close(fd);
182138570Ssam		return (error);
183138570Ssam	}
184170530Ssam
185138570Ssam	for (i = 0; i < list.ld_count; i++) {
186138570Ssam		if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) {
187138570Ssam			warnx("Volume %s is busy and cannot be deleted",
188138570Ssam			    mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
189138570Ssam			close(fd);
190178354Ssam			return (EBUSY);
191178354Ssam		}
192178354Ssam	}
193178354Ssam
194138570Ssam	printf(
195178354Ssam	    "Are you sure you wish to clear the configuration on %s? [y/N] ",
196178354Ssam	    mfi_device);
197178354Ssam	ch = getchar();
198178354Ssam	if (ch != 'y' && ch != 'Y') {
199178354Ssam		printf("\nAborting\n");
200178354Ssam		close(fd);
201178354Ssam		return (0);
202178354Ssam	}
203178354Ssam
204178354Ssam	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) {
205178354Ssam		error = errno;
206178354Ssam		warn("Failed to clear configuration");
207178354Ssam		close(fd);
208178354Ssam		return (error);
209178354Ssam	}
210155491Ssam
211155486Ssam	printf("%s: Configuration cleared\n", mfi_device);
212155486Ssam	close(fd);
213116743Ssam
214147256Sbrooks	return (0);
215138570Ssam}
216138570SsamMFI_COMMAND(top, clear, clear_config);
217178354Ssam
218178354Ssam#define MAX_DRIVES_PER_ARRAY MFI_MAX_ROW_SIZE
219178354Ssam#define MFI_ARRAY_SIZE sizeof(struct mfi_array)
220178354Ssam
221178354Ssam#define	RT_RAID0	0
222178354Ssam#define	RT_RAID1	1
223138570Ssam#define	RT_RAID5	2
224116743Ssam#define	RT_RAID6	3
225159290Ssam#define	RT_JBOD		4
226159290Ssam#define	RT_CONCAT	5
227116743Ssam#define	RT_RAID10	6
228116743Ssam#define	RT_RAID50	7
229155491Ssam#define	RT_RAID60	8
230116743Ssam
231138570Ssamstatic int
232155486Ssamcompare_int(const void *one, const void *two)
233138570Ssam{
234178354Ssam	int first, second;
235178354Ssam
236178354Ssam	first = *(const int *)one;
237178354Ssam	second = *(const int *)two;
238178354Ssam
239178354Ssam	return (first - second);
240178354Ssam}
241178354Ssam
242178354Ssamstatic struct raid_type_entry {
243178354Ssam	const char *name;
244178354Ssam	int	raid_type;
245155496Ssam} raid_type_table[] = {
246178354Ssam	{ "raid0",	RT_RAID0 },
247165571Ssam	{ "raid-0",	RT_RAID0 },
248170530Ssam	{ "raid1",	RT_RAID1 },
249178354Ssam	{ "raid-1",	RT_RAID1 },
250178354Ssam	{ "mirror",	RT_RAID1 },
251178354Ssam	{ "raid5",	RT_RAID5 },
252178354Ssam	{ "raid-5",	RT_RAID5 },
253178354Ssam	{ "raid6",	RT_RAID6 },
254178354Ssam	{ "raid-6",	RT_RAID6 },
255179401Ssam	{ "jbod",	RT_JBOD },
256185744Ssam	{ "concat",	RT_CONCAT },
257186904Ssam	{ "raid10",	RT_RAID10 },
258189380Ssam	{ "raid1+0",	RT_RAID10 },
259185744Ssam	{ "raid-10",	RT_RAID10 },
260178751Ssam	{ "raid-1+0",	RT_RAID10 },
261178751Ssam	{ "raid50",	RT_RAID50 },
262116743Ssam	{ "raid5+0",	RT_RAID50 },
263188783Ssam	{ "raid-50",	RT_RAID50 },
264116743Ssam	{ "raid-5+0",	RT_RAID50 },
265116743Ssam	{ "raid60",	RT_RAID60 },
266155490Ssam	{ "raid6+0",	RT_RAID60 },
267138570Ssam	{ "raid-60",	RT_RAID60 },
268170530Ssam	{ "raid-6+0",	RT_RAID60 },
269187831Ssam	{ NULL,		0 },
270170530Ssam};
271116743Ssam
272140432Ssamstruct config_id_state {
273140432Ssam	int	array_count;
274140761Ssam	int	log_drv_count;
275140761Ssam	int	*arrays;
276140432Ssam	int	*volumes;
277140432Ssam	uint16_t array_ref;
278140432Ssam	uint8_t	target_id;
279138570Ssam};
280170530Ssam
281155483Ssamstruct array_info {
282170530Ssam	int	drive_count;
283170530Ssam	struct mfi_pd_info *drives;
284138570Ssam	struct mfi_array *array;
285116743Ssam};
286138570Ssam
287147057Ssam/* Parse a comma-separated list of drives for an array. */
288116743Ssamstatic int
289140432Ssamparse_array(int fd, int raid_type, char *array_str, struct array_info *info)
290140432Ssam{
291140432Ssam	struct mfi_pd_info *pinfo;
292140432Ssam	uint16_t device_id;
293184368Ssam	char *cp;
294140432Ssam	u_int count;
295140432Ssam	int error;
296138570Ssam
297155515Ssam	cp = array_str;
298155515Ssam	for (count = 0; cp != NULL; count++) {
299155515Ssam		cp = strchr(cp, ',');
300178354Ssam		if (cp != NULL) {
301127698Ssam			cp++;
302178354Ssam			if (*cp == ',') {
303140761Ssam				warnx("Invalid drive list '%s'", array_str);
304154140Ssam				return (EINVAL);
305119783Ssam			}
306178354Ssam		}
307138570Ssam	}
308170530Ssam
309116743Ssam	/* Validate the number of drives for this array. */
310116743Ssam	if (count >= MAX_DRIVES_PER_ARRAY) {
311138570Ssam		warnx("Too many drives for a single array: max is %d",
312138570Ssam		    MAX_DRIVES_PER_ARRAY);
313155492Ssam		return (EINVAL);
314116743Ssam	}
315138570Ssam	switch (raid_type) {
316138570Ssam	case RT_RAID1:
317138570Ssam	case RT_RAID10:
318155482Ssam		if (count % 2 != 0) {
319138570Ssam			warnx("RAID1 and RAID10 require an even number of "
320138570Ssam			    "drives in each array");
321138570Ssam			return (EINVAL);
322138570Ssam		}
323116743Ssam		break;
324116743Ssam	case RT_RAID5:
325138570Ssam	case RT_RAID50:
326138570Ssam		if (count < 3) {
327116743Ssam			warnx("RAID5 and RAID50 require at least 3 drives in "
328138570Ssam			    "each array");
329138570Ssam			return (EINVAL);
330138570Ssam		}
331116743Ssam		break;
332138570Ssam	case RT_RAID6:
333138570Ssam	case RT_RAID60:
334138570Ssam		if (count < 4) {
335138570Ssam			warnx("RAID6 and RAID60 require at least 4 drives in "
336138570Ssam			    "each array");
337138570Ssam			return (EINVAL);
338178354Ssam		}
339178354Ssam		break;
340178354Ssam	}
341116743Ssam
342116743Ssam	/* Validate each drive. */
343185744Ssam	info->drives = calloc(count, sizeof(struct mfi_pd_info));
344185744Ssam	if (info->drives == NULL) {
345155485Ssam		warnx("malloc failed");
346186904Ssam		return (ENOMEM);
347186904Ssam	}
348186904Ssam	info->drive_count = count;
349186904Ssam	for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL;
350186904Ssam	     pinfo++) {
351186904Ssam		error = mfi_lookup_drive(fd, cp, &device_id);
352186904Ssam		if (error) {
353186904Ssam			free(info->drives);
354186904Ssam			info->drives = NULL;
355186904Ssam			return (error);
356186904Ssam		}
357186904Ssam
358116743Ssam		if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) {
359116743Ssam			error = errno;
360121100Ssam			warn("Failed to fetch drive info for drive %s", cp);
361121100Ssam			free(info->drives);
362167252Ssam			info->drives = NULL;
363121100Ssam			return (error);
364121100Ssam		}
365121100Ssam
366121100Ssam		if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
367121100Ssam			warnx("Drive %u is not available", device_id);
368138570Ssam			free(info->drives);
369138570Ssam			info->drives = NULL;
370155482Ssam			return (EINVAL);
371155482Ssam		}
372155482Ssam
373167252Ssam		if (pinfo->state.ddf.v.pd_type.is_foreign) {
374155482Ssam			warnx("Drive %u is foreign", device_id);
375121100Ssam			free(info->drives);
376121100Ssam			info->drives = NULL;
377121100Ssam			return (EINVAL);
378121100Ssam		}
379121100Ssam	}
380121100Ssam
381116743Ssam	return (0);
382116743Ssam}
383116743Ssam
384116743Ssam/*
385116743Ssam * Find the next free array ref assuming that 'array_ref' is the last
386116743Ssam * one used.  'array_ref' should be 0xffff for the initial test.
387116743Ssam */
388116743Ssamstatic uint16_t
389116743Ssamfind_next_array(struct config_id_state *state)
390116743Ssam{
391138570Ssam	int i;
392138570Ssam
393116743Ssam	/* Assume the current one is used. */
394116743Ssam	state->array_ref++;
395186904Ssam
396186904Ssam	/* Find the next free one. */
397116743Ssam	for (i = 0; i < state->array_count; i++)
398116743Ssam		if (state->arrays[i] == state->array_ref)
399116743Ssam			state->array_ref++;
400116743Ssam	return (state->array_ref);
401138570Ssam}
402138570Ssam
403178354Ssam/*
404178354Ssam * Find the next free volume ID assuming that 'target_id' is the last
405178354Ssam * one used.  'target_id' should be 0xff for the initial test.
406178354Ssam */
407116743Ssamstatic uint8_t
408116743Ssamfind_next_volume(struct config_id_state *state)
409116743Ssam{
410116743Ssam	int i;
411116743Ssam
412116743Ssam	/* Assume the current one is used. */
413116743Ssam	state->target_id++;
414116743Ssam
415116743Ssam	/* Find the next free one. */
416116743Ssam	for (i = 0; i < state->log_drv_count; i++)
417155515Ssam		if (state->volumes[i] == state->target_id)
418155515Ssam			state->target_id++;
419138570Ssam	return (state->target_id);
420138570Ssam}
421116743Ssam
422116743Ssam/* Populate an array with drives. */
423138570Ssamstatic void
424138570Ssambuild_array(int fd __unused, char *arrayp, struct array_info *array_info,
425116743Ssam    struct config_id_state *state, int verbose)
426116743Ssam{
427116743Ssam	struct mfi_array *ar = (struct mfi_array *)arrayp;
428116743Ssam	int i;
429116743Ssam
430116743Ssam	ar->size = array_info->drives[0].coerced_size;
431116743Ssam	ar->num_drives = array_info->drive_count;
432116743Ssam	ar->array_ref = find_next_array(state);
433116743Ssam	for (i = 0; i < array_info->drive_count; i++) {
434116743Ssam		if (verbose)
435116743Ssam			printf("Adding drive %s to array %u\n",
436116743Ssam			    mfi_drive_name(NULL,
437116743Ssam			    array_info->drives[i].ref.v.device_id,
438116743Ssam			    MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS),
439186904Ssam			    ar->array_ref);
440186904Ssam		if (ar->size > array_info->drives[i].coerced_size)
441116743Ssam			ar->size = array_info->drives[i].coerced_size;
442186904Ssam		ar->pd[i].ref = array_info->drives[i].ref;
443116743Ssam		ar->pd[i].fw_state = MFI_PD_STATE_ONLINE;
444116743Ssam	}
445116743Ssam	array_info->array = ar;
446116743Ssam}
447116743Ssam
448116743Ssam/*
449116743Ssam * Create a volume that spans one or more arrays.
450116743Ssam */
451116743Ssamstatic void
452116743Ssambuild_volume(char *volumep, int narrays, struct array_info *arrays,
453138570Ssam    int raid_type, long stripe_size, struct config_id_state *state, int verbose)
454138570Ssam{
455116743Ssam	struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
456116743Ssam	struct mfi_array *ar;
457116743Ssam	int i;
458116743Ssam
459116743Ssam	/* properties */
460116743Ssam	ld->properties.ld.v.target_id = find_next_volume(state);
461155515Ssam	ld->properties.ld.v.seq = 0;
462155515Ssam	ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
463185744Ssam	    MR_LD_CACHE_WRITE_BACK;
464185744Ssam	ld->properties.access_policy = MFI_LD_ACCESS_RW;
465185744Ssam	ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED;
466185744Ssam	ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
467185744Ssam	    MR_LD_CACHE_WRITE_BACK;
468185744Ssam	ld->properties.no_bgi = 0;
469185744Ssam
470185744Ssam	/* params */
471185744Ssam	switch (raid_type) {
472185744Ssam	case RT_RAID0:
473116743Ssam	case RT_JBOD:
474116743Ssam		ld->params.primary_raid_level = DDF_RAID0;
475138570Ssam		ld->params.raid_level_qualifier = 0;
476138570Ssam		ld->params.secondary_raid_level = 0;
477116743Ssam		break;
478116743Ssam	case RT_RAID1:
479186904Ssam		ld->params.primary_raid_level = DDF_RAID1;
480186904Ssam		ld->params.raid_level_qualifier = 0;
481138570Ssam		ld->params.secondary_raid_level = 0;
482138570Ssam		break;
483116743Ssam	case RT_RAID5:
484138570Ssam		ld->params.primary_raid_level = DDF_RAID5;
485138570Ssam		ld->params.raid_level_qualifier = 3;
486138570Ssam		ld->params.secondary_raid_level = 0;
487138570Ssam		break;
488138570Ssam	case RT_RAID6:
489116743Ssam		ld->params.primary_raid_level = DDF_RAID6;
490116743Ssam		ld->params.raid_level_qualifier = 3;
491116743Ssam		ld->params.secondary_raid_level = 0;
492116743Ssam		break;
493116743Ssam	case RT_CONCAT:
494116743Ssam		ld->params.primary_raid_level = DDF_CONCAT;
495116743Ssam		ld->params.raid_level_qualifier = 0;
496116743Ssam		ld->params.secondary_raid_level = 0;
497138570Ssam		break;
498138570Ssam	case RT_RAID10:
499138570Ssam		ld->params.primary_raid_level = DDF_RAID1;
500155732Ssam		ld->params.raid_level_qualifier = 0;
501170530Ssam		ld->params.secondary_raid_level = 3; /* XXX? */
502116743Ssam		break;
503116743Ssam	case RT_RAID50:
504116743Ssam		/*
505116743Ssam		 * XXX: This appears to work though the card's BIOS
506116743Ssam		 * complains that the configuration is foreign.  The
507116743Ssam		 * BIOS setup does not allow for creation of RAID-50
508138570Ssam		 * or RAID-60 arrays.  The only nested array
509138570Ssam		 * configuration it allows for is RAID-10.
510138570Ssam		 */
511138570Ssam		ld->params.primary_raid_level = DDF_RAID5;
512186904Ssam		ld->params.raid_level_qualifier = 3;
513186904Ssam		ld->params.secondary_raid_level = 3; /* XXX? */
514186904Ssam		break;
515186904Ssam	case RT_RAID60:
516116743Ssam		ld->params.primary_raid_level = DDF_RAID6;
517116743Ssam		ld->params.raid_level_qualifier = 3;
518138570Ssam		ld->params.secondary_raid_level = 3; /* XXX? */
519138570Ssam		break;
520138570Ssam	}
521138570Ssam
522155515Ssam	/*
523155515Ssam	 * Stripe size is encoded as (2 ^ N) * 512 = stripe_size.  Use
524138570Ssam	 * ffs() to simulate log2(stripe_size).
525138570Ssam	 */
526138570Ssam	ld->params.stripe_size = ffs(stripe_size) - 1 - 9;
527138570Ssam	ld->params.num_drives = arrays[0].array->num_drives;
528138570Ssam	ld->params.span_depth = narrays;
529138570Ssam	ld->params.state = MFI_LD_STATE_OPTIMAL;
530138570Ssam	ld->params.init_state = MFI_LD_PARAMS_INIT_NO;
531138570Ssam	ld->params.is_consistent = 0;
532138570Ssam
533138570Ssam	/* spans */
534138570Ssam	for (i = 0; i < narrays; i++) {
535138570Ssam		ar = arrays[i].array;
536138570Ssam		if (verbose)
537138570Ssam			printf("Adding array %u to volume %u\n", ar->array_ref,
538138570Ssam			    ld->properties.ld.v.target_id);
539138570Ssam		ld->span[i].start_block = 0;
540138570Ssam		ld->span[i].num_blocks = ar->size;
541138570Ssam		ld->span[i].array_ref = ar->array_ref;
542138570Ssam	}
543138570Ssam}
544138570Ssam
545155515Ssamstatic int
546184369Ssamcreate_volume(int ac, char **av)
547184369Ssam{
548183221Ssam	struct mfi_config_data *config;
549155489Ssam	struct mfi_array *ar;
550183221Ssam	struct mfi_ld_config *ld;
551183221Ssam	struct config_id_state state;
552183221Ssam	size_t config_size;
553182893Srpaulo	char *p, *cfg_arrays, *cfg_volumes;
554183221Ssam	int error, fd, i, raid_type;
555184369Ssam	int narrays, nvolumes, arrays_per_volume;
556184369Ssam	struct array_info *arrays;
557184369Ssam	long stripe_size;
558184369Ssam#ifdef DEBUG
559138570Ssam	int dump;
560138570Ssam#endif
561178354Ssam	int ch, verbose;
562178354Ssam
563178354Ssam	/*
564178354Ssam	 * Backwards compat.  Map 'create volume' to 'create' and
565162410Ssam	 * 'create spare' to 'add'.
566138570Ssam	 */
567162410Ssam	if (ac > 1) {
568162410Ssam		if (strcmp(av[1], "volume") == 0) {
569162410Ssam			av++;
570162410Ssam			ac--;
571178354Ssam		} else if (strcmp(av[1], "spare") == 0) {
572178354Ssam			av++;
573138570Ssam			ac--;
574138570Ssam			return (add_spare(ac, av));
575138570Ssam		}
576138570Ssam	}
577138570Ssam
578138570Ssam	if (ac < 2) {
579138570Ssam		warnx("create volume: volume type required");
580138570Ssam		return (EINVAL);
581166954Ssam	}
582166954Ssam
583166954Ssam	bzero(&state, sizeof(state));
584166954Ssam	config = NULL;
585138570Ssam	arrays = NULL;
586138570Ssam	narrays = 0;
587138570Ssam	error = 0;
588138570Ssam
589138570Ssam	fd = mfi_open(mfi_device, O_RDWR);
590138570Ssam	if (fd < 0) {
591138570Ssam		error = errno;
592138570Ssam		warn("mfi_open");
593138570Ssam		return (error);
594138570Ssam	}
595138570Ssam
596138570Ssam	if (!mfi_reconfig_supported(mfi_device)) {
597138570Ssam		warnx("The current %s(4) driver does not support "
598138570Ssam		    "configuration changes.", mfi_device);
599138570Ssam		error = EOPNOTSUPP;
600138570Ssam		goto error;
601138570Ssam	}
602138570Ssam
603138570Ssam	/* Lookup the RAID type first. */
604138570Ssam	raid_type = -1;
605138570Ssam	for (i = 0; raid_type_table[i].name != NULL; i++)
606138570Ssam		if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
607138570Ssam			raid_type = raid_type_table[i].raid_type;
608138570Ssam			break;
609138570Ssam		}
610138570Ssam
611138570Ssam	if (raid_type == -1) {
612138570Ssam		warnx("Unknown or unsupported volume type %s", av[1]);
613147057Ssam		error = EINVAL;
614147057Ssam		goto error;
615147057Ssam	}
616147057Ssam
617147057Ssam	/* Parse any options. */
618147057Ssam	optind = 2;
619147057Ssam#ifdef DEBUG
620147057Ssam	dump = 0;
621170530Ssam#endif
622170530Ssam	verbose = 0;
623178354Ssam	stripe_size = 64 * 1024;
624178354Ssam
625178354Ssam	while ((ch = getopt(ac, av, "ds:v")) != -1) {
626178354Ssam		switch (ch) {
627178354Ssam#ifdef DEBUG
628178354Ssam		case 'd':
629178354Ssam			dump = 1;
630178354Ssam			break;
631155515Ssam#endif
632155515Ssam		case 's':
633155515Ssam			stripe_size = dehumanize(optarg);
634155515Ssam			if ((stripe_size < 512) || (!powerof2(stripe_size)))
635155515Ssam				stripe_size = 64 * 1024;
636155515Ssam			break;
637155515Ssam		case 'v':
638155515Ssam			verbose = 1;
639155515Ssam			break;
640155515Ssam		case '?':
641155515Ssam		default:
642155515Ssam			error = EINVAL;
643155515Ssam			goto error;
644155515Ssam		}
645155515Ssam	}
646155515Ssam	ac -= optind;
647155515Ssam	av += optind;
648155515Ssam
649184354Ssam	/* Parse all the arrays. */
650184354Ssam	narrays = ac;
651184354Ssam	if (narrays == 0) {
652184354Ssam		warnx("At least one drive list is required");
653184354Ssam		error = EINVAL;
654184354Ssam		goto error;
655154140Ssam	}
656154140Ssam	switch (raid_type) {
657155515Ssam	case RT_RAID0:
658155515Ssam	case RT_RAID1:
659155515Ssam	case RT_RAID5:
660155515Ssam	case RT_RAID6:
661155515Ssam	case RT_CONCAT:
662155515Ssam		if (narrays != 1) {
663155515Ssam			warnx("Only one drive list can be specified");
664165571Ssam			error = EINVAL;
665165571Ssam			goto error;
666165571Ssam		}
667165571Ssam		break;
668165571Ssam	case RT_RAID10:
669166016Ssam	case RT_RAID50:
670166016Ssam	case RT_RAID60:
671166016Ssam		if (narrays < 1) {
672166016Ssam			warnx("RAID10, RAID50, and RAID60 require at least "
673166016Ssam			    "two drive lists");
674170530Ssam			error = EINVAL;
675170530Ssam			goto error;
676170530Ssam		}
677170530Ssam		if (narrays > MFI_MAX_SPAN_DEPTH) {
678170530Ssam			warnx("Volume spans more than %d arrays",
679170530Ssam			    MFI_MAX_SPAN_DEPTH);
680170530Ssam			error = EINVAL;
681170530Ssam			goto error;
682116743Ssam		}
683116743Ssam		break;
684116743Ssam	}
685165185Ssam	arrays = calloc(narrays, sizeof(*arrays));
686165185Ssam	if (arrays == NULL) {
687116743Ssam		warnx("malloc failed");
688116743Ssam		error = ENOMEM;
689116743Ssam		goto error;
690116743Ssam	}
691116743Ssam	for (i = 0; i < narrays; i++) {
692155515Ssam		error = parse_array(fd, raid_type, av[i], &arrays[i]);
693138570Ssam		if (error)
694116743Ssam			goto error;
695138570Ssam	}
696116743Ssam
697138570Ssam	switch (raid_type) {
698138570Ssam	case RT_RAID10:
699165185Ssam	case RT_RAID50:
700165185Ssam	case RT_RAID60:
701155515Ssam		for (i = 1; i < narrays; i++) {
702155515Ssam			if (arrays[i].drive_count != arrays[0].drive_count) {
703116743Ssam				warnx("All arrays must contain the same "
704188974Ssam				    "number of drives");
705188974Ssam				error = EINVAL;
706138570Ssam				goto error;
707138570Ssam			}
708155515Ssam		}
709155515Ssam		break;
710155515Ssam	}
711155515Ssam
712138570Ssam	/*
713155515Ssam	 * Fetch the current config and build sorted lists of existing
714155515Ssam	 * array and volume identifiers.
715155515Ssam	 */
716116743Ssam	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