mfi_config.c revision 213674
1196200Sscottl/*-
2196200Sscottl * Copyright (c) 2008, 2009 Yahoo!, Inc.
3196200Sscottl * All rights reserved.
4196200Sscottl *
5196200Sscottl * Redistribution and use in source and binary forms, with or without
6196200Sscottl * modification, are permitted provided that the following conditions
7196200Sscottl * are met:
8196200Sscottl * 1. Redistributions of source code must retain the above copyright
9196200Sscottl *    notice, this list of conditions and the following disclaimer.
10196200Sscottl * 2. Redistributions in binary form must reproduce the above copyright
11196200Sscottl *    notice, this list of conditions and the following disclaimer in the
12196200Sscottl *    documentation and/or other materials provided with the distribution.
13196200Sscottl * 3. The names of the authors may not be used to endorse or promote
14196200Sscottl *    products derived from this software without specific prior written
15196200Sscottl *    permission.
16196200Sscottl *
17196200Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18196200Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19196200Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20196200Sscottl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21196200Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22196200Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23196200Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24196200Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25196200Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26196200Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27196200Sscottl * SUCH DAMAGE.
28196200Sscottl *
29196200Sscottl * $FreeBSD: head/usr.sbin/mfiutil/mfi_config.c 213674 2010-10-10 20:54:01Z randi $
30196200Sscottl */
31196200Sscottl
32196200Sscottl#include <sys/types.h>
33196200Sscottl#ifdef DEBUG
34196200Sscottl#include <sys/sysctl.h>
35196200Sscottl#endif
36196200Sscottl#include <sys/errno.h>
37196200Sscottl#include <err.h>
38196200Sscottl#include <libutil.h>
39196200Sscottl#ifdef DEBUG
40196200Sscottl#include <stdint.h>
41196200Sscottl#endif
42196200Sscottl#include <stdio.h>
43196200Sscottl#include <stdlib.h>
44196200Sscottl#include <string.h>
45196200Sscottl#include <unistd.h>
46196200Sscottl#include "mfiutil.h"
47196200Sscottl
48196200Sscottl#ifdef DEBUG
49196200Sscottlstatic void	dump_config(int fd, struct mfi_config_data *config);
50196200Sscottl#endif
51196200Sscottl
52196200Sscottlstatic int	add_spare(int ac, char **av);
53196200Sscottlstatic int	remove_spare(int ac, char **av);
54196200Sscottl
55196200Sscottl#define powerof2(x)    ((((x)-1)&(x))==0)
56196200Sscottl
57196200Sscottlstatic long
58196200Sscottldehumanize(const char *value)
59196200Sscottl{
60196200Sscottl        char    *vtp;
61196200Sscottl        long    iv;
62196200Sscottl
63196200Sscottl        if (value == NULL)
64196200Sscottl                return (0);
65196200Sscottl        iv = strtoq(value, &vtp, 0);
66196200Sscottl        if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
67196200Sscottl                return (0);
68196200Sscottl        }
69196200Sscottl        switch (vtp[0]) {
70196200Sscottl        case 't': case 'T':
71196200Sscottl                iv *= 1024;
72196200Sscottl        case 'g': case 'G':
73196200Sscottl                iv *= 1024;
74196200Sscottl        case 'm': case 'M':
75196200Sscottl                iv *= 1024;
76196200Sscottl        case 'k': case 'K':
77196200Sscottl                iv *= 1024;
78196200Sscottl        case '\0':
79196200Sscottl                break;
80196200Sscottl        default:
81196200Sscottl                return (0);
82196200Sscottl        }
83196200Sscottl        return (iv);
84196200Sscottl}
85196200Sscottlint
86196200Sscottlmfi_config_read(int fd, struct mfi_config_data **configp)
87196200Sscottl{
88196200Sscottl	struct mfi_config_data *config;
89196200Sscottl	uint32_t config_size;
90196200Sscottl
91196200Sscottl	/*
92196200Sscottl	 * Keep fetching the config in a loop until we have a large enough
93196200Sscottl	 * buffer to hold the entire configuration.
94196200Sscottl	 */
95196200Sscottl	config = NULL;
96196200Sscottl	config_size = 1024;
97196200Sscottlfetch:
98196200Sscottl	config = reallocf(config, config_size);
99196200Sscottl	if (config == NULL)
100196200Sscottl		return (-1);
101196200Sscottl	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_READ, config,
102196200Sscottl	    config_size, NULL, 0, NULL) < 0)
103196200Sscottl		return (-1);
104196200Sscottl
105196200Sscottl	if (config->size > config_size) {
106196200Sscottl		config_size = config->size;
107196200Sscottl		goto fetch;
108196200Sscottl	}
109196200Sscottl
110196200Sscottl	*configp = config;
111196200Sscottl	return (0);
112196200Sscottl}
113196200Sscottl
114196200Sscottlstatic struct mfi_array *
115196200Sscottlmfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
116196200Sscottl{
117196200Sscottl	struct mfi_array *ar;
118196200Sscottl	char *p;
119196200Sscottl	int i;
120196200Sscottl
121196200Sscottl	p = (char *)config->array;
122196200Sscottl	for (i = 0; i < config->array_count; i++) {
123196200Sscottl		ar = (struct mfi_array *)p;
124196200Sscottl		if (ar->array_ref == array_ref)
125196200Sscottl			return (ar);
126196200Sscottl		p += config->array_size;
127196200Sscottl	}
128196200Sscottl
129196200Sscottl	return (NULL);
130196200Sscottl}
131196200Sscottl
132196200Sscottlstatic struct mfi_ld_config *
133196200Sscottlmfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
134196200Sscottl{
135196200Sscottl	struct mfi_ld_config *ld;
136196200Sscottl	char *p;
137196200Sscottl	int i;
138196200Sscottl
139196200Sscottl	p = (char *)config->array + config->array_count * config->array_size;
140196200Sscottl	for (i = 0; i < config->log_drv_count; i++) {
141196200Sscottl		ld = (struct mfi_ld_config *)p;
142196200Sscottl		if (ld->properties.ld.v.target_id == target_id)
143196200Sscottl			return (ld);
144196200Sscottl		p += config->log_drv_size;
145196200Sscottl	}
146196200Sscottl
147196200Sscottl	return (NULL);
148196200Sscottl}
149196200Sscottl
150196200Sscottlstatic int
151196200Sscottlclear_config(int ac, char **av)
152196200Sscottl{
153196200Sscottl	struct mfi_ld_list list;
154196200Sscottl	int ch, fd;
155196200Sscottl	u_int i;
156196200Sscottl
157196200Sscottl	fd = mfi_open(mfi_unit);
158196200Sscottl	if (fd < 0) {
159196200Sscottl		warn("mfi_open");
160196200Sscottl		return (errno);
161196200Sscottl	}
162196200Sscottl
163196200Sscottl	if (!mfi_reconfig_supported()) {
164196200Sscottl		warnx("The current mfi(4) driver does not support "
165196200Sscottl		    "configuration changes.");
166196200Sscottl		return (EOPNOTSUPP);
167196200Sscottl	}
168196200Sscottl
169196200Sscottl	if (mfi_ld_get_list(fd, &list, NULL) < 0) {
170196200Sscottl		warn("Failed to get volume list");
171196200Sscottl		return (errno);
172196200Sscottl	}
173196200Sscottl
174196200Sscottl	for (i = 0; i < list.ld_count; i++) {
175196200Sscottl		if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) {
176196200Sscottl			warnx("Volume %s is busy and cannot be deleted",
177196200Sscottl			    mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
178196200Sscottl			return (EBUSY);
179196200Sscottl		}
180196200Sscottl	}
181196200Sscottl
182196200Sscottl	printf(
183196200Sscottl	    "Are you sure you wish to clear the configuration on mfi%u? [y/N] ",
184196200Sscottl	    mfi_unit);
185196200Sscottl	ch = getchar();
186196200Sscottl	if (ch != 'y' && ch != 'Y') {
187196200Sscottl		printf("\nAborting\n");
188196200Sscottl		return (0);
189196200Sscottl	}
190196200Sscottl
191196200Sscottl	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) {
192196200Sscottl		warn("Failed to clear configuration");
193196200Sscottl		return (errno);
194196200Sscottl	}
195196200Sscottl
196196200Sscottl	printf("mfi%d: Configuration cleared\n", mfi_unit);
197196200Sscottl	close(fd);
198196200Sscottl
199196200Sscottl	return (0);
200196200Sscottl}
201196200SscottlMFI_COMMAND(top, clear, clear_config);
202196200Sscottl
203196200Sscottl#define	MFI_ARRAY_SIZE		288
204196200Sscottl#define	MAX_DRIVES_PER_ARRAY						\
205196200Sscottl	((MFI_ARRAY_SIZE - sizeof(struct mfi_array)) / 8)
206196200Sscottl
207196200Sscottl#define	RT_RAID0	0
208196200Sscottl#define	RT_RAID1	1
209196200Sscottl#define	RT_RAID5	2
210196200Sscottl#define	RT_RAID6	3
211196200Sscottl#define	RT_JBOD		4
212196200Sscottl#define	RT_CONCAT	5
213196200Sscottl#define	RT_RAID10	6
214196200Sscottl#define	RT_RAID50	7
215196200Sscottl#define	RT_RAID60	8
216196200Sscottl
217196200Sscottlstatic int
218196200Sscottlcompare_int(const void *one, const void *two)
219196200Sscottl{
220196200Sscottl	int first, second;
221196200Sscottl
222196200Sscottl	first = *(const int *)one;
223196200Sscottl	second = *(const int *)two;
224196200Sscottl
225196200Sscottl	return (first - second);
226196200Sscottl}
227196200Sscottl
228196200Sscottlstatic struct raid_type_entry {
229196200Sscottl	const char *name;
230196200Sscottl	int	raid_type;
231196200Sscottl} raid_type_table[] = {
232196200Sscottl	{ "raid0",	RT_RAID0 },
233196200Sscottl	{ "raid-0",	RT_RAID0 },
234196200Sscottl	{ "raid1",	RT_RAID1 },
235196200Sscottl	{ "raid-1",	RT_RAID1 },
236196200Sscottl	{ "mirror",	RT_RAID1 },
237196200Sscottl	{ "raid5",	RT_RAID5 },
238196200Sscottl	{ "raid-5",	RT_RAID5 },
239196200Sscottl	{ "raid6",	RT_RAID6 },
240196200Sscottl	{ "raid-6",	RT_RAID6 },
241196200Sscottl	{ "jbod",	RT_JBOD },
242196200Sscottl	{ "concat",	RT_CONCAT },
243196200Sscottl	{ "raid10",	RT_RAID10 },
244196200Sscottl	{ "raid1+0",	RT_RAID10 },
245196200Sscottl	{ "raid-10",	RT_RAID10 },
246196200Sscottl	{ "raid-1+0",	RT_RAID10 },
247196200Sscottl	{ "raid50",	RT_RAID50 },
248196200Sscottl	{ "raid5+0",	RT_RAID50 },
249196200Sscottl	{ "raid-50",	RT_RAID50 },
250196200Sscottl	{ "raid-5+0",	RT_RAID50 },
251196200Sscottl	{ "raid60",	RT_RAID60 },
252196200Sscottl	{ "raid6+0",	RT_RAID60 },
253196200Sscottl	{ "raid-60",	RT_RAID60 },
254196200Sscottl	{ "raid-6+0",	RT_RAID60 },
255196200Sscottl	{ NULL,		0 },
256196200Sscottl};
257196200Sscottl
258196200Sscottlstruct config_id_state {
259196200Sscottl	int	array_count;
260196200Sscottl	int	log_drv_count;
261196200Sscottl	int	*arrays;
262196200Sscottl	int	*volumes;
263196200Sscottl	uint16_t array_ref;
264196200Sscottl	uint8_t	target_id;
265196200Sscottl};
266196200Sscottl
267196200Sscottlstruct array_info {
268196200Sscottl	int	drive_count;
269196200Sscottl	struct mfi_pd_info *drives;
270196200Sscottl	struct mfi_array *array;
271196200Sscottl};
272196200Sscottl
273196200Sscottl/* Parse a comma-separated list of drives for an array. */
274196200Sscottlstatic int
275196200Sscottlparse_array(int fd, int raid_type, char *array_str, struct array_info *info)
276196200Sscottl{
277196200Sscottl	struct mfi_pd_info *pinfo;
278196200Sscottl	uint16_t device_id;
279196200Sscottl	char *cp;
280196200Sscottl	u_int count;
281196200Sscottl	int error;
282196200Sscottl
283196200Sscottl	cp = array_str;
284196200Sscottl	for (count = 0; cp != NULL; count++) {
285196200Sscottl		cp = strchr(cp, ',');
286196200Sscottl		if (cp != NULL) {
287196200Sscottl			cp++;
288196200Sscottl			if (*cp == ',') {
289196200Sscottl				warnx("Invalid drive list '%s'", array_str);
290196200Sscottl				return (EINVAL);
291196200Sscottl			}
292196200Sscottl		}
293196200Sscottl	}
294196200Sscottl
295196200Sscottl	/* Validate the number of drives for this array. */
296196200Sscottl	if (count >= MAX_DRIVES_PER_ARRAY) {
297196200Sscottl		warnx("Too many drives for a single array: max is %zu",
298196200Sscottl		    MAX_DRIVES_PER_ARRAY);
299196200Sscottl		return (EINVAL);
300196200Sscottl	}
301196200Sscottl	switch (raid_type) {
302196200Sscottl	case RT_RAID1:
303196200Sscottl	case RT_RAID10:
304196200Sscottl		if (count % 2 != 0) {
305196200Sscottl			warnx("RAID1 and RAID10 require an even number of "
306196200Sscottl			    "drives in each array");
307196200Sscottl			return (EINVAL);
308196200Sscottl		}
309196200Sscottl		break;
310196200Sscottl	case RT_RAID5:
311196200Sscottl	case RT_RAID50:
312196200Sscottl		if (count < 3) {
313196200Sscottl			warnx("RAID5 and RAID50 require at least 3 drives in "
314196200Sscottl			    "each array");
315196200Sscottl			return (EINVAL);
316196200Sscottl		}
317196200Sscottl		break;
318196200Sscottl	case RT_RAID6:
319196200Sscottl	case RT_RAID60:
320196200Sscottl		if (count < 4) {
321196200Sscottl			warnx("RAID6 and RAID60 require at least 4 drives in "
322196200Sscottl			    "each array");
323196200Sscottl			return (EINVAL);
324196200Sscottl		}
325196200Sscottl		break;
326196200Sscottl	}
327196200Sscottl
328196200Sscottl	/* Validate each drive. */
329196200Sscottl	info->drives = calloc(count, sizeof(struct mfi_pd_info));
330196200Sscottl	info->drive_count = count;
331196200Sscottl	for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL;
332196200Sscottl	     pinfo++) {
333196200Sscottl		error = mfi_lookup_drive(fd, cp, &device_id);
334196200Sscottl		if (error)
335196200Sscottl			return (error);
336196200Sscottl
337196200Sscottl		if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) {
338196200Sscottl			warn("Failed to fetch drive info for drive %s", cp);
339196200Sscottl			return (errno);
340196200Sscottl		}
341196200Sscottl
342196200Sscottl		if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
343196200Sscottl			warnx("Drive %u is not available", device_id);
344196200Sscottl			return (EINVAL);
345196200Sscottl		}
346196200Sscottl	}
347196200Sscottl
348196200Sscottl	return (0);
349196200Sscottl}
350196200Sscottl
351196200Sscottl/*
352196200Sscottl * Find the next free array ref assuming that 'array_ref' is the last
353196200Sscottl * one used.  'array_ref' should be 0xffff for the initial test.
354196200Sscottl */
355196200Sscottlstatic uint16_t
356196200Sscottlfind_next_array(struct config_id_state *state)
357196200Sscottl{
358196200Sscottl	int i;
359196200Sscottl
360196200Sscottl	/* Assume the current one is used. */
361196200Sscottl	state->array_ref++;
362196200Sscottl
363196200Sscottl	/* Find the next free one. */
364196200Sscottl	for (i = 0; i < state->array_count; i++)
365196200Sscottl		if (state->arrays[i] == state->array_ref)
366196200Sscottl			state->array_ref++;
367196200Sscottl	return (state->array_ref);
368196200Sscottl}
369196200Sscottl
370196200Sscottl/*
371196200Sscottl * Find the next free volume ID assuming that 'target_id' is the last
372196200Sscottl * one used.  'target_id' should be 0xff for the initial test.
373196200Sscottl */
374196200Sscottlstatic uint8_t
375196200Sscottlfind_next_volume(struct config_id_state *state)
376196200Sscottl{
377196200Sscottl	int i;
378196200Sscottl
379196200Sscottl	/* Assume the current one is used. */
380196200Sscottl	state->target_id++;
381196200Sscottl
382196200Sscottl	/* Find the next free one. */
383196200Sscottl	for (i = 0; i < state->log_drv_count; i++)
384196200Sscottl		if (state->volumes[i] == state->target_id)
385196200Sscottl			state->target_id++;
386196200Sscottl	return (state->target_id);
387196200Sscottl}
388196200Sscottl
389196200Sscottl/* Populate an array with drives. */
390196200Sscottlstatic void
391196200Sscottlbuild_array(int fd, char *arrayp, struct array_info *array_info,
392196200Sscottl    struct config_id_state *state, int verbose)
393196200Sscottl{
394196200Sscottl	struct mfi_array *ar = (struct mfi_array *)arrayp;
395196200Sscottl	int i;
396196200Sscottl
397196200Sscottl	ar->size = array_info->drives[0].coerced_size;
398196200Sscottl	ar->num_drives = array_info->drive_count;
399196200Sscottl	ar->array_ref = find_next_array(state);
400196200Sscottl	for (i = 0; i < array_info->drive_count; i++) {
401196200Sscottl		if (verbose)
402196200Sscottl			printf("Adding drive %u to array %u\n",
403196200Sscottl			    array_info->drives[i].ref.v.device_id,
404196200Sscottl			    ar->array_ref);
405196200Sscottl		if (ar->size > array_info->drives[i].coerced_size)
406196200Sscottl			ar->size = array_info->drives[i].coerced_size;
407196200Sscottl		ar->pd[i].ref = array_info->drives[i].ref;
408196200Sscottl		ar->pd[i].fw_state = MFI_PD_STATE_ONLINE;
409196200Sscottl	}
410196200Sscottl	array_info->array = ar;
411196200Sscottl}
412196200Sscottl
413196200Sscottl/*
414196200Sscottl * Create a volume that spans one or more arrays.
415196200Sscottl */
416196200Sscottlstatic void
417196200Sscottlbuild_volume(char *volumep, int narrays, struct array_info *arrays,
418196200Sscottl    int raid_type, long stripe_size, struct config_id_state *state, int verbose)
419196200Sscottl{
420196200Sscottl	struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
421196200Sscottl	struct mfi_array *ar;
422196200Sscottl	int i;
423196200Sscottl
424196200Sscottl	/* properties */
425196200Sscottl	ld->properties.ld.v.target_id = find_next_volume(state);
426196200Sscottl	ld->properties.ld.v.seq = 0;
427196200Sscottl	ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
428196200Sscottl	    MR_LD_CACHE_WRITE_BACK;
429196200Sscottl	ld->properties.access_policy = MFI_LD_ACCESS_RW;
430196200Sscottl	ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED;
431196200Sscottl	ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
432196200Sscottl	    MR_LD_CACHE_WRITE_BACK;
433196200Sscottl	ld->properties.no_bgi = 0;
434196200Sscottl
435196200Sscottl	/* params */
436196200Sscottl	switch (raid_type) {
437196200Sscottl	case RT_RAID0:
438196200Sscottl	case RT_JBOD:
439196200Sscottl		ld->params.primary_raid_level = DDF_RAID0;
440196200Sscottl		ld->params.raid_level_qualifier = 0;
441196200Sscottl		ld->params.secondary_raid_level = 0;
442196200Sscottl		break;
443196200Sscottl	case RT_RAID1:
444196200Sscottl		ld->params.primary_raid_level = DDF_RAID1;
445196200Sscottl		ld->params.raid_level_qualifier = 0;
446196200Sscottl		ld->params.secondary_raid_level = 0;
447196200Sscottl		break;
448196200Sscottl	case RT_RAID5:
449196200Sscottl		ld->params.primary_raid_level = DDF_RAID5;
450196200Sscottl		ld->params.raid_level_qualifier = 3;
451196200Sscottl		ld->params.secondary_raid_level = 0;
452196200Sscottl		break;
453196200Sscottl	case RT_RAID6:
454196200Sscottl		ld->params.primary_raid_level = DDF_RAID6;
455196200Sscottl		ld->params.raid_level_qualifier = 3;
456196200Sscottl		ld->params.secondary_raid_level = 0;
457196200Sscottl		break;
458196200Sscottl	case RT_CONCAT:
459196200Sscottl		ld->params.primary_raid_level = DDF_CONCAT;
460196200Sscottl		ld->params.raid_level_qualifier = 0;
461196200Sscottl		ld->params.secondary_raid_level = 0;
462196200Sscottl		break;
463196200Sscottl	case RT_RAID10:
464196200Sscottl		ld->params.primary_raid_level = DDF_RAID1;
465196200Sscottl		ld->params.raid_level_qualifier = 0;
466196200Sscottl		ld->params.secondary_raid_level = 3; /* XXX? */
467196200Sscottl		break;
468196200Sscottl	case RT_RAID50:
469196200Sscottl		/*
470196200Sscottl		 * XXX: This appears to work though the card's BIOS
471196200Sscottl		 * complains that the configuration is foreign.  The
472196200Sscottl		 * BIOS setup does not allow for creation of RAID-50
473196200Sscottl		 * or RAID-60 arrays.  The only nested array
474196200Sscottl		 * configuration it allows for is RAID-10.
475196200Sscottl		 */
476196200Sscottl		ld->params.primary_raid_level = DDF_RAID5;
477196200Sscottl		ld->params.raid_level_qualifier = 3;
478196200Sscottl		ld->params.secondary_raid_level = 3; /* XXX? */
479196200Sscottl		break;
480196200Sscottl	case RT_RAID60:
481196200Sscottl		ld->params.primary_raid_level = DDF_RAID6;
482196200Sscottl		ld->params.raid_level_qualifier = 3;
483196200Sscottl		ld->params.secondary_raid_level = 3; /* XXX? */
484196200Sscottl		break;
485196200Sscottl	}
486196200Sscottl
487196200Sscottl	/*
488196200Sscottl	 * Stripe size is encoded as (2 ^ N) * 512 = stripe_size.  Use
489196200Sscottl	 * ffs() to simulate log2(stripe_size).
490196200Sscottl	 */
491196200Sscottl	ld->params.stripe_size = ffs(stripe_size) - 1 - 9;
492196200Sscottl	ld->params.num_drives = arrays[0].array->num_drives;
493196200Sscottl	ld->params.span_depth = narrays;
494196200Sscottl	ld->params.state = MFI_LD_STATE_OPTIMAL;
495196200Sscottl	ld->params.init_state = MFI_LD_PARAMS_INIT_NO;
496196200Sscottl	ld->params.is_consistent = 0;
497196200Sscottl
498196200Sscottl	/* spans */
499196200Sscottl	for (i = 0; i < narrays; i++) {
500196200Sscottl		ar = arrays[i].array;
501196200Sscottl		if (verbose)
502196200Sscottl			printf("Adding array %u to volume %u\n", ar->array_ref,
503196200Sscottl			    ld->properties.ld.v.target_id);
504196200Sscottl		ld->span[i].start_block = 0;
505196200Sscottl		ld->span[i].num_blocks = ar->size;
506196200Sscottl		ld->span[i].array_ref = ar->array_ref;
507196200Sscottl	}
508196200Sscottl}
509196200Sscottl
510196200Sscottlstatic int
511196200Sscottlcreate_volume(int ac, char **av)
512196200Sscottl{
513196200Sscottl	struct mfi_config_data *config;
514196200Sscottl	struct mfi_array *ar;
515196200Sscottl	struct mfi_ld_config *ld;
516196200Sscottl	struct config_id_state state;
517196200Sscottl	size_t config_size;
518196200Sscottl	char *p, *cfg_arrays, *cfg_volumes;
519196200Sscottl	int error, fd, i, raid_type;
520196200Sscottl	int narrays, nvolumes, arrays_per_volume;
521196200Sscottl	struct array_info *arrays;
522196200Sscottl	long stripe_size;
523196200Sscottl#ifdef DEBUG
524196200Sscottl	int dump;
525196200Sscottl#endif
526196200Sscottl	int ch, verbose;
527196200Sscottl
528196200Sscottl	/*
529196200Sscottl	 * Backwards compat.  Map 'create volume' to 'create' and
530196200Sscottl	 * 'create spare' to 'add'.
531196200Sscottl	 */
532196200Sscottl	if (ac > 1) {
533196200Sscottl		if (strcmp(av[1], "volume") == 0) {
534196200Sscottl			av++;
535196200Sscottl			ac--;
536196200Sscottl		} else if (strcmp(av[1], "spare") == 0) {
537196200Sscottl			av++;
538196200Sscottl			ac--;
539196200Sscottl			return (add_spare(ac, av));
540196200Sscottl		}
541196200Sscottl	}
542196200Sscottl
543196200Sscottl	if (ac < 2) {
544196200Sscottl		warnx("create volume: volume type required");
545196200Sscottl		return (EINVAL);
546196200Sscottl	}
547196200Sscottl
548196200Sscottl
549196200Sscottl	fd = mfi_open(mfi_unit);
550196200Sscottl	if (fd < 0) {
551196200Sscottl		warn("mfi_open");
552196200Sscottl		return (errno);
553196200Sscottl	}
554196200Sscottl
555196200Sscottl	if (!mfi_reconfig_supported()) {
556196200Sscottl		warnx("The current mfi(4) driver does not support "
557196200Sscottl		    "configuration changes.");
558196200Sscottl		return (EOPNOTSUPP);
559196200Sscottl	}
560196200Sscottl
561196200Sscottl	/* Lookup the RAID type first. */
562196200Sscottl	raid_type = -1;
563196200Sscottl	for (i = 0; raid_type_table[i].name != NULL; i++)
564196200Sscottl		if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
565196200Sscottl			raid_type = raid_type_table[i].raid_type;
566196200Sscottl			break;
567196200Sscottl		}
568196200Sscottl
569196200Sscottl	if (raid_type == -1) {
570196200Sscottl		warnx("Unknown or unsupported volume type %s", av[1]);
571196200Sscottl		return (EINVAL);
572196200Sscottl	}
573196200Sscottl
574196200Sscottl	/* Parse any options. */
575196200Sscottl	optind = 2;
576196200Sscottl#ifdef DEBUG
577196200Sscottl	dump = 0;
578196200Sscottl#endif
579196200Sscottl	verbose = 0;
580196200Sscottl	stripe_size = 64 * 1024;
581196200Sscottl
582196200Sscottl	while ((ch = getopt(ac, av, "ds:v")) != -1) {
583196200Sscottl		switch (ch) {
584196200Sscottl#ifdef DEBUG
585196200Sscottl		case 'd':
586196200Sscottl			dump = 1;
587196200Sscottl			break;
588196200Sscottl#endif
589196200Sscottl		case 's':
590196200Sscottl			stripe_size = dehumanize(optarg);
591196200Sscottl			if ((stripe_size < 512) || (!powerof2(stripe_size)))
592196200Sscottl				stripe_size = 64 * 1024;
593196200Sscottl			break;
594196200Sscottl		case 'v':
595196200Sscottl			verbose = 1;
596196200Sscottl			break;
597196200Sscottl		case '?':
598196200Sscottl		default:
599196200Sscottl			return (EINVAL);
600196200Sscottl		}
601196200Sscottl	}
602196200Sscottl	ac -= optind;
603196200Sscottl	av += optind;
604196200Sscottl
605196200Sscottl	/* Parse all the arrays. */
606196200Sscottl	narrays = ac;
607196200Sscottl	if (narrays == 0) {
608196200Sscottl		warnx("At least one drive list is required");
609196200Sscottl		return (EINVAL);
610196200Sscottl	}
611196200Sscottl	switch (raid_type) {
612196200Sscottl	case RT_RAID0:
613196200Sscottl	case RT_RAID1:
614196200Sscottl	case RT_RAID5:
615196200Sscottl	case RT_RAID6:
616196200Sscottl	case RT_CONCAT:
617196200Sscottl		if (narrays != 1) {
618196200Sscottl			warnx("Only one drive list can be specified");
619196200Sscottl			return (EINVAL);
620196200Sscottl		}
621196200Sscottl		break;
622196200Sscottl	case RT_RAID10:
623196200Sscottl	case RT_RAID50:
624196200Sscottl	case RT_RAID60:
625196200Sscottl		if (narrays < 1) {
626196200Sscottl			warnx("RAID10, RAID50, and RAID60 require at least "
627196200Sscottl			    "two drive lists");
628196200Sscottl			return (EINVAL);
629196200Sscottl		}
630196200Sscottl		if (narrays > MFI_MAX_SPAN_DEPTH) {
631196200Sscottl			warnx("Volume spans more than %d arrays",
632196200Sscottl			    MFI_MAX_SPAN_DEPTH);
633196200Sscottl			return (EINVAL);
634196200Sscottl		}
635196200Sscottl		break;
636196200Sscottl	}
637196200Sscottl	arrays = calloc(narrays, sizeof(*arrays));
638196200Sscottl	for (i = 0; i < narrays; i++) {
639196200Sscottl		error = parse_array(fd, raid_type, av[i], &arrays[i]);
640196200Sscottl		if (error)
641196200Sscottl			return (error);
642196200Sscottl	}
643196200Sscottl
644196200Sscottl	switch (raid_type) {
645196200Sscottl	case RT_RAID10:
646196200Sscottl	case RT_RAID50:
647196200Sscottl	case RT_RAID60:
648196200Sscottl		for (i = 1; i < narrays; i++) {
649196200Sscottl			if (arrays[i].drive_count != arrays[0].drive_count) {
650196200Sscottl				warnx("All arrays must contain the same "
651196200Sscottl				    "number of drives");
652196200Sscottl				return (EINVAL);
653196200Sscottl			}
654196200Sscottl		}
655196200Sscottl		break;
656196200Sscottl	}
657196200Sscottl
658196200Sscottl	/*
659196200Sscottl	 * Fetch the current config and build sorted lists of existing
660196200Sscottl	 * array and volume identifiers.
661196200Sscottl	 */
662196200Sscottl	if (mfi_config_read(fd, &config) < 0) {
663196200Sscottl		warn("Failed to read configuration");
664196200Sscottl		return (errno);
665196200Sscottl	}
666196200Sscottl	p = (char *)config->array;
667196200Sscottl	state.array_ref = 0xffff;
668196200Sscottl	state.target_id = 0xff;
669196200Sscottl	state.array_count = config->array_count;
670196200Sscottl	if (config->array_count > 0) {
671196200Sscottl		state.arrays = calloc(config->array_count, sizeof(int));
672196200Sscottl		for (i = 0; i < config->array_count; i++) {
673196200Sscottl			ar = (struct mfi_array *)p;
674196200Sscottl			state.arrays[i] = ar->array_ref;
675196200Sscottl			p += config->array_size;
676196200Sscottl		}
677196200Sscottl		qsort(state.arrays, config->array_count, sizeof(int),
678196200Sscottl		    compare_int);
679196200Sscottl	} else
680196200Sscottl		state.arrays = NULL;
681196200Sscottl	state.log_drv_count = config->log_drv_count;
682196200Sscottl	if (config->log_drv_count) {
683196200Sscottl		state.volumes = calloc(config->log_drv_count, sizeof(int));
684196200Sscottl		for (i = 0; i < config->log_drv_count; i++) {
685196200Sscottl			ld = (struct mfi_ld_config *)p;
686196200Sscottl			state.volumes[i] = ld->properties.ld.v.target_id;
687196200Sscottl			p += config->log_drv_size;
688196200Sscottl		}
689196200Sscottl		qsort(state.volumes, config->log_drv_count, sizeof(int),
690196200Sscottl		    compare_int);
691196200Sscottl	} else
692196200Sscottl		state.volumes = NULL;
693196200Sscottl	free(config);
694196200Sscottl
695196200Sscottl	/* Determine the size of the configuration we will build. */
696196200Sscottl	switch (raid_type) {
697196200Sscottl	case RT_RAID0:
698196200Sscottl	case RT_RAID1:
699196200Sscottl	case RT_RAID5:
700196200Sscottl	case RT_RAID6:
701196200Sscottl	case RT_CONCAT:
702196200Sscottl	case RT_JBOD:
703196200Sscottl		/* Each volume spans a single array. */
704196200Sscottl		nvolumes = narrays;
705196200Sscottl		break;
706196200Sscottl	case RT_RAID10:
707196200Sscottl	case RT_RAID50:
708196200Sscottl	case RT_RAID60:
709196200Sscottl		/* A single volume spans multiple arrays. */
710196200Sscottl		nvolumes = 1;
711196200Sscottl		break;
712196200Sscottl	default:
713196200Sscottl		/* Pacify gcc. */
714196200Sscottl		abort();
715196200Sscottl	}
716196200Sscottl
717196200Sscottl	config_size = sizeof(struct mfi_config_data) +
718196200Sscottl	    sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays;
719196200Sscottl	config = calloc(1, config_size);
720196200Sscottl	config->size = config_size;
721196200Sscottl	config->array_count = narrays;
722196200Sscottl	config->array_size = MFI_ARRAY_SIZE;	/* XXX: Firmware hardcode */
723196200Sscottl	config->log_drv_count = nvolumes;
724196200Sscottl	config->log_drv_size = sizeof(struct mfi_ld_config);
725196200Sscottl	config->spares_count = 0;
726196200Sscottl	config->spares_size = 40;		/* XXX: Firmware hardcode */
727196200Sscottl	cfg_arrays = (char *)config->array;
728196200Sscottl	cfg_volumes = cfg_arrays + config->array_size * narrays;
729196200Sscottl
730196200Sscottl	/* Build the arrays. */
731196200Sscottl	for (i = 0; i < narrays; i++) {
732196200Sscottl		build_array(fd, cfg_arrays, &arrays[i], &state, verbose);
733196200Sscottl		cfg_arrays += config->array_size;
734196200Sscottl	}
735196200Sscottl
736196200Sscottl	/* Now build the volume(s). */
737196200Sscottl	arrays_per_volume = narrays / nvolumes;
738196200Sscottl	for (i = 0; i < nvolumes; i++) {
739196200Sscottl		build_volume(cfg_volumes, arrays_per_volume,
740196200Sscottl		    &arrays[i * arrays_per_volume], raid_type, stripe_size,
741196200Sscottl		    &state, verbose);
742196200Sscottl		cfg_volumes += config->log_drv_size;
743196200Sscottl	}
744196200Sscottl
745196200Sscottl#ifdef DEBUG
746196200Sscottl	if (dump)
747196200Sscottl		dump_config(fd, config);
748196200Sscottl	else
749196200Sscottl#endif
750196200Sscottl
751196200Sscottl	/* Send the new config to the controller. */
752196200Sscottl	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size,
753196200Sscottl	    NULL, 0, NULL) < 0) {
754196200Sscottl		warn("Failed to add volume");
755196200Sscottl		return (errno);
756196200Sscottl	}
757196200Sscottl
758196200Sscottl	/* Clean up. */
759196200Sscottl	free(config);
760196200Sscottl	if (state.log_drv_count > 0)
761196200Sscottl		free(state.volumes);
762196200Sscottl	if (state.array_count > 0)
763196200Sscottl		free(state.arrays);
764196200Sscottl	for (i = 0; i < narrays; i++)
765196200Sscottl		free(arrays[i].drives);
766196200Sscottl	free(arrays);
767196200Sscottl	close(fd);
768196200Sscottl
769196200Sscottl	return (0);
770196200Sscottl}
771196200SscottlMFI_COMMAND(top, create, create_volume);
772196200Sscottl
773196200Sscottlstatic int
774196200Sscottldelete_volume(int ac, char **av)
775196200Sscottl{
776196200Sscottl	struct mfi_ld_info info;
777196200Sscottl	int fd;
778196200Sscottl	uint8_t target_id, mbox[4];
779196200Sscottl
780196200Sscottl	/*
781196200Sscottl	 * Backwards compat.  Map 'delete volume' to 'delete' and
782196200Sscottl	 * 'delete spare' to 'remove'.
783196200Sscottl	 */
784196200Sscottl	if (ac > 1) {
785196200Sscottl		if (strcmp(av[1], "volume") == 0) {
786196200Sscottl			av++;
787196200Sscottl			ac--;
788196200Sscottl		} else if (strcmp(av[1], "spare") == 0) {
789196200Sscottl			av++;
790196200Sscottl			ac--;
791196200Sscottl			return (remove_spare(ac, av));
792196200Sscottl		}
793196200Sscottl	}
794196200Sscottl
795196200Sscottl	if (ac != 2) {
796196200Sscottl		warnx("delete volume: volume required");
797196200Sscottl		return (EINVAL);
798196200Sscottl	}
799196200Sscottl
800196200Sscottl	fd = mfi_open(mfi_unit);
801196200Sscottl	if (fd < 0) {
802196200Sscottl		warn("mfi_open");
803196200Sscottl		return (errno);
804196200Sscottl	}
805196200Sscottl
806196200Sscottl	if (!mfi_reconfig_supported()) {
807196200Sscottl		warnx("The current mfi(4) driver does not support "
808196200Sscottl		    "configuration changes.");
809196200Sscottl		return (EOPNOTSUPP);
810196200Sscottl	}
811196200Sscottl
812196200Sscottl	if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
813196200Sscottl		warn("Invalid volume %s", av[1]);
814196200Sscottl		return (errno);
815196200Sscottl	}
816196200Sscottl
817196200Sscottl	if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
818196200Sscottl		warn("Failed to get info for volume %d", target_id);
819196200Sscottl		return (errno);
820196200Sscottl	}
821196200Sscottl
822196200Sscottl	if (mfi_volume_busy(fd, target_id)) {
823196200Sscottl		warnx("Volume %s is busy and cannot be deleted",
824196200Sscottl		    mfi_volume_name(fd, target_id));
825196200Sscottl		return (EBUSY);
826196200Sscottl	}
827196200Sscottl
828196200Sscottl	mbox_store_ldref(mbox, &info.ld_config.properties.ld);
829196200Sscottl	if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox,
830196200Sscottl	    sizeof(mbox), NULL) < 0) {
831196200Sscottl		warn("Failed to delete volume");
832196200Sscottl		return (errno);
833196200Sscottl	}
834196200Sscottl
835196200Sscottl	close(fd);
836196200Sscottl
837196200Sscottl	return (0);
838196200Sscottl}
839196200SscottlMFI_COMMAND(top, delete, delete_volume);
840196200Sscottl
841196200Sscottlstatic int
842196200Sscottladd_spare(int ac, char **av)
843196200Sscottl{
844196200Sscottl	struct mfi_pd_info info;
845196200Sscottl	struct mfi_config_data *config;
846196200Sscottl	struct mfi_array *ar;
847196200Sscottl	struct mfi_ld_config *ld;
848196200Sscottl	struct mfi_spare *spare;
849196200Sscottl	uint16_t device_id;
850196200Sscottl	uint8_t target_id;
851196200Sscottl	char *p;
852196200Sscottl	int error, fd, i;
853196200Sscottl
854196200Sscottl	if (ac < 2) {
855196200Sscottl		warnx("add spare: drive required");
856196200Sscottl		return (EINVAL);
857196200Sscottl	}
858196200Sscottl
859196200Sscottl	fd = mfi_open(mfi_unit);
860196200Sscottl	if (fd < 0) {
861196200Sscottl		warn("mfi_open");
862196200Sscottl		return (errno);
863196200Sscottl	}
864196200Sscottl
865196200Sscottl	error = mfi_lookup_drive(fd, av[1], &device_id);
866196200Sscottl	if (error)
867196200Sscottl		return (error);
868196200Sscottl
869196200Sscottl	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
870196200Sscottl		warn("Failed to fetch drive info");
871196200Sscottl		return (errno);
872196200Sscottl	}
873196200Sscottl
874196200Sscottl	if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
875196200Sscottl		warnx("Drive %u is not available", device_id);
876196200Sscottl		return (EINVAL);
877196200Sscottl	}
878196200Sscottl
879196200Sscottl	if (ac > 2) {
880196200Sscottl		if (mfi_lookup_volume(fd, av[2], &target_id) < 0) {
881196200Sscottl			warn("Invalid volume %s", av[2]);
882196200Sscottl			return (errno);
883196200Sscottl		}
884196200Sscottl	}
885196200Sscottl
886196200Sscottl	if (mfi_config_read(fd, &config) < 0) {
887196200Sscottl		warn("Failed to read configuration");
888196200Sscottl		return (errno);
889196200Sscottl	}
890196200Sscottl
891196200Sscottl	spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) *
892196200Sscottl	    config->array_count);
893196200Sscottl	bzero(spare, sizeof(struct mfi_spare));
894196200Sscottl	spare->ref = info.ref;
895196200Sscottl
896196200Sscottl	if (ac == 2) {
897196200Sscottl		/* Global spare backs all arrays. */
898196200Sscottl		p = (char *)config->array;
899196200Sscottl		for (i = 0; i < config->array_count; i++) {
900196200Sscottl			ar = (struct mfi_array *)p;
901196200Sscottl			if (ar->size > info.coerced_size) {
902196200Sscottl				warnx("Spare isn't large enough for array %u",
903196200Sscottl				    ar->array_ref);
904196200Sscottl				return (EINVAL);
905196200Sscottl			}
906196200Sscottl			p += config->array_size;
907196200Sscottl		}
908196200Sscottl		spare->array_count = 0;
909196200Sscottl	} else  {
910196200Sscottl		/*
911196200Sscottl		 * Dedicated spares only back the arrays for a
912196200Sscottl		 * specific volume.
913196200Sscottl		 */
914196200Sscottl		ld = mfi_config_lookup_volume(config, target_id);
915196200Sscottl		if (ld == NULL) {
916196200Sscottl			warnx("Did not find volume %d", target_id);
917196200Sscottl			return (EINVAL);
918196200Sscottl		}
919196200Sscottl
920196200Sscottl		spare->spare_type |= MFI_SPARE_DEDICATED;
921196200Sscottl		spare->array_count = ld->params.span_depth;
922196200Sscottl		for (i = 0; i < ld->params.span_depth; i++) {
923196200Sscottl			ar = mfi_config_lookup_array(config,
924196200Sscottl			    ld->span[i].array_ref);
925196200Sscottl			if (ar == NULL) {
926196200Sscottl				warnx("Missing array; inconsistent config?");
927196200Sscottl				return (ENXIO);
928196200Sscottl			}
929196200Sscottl			if (ar->size > info.coerced_size) {
930196200Sscottl				warnx("Spare isn't large enough for array %u",
931196200Sscottl				    ar->array_ref);
932196200Sscottl				return (EINVAL);
933196200Sscottl			}
934196200Sscottl			spare->array_ref[i] = ar->array_ref;
935196200Sscottl		}
936196200Sscottl	}
937196200Sscottl	free(config);
938196200Sscottl
939196200Sscottl	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare,
940196200Sscottl	    sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count,
941196200Sscottl	    NULL, 0, NULL) < 0) {
942196200Sscottl		warn("Failed to assign spare");
943196200Sscottl		return (errno);
944196200Sscottl	}
945196200Sscottl
946196200Sscottl	close(fd);
947196200Sscottl
948196200Sscottl	return (0);
949196200Sscottl}
950196200SscottlMFI_COMMAND(top, add, add_spare);
951196200Sscottl
952196200Sscottlstatic int
953196200Sscottlremove_spare(int ac, char **av)
954196200Sscottl{
955196200Sscottl	struct mfi_pd_info info;
956196200Sscottl	int error, fd;
957196200Sscottl	uint16_t device_id;
958196200Sscottl	uint8_t mbox[4];
959196200Sscottl
960196200Sscottl	if (ac != 2) {
961196200Sscottl		warnx("remove spare: drive required");
962196200Sscottl		return (EINVAL);
963196200Sscottl	}
964196200Sscottl
965196200Sscottl	fd = mfi_open(mfi_unit);
966196200Sscottl	if (fd < 0) {
967196200Sscottl		warn("mfi_open");
968196200Sscottl		return (errno);
969196200Sscottl	}
970196200Sscottl
971196200Sscottl	error = mfi_lookup_drive(fd, av[1], &device_id);
972196200Sscottl	if (error)
973196200Sscottl		return (error);
974196200Sscottl
975196200Sscottl	/* Get the info for this drive. */
976196200Sscottl	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
977196200Sscottl		warn("Failed to fetch info for drive %u", device_id);
978196200Sscottl		return (errno);
979196200Sscottl	}
980196200Sscottl
981196200Sscottl	if (info.fw_state != MFI_PD_STATE_HOT_SPARE) {
982196200Sscottl		warnx("Drive %u is not a hot spare", device_id);
983196200Sscottl		return (EINVAL);
984196200Sscottl	}
985196200Sscottl
986196200Sscottl	mbox_store_pdref(mbox, &info.ref);
987196200Sscottl	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox,
988196200Sscottl	    sizeof(mbox), NULL) < 0) {
989196200Sscottl		warn("Failed to delete spare");
990196200Sscottl		return (errno);
991196200Sscottl	}
992196200Sscottl
993196200Sscottl	close(fd);
994196200Sscottl
995196200Sscottl	return (0);
996196200Sscottl}
997196200SscottlMFI_COMMAND(top, remove, remove_spare);
998196200Sscottl
999196200Sscottl#ifdef DEBUG
1000196200Sscottl/* Display raw data about a config. */
1001196200Sscottlstatic void
1002196200Sscottldump_config(int fd, struct mfi_config_data *config)
1003196200Sscottl{
1004196200Sscottl	struct mfi_array *ar;
1005196200Sscottl	struct mfi_ld_config *ld;
1006196200Sscottl	struct mfi_spare *sp;
1007196200Sscottl	struct mfi_pd_info pinfo;
1008196200Sscottl	uint16_t device_id;
1009196200Sscottl	char *p;
1010196200Sscottl	int i, j;
1011196200Sscottl
1012196200Sscottl	printf(
1013196200Sscottl	    "mfi%d Configuration (Debug): %d arrays, %d volumes, %d spares\n",
1014196200Sscottl	    mfi_unit, config->array_count, config->log_drv_count,
1015196200Sscottl	    config->spares_count);
1016196200Sscottl	printf("  array size: %u\n", config->array_size);
1017196200Sscottl	printf("  volume size: %u\n", config->log_drv_size);
1018196200Sscottl	printf("  spare size: %u\n", config->spares_size);
1019196200Sscottl	p = (char *)config->array;
1020196200Sscottl
1021196200Sscottl	for (i = 0; i < config->array_count; i++) {
1022196200Sscottl		ar = (struct mfi_array *)p;
1023196200Sscottl		printf("    array %u of %u drives:\n", ar->array_ref,
1024196200Sscottl		    ar->num_drives);
1025196200Sscottl		printf("      size = %ju\n", (uintmax_t)ar->size);
1026196200Sscottl		for (j = 0; j < ar->num_drives; j++) {
1027213674Srandi			device_id = ar->pd[j].ref.v.device_id;
1028196200Sscottl			if (device_id == 0xffff)
1029196200Sscottl				printf("        drive MISSING\n");
1030196200Sscottl			else {
1031196200Sscottl				printf("        drive %u %s\n", device_id,
1032196200Sscottl				    mfi_pdstate(ar->pd[j].fw_state));
1033196200Sscottl				if (mfi_pd_get_info(fd, device_id, &pinfo,
1034196200Sscottl				    NULL) >= 0) {
1035196200Sscottl					printf("          raw size: %ju\n",
1036196200Sscottl					    (uintmax_t)pinfo.raw_size);
1037196200Sscottl					printf("          non-coerced size: %ju\n",
1038196200Sscottl					    (uintmax_t)pinfo.non_coerced_size);
1039196200Sscottl					printf("          coerced size: %ju\n",
1040196200Sscottl					    (uintmax_t)pinfo.coerced_size);
1041196200Sscottl				}
1042196200Sscottl			}
1043196200Sscottl		}
1044196200Sscottl		p += config->array_size;
1045196200Sscottl	}
1046196200Sscottl
1047196200Sscottl	for (i = 0; i < config->log_drv_count; i++) {
1048196200Sscottl		ld = (struct mfi_ld_config *)p;
1049196200Sscottl		printf("    volume %s ",
1050196200Sscottl		    mfi_volume_name(fd, ld->properties.ld.v.target_id));
1051196200Sscottl		printf("%s %s",
1052196200Sscottl		    mfi_raid_level(ld->params.primary_raid_level,
1053196200Sscottl			ld->params.secondary_raid_level),
1054196200Sscottl		    mfi_ldstate(ld->params.state));
1055196200Sscottl		if (ld->properties.name[0] != '\0')
1056196200Sscottl			printf(" <%s>", ld->properties.name);
1057196200Sscottl		printf("\n");
1058196200Sscottl		printf("      primary raid level: %u\n",
1059196200Sscottl		    ld->params.primary_raid_level);
1060196200Sscottl		printf("      raid level qualifier: %u\n",
1061196200Sscottl		    ld->params.raid_level_qualifier);
1062196200Sscottl		printf("      secondary raid level: %u\n",
1063196200Sscottl		    ld->params.secondary_raid_level);
1064196200Sscottl		printf("      stripe size: %u\n", ld->params.stripe_size);
1065196200Sscottl		printf("      num drives: %u\n", ld->params.num_drives);
1066196200Sscottl		printf("      init state: %u\n", ld->params.init_state);
1067196200Sscottl		printf("      consistent: %u\n", ld->params.is_consistent);
1068196200Sscottl		printf("      no bgi: %u\n", ld->properties.no_bgi);
1069196200Sscottl		printf("      spans:\n");
1070196200Sscottl		for (j = 0; j < ld->params.span_depth; j++) {
1071196200Sscottl			printf("        array %u @ ", ld->span[j].array_ref);
1072196200Sscottl			printf("%ju : %ju\n",
1073196200Sscottl			    (uintmax_t)ld->span[j].start_block,
1074196200Sscottl			    (uintmax_t)ld->span[j].num_blocks);
1075196200Sscottl		}
1076196200Sscottl		p += config->log_drv_size;
1077196200Sscottl	}
1078196200Sscottl
1079196200Sscottl	for (i = 0; i < config->spares_count; i++) {
1080196200Sscottl		sp = (struct mfi_spare *)p;
1081196200Sscottl		printf("    %s spare %u ",
1082196200Sscottl		    sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" :
1083213674Srandi		    "global", sp->ref.v.device_id);
1084196200Sscottl		printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE));
1085196200Sscottl		printf(" backs:\n");
1086196200Sscottl		for (j = 0; j < sp->array_count; j++)
1087196200Sscottl			printf("        array %u\n", sp->array_ref[j]);
1088196200Sscottl		p += config->spares_size;
1089196200Sscottl	}
1090196200Sscottl}
1091196200Sscottl
1092196200Sscottlstatic int
1093196200Sscottldebug_config(int ac, char **av)
1094196200Sscottl{
1095196200Sscottl	struct mfi_config_data *config;
1096196200Sscottl	int fd;
1097196200Sscottl
1098196200Sscottl	if (ac != 1) {
1099196200Sscottl		warnx("debug: extra arguments");
1100196200Sscottl		return (EINVAL);
1101196200Sscottl	}
1102196200Sscottl
1103196200Sscottl	fd = mfi_open(mfi_unit);
1104196200Sscottl	if (fd < 0) {
1105196200Sscottl		warn("mfi_open");
1106196200Sscottl		return (errno);
1107196200Sscottl	}
1108196200Sscottl
1109196200Sscottl	/* Get the config from the controller. */
1110196200Sscottl	if (mfi_config_read(fd, &config) < 0) {
1111196200Sscottl		warn("Failed to get config");
1112196200Sscottl		return (errno);
1113196200Sscottl	}
1114196200Sscottl
1115196200Sscottl	/* Dump out the configuration. */
1116196200Sscottl	dump_config(fd, config);
1117196200Sscottl	free(config);
1118196200Sscottl	close(fd);
1119196200Sscottl
1120196200Sscottl	return (0);
1121196200Sscottl}
1122196200SscottlMFI_COMMAND(top, debug, debug_config);
1123196200Sscottl
1124196200Sscottlstatic int
1125196200Sscottldump(int ac, char **av)
1126196200Sscottl{
1127196200Sscottl	struct mfi_config_data *config;
1128196200Sscottl	char buf[64];
1129196200Sscottl	size_t len;
1130196200Sscottl	int fd;
1131196200Sscottl
1132196200Sscottl	if (ac != 1) {
1133196200Sscottl		warnx("dump: extra arguments");
1134196200Sscottl		return (EINVAL);
1135196200Sscottl	}
1136196200Sscottl
1137196200Sscottl	fd = mfi_open(mfi_unit);
1138196200Sscottl	if (fd < 0) {
1139196200Sscottl		warn("mfi_open");
1140196200Sscottl		return (errno);
1141196200Sscottl	}
1142196200Sscottl
1143196200Sscottl	/* Get the stashed copy of the last dcmd from the driver. */
1144196200Sscottl	snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit);
1145196200Sscottl	if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) {
1146196200Sscottl		warn("Failed to read debug command");
1147196200Sscottl		if (errno == ENOENT)
1148196200Sscottl			errno = EOPNOTSUPP;
1149196200Sscottl		return (errno);
1150196200Sscottl	}
1151196200Sscottl
1152196200Sscottl	config = malloc(len);
1153196200Sscottl	if (sysctlbyname(buf, config, &len, NULL, 0) < 0) {
1154196200Sscottl		warn("Failed to read debug command");
1155196200Sscottl		return (errno);
1156196200Sscottl	}
1157196200Sscottl	dump_config(fd, config);
1158196200Sscottl	free(config);
1159196200Sscottl	close(fd);
1160196200Sscottl
1161196200Sscottl	return (0);
1162196200Sscottl}
1163196200SscottlMFI_COMMAND(top, dump, dump);
1164196200Sscottl#endif
1165