mfi_config.c revision 237260
1189251Ssam/*-
2189251Ssam * Copyright (c) 2008, 2009 Yahoo!, Inc.
3189251Ssam * All rights reserved.
4189251Ssam *
5189251Ssam * Redistribution and use in source and binary forms, with or without
6189251Ssam * modification, are permitted provided that the following conditions
7189251Ssam * are met:
8189251Ssam * 1. Redistributions of source code must retain the above copyright
9189251Ssam *    notice, this list of conditions and the following disclaimer.
10189251Ssam * 2. Redistributions in binary form must reproduce the above copyright
11189251Ssam *    notice, this list of conditions and the following disclaimer in the
12189251Ssam *    documentation and/or other materials provided with the distribution.
13189251Ssam * 3. The names of the authors may not be used to endorse or promote
14189251Ssam *    products derived from this software without specific prior written
15189251Ssam *    permission.
16189251Ssam *
17189251Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18189251Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19189251Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20189251Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21189251Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22189251Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23189251Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24189251Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25189251Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26189251Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27189251Ssam * SUCH DAMAGE.
28189251Ssam *
29189251Ssam * $FreeBSD: head/usr.sbin/mfiutil/mfi_config.c 237260 2012-06-19 06:18:42Z eadler $
30189251Ssam */
31189251Ssam
32189251Ssam#include <sys/param.h>
33189251Ssam#ifdef DEBUG
34189251Ssam#include <sys/sysctl.h>
35189251Ssam#endif
36189251Ssam#include <err.h>
37189251Ssam#include <errno.h>
38189251Ssam#include <fcntl.h>
39189251Ssam#include <libutil.h>
40189251Ssam#ifdef DEBUG
41189251Ssam#include <stdint.h>
42189251Ssam#endif
43189251Ssam#include <stdio.h>
44189251Ssam#include <stdlib.h>
45189251Ssam#include <string.h>
46189251Ssam#include <unistd.h>
47189251Ssam#include "mfiutil.h"
48189251Ssam
49189251Ssam#ifdef DEBUG
50189251Ssamstatic void	dump_config(int fd, struct mfi_config_data *config);
51189251Ssam#endif
52189251Ssam
53189251Ssamstatic int	add_spare(int ac, char **av);
54189251Ssamstatic int	remove_spare(int ac, char **av);
55189251Ssam
56189251Ssamstatic long
57189251Ssamdehumanize(const char *value)
58189251Ssam{
59189251Ssam        char    *vtp;
60189251Ssam        long    iv;
61189251Ssam
62189251Ssam        if (value == NULL)
63189251Ssam                return (0);
64189251Ssam        iv = strtoq(value, &vtp, 0);
65189251Ssam        if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
66189251Ssam                return (0);
67189251Ssam        }
68189251Ssam        switch (vtp[0]) {
69189251Ssam        case 't': case 'T':
70189251Ssam                iv *= 1024;
71189251Ssam        case 'g': case 'G':
72189251Ssam                iv *= 1024;
73189251Ssam        case 'm': case 'M':
74189251Ssam                iv *= 1024;
75189251Ssam        case 'k': case 'K':
76189251Ssam                iv *= 1024;
77189251Ssam        case '\0':
78189251Ssam                break;
79189251Ssam        default:
80189251Ssam                return (0);
81189251Ssam        }
82189251Ssam        return (iv);
83189251Ssam}
84189251Ssamint
85189251Ssammfi_config_read(int fd, struct mfi_config_data **configp)
86189251Ssam{
87189251Ssam	struct mfi_config_data *config;
88189251Ssam	uint32_t config_size;
89189251Ssam	int error;
90189251Ssam
91189251Ssam	/*
92189251Ssam	 * Keep fetching the config in a loop until we have a large enough
93189251Ssam	 * buffer to hold the entire configuration.
94189251Ssam	 */
95189251Ssam	config = NULL;
96189251Ssam	config_size = 1024;
97189251Ssamfetch:
98189251Ssam	config = reallocf(config, config_size);
99189251Ssam	if (config == NULL)
100189251Ssam		return (-1);
101189251Ssam	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_READ, config,
102189251Ssam	    config_size, NULL, 0, NULL) < 0) {
103189251Ssam		error = errno;
104189251Ssam		free(config);
105189251Ssam		errno = error;
106189251Ssam		return (-1);
107189251Ssam	}
108189251Ssam
109189251Ssam	if (config->size > config_size) {
110189251Ssam		config_size = config->size;
111189251Ssam		goto fetch;
112189251Ssam	}
113189251Ssam
114189251Ssam	*configp = config;
115189251Ssam	return (0);
116189251Ssam}
117189251Ssam
118189251Ssamstatic struct mfi_array *
119189251Ssammfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
120209158Srpaulo{
121209158Srpaulo	struct mfi_array *ar;
122189251Ssam	char *p;
123189251Ssam	int i;
124189251Ssam
125189251Ssam	p = (char *)config->array;
126189251Ssam	for (i = 0; i < config->array_count; i++) {
127189251Ssam		ar = (struct mfi_array *)p;
128189251Ssam		if (ar->array_ref == array_ref)
129189251Ssam			return (ar);
130189251Ssam		p += config->array_size;
131189251Ssam	}
132189251Ssam
133189251Ssam	return (NULL);
134189251Ssam}
135189251Ssam
136189251Ssamstatic struct mfi_ld_config *
137189251Ssammfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
138189251Ssam{
139189251Ssam	struct mfi_ld_config *ld;
140189251Ssam	char *p;
141189251Ssam	int i;
142189251Ssam
143189251Ssam	p = (char *)config->array + config->array_count * config->array_size;
144189251Ssam	for (i = 0; i < config->log_drv_count; i++) {
145189251Ssam		ld = (struct mfi_ld_config *)p;
146189251Ssam		if (ld->properties.ld.v.target_id == target_id)
147189251Ssam			return (ld);
148189251Ssam		p += config->log_drv_size;
149189251Ssam	}
150189251Ssam
151189251Ssam	return (NULL);
152189251Ssam}
153189251Ssam
154189251Ssamstatic int
155189251Ssamclear_config(int ac __unused, char **av __unused)
156189251Ssam{
157189251Ssam	struct mfi_ld_list list;
158189251Ssam	int ch, error, fd;
159189251Ssam	u_int i;
160189251Ssam
161189251Ssam	fd = mfi_open(mfi_unit, O_RDWR);
162189251Ssam	if (fd < 0) {
163189251Ssam		error = errno;
164189251Ssam		warn("mfi_open");
165189251Ssam		return (error);
166189251Ssam	}
167189251Ssam
168189251Ssam	if (!mfi_reconfig_supported()) {
169189251Ssam		warnx("The current mfi(4) driver does not support "
170189251Ssam		    "configuration changes.");
171189251Ssam		close(fd);
172189251Ssam		return (EOPNOTSUPP);
173189251Ssam	}
174189251Ssam
175189251Ssam	if (mfi_ld_get_list(fd, &list, NULL) < 0) {
176189251Ssam		error = errno;
177189251Ssam		warn("Failed to get volume list");
178189251Ssam		close(fd);
179189251Ssam		return (error);
180189251Ssam	}
181189251Ssam
182189251Ssam	for (i = 0; i < list.ld_count; i++) {
183209158Srpaulo		if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) {
184189251Ssam			warnx("Volume %s is busy and cannot be deleted",
185189251Ssam			    mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
186189251Ssam			close(fd);
187189251Ssam			return (EBUSY);
188189251Ssam		}
189189251Ssam	}
190189251Ssam
191189251Ssam	printf(
192189251Ssam	    "Are you sure you wish to clear the configuration on mfi%u? [y/N] ",
193189251Ssam	    mfi_unit);
194189251Ssam	ch = getchar();
195189251Ssam	if (ch != 'y' && ch != 'Y') {
196189251Ssam		printf("\nAborting\n");
197189251Ssam		close(fd);
198189251Ssam		return (0);
199189251Ssam	}
200189251Ssam
201189251Ssam	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) {
202189251Ssam		error = errno;
203189251Ssam		warn("Failed to clear configuration");
204189251Ssam		close(fd);
205189251Ssam		return (error);
206189251Ssam	}
207189251Ssam
208189251Ssam	printf("mfi%d: Configuration cleared\n", mfi_unit);
209189251Ssam	close(fd);
210189251Ssam
211189251Ssam	return (0);
212189251Ssam}
213189251SsamMFI_COMMAND(top, clear, clear_config);
214189251Ssam
215189251Ssam#define MAX_DRIVES_PER_ARRAY MFI_MAX_ROW_SIZE
216189251Ssam#define MFI_ARRAY_SIZE sizeof(struct mfi_array)
217189251Ssam
218189251Ssam#define	RT_RAID0	0
219189251Ssam#define	RT_RAID1	1
220189251Ssam#define	RT_RAID5	2
221189251Ssam#define	RT_RAID6	3
222189251Ssam#define	RT_JBOD		4
223189251Ssam#define	RT_CONCAT	5
224189251Ssam#define	RT_RAID10	6
225189251Ssam#define	RT_RAID50	7
226189251Ssam#define	RT_RAID60	8
227189251Ssam
228189251Ssamstatic int
229189251Ssamcompare_int(const void *one, const void *two)
230189251Ssam{
231189251Ssam	int first, second;
232189251Ssam
233189251Ssam	first = *(const int *)one;
234189251Ssam	second = *(const int *)two;
235189251Ssam
236189251Ssam	return (first - second);
237189251Ssam}
238189251Ssam
239189251Ssamstatic struct raid_type_entry {
240189251Ssam	const char *name;
241189251Ssam	int	raid_type;
242189251Ssam} raid_type_table[] = {
243189251Ssam	{ "raid0",	RT_RAID0 },
244189251Ssam	{ "raid-0",	RT_RAID0 },
245189251Ssam	{ "raid1",	RT_RAID1 },
246189251Ssam	{ "raid-1",	RT_RAID1 },
247189251Ssam	{ "mirror",	RT_RAID1 },
248189251Ssam	{ "raid5",	RT_RAID5 },
249189251Ssam	{ "raid-5",	RT_RAID5 },
250189251Ssam	{ "raid6",	RT_RAID6 },
251189251Ssam	{ "raid-6",	RT_RAID6 },
252189251Ssam	{ "jbod",	RT_JBOD },
253189251Ssam	{ "concat",	RT_CONCAT },
254189251Ssam	{ "raid10",	RT_RAID10 },
255189251Ssam	{ "raid1+0",	RT_RAID10 },
256189251Ssam	{ "raid-10",	RT_RAID10 },
257189251Ssam	{ "raid-1+0",	RT_RAID10 },
258189251Ssam	{ "raid50",	RT_RAID50 },
259189251Ssam	{ "raid5+0",	RT_RAID50 },
260189251Ssam	{ "raid-50",	RT_RAID50 },
261189251Ssam	{ "raid-5+0",	RT_RAID50 },
262189251Ssam	{ "raid60",	RT_RAID60 },
263189251Ssam	{ "raid6+0",	RT_RAID60 },
264189251Ssam	{ "raid-60",	RT_RAID60 },
265189251Ssam	{ "raid-6+0",	RT_RAID60 },
266189251Ssam	{ NULL,		0 },
267189251Ssam};
268189251Ssam
269189251Ssamstruct config_id_state {
270189251Ssam	int	array_count;
271189251Ssam	int	log_drv_count;
272189251Ssam	int	*arrays;
273189251Ssam	int	*volumes;
274189251Ssam	uint16_t array_ref;
275189251Ssam	uint8_t	target_id;
276189251Ssam};
277189251Ssam
278189251Ssamstruct array_info {
279189251Ssam	int	drive_count;
280189251Ssam	struct mfi_pd_info *drives;
281189251Ssam	struct mfi_array *array;
282189251Ssam};
283189251Ssam
284189251Ssam/* Parse a comma-separated list of drives for an array. */
285189251Ssamstatic int
286189251Ssamparse_array(int fd, int raid_type, char *array_str, struct array_info *info)
287189251Ssam{
288189251Ssam	struct mfi_pd_info *pinfo;
289189251Ssam	uint16_t device_id;
290189251Ssam	char *cp;
291189251Ssam	u_int count;
292189251Ssam	int error;
293189251Ssam
294189251Ssam	cp = array_str;
295189251Ssam	for (count = 0; cp != NULL; count++) {
296189251Ssam		cp = strchr(cp, ',');
297189251Ssam		if (cp != NULL) {
298189251Ssam			cp++;
299189251Ssam			if (*cp == ',') {
300189251Ssam				warnx("Invalid drive list '%s'", array_str);
301189251Ssam				return (EINVAL);
302189251Ssam			}
303189251Ssam		}
304189251Ssam	}
305189251Ssam
306189251Ssam	/* Validate the number of drives for this array. */
307189251Ssam	if (count >= MAX_DRIVES_PER_ARRAY) {
308189251Ssam		warnx("Too many drives for a single array: max is %d",
309189251Ssam		    MAX_DRIVES_PER_ARRAY);
310189251Ssam		return (EINVAL);
311189251Ssam	}
312189251Ssam	switch (raid_type) {
313189251Ssam	case RT_RAID1:
314189251Ssam	case RT_RAID10:
315189251Ssam		if (count % 2 != 0) {
316189251Ssam			warnx("RAID1 and RAID10 require an even number of "
317189251Ssam			    "drives in each array");
318189251Ssam			return (EINVAL);
319189251Ssam		}
320189251Ssam		break;
321189251Ssam	case RT_RAID5:
322189251Ssam	case RT_RAID50:
323189251Ssam		if (count < 3) {
324189251Ssam			warnx("RAID5 and RAID50 require at least 3 drives in "
325189251Ssam			    "each array");
326189251Ssam			return (EINVAL);
327189251Ssam		}
328189251Ssam		break;
329189251Ssam	case RT_RAID6:
330189251Ssam	case RT_RAID60:
331189251Ssam		if (count < 4) {
332189251Ssam			warnx("RAID6 and RAID60 require at least 4 drives in "
333189251Ssam			    "each array");
334189251Ssam			return (EINVAL);
335189251Ssam		}
336189251Ssam		break;
337189251Ssam	}
338189251Ssam
339189251Ssam	/* Validate each drive. */
340189251Ssam	info->drives = calloc(count, sizeof(struct mfi_pd_info));
341189251Ssam	if (info->drives == NULL) {
342189251Ssam		warnx("malloc failed");
343189251Ssam		return (ENOMEM);
344189251Ssam	}
345189251Ssam	info->drive_count = count;
346189251Ssam	for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL;
347189251Ssam	     pinfo++) {
348189251Ssam		error = mfi_lookup_drive(fd, cp, &device_id);
349189251Ssam		if (error) {
350189251Ssam			free(info->drives);
351189251Ssam			info->drives = NULL;
352189251Ssam			return (error);
353189251Ssam		}
354189251Ssam
355189251Ssam		if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) {
356189251Ssam			error = errno;
357189251Ssam			warn("Failed to fetch drive info for drive %s", cp);
358189251Ssam			free(info->drives);
359189251Ssam			info->drives = NULL;
360189251Ssam			return (error);
361189251Ssam		}
362189251Ssam
363189251Ssam		if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
364189251Ssam			warnx("Drive %u is not available", device_id);
365189251Ssam			free(info->drives);
366189251Ssam			info->drives = NULL;
367189251Ssam			return (EINVAL);
368189251Ssam		}
369189251Ssam	}
370189251Ssam
371189251Ssam	return (0);
372189251Ssam}
373189251Ssam
374189251Ssam/*
375189251Ssam * Find the next free array ref assuming that 'array_ref' is the last
376189251Ssam * one used.  'array_ref' should be 0xffff for the initial test.
377189251Ssam */
378189251Ssamstatic uint16_t
379189251Ssamfind_next_array(struct config_id_state *state)
380189251Ssam{
381189251Ssam	int i;
382189251Ssam
383189251Ssam	/* Assume the current one is used. */
384189251Ssam	state->array_ref++;
385189251Ssam
386189251Ssam	/* Find the next free one. */
387189251Ssam	for (i = 0; i < state->array_count; i++)
388189251Ssam		if (state->arrays[i] == state->array_ref)
389189251Ssam			state->array_ref++;
390189251Ssam	return (state->array_ref);
391189251Ssam}
392189251Ssam
393189251Ssam/*
394189251Ssam * Find the next free volume ID assuming that 'target_id' is the last
395189251Ssam * one used.  'target_id' should be 0xff for the initial test.
396189251Ssam */
397189251Ssamstatic uint8_t
398189251Ssamfind_next_volume(struct config_id_state *state)
399189251Ssam{
400189251Ssam	int i;
401189251Ssam
402189251Ssam	/* Assume the current one is used. */
403189251Ssam	state->target_id++;
404189251Ssam
405189251Ssam	/* Find the next free one. */
406189251Ssam	for (i = 0; i < state->log_drv_count; i++)
407189251Ssam		if (state->volumes[i] == state->target_id)
408189251Ssam			state->target_id++;
409189251Ssam	return (state->target_id);
410189251Ssam}
411189251Ssam
412189251Ssam/* Populate an array with drives. */
413189251Ssamstatic void
414189251Ssambuild_array(int fd __unused, char *arrayp, struct array_info *array_info,
415189251Ssam    struct config_id_state *state, int verbose)
416189251Ssam{
417189251Ssam	struct mfi_array *ar = (struct mfi_array *)arrayp;
418189251Ssam	int i;
419189251Ssam
420189251Ssam	ar->size = array_info->drives[0].coerced_size;
421189251Ssam	ar->num_drives = array_info->drive_count;
422189251Ssam	ar->array_ref = find_next_array(state);
423189251Ssam	for (i = 0; i < array_info->drive_count; i++) {
424189251Ssam		if (verbose)
425189251Ssam			printf("Adding drive %s to array %u\n",
426189251Ssam			    mfi_drive_name(NULL,
427189251Ssam			    array_info->drives[i].ref.v.device_id,
428189251Ssam			    MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS),
429189251Ssam			    ar->array_ref);
430189251Ssam		if (ar->size > array_info->drives[i].coerced_size)
431189251Ssam			ar->size = array_info->drives[i].coerced_size;
432189251Ssam		ar->pd[i].ref = array_info->drives[i].ref;
433189251Ssam		ar->pd[i].fw_state = MFI_PD_STATE_ONLINE;
434189251Ssam	}
435189251Ssam	array_info->array = ar;
436189251Ssam}
437189251Ssam
438189251Ssam/*
439189251Ssam * Create a volume that spans one or more arrays.
440189251Ssam */
441189251Ssamstatic void
442189251Ssambuild_volume(char *volumep, int narrays, struct array_info *arrays,
443189251Ssam    int raid_type, long stripe_size, struct config_id_state *state, int verbose)
444189251Ssam{
445189251Ssam	struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
446189251Ssam	struct mfi_array *ar;
447189251Ssam	int i;
448189251Ssam
449189251Ssam	/* properties */
450189251Ssam	ld->properties.ld.v.target_id = find_next_volume(state);
451189251Ssam	ld->properties.ld.v.seq = 0;
452189251Ssam	ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
453189251Ssam	    MR_LD_CACHE_WRITE_BACK;
454189251Ssam	ld->properties.access_policy = MFI_LD_ACCESS_RW;
455189251Ssam	ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED;
456189251Ssam	ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
457189251Ssam	    MR_LD_CACHE_WRITE_BACK;
458189251Ssam	ld->properties.no_bgi = 0;
459189251Ssam
460189251Ssam	/* params */
461189251Ssam	switch (raid_type) {
462189251Ssam	case RT_RAID0:
463189251Ssam	case RT_JBOD:
464189251Ssam		ld->params.primary_raid_level = DDF_RAID0;
465189251Ssam		ld->params.raid_level_qualifier = 0;
466189251Ssam		ld->params.secondary_raid_level = 0;
467189251Ssam		break;
468189251Ssam	case RT_RAID1:
469189251Ssam		ld->params.primary_raid_level = DDF_RAID1;
470189251Ssam		ld->params.raid_level_qualifier = 0;
471189251Ssam		ld->params.secondary_raid_level = 0;
472189251Ssam		break;
473189251Ssam	case RT_RAID5:
474189251Ssam		ld->params.primary_raid_level = DDF_RAID5;
475189251Ssam		ld->params.raid_level_qualifier = 3;
476189251Ssam		ld->params.secondary_raid_level = 0;
477189251Ssam		break;
478189251Ssam	case RT_RAID6:
479189251Ssam		ld->params.primary_raid_level = DDF_RAID6;
480189251Ssam		ld->params.raid_level_qualifier = 3;
481189251Ssam		ld->params.secondary_raid_level = 0;
482189251Ssam		break;
483189251Ssam	case RT_CONCAT:
484189251Ssam		ld->params.primary_raid_level = DDF_CONCAT;
485189251Ssam		ld->params.raid_level_qualifier = 0;
486189251Ssam		ld->params.secondary_raid_level = 0;
487189251Ssam		break;
488189251Ssam	case RT_RAID10:
489189251Ssam		ld->params.primary_raid_level = DDF_RAID1;
490189251Ssam		ld->params.raid_level_qualifier = 0;
491189251Ssam		ld->params.secondary_raid_level = 3; /* XXX? */
492189251Ssam		break;
493189251Ssam	case RT_RAID50:
494189251Ssam		/*
495209158Srpaulo		 * XXX: This appears to work though the card's BIOS
496189251Ssam		 * complains that the configuration is foreign.  The
497189251Ssam		 * BIOS setup does not allow for creation of RAID-50
498189251Ssam		 * or RAID-60 arrays.  The only nested array
499189251Ssam		 * configuration it allows for is RAID-10.
500189251Ssam		 */
501189251Ssam		ld->params.primary_raid_level = DDF_RAID5;
502189251Ssam		ld->params.raid_level_qualifier = 3;
503189251Ssam		ld->params.secondary_raid_level = 3; /* XXX? */
504189251Ssam		break;
505189251Ssam	case RT_RAID60:
506189251Ssam		ld->params.primary_raid_level = DDF_RAID6;
507189251Ssam		ld->params.raid_level_qualifier = 3;
508189251Ssam		ld->params.secondary_raid_level = 3; /* XXX? */
509189251Ssam		break;
510189251Ssam	}
511189251Ssam
512189251Ssam	/*
513189251Ssam	 * Stripe size is encoded as (2 ^ N) * 512 = stripe_size.  Use
514189251Ssam	 * ffs() to simulate log2(stripe_size).
515189251Ssam	 */
516189251Ssam	ld->params.stripe_size = ffs(stripe_size) - 1 - 9;
517189251Ssam	ld->params.num_drives = arrays[0].array->num_drives;
518189251Ssam	ld->params.span_depth = narrays;
519189251Ssam	ld->params.state = MFI_LD_STATE_OPTIMAL;
520189251Ssam	ld->params.init_state = MFI_LD_PARAMS_INIT_NO;
521189251Ssam	ld->params.is_consistent = 0;
522189251Ssam
523189251Ssam	/* spans */
524189251Ssam	for (i = 0; i < narrays; i++) {
525189251Ssam		ar = arrays[i].array;
526189251Ssam		if (verbose)
527189251Ssam			printf("Adding array %u to volume %u\n", ar->array_ref,
528189251Ssam			    ld->properties.ld.v.target_id);
529189251Ssam		ld->span[i].start_block = 0;
530189251Ssam		ld->span[i].num_blocks = ar->size;
531189251Ssam		ld->span[i].array_ref = ar->array_ref;
532189251Ssam	}
533189251Ssam}
534189251Ssam
535189251Ssamstatic int
536189251Ssamcreate_volume(int ac, char **av)
537189251Ssam{
538189251Ssam	struct mfi_config_data *config;
539189251Ssam	struct mfi_array *ar;
540189251Ssam	struct mfi_ld_config *ld;
541189251Ssam	struct config_id_state state;
542189251Ssam	size_t config_size;
543189251Ssam	char *p, *cfg_arrays, *cfg_volumes;
544189251Ssam	int error, fd, i, raid_type;
545189251Ssam	int narrays, nvolumes, arrays_per_volume;
546189251Ssam	struct array_info *arrays;
547189251Ssam	long stripe_size;
548189251Ssam#ifdef DEBUG
549189251Ssam	int dump;
550189251Ssam#endif
551189251Ssam	int ch, verbose;
552189251Ssam
553189251Ssam	/*
554189251Ssam	 * Backwards compat.  Map 'create volume' to 'create' and
555189251Ssam	 * 'create spare' to 'add'.
556189251Ssam	 */
557189251Ssam	if (ac > 1) {
558189251Ssam		if (strcmp(av[1], "volume") == 0) {
559189251Ssam			av++;
560189251Ssam			ac--;
561189251Ssam		} else if (strcmp(av[1], "spare") == 0) {
562189251Ssam			av++;
563189251Ssam			ac--;
564189251Ssam			return (add_spare(ac, av));
565189251Ssam		}
566189251Ssam	}
567189251Ssam
568189251Ssam	if (ac < 2) {
569189251Ssam		warnx("create volume: volume type required");
570189251Ssam		return (EINVAL);
571189251Ssam	}
572189251Ssam
573189251Ssam	bzero(&state, sizeof(state));
574189251Ssam	config = NULL;
575189251Ssam	arrays = NULL;
576189251Ssam	narrays = 0;
577189251Ssam	error = 0;
578189251Ssam
579189251Ssam	fd = mfi_open(mfi_unit, O_RDWR);
580189251Ssam	if (fd < 0) {
581189251Ssam		error = errno;
582189251Ssam		warn("mfi_open");
583189251Ssam		return (error);
584189251Ssam	}
585189251Ssam
586189251Ssam	if (!mfi_reconfig_supported()) {
587189251Ssam		warnx("The current mfi(4) driver does not support "
588189251Ssam		    "configuration changes.");
589189251Ssam		error = EOPNOTSUPP;
590189251Ssam		goto error;
591189251Ssam	}
592189251Ssam
593189251Ssam	/* Lookup the RAID type first. */
594189251Ssam	raid_type = -1;
595189251Ssam	for (i = 0; raid_type_table[i].name != NULL; i++)
596189251Ssam		if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
597189251Ssam			raid_type = raid_type_table[i].raid_type;
598189251Ssam			break;
599189251Ssam		}
600189251Ssam
601189251Ssam	if (raid_type == -1) {
602189251Ssam		warnx("Unknown or unsupported volume type %s", av[1]);
603189251Ssam		error = EINVAL;
604189251Ssam		goto error;
605189251Ssam	}
606209158Srpaulo
607209158Srpaulo	/* Parse any options. */
608209158Srpaulo	optind = 2;
609189251Ssam#ifdef DEBUG
610189251Ssam	dump = 0;
611189251Ssam#endif
612189251Ssam	verbose = 0;
613189251Ssam	stripe_size = 64 * 1024;
614189251Ssam
615189251Ssam	while ((ch = getopt(ac, av, "ds:v")) != -1) {
616189251Ssam		switch (ch) {
617189251Ssam#ifdef DEBUG
618189251Ssam		case 'd':
619189251Ssam			dump = 1;
620189251Ssam			break;
621189251Ssam#endif
622189251Ssam		case 's':
623189251Ssam			stripe_size = dehumanize(optarg);
624189251Ssam			if ((stripe_size < 512) || (!powerof2(stripe_size)))
625189251Ssam				stripe_size = 64 * 1024;
626189251Ssam			break;
627189251Ssam		case 'v':
628189251Ssam			verbose = 1;
629189251Ssam			break;
630189251Ssam		case '?':
631189251Ssam		default:
632189251Ssam			error = EINVAL;
633189251Ssam			goto error;
634189251Ssam		}
635189251Ssam	}
636189251Ssam	ac -= optind;
637189251Ssam	av += optind;
638189251Ssam
639189251Ssam	/* Parse all the arrays. */
640189251Ssam	narrays = ac;
641189251Ssam	if (narrays == 0) {
642189251Ssam		warnx("At least one drive list is required");
643189251Ssam		error = EINVAL;
644189251Ssam		goto error;
645189251Ssam	}
646189251Ssam	switch (raid_type) {
647189251Ssam	case RT_RAID0:
648189251Ssam	case RT_RAID1:
649189251Ssam	case RT_RAID5:
650209158Srpaulo	case RT_RAID6:
651189251Ssam	case RT_CONCAT:
652189251Ssam		if (narrays != 1) {
653189251Ssam			warnx("Only one drive list can be specified");
654189251Ssam			error = EINVAL;
655189251Ssam			goto error;
656189251Ssam		}
657189251Ssam		break;
658189251Ssam	case RT_RAID10:
659189251Ssam	case RT_RAID50:
660189251Ssam	case RT_RAID60:
661189251Ssam		if (narrays < 1) {
662189251Ssam			warnx("RAID10, RAID50, and RAID60 require at least "
663189251Ssam			    "two drive lists");
664189251Ssam			error = EINVAL;
665189251Ssam			goto error;
666189251Ssam		}
667189251Ssam		if (narrays > MFI_MAX_SPAN_DEPTH) {
668189251Ssam			warnx("Volume spans more than %d arrays",
669209158Srpaulo			    MFI_MAX_SPAN_DEPTH);
670209158Srpaulo			error = EINVAL;
671209158Srpaulo			goto error;
672209158Srpaulo		}
673209158Srpaulo		break;
674209158Srpaulo	}
675209158Srpaulo	arrays = calloc(narrays, sizeof(*arrays));
676189251Ssam	if (arrays == NULL) {
677189251Ssam		warnx("malloc failed");
678189251Ssam		error = ENOMEM;
679189251Ssam		goto error;
680189251Ssam	}
681189251Ssam	for (i = 0; i < narrays; i++) {
682189251Ssam		error = parse_array(fd, raid_type, av[i], &arrays[i]);
683189251Ssam		if (error)
684189251Ssam			goto error;
685189251Ssam	}
686189251Ssam
687189251Ssam	switch (raid_type) {
688189251Ssam	case RT_RAID10:
689189251Ssam	case RT_RAID50:
690209158Srpaulo	case RT_RAID60:
691189251Ssam		for (i = 1; i < narrays; i++) {
692189251Ssam			if (arrays[i].drive_count != arrays[0].drive_count) {
693189251Ssam				warnx("All arrays must contain the same "
694189251Ssam				    "number of drives");
695189251Ssam				error = EINVAL;
696189251Ssam				goto error;
697189251Ssam			}
698209158Srpaulo		}
699209158Srpaulo		break;
700189251Ssam	}
701209158Srpaulo
702189251Ssam	/*
703189251Ssam	 * Fetch the current config and build sorted lists of existing
704189251Ssam	 * array and volume identifiers.
705189251Ssam	 */
706189251Ssam	if (mfi_config_read(fd, &config) < 0) {
707209158Srpaulo		error = errno;
708189251Ssam		warn("Failed to read configuration");
709189251Ssam		goto error;
710189251Ssam	}
711189251Ssam	p = (char *)config->array;
712209158Srpaulo	state.array_ref = 0xffff;
713209158Srpaulo	state.target_id = 0xff;
714189251Ssam	state.array_count = config->array_count;
715189251Ssam	if (config->array_count > 0) {
716189251Ssam		state.arrays = calloc(config->array_count, sizeof(int));
717209158Srpaulo		if (state.arrays == NULL) {
718189251Ssam			warnx("malloc failed");
719189251Ssam			error = ENOMEM;
720189251Ssam			goto error;
721189251Ssam		}
722189251Ssam		for (i = 0; i < config->array_count; i++) {
723189251Ssam			ar = (struct mfi_array *)p;
724189251Ssam			state.arrays[i] = ar->array_ref;
725189251Ssam			p += config->array_size;
726189251Ssam		}
727189251Ssam		qsort(state.arrays, config->array_count, sizeof(int),
728189251Ssam		    compare_int);
729189251Ssam	} else
730189251Ssam		state.arrays = NULL;
731189251Ssam	state.log_drv_count = config->log_drv_count;
732189251Ssam	if (config->log_drv_count) {
733189251Ssam		state.volumes = calloc(config->log_drv_count, sizeof(int));
734189251Ssam		if (state.volumes == NULL) {
735189251Ssam			warnx("malloc failed");
736189251Ssam			error = ENOMEM;
737189251Ssam			goto error;
738189251Ssam		}
739189251Ssam		for (i = 0; i < config->log_drv_count; i++) {
740189251Ssam			ld = (struct mfi_ld_config *)p;
741189251Ssam			state.volumes[i] = ld->properties.ld.v.target_id;
742189251Ssam			p += config->log_drv_size;
743189251Ssam		}
744189251Ssam		qsort(state.volumes, config->log_drv_count, sizeof(int),
745189251Ssam		    compare_int);
746189251Ssam	} else
747189251Ssam		state.volumes = NULL;
748189251Ssam	free(config);
749189251Ssam
750189251Ssam	/* Determine the size of the configuration we will build. */
751189251Ssam	switch (raid_type) {
752189251Ssam	case RT_RAID0:
753189251Ssam	case RT_RAID1:
754189251Ssam	case RT_RAID5:
755189251Ssam	case RT_RAID6:
756189251Ssam	case RT_CONCAT:
757189251Ssam	case RT_JBOD:
758189251Ssam		/* Each volume spans a single array. */
759189251Ssam		nvolumes = narrays;
760189251Ssam		break;
761189251Ssam	case RT_RAID10:
762189251Ssam	case RT_RAID50:
763189251Ssam	case RT_RAID60:
764189251Ssam		/* A single volume spans multiple arrays. */
765189251Ssam		nvolumes = 1;
766189251Ssam		break;
767189251Ssam	default:
768189251Ssam		/* Pacify gcc. */
769189251Ssam		abort();
770189251Ssam	}
771189251Ssam
772189251Ssam	config_size = sizeof(struct mfi_config_data) +
773189251Ssam	    sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays;
774189251Ssam	config = calloc(1, config_size);
775209158Srpaulo	if (config == NULL) {
776209158Srpaulo		warnx("malloc failed");
777209158Srpaulo		error = ENOMEM;
778209158Srpaulo		goto error;
779209158Srpaulo	}
780209158Srpaulo	config->size = config_size;
781209158Srpaulo	config->array_count = narrays;
782209158Srpaulo	config->array_size = MFI_ARRAY_SIZE;	/* XXX: Firmware hardcode */
783209158Srpaulo	config->log_drv_count = nvolumes;
784209158Srpaulo	config->log_drv_size = sizeof(struct mfi_ld_config);
785209158Srpaulo	config->spares_count = 0;
786209158Srpaulo	config->spares_size = 40;		/* XXX: Firmware hardcode */
787209158Srpaulo	cfg_arrays = (char *)config->array;
788209158Srpaulo	cfg_volumes = cfg_arrays + config->array_size * narrays;
789209158Srpaulo
790209158Srpaulo	/* Build the arrays. */
791189251Ssam	for (i = 0; i < narrays; i++) {
792189251Ssam		build_array(fd, cfg_arrays, &arrays[i], &state, verbose);
793189251Ssam		cfg_arrays += config->array_size;
794189251Ssam	}
795189251Ssam
796189251Ssam	/* Now build the volume(s). */
797189251Ssam	arrays_per_volume = narrays / nvolumes;
798189251Ssam	for (i = 0; i < nvolumes; i++) {
799189251Ssam		build_volume(cfg_volumes, arrays_per_volume,
800189251Ssam		    &arrays[i * arrays_per_volume], raid_type, stripe_size,
801189251Ssam		    &state, verbose);
802209158Srpaulo		cfg_volumes += config->log_drv_size;
803209158Srpaulo	}
804189251Ssam
805189251Ssam#ifdef DEBUG
806189251Ssam	if (dump)
807189251Ssam		dump_config(fd, config);
808189251Ssam#endif
809189251Ssam
810189251Ssam	/* Send the new config to the controller. */
811189251Ssam	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size,
812189251Ssam	    NULL, 0, NULL) < 0) {
813189251Ssam		error = errno;
814189251Ssam		warn("Failed to add volume");
815189251Ssam		/* FALLTHROUGH */
816189251Ssam	}
817189251Ssam
818189251Ssamerror:
819189251Ssam	/* Clean up. */
820189251Ssam	free(config);
821189251Ssam	free(state.volumes);
822189251Ssam	free(state.arrays);
823189251Ssam	if (arrays != NULL) {
824189251Ssam		for (i = 0; i < narrays; i++)
825189251Ssam			free(arrays[i].drives);
826189251Ssam		free(arrays);
827189251Ssam	}
828189251Ssam	close(fd);
829189251Ssam
830189251Ssam	return (error);
831189251Ssam}
832189251SsamMFI_COMMAND(top, create, create_volume);
833189251Ssam
834189251Ssamstatic int
835189251Ssamdelete_volume(int ac, char **av)
836189251Ssam{
837189251Ssam	struct mfi_ld_info info;
838189251Ssam	int error, fd;
839189251Ssam	uint8_t target_id, mbox[4];
840189251Ssam
841189251Ssam	/*
842189251Ssam	 * Backwards compat.  Map 'delete volume' to 'delete' and
843189251Ssam	 * 'delete spare' to 'remove'.
844189251Ssam	 */
845189251Ssam	if (ac > 1) {
846189251Ssam		if (strcmp(av[1], "volume") == 0) {
847189251Ssam			av++;
848189251Ssam			ac--;
849189251Ssam		} else if (strcmp(av[1], "spare") == 0) {
850189251Ssam			av++;
851189251Ssam			ac--;
852189251Ssam			return (remove_spare(ac, av));
853189251Ssam		}
854189251Ssam	}
855189251Ssam
856189251Ssam	if (ac != 2) {
857189251Ssam		warnx("delete volume: volume required");
858189251Ssam		return (EINVAL);
859189251Ssam	}
860189251Ssam
861189251Ssam	fd = mfi_open(mfi_unit, O_RDWR);
862189251Ssam	if (fd < 0) {
863189251Ssam		error = errno;
864189251Ssam		warn("mfi_open");
865189251Ssam		return (error);
866189251Ssam	}
867189251Ssam
868189251Ssam	if (!mfi_reconfig_supported()) {
869189251Ssam		warnx("The current mfi(4) driver does not support "
870189251Ssam		    "configuration changes.");
871189251Ssam		close(fd);
872189251Ssam		return (EOPNOTSUPP);
873189251Ssam	}
874189251Ssam
875189251Ssam	if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
876189251Ssam		error = errno;
877189251Ssam		warn("Invalid volume %s", av[1]);
878189251Ssam		close(fd);
879189251Ssam		return (error);
880189251Ssam	}
881189251Ssam
882189251Ssam	if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
883189251Ssam		error = errno;
884189251Ssam		warn("Failed to get info for volume %d", target_id);
885189251Ssam		close(fd);
886189251Ssam		return (error);
887189251Ssam	}
888189251Ssam
889189251Ssam	if (mfi_volume_busy(fd, target_id)) {
890189251Ssam		warnx("Volume %s is busy and cannot be deleted",
891189251Ssam		    mfi_volume_name(fd, target_id));
892189251Ssam		close(fd);
893189251Ssam		return (EBUSY);
894189251Ssam	}
895189251Ssam
896189251Ssam	mbox_store_ldref(mbox, &info.ld_config.properties.ld);
897189251Ssam	if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox,
898189251Ssam	    sizeof(mbox), NULL) < 0) {
899189251Ssam		error = errno;
900189251Ssam		warn("Failed to delete volume");
901189251Ssam		close(fd);
902189251Ssam		return (error);
903189251Ssam	}
904189251Ssam
905189251Ssam	close(fd);
906189251Ssam
907189251Ssam	return (0);
908189251Ssam}
909189251SsamMFI_COMMAND(top, delete, delete_volume);
910189251Ssam
911189251Ssamstatic int
912189251Ssamadd_spare(int ac, char **av)
913189251Ssam{
914189251Ssam	struct mfi_pd_info info;
915189251Ssam	struct mfi_config_data *config;
916189251Ssam	struct mfi_array *ar;
917189251Ssam	struct mfi_ld_config *ld;
918189251Ssam	struct mfi_spare *spare;
919189251Ssam	uint16_t device_id;
920189251Ssam	uint8_t target_id;
921189251Ssam	char *p;
922189251Ssam	int error, fd, i;
923189251Ssam
924189251Ssam	if (ac < 2) {
925189251Ssam		warnx("add spare: drive required");
926189251Ssam		return (EINVAL);
927189251Ssam	}
928189251Ssam
929189251Ssam	fd = mfi_open(mfi_unit, O_RDWR);
930189251Ssam	if (fd < 0) {
931189251Ssam		error = errno;
932189251Ssam		warn("mfi_open");
933189251Ssam		return (error);
934189251Ssam	}
935189251Ssam
936189251Ssam	config = NULL;
937189251Ssam	spare = NULL;
938189251Ssam	error = mfi_lookup_drive(fd, av[1], &device_id);
939189251Ssam	if (error)
940189251Ssam		goto error;
941189251Ssam
942189251Ssam	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
943189251Ssam		error = errno;
944189251Ssam		warn("Failed to fetch drive info");
945189251Ssam		goto error;
946189251Ssam	}
947189251Ssam
948189251Ssam	if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
949189251Ssam		warnx("Drive %u is not available", device_id);
950189251Ssam		error = EINVAL;
951189251Ssam		goto error;
952189251Ssam	}
953189251Ssam
954189251Ssam	if (ac > 2) {
955189251Ssam		if (mfi_lookup_volume(fd, av[2], &target_id) < 0) {
956189251Ssam			error = errno;
957189251Ssam			warn("Invalid volume %s", av[2]);
958189251Ssam			goto error;
959189251Ssam		}
960189251Ssam	}
961189251Ssam
962189251Ssam	if (mfi_config_read(fd, &config) < 0) {
963189251Ssam		error = errno;
964189251Ssam		warn("Failed to read configuration");
965189251Ssam		goto error;
966189251Ssam	}
967189251Ssam
968189251Ssam	spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) *
969189251Ssam	    config->array_count);
970189251Ssam	if (spare == NULL) {
971189251Ssam		warnx("malloc failed");
972189251Ssam		error = ENOMEM;
973189251Ssam		goto error;
974189251Ssam	}
975189251Ssam	bzero(spare, sizeof(struct mfi_spare));
976189251Ssam	spare->ref = info.ref;
977189251Ssam
978189251Ssam	if (ac == 2) {
979189251Ssam		/* Global spare backs all arrays. */
980189251Ssam		p = (char *)config->array;
981189251Ssam		for (i = 0; i < config->array_count; i++) {
982189251Ssam			ar = (struct mfi_array *)p;
983189251Ssam			if (ar->size > info.coerced_size) {
984189251Ssam				warnx("Spare isn't large enough for array %u",
985189251Ssam				    ar->array_ref);
986189251Ssam				error = EINVAL;
987189251Ssam				goto error;
988189251Ssam			}
989189251Ssam			p += config->array_size;
990189251Ssam		}
991189251Ssam		spare->array_count = 0;
992189251Ssam	} else  {
993189251Ssam		/*
994189251Ssam		 * Dedicated spares only back the arrays for a
995189251Ssam		 * specific volume.
996189251Ssam		 */
997189251Ssam		ld = mfi_config_lookup_volume(config, target_id);
998189251Ssam		if (ld == NULL) {
999189251Ssam			warnx("Did not find volume %d", target_id);
1000189251Ssam			error = EINVAL;
1001189251Ssam			goto error;
1002189251Ssam		}
1003189251Ssam
1004189251Ssam		spare->spare_type |= MFI_SPARE_DEDICATED;
1005189251Ssam		spare->array_count = ld->params.span_depth;
1006189251Ssam		for (i = 0; i < ld->params.span_depth; i++) {
1007189251Ssam			ar = mfi_config_lookup_array(config,
1008189251Ssam			    ld->span[i].array_ref);
1009189251Ssam			if (ar == NULL) {
1010189251Ssam				warnx("Missing array; inconsistent config?");
1011189251Ssam				error = ENXIO;
1012189251Ssam				goto error;
1013189251Ssam			}
1014189251Ssam			if (ar->size > info.coerced_size) {
1015189251Ssam				warnx("Spare isn't large enough for array %u",
1016189251Ssam				    ar->array_ref);
1017189251Ssam				error = EINVAL;
1018189251Ssam				goto error;
1019189251Ssam			}
1020189251Ssam			spare->array_ref[i] = ar->array_ref;
1021189251Ssam		}
1022189251Ssam	}
1023189251Ssam
1024189251Ssam	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare,
1025189251Ssam	    sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count,
1026189251Ssam	    NULL, 0, NULL) < 0) {
1027189251Ssam		error = errno;
1028189251Ssam		warn("Failed to assign spare");
1029189251Ssam		/* FALLTHROUGH. */
1030189251Ssam	}
1031189251Ssam
1032189251Ssamerror:
1033189251Ssam	free(spare);
1034189251Ssam	free(config);
1035189251Ssam	close(fd);
1036189251Ssam
1037189251Ssam	return (error);
1038189251Ssam}
1039189251SsamMFI_COMMAND(top, add, add_spare);
1040189251Ssam
1041189251Ssamstatic int
1042189251Ssamremove_spare(int ac, char **av)
1043189251Ssam{
1044189251Ssam	struct mfi_pd_info info;
1045189251Ssam	int error, fd;
1046189251Ssam	uint16_t device_id;
1047189251Ssam	uint8_t mbox[4];
1048189251Ssam
1049189251Ssam	if (ac != 2) {
1050189251Ssam		warnx("remove spare: drive required");
1051189251Ssam		return (EINVAL);
1052189251Ssam	}
1053189251Ssam
1054189251Ssam	fd = mfi_open(mfi_unit, O_RDWR);
1055189251Ssam	if (fd < 0) {
1056189251Ssam		error = errno;
1057189251Ssam		warn("mfi_open");
1058189251Ssam		return (error);
1059189251Ssam	}
1060189251Ssam
1061189251Ssam	error = mfi_lookup_drive(fd, av[1], &device_id);
1062189251Ssam	if (error) {
1063189251Ssam		close(fd);
1064189251Ssam		return (error);
1065189251Ssam	}
1066189251Ssam
1067189251Ssam	/* Get the info for this drive. */
1068189251Ssam	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
1069189251Ssam		error = errno;
1070189251Ssam		warn("Failed to fetch info for drive %u", device_id);
1071189251Ssam		close(fd);
1072189251Ssam		return (error);
1073189251Ssam	}
1074189251Ssam
1075189251Ssam	if (info.fw_state != MFI_PD_STATE_HOT_SPARE) {
1076189251Ssam		warnx("Drive %u is not a hot spare", device_id);
1077189251Ssam		close(fd);
1078189251Ssam		return (EINVAL);
1079189251Ssam	}
1080189251Ssam
1081189251Ssam	mbox_store_pdref(mbox, &info.ref);
1082189251Ssam	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox,
1083189251Ssam	    sizeof(mbox), NULL) < 0) {
1084189251Ssam		error = errno;
1085189251Ssam		warn("Failed to delete spare");
1086189251Ssam		close(fd);
1087189251Ssam		return (error);
1088189251Ssam	}
1089189251Ssam
1090189251Ssam	close(fd);
1091189251Ssam
1092189251Ssam	return (0);
1093189251Ssam}
1094189251SsamMFI_COMMAND(top, remove, remove_spare);
1095189251Ssam
1096189251Ssam#ifdef DEBUG
1097189251Ssam/* Display raw data about a config. */
1098189251Ssamstatic void
1099189251Ssamdump_config(int fd, struct mfi_config_data *config)
1100189251Ssam{
1101189251Ssam	struct mfi_array *ar;
1102189251Ssam	struct mfi_ld_config *ld;
1103189251Ssam	struct mfi_spare *sp;
1104189251Ssam	struct mfi_pd_info pinfo;
1105189251Ssam	uint16_t device_id;
1106189251Ssam	char *p;
1107189251Ssam	int i, j;
1108189251Ssam
1109189251Ssam	printf(
1110189251Ssam	    "mfi%d Configuration (Debug): %d arrays, %d volumes, %d spares\n",
1111189251Ssam	    mfi_unit, config->array_count, config->log_drv_count,
1112189251Ssam	    config->spares_count);
1113189251Ssam	printf("  array size: %u\n", config->array_size);
1114189251Ssam	printf("  volume size: %u\n", config->log_drv_size);
1115189251Ssam	printf("  spare size: %u\n", config->spares_size);
1116189251Ssam	p = (char *)config->array;
1117189251Ssam
1118189251Ssam	for (i = 0; i < config->array_count; i++) {
1119189251Ssam		ar = (struct mfi_array *)p;
1120189251Ssam		printf("    array %u of %u drives:\n", ar->array_ref,
1121189251Ssam		    ar->num_drives);
1122189251Ssam		printf("      size = %ju\n", (uintmax_t)ar->size);
1123189251Ssam		for (j = 0; j < ar->num_drives; j++) {
1124189251Ssam			device_id = ar->pd[j].ref.v.device_id;
1125189251Ssam			if (device_id == 0xffff)
1126189251Ssam				printf("        drive MISSING\n");
1127189251Ssam			else {
1128189251Ssam				printf("        drive %u %s\n", device_id,
1129189251Ssam				    mfi_pdstate(ar->pd[j].fw_state));
1130189251Ssam				if (mfi_pd_get_info(fd, device_id, &pinfo,
1131189251Ssam				    NULL) >= 0) {
1132189251Ssam					printf("          raw size: %ju\n",
1133189251Ssam					    (uintmax_t)pinfo.raw_size);
1134189251Ssam					printf("          non-coerced size: %ju\n",
1135189251Ssam					    (uintmax_t)pinfo.non_coerced_size);
1136189251Ssam					printf("          coerced size: %ju\n",
1137189251Ssam					    (uintmax_t)pinfo.coerced_size);
1138189251Ssam				}
1139189251Ssam			}
1140189251Ssam		}
1141189251Ssam		p += config->array_size;
1142189251Ssam	}
1143189251Ssam
1144189251Ssam	for (i = 0; i < config->log_drv_count; i++) {
1145189251Ssam		ld = (struct mfi_ld_config *)p;
1146189251Ssam		printf("    volume %s ",
1147189251Ssam		    mfi_volume_name(fd, ld->properties.ld.v.target_id));
1148189251Ssam		printf("%s %s",
1149189251Ssam		    mfi_raid_level(ld->params.primary_raid_level,
1150189251Ssam			ld->params.secondary_raid_level),
1151189251Ssam		    mfi_ldstate(ld->params.state));
1152189251Ssam		if (ld->properties.name[0] != '\0')
1153189251Ssam			printf(" <%s>", ld->properties.name);
1154189251Ssam		printf("\n");
1155189251Ssam		printf("      primary raid level: %u\n",
1156189251Ssam		    ld->params.primary_raid_level);
1157189251Ssam		printf("      raid level qualifier: %u\n",
1158189251Ssam		    ld->params.raid_level_qualifier);
1159189251Ssam		printf("      secondary raid level: %u\n",
1160189251Ssam		    ld->params.secondary_raid_level);
1161189251Ssam		printf("      stripe size: %u\n", ld->params.stripe_size);
1162189251Ssam		printf("      num drives: %u\n", ld->params.num_drives);
1163189251Ssam		printf("      init state: %u\n", ld->params.init_state);
1164189251Ssam		printf("      consistent: %u\n", ld->params.is_consistent);
1165189251Ssam		printf("      no bgi: %u\n", ld->properties.no_bgi);
1166189251Ssam		printf("      spans:\n");
1167189251Ssam		for (j = 0; j < ld->params.span_depth; j++) {
1168189251Ssam			printf("        array %u @ ", ld->span[j].array_ref);
1169189251Ssam			printf("%ju : %ju\n",
1170189251Ssam			    (uintmax_t)ld->span[j].start_block,
1171189251Ssam			    (uintmax_t)ld->span[j].num_blocks);
1172189251Ssam		}
1173189251Ssam		p += config->log_drv_size;
1174189251Ssam	}
1175189251Ssam
1176189251Ssam	for (i = 0; i < config->spares_count; i++) {
1177189251Ssam		sp = (struct mfi_spare *)p;
1178189251Ssam		printf("    %s spare %u ",
1179189251Ssam		    sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" :
1180189251Ssam		    "global", sp->ref.v.device_id);
1181189251Ssam		printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE));
1182189251Ssam		printf(" backs:\n");
1183189251Ssam		for (j = 0; j < sp->array_count; j++)
1184189251Ssam			printf("        array %u\n", sp->array_ref[j]);
1185189251Ssam		p += config->spares_size;
1186189251Ssam	}
1187189251Ssam}
1188189251Ssam
1189189251Ssamstatic int
1190189251Ssamdebug_config(int ac, char **av)
1191189251Ssam{
1192189251Ssam	struct mfi_config_data *config;
1193189251Ssam	int error, fd;
1194189251Ssam
1195189251Ssam	if (ac != 1) {
1196189251Ssam		warnx("debug: extra arguments");
1197189251Ssam		return (EINVAL);
1198189251Ssam	}
1199189251Ssam
1200189251Ssam	fd = mfi_open(mfi_unit, O_RDWR);
1201189251Ssam	if (fd < 0) {
1202189251Ssam		error = errno;
1203189251Ssam		warn("mfi_open");
1204189251Ssam		return (error);
1205189251Ssam	}
1206189251Ssam
1207189251Ssam	/* Get the config from the controller. */
1208189251Ssam	if (mfi_config_read(fd, &config) < 0) {
1209189251Ssam		error = errno;
1210189251Ssam		warn("Failed to get config");
1211189251Ssam		close(fd);
1212189251Ssam		return (error);
1213189251Ssam	}
1214189251Ssam
1215189251Ssam	/* Dump out the configuration. */
1216189251Ssam	dump_config(fd, config);
1217189251Ssam	free(config);
1218189251Ssam	close(fd);
1219189251Ssam
1220189251Ssam	return (0);
1221189251Ssam}
1222189251SsamMFI_COMMAND(top, debug, debug_config);
1223189251Ssam
1224189251Ssamstatic int
1225189251Ssamdump(int ac, char **av)
1226189251Ssam{
1227189251Ssam	struct mfi_config_data *config;
1228189251Ssam	char buf[64];
1229189251Ssam	size_t len;
1230189251Ssam	int error, fd;
1231189251Ssam
1232189251Ssam	if (ac != 1) {
1233189251Ssam		warnx("dump: extra arguments");
1234189251Ssam		return (EINVAL);
1235189251Ssam	}
1236189251Ssam
1237189251Ssam	fd = mfi_open(mfi_unit, O_RDWR);
1238189251Ssam	if (fd < 0) {
1239189251Ssam		error = errno;
1240189251Ssam		warn("mfi_open");
1241189251Ssam		return (error);
1242189251Ssam	}
1243189251Ssam
1244189251Ssam	/* Get the stashed copy of the last dcmd from the driver. */
1245189251Ssam	snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit);
1246189251Ssam	if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) {
1247189251Ssam		error = errno;
1248189251Ssam		warn("Failed to read debug command");
1249189251Ssam		if (error == ENOENT)
1250189251Ssam			error = EOPNOTSUPP;
1251189251Ssam		close(fd);
1252189251Ssam		return (error);
1253189251Ssam	}
1254189251Ssam
1255189251Ssam	config = malloc(len);
1256189251Ssam	if (config == NULL) {
1257189251Ssam		warnx("malloc failed");
1258189251Ssam		close(fd);
1259189251Ssam		return (ENOMEM);
1260189251Ssam	}
1261189251Ssam	if (sysctlbyname(buf, config, &len, NULL, 0) < 0) {
1262189251Ssam		error = errno;
1263189251Ssam		warn("Failed to read debug command");
1264189251Ssam		free(config);
1265189251Ssam		close(fd);
1266189251Ssam		return (error);
1267189251Ssam	}
1268189251Ssam	dump_config(fd, config);
1269189251Ssam	free(config);
1270189251Ssam	close(fd);
1271189251Ssam
1272189251Ssam	return (0);
1273189251Ssam}
1274189251SsamMFI_COMMAND(top, dump, dump);
1275189251Ssam#endif
1276189251Ssam