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: releng/11.0/usr.sbin/mfiutil/mfi_config.c 251516 2013-06-08 02:54:59Z sbruno $
30196200Sscottl */
31196200Sscottl
32214396Sjhb#include <sys/param.h>
33196200Sscottl#ifdef DEBUG
34196200Sscottl#include <sys/sysctl.h>
35196200Sscottl#endif
36196200Sscottl#include <err.h>
37214396Sjhb#include <errno.h>
38237259Seadler#include <fcntl.h>
39196200Sscottl#include <libutil.h>
40196200Sscottl#include <stdint.h>
41196200Sscottl#include <stdio.h>
42196200Sscottl#include <stdlib.h>
43196200Sscottl#include <string.h>
44196200Sscottl#include <unistd.h>
45196200Sscottl#include "mfiutil.h"
46196200Sscottl
47196200Sscottlstatic int	add_spare(int ac, char **av);
48196200Sscottlstatic int	remove_spare(int ac, char **av);
49196200Sscottl
50196200Sscottlstatic long
51196200Sscottldehumanize(const char *value)
52196200Sscottl{
53196200Sscottl        char    *vtp;
54196200Sscottl        long    iv;
55196200Sscottl
56196200Sscottl        if (value == NULL)
57196200Sscottl                return (0);
58196200Sscottl        iv = strtoq(value, &vtp, 0);
59196200Sscottl        if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
60196200Sscottl                return (0);
61196200Sscottl        }
62196200Sscottl        switch (vtp[0]) {
63196200Sscottl        case 't': case 'T':
64196200Sscottl                iv *= 1024;
65196200Sscottl        case 'g': case 'G':
66196200Sscottl                iv *= 1024;
67196200Sscottl        case 'm': case 'M':
68196200Sscottl                iv *= 1024;
69196200Sscottl        case 'k': case 'K':
70196200Sscottl                iv *= 1024;
71196200Sscottl        case '\0':
72196200Sscottl                break;
73196200Sscottl        default:
74196200Sscottl                return (0);
75196200Sscottl        }
76196200Sscottl        return (iv);
77196200Sscottl}
78251516Ssbruno
79196200Sscottlint
80196200Sscottlmfi_config_read(int fd, struct mfi_config_data **configp)
81196200Sscottl{
82251516Ssbruno	return mfi_config_read_opcode(fd, MFI_DCMD_CFG_READ, configp, NULL, 0);
83251516Ssbruno}
84251516Ssbruno
85251516Ssbrunoint
86251516Ssbrunomfi_config_read_opcode(int fd, uint32_t opcode, struct mfi_config_data **configp,
87251516Ssbruno	uint8_t *mbox, size_t mboxlen)
88251516Ssbruno{
89196200Sscottl	struct mfi_config_data *config;
90196200Sscottl	uint32_t config_size;
91222899Sbz	int error;
92196200Sscottl
93196200Sscottl	/*
94196200Sscottl	 * Keep fetching the config in a loop until we have a large enough
95196200Sscottl	 * buffer to hold the entire configuration.
96196200Sscottl	 */
97196200Sscottl	config = NULL;
98196200Sscottl	config_size = 1024;
99196200Sscottlfetch:
100196200Sscottl	config = reallocf(config, config_size);
101196200Sscottl	if (config == NULL)
102196200Sscottl		return (-1);
103251516Ssbruno	if (mfi_dcmd_command(fd, opcode, config,
104251516Ssbruno	    config_size, mbox, mboxlen, NULL) < 0) {
105222899Sbz		error = errno;
106222899Sbz		free(config);
107222899Sbz		errno = error;
108196200Sscottl		return (-1);
109222899Sbz	}
110196200Sscottl
111196200Sscottl	if (config->size > config_size) {
112196200Sscottl		config_size = config->size;
113196200Sscottl		goto fetch;
114196200Sscottl	}
115196200Sscottl
116196200Sscottl	*configp = config;
117196200Sscottl	return (0);
118196200Sscottl}
119196200Sscottl
120196200Sscottlstatic struct mfi_array *
121196200Sscottlmfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
122196200Sscottl{
123196200Sscottl	struct mfi_array *ar;
124196200Sscottl	char *p;
125196200Sscottl	int i;
126196200Sscottl
127196200Sscottl	p = (char *)config->array;
128196200Sscottl	for (i = 0; i < config->array_count; i++) {
129196200Sscottl		ar = (struct mfi_array *)p;
130196200Sscottl		if (ar->array_ref == array_ref)
131196200Sscottl			return (ar);
132196200Sscottl		p += config->array_size;
133196200Sscottl	}
134196200Sscottl
135196200Sscottl	return (NULL);
136196200Sscottl}
137196200Sscottl
138196200Sscottlstatic struct mfi_ld_config *
139196200Sscottlmfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
140196200Sscottl{
141196200Sscottl	struct mfi_ld_config *ld;
142196200Sscottl	char *p;
143196200Sscottl	int i;
144196200Sscottl
145196200Sscottl	p = (char *)config->array + config->array_count * config->array_size;
146196200Sscottl	for (i = 0; i < config->log_drv_count; i++) {
147196200Sscottl		ld = (struct mfi_ld_config *)p;
148196200Sscottl		if (ld->properties.ld.v.target_id == target_id)
149196200Sscottl			return (ld);
150196200Sscottl		p += config->log_drv_size;
151196200Sscottl	}
152196200Sscottl
153196200Sscottl	return (NULL);
154196200Sscottl}
155196200Sscottl
156196200Sscottlstatic int
157237260Seadlerclear_config(int ac __unused, char **av __unused)
158196200Sscottl{
159196200Sscottl	struct mfi_ld_list list;
160214396Sjhb	int ch, error, fd;
161196200Sscottl	u_int i;
162196200Sscottl
163237259Seadler	fd = mfi_open(mfi_unit, O_RDWR);
164196200Sscottl	if (fd < 0) {
165214396Sjhb		error = errno;
166196200Sscottl		warn("mfi_open");
167214396Sjhb		return (error);
168196200Sscottl	}
169196200Sscottl
170196200Sscottl	if (!mfi_reconfig_supported()) {
171196200Sscottl		warnx("The current mfi(4) driver does not support "
172196200Sscottl		    "configuration changes.");
173222899Sbz		close(fd);
174196200Sscottl		return (EOPNOTSUPP);
175196200Sscottl	}
176196200Sscottl
177196200Sscottl	if (mfi_ld_get_list(fd, &list, NULL) < 0) {
178214396Sjhb		error = errno;
179196200Sscottl		warn("Failed to get volume list");
180222899Sbz		close(fd);
181214396Sjhb		return (error);
182196200Sscottl	}
183196200Sscottl
184196200Sscottl	for (i = 0; i < list.ld_count; i++) {
185196200Sscottl		if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) {
186196200Sscottl			warnx("Volume %s is busy and cannot be deleted",
187196200Sscottl			    mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
188222899Sbz			close(fd);
189196200Sscottl			return (EBUSY);
190196200Sscottl		}
191196200Sscottl	}
192196200Sscottl
193196200Sscottl	printf(
194196200Sscottl	    "Are you sure you wish to clear the configuration on mfi%u? [y/N] ",
195196200Sscottl	    mfi_unit);
196196200Sscottl	ch = getchar();
197196200Sscottl	if (ch != 'y' && ch != 'Y') {
198196200Sscottl		printf("\nAborting\n");
199222899Sbz		close(fd);
200196200Sscottl		return (0);
201196200Sscottl	}
202196200Sscottl
203196200Sscottl	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) {
204214396Sjhb		error = errno;
205196200Sscottl		warn("Failed to clear configuration");
206222899Sbz		close(fd);
207214396Sjhb		return (error);
208196200Sscottl	}
209196200Sscottl
210196200Sscottl	printf("mfi%d: Configuration cleared\n", mfi_unit);
211196200Sscottl	close(fd);
212196200Sscottl
213196200Sscottl	return (0);
214196200Sscottl}
215196200SscottlMFI_COMMAND(top, clear, clear_config);
216196200Sscottl
217233713Sambrisko#define MAX_DRIVES_PER_ARRAY MFI_MAX_ROW_SIZE
218233713Sambrisko#define MFI_ARRAY_SIZE sizeof(struct mfi_array)
219196200Sscottl
220196200Sscottl#define	RT_RAID0	0
221196200Sscottl#define	RT_RAID1	1
222196200Sscottl#define	RT_RAID5	2
223196200Sscottl#define	RT_RAID6	3
224196200Sscottl#define	RT_JBOD		4
225196200Sscottl#define	RT_CONCAT	5
226196200Sscottl#define	RT_RAID10	6
227196200Sscottl#define	RT_RAID50	7
228196200Sscottl#define	RT_RAID60	8
229196200Sscottl
230196200Sscottlstatic int
231196200Sscottlcompare_int(const void *one, const void *two)
232196200Sscottl{
233196200Sscottl	int first, second;
234196200Sscottl
235196200Sscottl	first = *(const int *)one;
236196200Sscottl	second = *(const int *)two;
237196200Sscottl
238196200Sscottl	return (first - second);
239196200Sscottl}
240196200Sscottl
241196200Sscottlstatic struct raid_type_entry {
242196200Sscottl	const char *name;
243196200Sscottl	int	raid_type;
244196200Sscottl} raid_type_table[] = {
245196200Sscottl	{ "raid0",	RT_RAID0 },
246196200Sscottl	{ "raid-0",	RT_RAID0 },
247196200Sscottl	{ "raid1",	RT_RAID1 },
248196200Sscottl	{ "raid-1",	RT_RAID1 },
249196200Sscottl	{ "mirror",	RT_RAID1 },
250196200Sscottl	{ "raid5",	RT_RAID5 },
251196200Sscottl	{ "raid-5",	RT_RAID5 },
252196200Sscottl	{ "raid6",	RT_RAID6 },
253196200Sscottl	{ "raid-6",	RT_RAID6 },
254196200Sscottl	{ "jbod",	RT_JBOD },
255196200Sscottl	{ "concat",	RT_CONCAT },
256196200Sscottl	{ "raid10",	RT_RAID10 },
257196200Sscottl	{ "raid1+0",	RT_RAID10 },
258196200Sscottl	{ "raid-10",	RT_RAID10 },
259196200Sscottl	{ "raid-1+0",	RT_RAID10 },
260196200Sscottl	{ "raid50",	RT_RAID50 },
261196200Sscottl	{ "raid5+0",	RT_RAID50 },
262196200Sscottl	{ "raid-50",	RT_RAID50 },
263196200Sscottl	{ "raid-5+0",	RT_RAID50 },
264196200Sscottl	{ "raid60",	RT_RAID60 },
265196200Sscottl	{ "raid6+0",	RT_RAID60 },
266196200Sscottl	{ "raid-60",	RT_RAID60 },
267196200Sscottl	{ "raid-6+0",	RT_RAID60 },
268196200Sscottl	{ NULL,		0 },
269196200Sscottl};
270196200Sscottl
271196200Sscottlstruct config_id_state {
272196200Sscottl	int	array_count;
273196200Sscottl	int	log_drv_count;
274196200Sscottl	int	*arrays;
275196200Sscottl	int	*volumes;
276196200Sscottl	uint16_t array_ref;
277196200Sscottl	uint8_t	target_id;
278196200Sscottl};
279196200Sscottl
280196200Sscottlstruct array_info {
281196200Sscottl	int	drive_count;
282196200Sscottl	struct mfi_pd_info *drives;
283196200Sscottl	struct mfi_array *array;
284196200Sscottl};
285196200Sscottl
286196200Sscottl/* Parse a comma-separated list of drives for an array. */
287196200Sscottlstatic int
288196200Sscottlparse_array(int fd, int raid_type, char *array_str, struct array_info *info)
289196200Sscottl{
290196200Sscottl	struct mfi_pd_info *pinfo;
291196200Sscottl	uint16_t device_id;
292196200Sscottl	char *cp;
293196200Sscottl	u_int count;
294196200Sscottl	int error;
295196200Sscottl
296196200Sscottl	cp = array_str;
297196200Sscottl	for (count = 0; cp != NULL; count++) {
298196200Sscottl		cp = strchr(cp, ',');
299196200Sscottl		if (cp != NULL) {
300196200Sscottl			cp++;
301196200Sscottl			if (*cp == ',') {
302196200Sscottl				warnx("Invalid drive list '%s'", array_str);
303196200Sscottl				return (EINVAL);
304196200Sscottl			}
305196200Sscottl		}
306196200Sscottl	}
307196200Sscottl
308196200Sscottl	/* Validate the number of drives for this array. */
309196200Sscottl	if (count >= MAX_DRIVES_PER_ARRAY) {
310233713Sambrisko		warnx("Too many drives for a single array: max is %d",
311196200Sscottl		    MAX_DRIVES_PER_ARRAY);
312196200Sscottl		return (EINVAL);
313196200Sscottl	}
314196200Sscottl	switch (raid_type) {
315196200Sscottl	case RT_RAID1:
316196200Sscottl	case RT_RAID10:
317196200Sscottl		if (count % 2 != 0) {
318196200Sscottl			warnx("RAID1 and RAID10 require an even number of "
319196200Sscottl			    "drives in each array");
320196200Sscottl			return (EINVAL);
321196200Sscottl		}
322196200Sscottl		break;
323196200Sscottl	case RT_RAID5:
324196200Sscottl	case RT_RAID50:
325196200Sscottl		if (count < 3) {
326196200Sscottl			warnx("RAID5 and RAID50 require at least 3 drives in "
327196200Sscottl			    "each array");
328196200Sscottl			return (EINVAL);
329196200Sscottl		}
330196200Sscottl		break;
331196200Sscottl	case RT_RAID6:
332196200Sscottl	case RT_RAID60:
333196200Sscottl		if (count < 4) {
334196200Sscottl			warnx("RAID6 and RAID60 require at least 4 drives in "
335196200Sscottl			    "each array");
336196200Sscottl			return (EINVAL);
337196200Sscottl		}
338196200Sscottl		break;
339196200Sscottl	}
340196200Sscottl
341196200Sscottl	/* Validate each drive. */
342196200Sscottl	info->drives = calloc(count, sizeof(struct mfi_pd_info));
343215526Sjhb	if (info->drives == NULL) {
344215526Sjhb		warnx("malloc failed");
345215526Sjhb		return (ENOMEM);
346215526Sjhb	}
347196200Sscottl	info->drive_count = count;
348196200Sscottl	for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL;
349196200Sscottl	     pinfo++) {
350196200Sscottl		error = mfi_lookup_drive(fd, cp, &device_id);
351222899Sbz		if (error) {
352222899Sbz			free(info->drives);
353227893Semaste			info->drives = NULL;
354196200Sscottl			return (error);
355222899Sbz		}
356196200Sscottl
357196200Sscottl		if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) {
358214396Sjhb			error = errno;
359196200Sscottl			warn("Failed to fetch drive info for drive %s", cp);
360222899Sbz			free(info->drives);
361227893Semaste			info->drives = NULL;
362214396Sjhb			return (error);
363196200Sscottl		}
364196200Sscottl
365196200Sscottl		if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
366196200Sscottl			warnx("Drive %u is not available", device_id);
367222899Sbz			free(info->drives);
368227893Semaste			info->drives = NULL;
369196200Sscottl			return (EINVAL);
370196200Sscottl		}
371251516Ssbruno
372251516Ssbruno		if (pinfo->state.ddf.v.pd_type.is_foreign) {
373251516Ssbruno			warnx("Drive %u is foreign", device_id);
374251516Ssbruno			free(info->drives);
375251516Ssbruno			info->drives = NULL;
376251516Ssbruno			return (EINVAL);
377251516Ssbruno		}
378196200Sscottl	}
379196200Sscottl
380196200Sscottl	return (0);
381196200Sscottl}
382196200Sscottl
383196200Sscottl/*
384196200Sscottl * Find the next free array ref assuming that 'array_ref' is the last
385196200Sscottl * one used.  'array_ref' should be 0xffff for the initial test.
386196200Sscottl */
387196200Sscottlstatic uint16_t
388196200Sscottlfind_next_array(struct config_id_state *state)
389196200Sscottl{
390196200Sscottl	int i;
391196200Sscottl
392196200Sscottl	/* Assume the current one is used. */
393196200Sscottl	state->array_ref++;
394196200Sscottl
395196200Sscottl	/* Find the next free one. */
396196200Sscottl	for (i = 0; i < state->array_count; i++)
397196200Sscottl		if (state->arrays[i] == state->array_ref)
398196200Sscottl			state->array_ref++;
399196200Sscottl	return (state->array_ref);
400196200Sscottl}
401196200Sscottl
402196200Sscottl/*
403196200Sscottl * Find the next free volume ID assuming that 'target_id' is the last
404196200Sscottl * one used.  'target_id' should be 0xff for the initial test.
405196200Sscottl */
406196200Sscottlstatic uint8_t
407196200Sscottlfind_next_volume(struct config_id_state *state)
408196200Sscottl{
409196200Sscottl	int i;
410196200Sscottl
411196200Sscottl	/* Assume the current one is used. */
412196200Sscottl	state->target_id++;
413196200Sscottl
414196200Sscottl	/* Find the next free one. */
415196200Sscottl	for (i = 0; i < state->log_drv_count; i++)
416196200Sscottl		if (state->volumes[i] == state->target_id)
417196200Sscottl			state->target_id++;
418196200Sscottl	return (state->target_id);
419196200Sscottl}
420196200Sscottl
421196200Sscottl/* Populate an array with drives. */
422196200Sscottlstatic void
423237260Seadlerbuild_array(int fd __unused, char *arrayp, struct array_info *array_info,
424196200Sscottl    struct config_id_state *state, int verbose)
425196200Sscottl{
426196200Sscottl	struct mfi_array *ar = (struct mfi_array *)arrayp;
427196200Sscottl	int i;
428196200Sscottl
429196200Sscottl	ar->size = array_info->drives[0].coerced_size;
430196200Sscottl	ar->num_drives = array_info->drive_count;
431196200Sscottl	ar->array_ref = find_next_array(state);
432196200Sscottl	for (i = 0; i < array_info->drive_count; i++) {
433196200Sscottl		if (verbose)
434223345Sbz			printf("Adding drive %s to array %u\n",
435223345Sbz			    mfi_drive_name(NULL,
436196200Sscottl			    array_info->drives[i].ref.v.device_id,
437223345Sbz			    MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS),
438196200Sscottl			    ar->array_ref);
439196200Sscottl		if (ar->size > array_info->drives[i].coerced_size)
440196200Sscottl			ar->size = array_info->drives[i].coerced_size;
441196200Sscottl		ar->pd[i].ref = array_info->drives[i].ref;
442196200Sscottl		ar->pd[i].fw_state = MFI_PD_STATE_ONLINE;
443196200Sscottl	}
444196200Sscottl	array_info->array = ar;
445196200Sscottl}
446196200Sscottl
447196200Sscottl/*
448196200Sscottl * Create a volume that spans one or more arrays.
449196200Sscottl */
450196200Sscottlstatic void
451196200Sscottlbuild_volume(char *volumep, int narrays, struct array_info *arrays,
452196200Sscottl    int raid_type, long stripe_size, struct config_id_state *state, int verbose)
453196200Sscottl{
454196200Sscottl	struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
455196200Sscottl	struct mfi_array *ar;
456196200Sscottl	int i;
457196200Sscottl
458196200Sscottl	/* properties */
459196200Sscottl	ld->properties.ld.v.target_id = find_next_volume(state);
460196200Sscottl	ld->properties.ld.v.seq = 0;
461196200Sscottl	ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
462196200Sscottl	    MR_LD_CACHE_WRITE_BACK;
463196200Sscottl	ld->properties.access_policy = MFI_LD_ACCESS_RW;
464196200Sscottl	ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED;
465196200Sscottl	ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
466196200Sscottl	    MR_LD_CACHE_WRITE_BACK;
467196200Sscottl	ld->properties.no_bgi = 0;
468196200Sscottl
469196200Sscottl	/* params */
470196200Sscottl	switch (raid_type) {
471196200Sscottl	case RT_RAID0:
472196200Sscottl	case RT_JBOD:
473196200Sscottl		ld->params.primary_raid_level = DDF_RAID0;
474196200Sscottl		ld->params.raid_level_qualifier = 0;
475196200Sscottl		ld->params.secondary_raid_level = 0;
476196200Sscottl		break;
477196200Sscottl	case RT_RAID1:
478196200Sscottl		ld->params.primary_raid_level = DDF_RAID1;
479196200Sscottl		ld->params.raid_level_qualifier = 0;
480196200Sscottl		ld->params.secondary_raid_level = 0;
481196200Sscottl		break;
482196200Sscottl	case RT_RAID5:
483196200Sscottl		ld->params.primary_raid_level = DDF_RAID5;
484196200Sscottl		ld->params.raid_level_qualifier = 3;
485196200Sscottl		ld->params.secondary_raid_level = 0;
486196200Sscottl		break;
487196200Sscottl	case RT_RAID6:
488196200Sscottl		ld->params.primary_raid_level = DDF_RAID6;
489196200Sscottl		ld->params.raid_level_qualifier = 3;
490196200Sscottl		ld->params.secondary_raid_level = 0;
491196200Sscottl		break;
492196200Sscottl	case RT_CONCAT:
493196200Sscottl		ld->params.primary_raid_level = DDF_CONCAT;
494196200Sscottl		ld->params.raid_level_qualifier = 0;
495196200Sscottl		ld->params.secondary_raid_level = 0;
496196200Sscottl		break;
497196200Sscottl	case RT_RAID10:
498196200Sscottl		ld->params.primary_raid_level = DDF_RAID1;
499196200Sscottl		ld->params.raid_level_qualifier = 0;
500196200Sscottl		ld->params.secondary_raid_level = 3; /* XXX? */
501196200Sscottl		break;
502196200Sscottl	case RT_RAID50:
503196200Sscottl		/*
504196200Sscottl		 * XXX: This appears to work though the card's BIOS
505196200Sscottl		 * complains that the configuration is foreign.  The
506196200Sscottl		 * BIOS setup does not allow for creation of RAID-50
507196200Sscottl		 * or RAID-60 arrays.  The only nested array
508196200Sscottl		 * configuration it allows for is RAID-10.
509196200Sscottl		 */
510196200Sscottl		ld->params.primary_raid_level = DDF_RAID5;
511196200Sscottl		ld->params.raid_level_qualifier = 3;
512196200Sscottl		ld->params.secondary_raid_level = 3; /* XXX? */
513196200Sscottl		break;
514196200Sscottl	case RT_RAID60:
515196200Sscottl		ld->params.primary_raid_level = DDF_RAID6;
516196200Sscottl		ld->params.raid_level_qualifier = 3;
517196200Sscottl		ld->params.secondary_raid_level = 3; /* XXX? */
518196200Sscottl		break;
519196200Sscottl	}
520196200Sscottl
521196200Sscottl	/*
522196200Sscottl	 * Stripe size is encoded as (2 ^ N) * 512 = stripe_size.  Use
523196200Sscottl	 * ffs() to simulate log2(stripe_size).
524196200Sscottl	 */
525196200Sscottl	ld->params.stripe_size = ffs(stripe_size) - 1 - 9;
526196200Sscottl	ld->params.num_drives = arrays[0].array->num_drives;
527196200Sscottl	ld->params.span_depth = narrays;
528196200Sscottl	ld->params.state = MFI_LD_STATE_OPTIMAL;
529196200Sscottl	ld->params.init_state = MFI_LD_PARAMS_INIT_NO;
530196200Sscottl	ld->params.is_consistent = 0;
531196200Sscottl
532196200Sscottl	/* spans */
533196200Sscottl	for (i = 0; i < narrays; i++) {
534196200Sscottl		ar = arrays[i].array;
535196200Sscottl		if (verbose)
536196200Sscottl			printf("Adding array %u to volume %u\n", ar->array_ref,
537196200Sscottl			    ld->properties.ld.v.target_id);
538196200Sscottl		ld->span[i].start_block = 0;
539196200Sscottl		ld->span[i].num_blocks = ar->size;
540196200Sscottl		ld->span[i].array_ref = ar->array_ref;
541196200Sscottl	}
542196200Sscottl}
543196200Sscottl
544196200Sscottlstatic int
545196200Sscottlcreate_volume(int ac, char **av)
546196200Sscottl{
547196200Sscottl	struct mfi_config_data *config;
548196200Sscottl	struct mfi_array *ar;
549196200Sscottl	struct mfi_ld_config *ld;
550196200Sscottl	struct config_id_state state;
551196200Sscottl	size_t config_size;
552196200Sscottl	char *p, *cfg_arrays, *cfg_volumes;
553196200Sscottl	int error, fd, i, raid_type;
554196200Sscottl	int narrays, nvolumes, arrays_per_volume;
555196200Sscottl	struct array_info *arrays;
556196200Sscottl	long stripe_size;
557196200Sscottl#ifdef DEBUG
558196200Sscottl	int dump;
559196200Sscottl#endif
560196200Sscottl	int ch, verbose;
561196200Sscottl
562196200Sscottl	/*
563196200Sscottl	 * Backwards compat.  Map 'create volume' to 'create' and
564196200Sscottl	 * 'create spare' to 'add'.
565196200Sscottl	 */
566196200Sscottl	if (ac > 1) {
567196200Sscottl		if (strcmp(av[1], "volume") == 0) {
568196200Sscottl			av++;
569196200Sscottl			ac--;
570196200Sscottl		} else if (strcmp(av[1], "spare") == 0) {
571196200Sscottl			av++;
572196200Sscottl			ac--;
573196200Sscottl			return (add_spare(ac, av));
574196200Sscottl		}
575196200Sscottl	}
576196200Sscottl
577196200Sscottl	if (ac < 2) {
578196200Sscottl		warnx("create volume: volume type required");
579196200Sscottl		return (EINVAL);
580196200Sscottl	}
581196200Sscottl
582222899Sbz	bzero(&state, sizeof(state));
583222899Sbz	config = NULL;
584222899Sbz	arrays = NULL;
585222899Sbz	narrays = 0;
586222899Sbz	error = 0;
587222899Sbz
588237259Seadler	fd = mfi_open(mfi_unit, O_RDWR);
589196200Sscottl	if (fd < 0) {
590214396Sjhb		error = errno;
591196200Sscottl		warn("mfi_open");
592214396Sjhb		return (error);
593196200Sscottl	}
594196200Sscottl
595196200Sscottl	if (!mfi_reconfig_supported()) {
596196200Sscottl		warnx("The current mfi(4) driver does not support "
597196200Sscottl		    "configuration changes.");
598222899Sbz		error = EOPNOTSUPP;
599222899Sbz		goto error;
600196200Sscottl	}
601196200Sscottl
602196200Sscottl	/* Lookup the RAID type first. */
603196200Sscottl	raid_type = -1;
604196200Sscottl	for (i = 0; raid_type_table[i].name != NULL; i++)
605196200Sscottl		if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
606196200Sscottl			raid_type = raid_type_table[i].raid_type;
607196200Sscottl			break;
608196200Sscottl		}
609196200Sscottl
610196200Sscottl	if (raid_type == -1) {
611196200Sscottl		warnx("Unknown or unsupported volume type %s", av[1]);
612222899Sbz		error = EINVAL;
613222899Sbz		goto error;
614196200Sscottl	}
615196200Sscottl
616196200Sscottl	/* Parse any options. */
617196200Sscottl	optind = 2;
618196200Sscottl#ifdef DEBUG
619196200Sscottl	dump = 0;
620196200Sscottl#endif
621196200Sscottl	verbose = 0;
622196200Sscottl	stripe_size = 64 * 1024;
623196200Sscottl
624196200Sscottl	while ((ch = getopt(ac, av, "ds:v")) != -1) {
625196200Sscottl		switch (ch) {
626196200Sscottl#ifdef DEBUG
627196200Sscottl		case 'd':
628196200Sscottl			dump = 1;
629196200Sscottl			break;
630196200Sscottl#endif
631196200Sscottl		case 's':
632196200Sscottl			stripe_size = dehumanize(optarg);
633196200Sscottl			if ((stripe_size < 512) || (!powerof2(stripe_size)))
634196200Sscottl				stripe_size = 64 * 1024;
635196200Sscottl			break;
636196200Sscottl		case 'v':
637196200Sscottl			verbose = 1;
638196200Sscottl			break;
639196200Sscottl		case '?':
640196200Sscottl		default:
641222899Sbz			error = EINVAL;
642222899Sbz			goto error;
643196200Sscottl		}
644196200Sscottl	}
645196200Sscottl	ac -= optind;
646196200Sscottl	av += optind;
647196200Sscottl
648196200Sscottl	/* Parse all the arrays. */
649196200Sscottl	narrays = ac;
650196200Sscottl	if (narrays == 0) {
651196200Sscottl		warnx("At least one drive list is required");
652222899Sbz		error = EINVAL;
653222899Sbz		goto error;
654196200Sscottl	}
655196200Sscottl	switch (raid_type) {
656196200Sscottl	case RT_RAID0:
657196200Sscottl	case RT_RAID1:
658196200Sscottl	case RT_RAID5:
659196200Sscottl	case RT_RAID6:
660196200Sscottl	case RT_CONCAT:
661196200Sscottl		if (narrays != 1) {
662196200Sscottl			warnx("Only one drive list can be specified");
663222899Sbz			error = EINVAL;
664222899Sbz			goto error;
665196200Sscottl		}
666196200Sscottl		break;
667196200Sscottl	case RT_RAID10:
668196200Sscottl	case RT_RAID50:
669196200Sscottl	case RT_RAID60:
670196200Sscottl		if (narrays < 1) {
671196200Sscottl			warnx("RAID10, RAID50, and RAID60 require at least "
672196200Sscottl			    "two drive lists");
673222899Sbz			error = EINVAL;
674222899Sbz			goto error;
675196200Sscottl		}
676196200Sscottl		if (narrays > MFI_MAX_SPAN_DEPTH) {
677196200Sscottl			warnx("Volume spans more than %d arrays",
678196200Sscottl			    MFI_MAX_SPAN_DEPTH);
679222899Sbz			error = EINVAL;
680222899Sbz			goto error;
681196200Sscottl		}
682196200Sscottl		break;
683196200Sscottl	}
684196200Sscottl	arrays = calloc(narrays, sizeof(*arrays));
685215526Sjhb	if (arrays == NULL) {
686215526Sjhb		warnx("malloc failed");
687222899Sbz		error = ENOMEM;
688222899Sbz		goto error;
689215526Sjhb	}
690196200Sscottl	for (i = 0; i < narrays; i++) {
691196200Sscottl		error = parse_array(fd, raid_type, av[i], &arrays[i]);
692196200Sscottl		if (error)
693222899Sbz			goto error;
694196200Sscottl	}
695196200Sscottl
696196200Sscottl	switch (raid_type) {
697196200Sscottl	case RT_RAID10:
698196200Sscottl	case RT_RAID50:
699196200Sscottl	case RT_RAID60:
700196200Sscottl		for (i = 1; i < narrays; i++) {
701196200Sscottl			if (arrays[i].drive_count != arrays[0].drive_count) {
702196200Sscottl				warnx("All arrays must contain the same "
703196200Sscottl				    "number of drives");
704222899Sbz				error = EINVAL;
705222899Sbz				goto error;
706196200Sscottl			}
707196200Sscottl		}
708196200Sscottl		break;
709196200Sscottl	}
710196200Sscottl
711196200Sscottl	/*
712196200Sscottl	 * Fetch the current config and build sorted lists of existing
713196200Sscottl	 * array and volume identifiers.
714196200Sscottl	 */
715196200Sscottl	if (mfi_config_read(fd, &config) < 0) {
716214396Sjhb		error = errno;
717196200Sscottl		warn("Failed to read configuration");
718222899Sbz		goto error;
719196200Sscottl	}
720196200Sscottl	p = (char *)config->array;
721196200Sscottl	state.array_ref = 0xffff;
722196200Sscottl	state.target_id = 0xff;
723196200Sscottl	state.array_count = config->array_count;
724196200Sscottl	if (config->array_count > 0) {
725196200Sscottl		state.arrays = calloc(config->array_count, sizeof(int));
726215526Sjhb		if (state.arrays == NULL) {
727215526Sjhb			warnx("malloc failed");
728222899Sbz			error = ENOMEM;
729222899Sbz			goto error;
730215526Sjhb		}
731196200Sscottl		for (i = 0; i < config->array_count; i++) {
732196200Sscottl			ar = (struct mfi_array *)p;
733196200Sscottl			state.arrays[i] = ar->array_ref;
734196200Sscottl			p += config->array_size;
735196200Sscottl		}
736196200Sscottl		qsort(state.arrays, config->array_count, sizeof(int),
737196200Sscottl		    compare_int);
738196200Sscottl	} else
739196200Sscottl		state.arrays = NULL;
740196200Sscottl	state.log_drv_count = config->log_drv_count;
741196200Sscottl	if (config->log_drv_count) {
742196200Sscottl		state.volumes = calloc(config->log_drv_count, sizeof(int));
743215526Sjhb		if (state.volumes == NULL) {
744215526Sjhb			warnx("malloc failed");
745222899Sbz			error = ENOMEM;
746222899Sbz			goto error;
747215526Sjhb		}
748196200Sscottl		for (i = 0; i < config->log_drv_count; i++) {
749196200Sscottl			ld = (struct mfi_ld_config *)p;
750196200Sscottl			state.volumes[i] = ld->properties.ld.v.target_id;
751196200Sscottl			p += config->log_drv_size;
752196200Sscottl		}
753196200Sscottl		qsort(state.volumes, config->log_drv_count, sizeof(int),
754196200Sscottl		    compare_int);
755196200Sscottl	} else
756196200Sscottl		state.volumes = NULL;
757196200Sscottl	free(config);
758196200Sscottl
759196200Sscottl	/* Determine the size of the configuration we will build. */
760196200Sscottl	switch (raid_type) {
761196200Sscottl	case RT_RAID0:
762196200Sscottl	case RT_RAID1:
763196200Sscottl	case RT_RAID5:
764196200Sscottl	case RT_RAID6:
765196200Sscottl	case RT_CONCAT:
766196200Sscottl	case RT_JBOD:
767196200Sscottl		/* Each volume spans a single array. */
768196200Sscottl		nvolumes = narrays;
769196200Sscottl		break;
770196200Sscottl	case RT_RAID10:
771196200Sscottl	case RT_RAID50:
772196200Sscottl	case RT_RAID60:
773196200Sscottl		/* A single volume spans multiple arrays. */
774196200Sscottl		nvolumes = 1;
775196200Sscottl		break;
776196200Sscottl	default:
777196200Sscottl		/* Pacify gcc. */
778196200Sscottl		abort();
779196200Sscottl	}
780196200Sscottl
781196200Sscottl	config_size = sizeof(struct mfi_config_data) +
782196200Sscottl	    sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays;
783196200Sscottl	config = calloc(1, config_size);
784215526Sjhb	if (config == NULL) {
785215526Sjhb		warnx("malloc failed");
786222899Sbz		error = ENOMEM;
787222899Sbz		goto error;
788215526Sjhb	}
789196200Sscottl	config->size = config_size;
790196200Sscottl	config->array_count = narrays;
791196200Sscottl	config->array_size = MFI_ARRAY_SIZE;	/* XXX: Firmware hardcode */
792196200Sscottl	config->log_drv_count = nvolumes;
793196200Sscottl	config->log_drv_size = sizeof(struct mfi_ld_config);
794196200Sscottl	config->spares_count = 0;
795196200Sscottl	config->spares_size = 40;		/* XXX: Firmware hardcode */
796196200Sscottl	cfg_arrays = (char *)config->array;
797196200Sscottl	cfg_volumes = cfg_arrays + config->array_size * narrays;
798196200Sscottl
799196200Sscottl	/* Build the arrays. */
800196200Sscottl	for (i = 0; i < narrays; i++) {
801196200Sscottl		build_array(fd, cfg_arrays, &arrays[i], &state, verbose);
802196200Sscottl		cfg_arrays += config->array_size;
803196200Sscottl	}
804196200Sscottl
805196200Sscottl	/* Now build the volume(s). */
806196200Sscottl	arrays_per_volume = narrays / nvolumes;
807196200Sscottl	for (i = 0; i < nvolumes; i++) {
808196200Sscottl		build_volume(cfg_volumes, arrays_per_volume,
809196200Sscottl		    &arrays[i * arrays_per_volume], raid_type, stripe_size,
810196200Sscottl		    &state, verbose);
811196200Sscottl		cfg_volumes += config->log_drv_size;
812196200Sscottl	}
813196200Sscottl
814196200Sscottl#ifdef DEBUG
815196200Sscottl	if (dump)
816251516Ssbruno		dump_config(fd, config, NULL);
817196200Sscottl#endif
818196200Sscottl
819196200Sscottl	/* Send the new config to the controller. */
820196200Sscottl	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size,
821196200Sscottl	    NULL, 0, NULL) < 0) {
822214396Sjhb		error = errno;
823196200Sscottl		warn("Failed to add volume");
824222899Sbz		/* FALLTHROUGH */
825196200Sscottl	}
826196200Sscottl
827222899Sbzerror:
828196200Sscottl	/* Clean up. */
829196200Sscottl	free(config);
830222899Sbz	free(state.volumes);
831222899Sbz	free(state.arrays);
832228119Sdelphij	if (arrays != NULL) {
833228119Sdelphij		for (i = 0; i < narrays; i++)
834228119Sdelphij			free(arrays[i].drives);
835228119Sdelphij		free(arrays);
836228119Sdelphij	}
837196200Sscottl	close(fd);
838196200Sscottl
839222899Sbz	return (error);
840196200Sscottl}
841196200SscottlMFI_COMMAND(top, create, create_volume);
842196200Sscottl
843196200Sscottlstatic int
844196200Sscottldelete_volume(int ac, char **av)
845196200Sscottl{
846196200Sscottl	struct mfi_ld_info info;
847214396Sjhb	int error, fd;
848196200Sscottl	uint8_t target_id, mbox[4];
849196200Sscottl
850196200Sscottl	/*
851196200Sscottl	 * Backwards compat.  Map 'delete volume' to 'delete' and
852196200Sscottl	 * 'delete spare' to 'remove'.
853196200Sscottl	 */
854196200Sscottl	if (ac > 1) {
855196200Sscottl		if (strcmp(av[1], "volume") == 0) {
856196200Sscottl			av++;
857196200Sscottl			ac--;
858196200Sscottl		} else if (strcmp(av[1], "spare") == 0) {
859196200Sscottl			av++;
860196200Sscottl			ac--;
861196200Sscottl			return (remove_spare(ac, av));
862196200Sscottl		}
863196200Sscottl	}
864196200Sscottl
865196200Sscottl	if (ac != 2) {
866196200Sscottl		warnx("delete volume: volume required");
867196200Sscottl		return (EINVAL);
868196200Sscottl	}
869196200Sscottl
870237259Seadler	fd = mfi_open(mfi_unit, O_RDWR);
871196200Sscottl	if (fd < 0) {
872214396Sjhb		error = errno;
873196200Sscottl		warn("mfi_open");
874214396Sjhb		return (error);
875196200Sscottl	}
876196200Sscottl
877196200Sscottl	if (!mfi_reconfig_supported()) {
878196200Sscottl		warnx("The current mfi(4) driver does not support "
879196200Sscottl		    "configuration changes.");
880222899Sbz		close(fd);
881196200Sscottl		return (EOPNOTSUPP);
882196200Sscottl	}
883196200Sscottl
884196200Sscottl	if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
885214396Sjhb		error = errno;
886196200Sscottl		warn("Invalid volume %s", av[1]);
887222899Sbz		close(fd);
888214396Sjhb		return (error);
889196200Sscottl	}
890196200Sscottl
891196200Sscottl	if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
892214396Sjhb		error = errno;
893196200Sscottl		warn("Failed to get info for volume %d", target_id);
894222899Sbz		close(fd);
895214396Sjhb		return (error);
896196200Sscottl	}
897196200Sscottl
898196200Sscottl	if (mfi_volume_busy(fd, target_id)) {
899196200Sscottl		warnx("Volume %s is busy and cannot be deleted",
900196200Sscottl		    mfi_volume_name(fd, target_id));
901222899Sbz		close(fd);
902196200Sscottl		return (EBUSY);
903196200Sscottl	}
904196200Sscottl
905196200Sscottl	mbox_store_ldref(mbox, &info.ld_config.properties.ld);
906196200Sscottl	if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox,
907196200Sscottl	    sizeof(mbox), NULL) < 0) {
908214396Sjhb		error = errno;
909196200Sscottl		warn("Failed to delete volume");
910222899Sbz		close(fd);
911214396Sjhb		return (error);
912196200Sscottl	}
913196200Sscottl
914196200Sscottl	close(fd);
915196200Sscottl
916196200Sscottl	return (0);
917196200Sscottl}
918196200SscottlMFI_COMMAND(top, delete, delete_volume);
919196200Sscottl
920196200Sscottlstatic int
921196200Sscottladd_spare(int ac, char **av)
922196200Sscottl{
923196200Sscottl	struct mfi_pd_info info;
924196200Sscottl	struct mfi_config_data *config;
925196200Sscottl	struct mfi_array *ar;
926196200Sscottl	struct mfi_ld_config *ld;
927196200Sscottl	struct mfi_spare *spare;
928196200Sscottl	uint16_t device_id;
929196200Sscottl	uint8_t target_id;
930196200Sscottl	char *p;
931196200Sscottl	int error, fd, i;
932196200Sscottl
933196200Sscottl	if (ac < 2) {
934196200Sscottl		warnx("add spare: drive required");
935196200Sscottl		return (EINVAL);
936196200Sscottl	}
937196200Sscottl
938237259Seadler	fd = mfi_open(mfi_unit, O_RDWR);
939196200Sscottl	if (fd < 0) {
940214396Sjhb		error = errno;
941196200Sscottl		warn("mfi_open");
942214396Sjhb		return (error);
943196200Sscottl	}
944196200Sscottl
945222899Sbz	config = NULL;
946222899Sbz	spare = NULL;
947196200Sscottl	error = mfi_lookup_drive(fd, av[1], &device_id);
948196200Sscottl	if (error)
949222899Sbz		goto error;
950196200Sscottl
951196200Sscottl	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
952214396Sjhb		error = errno;
953196200Sscottl		warn("Failed to fetch drive info");
954222899Sbz		goto error;
955196200Sscottl	}
956196200Sscottl
957196200Sscottl	if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
958196200Sscottl		warnx("Drive %u is not available", device_id);
959222899Sbz		error = EINVAL;
960222899Sbz		goto error;
961196200Sscottl	}
962196200Sscottl
963196200Sscottl	if (ac > 2) {
964196200Sscottl		if (mfi_lookup_volume(fd, av[2], &target_id) < 0) {
965214396Sjhb			error = errno;
966196200Sscottl			warn("Invalid volume %s", av[2]);
967222899Sbz			goto error;
968196200Sscottl		}
969196200Sscottl	}
970196200Sscottl
971196200Sscottl	if (mfi_config_read(fd, &config) < 0) {
972214396Sjhb		error = errno;
973196200Sscottl		warn("Failed to read configuration");
974222899Sbz		goto error;
975196200Sscottl	}
976196200Sscottl
977196200Sscottl	spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) *
978196200Sscottl	    config->array_count);
979215526Sjhb	if (spare == NULL) {
980215526Sjhb		warnx("malloc failed");
981222899Sbz		error = ENOMEM;
982222899Sbz		goto error;
983215526Sjhb	}
984196200Sscottl	bzero(spare, sizeof(struct mfi_spare));
985196200Sscottl	spare->ref = info.ref;
986196200Sscottl
987196200Sscottl	if (ac == 2) {
988196200Sscottl		/* Global spare backs all arrays. */
989196200Sscottl		p = (char *)config->array;
990196200Sscottl		for (i = 0; i < config->array_count; i++) {
991196200Sscottl			ar = (struct mfi_array *)p;
992196200Sscottl			if (ar->size > info.coerced_size) {
993196200Sscottl				warnx("Spare isn't large enough for array %u",
994196200Sscottl				    ar->array_ref);
995222899Sbz				error = EINVAL;
996222899Sbz				goto error;
997196200Sscottl			}
998196200Sscottl			p += config->array_size;
999196200Sscottl		}
1000196200Sscottl		spare->array_count = 0;
1001196200Sscottl	} else  {
1002196200Sscottl		/*
1003196200Sscottl		 * Dedicated spares only back the arrays for a
1004196200Sscottl		 * specific volume.
1005196200Sscottl		 */
1006196200Sscottl		ld = mfi_config_lookup_volume(config, target_id);
1007196200Sscottl		if (ld == NULL) {
1008196200Sscottl			warnx("Did not find volume %d", target_id);
1009222899Sbz			error = EINVAL;
1010222899Sbz			goto error;
1011196200Sscottl		}
1012196200Sscottl
1013196200Sscottl		spare->spare_type |= MFI_SPARE_DEDICATED;
1014196200Sscottl		spare->array_count = ld->params.span_depth;
1015196200Sscottl		for (i = 0; i < ld->params.span_depth; i++) {
1016196200Sscottl			ar = mfi_config_lookup_array(config,
1017196200Sscottl			    ld->span[i].array_ref);
1018196200Sscottl			if (ar == NULL) {
1019196200Sscottl				warnx("Missing array; inconsistent config?");
1020222899Sbz				error = ENXIO;
1021222899Sbz				goto error;
1022196200Sscottl			}
1023196200Sscottl			if (ar->size > info.coerced_size) {
1024196200Sscottl				warnx("Spare isn't large enough for array %u",
1025196200Sscottl				    ar->array_ref);
1026222899Sbz				error = EINVAL;
1027222899Sbz				goto error;
1028196200Sscottl			}
1029196200Sscottl			spare->array_ref[i] = ar->array_ref;
1030196200Sscottl		}
1031196200Sscottl	}
1032196200Sscottl
1033196200Sscottl	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare,
1034196200Sscottl	    sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count,
1035196200Sscottl	    NULL, 0, NULL) < 0) {
1036214396Sjhb		error = errno;
1037196200Sscottl		warn("Failed to assign spare");
1038222899Sbz		/* FALLTHROUGH. */
1039196200Sscottl	}
1040196200Sscottl
1041222899Sbzerror:
1042222899Sbz	free(spare);
1043222899Sbz	free(config);
1044196200Sscottl	close(fd);
1045196200Sscottl
1046222899Sbz	return (error);
1047196200Sscottl}
1048196200SscottlMFI_COMMAND(top, add, add_spare);
1049196200Sscottl
1050196200Sscottlstatic int
1051196200Sscottlremove_spare(int ac, char **av)
1052196200Sscottl{
1053196200Sscottl	struct mfi_pd_info info;
1054196200Sscottl	int error, fd;
1055196200Sscottl	uint16_t device_id;
1056196200Sscottl	uint8_t mbox[4];
1057196200Sscottl
1058196200Sscottl	if (ac != 2) {
1059196200Sscottl		warnx("remove spare: drive required");
1060196200Sscottl		return (EINVAL);
1061196200Sscottl	}
1062196200Sscottl
1063237259Seadler	fd = mfi_open(mfi_unit, O_RDWR);
1064196200Sscottl	if (fd < 0) {
1065214396Sjhb		error = errno;
1066196200Sscottl		warn("mfi_open");
1067214396Sjhb		return (error);
1068196200Sscottl	}
1069196200Sscottl
1070196200Sscottl	error = mfi_lookup_drive(fd, av[1], &device_id);
1071222899Sbz	if (error) {
1072222899Sbz		close(fd);
1073196200Sscottl		return (error);
1074222899Sbz	}
1075196200Sscottl
1076196200Sscottl	/* Get the info for this drive. */
1077196200Sscottl	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
1078214396Sjhb		error = errno;
1079196200Sscottl		warn("Failed to fetch info for drive %u", device_id);
1080222899Sbz		close(fd);
1081214396Sjhb		return (error);
1082196200Sscottl	}
1083196200Sscottl
1084196200Sscottl	if (info.fw_state != MFI_PD_STATE_HOT_SPARE) {
1085196200Sscottl		warnx("Drive %u is not a hot spare", device_id);
1086222899Sbz		close(fd);
1087196200Sscottl		return (EINVAL);
1088196200Sscottl	}
1089196200Sscottl
1090196200Sscottl	mbox_store_pdref(mbox, &info.ref);
1091196200Sscottl	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox,
1092196200Sscottl	    sizeof(mbox), NULL) < 0) {
1093214396Sjhb		error = errno;
1094196200Sscottl		warn("Failed to delete spare");
1095222899Sbz		close(fd);
1096214396Sjhb		return (error);
1097196200Sscottl	}
1098196200Sscottl
1099196200Sscottl	close(fd);
1100196200Sscottl
1101196200Sscottl	return (0);
1102196200Sscottl}
1103196200SscottlMFI_COMMAND(top, remove, remove_spare);
1104196200Sscottl
1105196200Sscottl/* Display raw data about a config. */
1106251516Ssbrunovoid
1107251516Ssbrunodump_config(int fd, struct mfi_config_data *config, const char *msg_prefix)
1108196200Sscottl{
1109196200Sscottl	struct mfi_array *ar;
1110196200Sscottl	struct mfi_ld_config *ld;
1111196200Sscottl	struct mfi_spare *sp;
1112196200Sscottl	struct mfi_pd_info pinfo;
1113196200Sscottl	uint16_t device_id;
1114196200Sscottl	char *p;
1115196200Sscottl	int i, j;
1116196200Sscottl
1117251516Ssbruno	if (NULL == msg_prefix)
1118251516Ssbruno		msg_prefix = "Configuration (Debug)";
1119251516Ssbruno
1120196200Sscottl	printf(
1121251516Ssbruno	    "mfi%d %s: %d arrays, %d volumes, %d spares\n", mfi_unit,
1122251516Ssbruno	    msg_prefix, config->array_count, config->log_drv_count,
1123196200Sscottl	    config->spares_count);
1124196200Sscottl	printf("  array size: %u\n", config->array_size);
1125196200Sscottl	printf("  volume size: %u\n", config->log_drv_size);
1126196200Sscottl	printf("  spare size: %u\n", config->spares_size);
1127196200Sscottl	p = (char *)config->array;
1128196200Sscottl
1129196200Sscottl	for (i = 0; i < config->array_count; i++) {
1130196200Sscottl		ar = (struct mfi_array *)p;
1131196200Sscottl		printf("    array %u of %u drives:\n", ar->array_ref,
1132196200Sscottl		    ar->num_drives);
1133196200Sscottl		printf("      size = %ju\n", (uintmax_t)ar->size);
1134196200Sscottl		for (j = 0; j < ar->num_drives; j++) {
1135213674Srandi			device_id = ar->pd[j].ref.v.device_id;
1136196200Sscottl			if (device_id == 0xffff)
1137196200Sscottl				printf("        drive MISSING\n");
1138196200Sscottl			else {
1139196200Sscottl				printf("        drive %u %s\n", device_id,
1140196200Sscottl				    mfi_pdstate(ar->pd[j].fw_state));
1141196200Sscottl				if (mfi_pd_get_info(fd, device_id, &pinfo,
1142196200Sscottl				    NULL) >= 0) {
1143196200Sscottl					printf("          raw size: %ju\n",
1144196200Sscottl					    (uintmax_t)pinfo.raw_size);
1145196200Sscottl					printf("          non-coerced size: %ju\n",
1146196200Sscottl					    (uintmax_t)pinfo.non_coerced_size);
1147196200Sscottl					printf("          coerced size: %ju\n",
1148196200Sscottl					    (uintmax_t)pinfo.coerced_size);
1149196200Sscottl				}
1150196200Sscottl			}
1151196200Sscottl		}
1152196200Sscottl		p += config->array_size;
1153196200Sscottl	}
1154196200Sscottl
1155196200Sscottl	for (i = 0; i < config->log_drv_count; i++) {
1156196200Sscottl		ld = (struct mfi_ld_config *)p;
1157196200Sscottl		printf("    volume %s ",
1158196200Sscottl		    mfi_volume_name(fd, ld->properties.ld.v.target_id));
1159196200Sscottl		printf("%s %s",
1160196200Sscottl		    mfi_raid_level(ld->params.primary_raid_level,
1161196200Sscottl			ld->params.secondary_raid_level),
1162196200Sscottl		    mfi_ldstate(ld->params.state));
1163196200Sscottl		if (ld->properties.name[0] != '\0')
1164196200Sscottl			printf(" <%s>", ld->properties.name);
1165196200Sscottl		printf("\n");
1166196200Sscottl		printf("      primary raid level: %u\n",
1167196200Sscottl		    ld->params.primary_raid_level);
1168196200Sscottl		printf("      raid level qualifier: %u\n",
1169196200Sscottl		    ld->params.raid_level_qualifier);
1170196200Sscottl		printf("      secondary raid level: %u\n",
1171196200Sscottl		    ld->params.secondary_raid_level);
1172196200Sscottl		printf("      stripe size: %u\n", ld->params.stripe_size);
1173196200Sscottl		printf("      num drives: %u\n", ld->params.num_drives);
1174196200Sscottl		printf("      init state: %u\n", ld->params.init_state);
1175196200Sscottl		printf("      consistent: %u\n", ld->params.is_consistent);
1176196200Sscottl		printf("      no bgi: %u\n", ld->properties.no_bgi);
1177196200Sscottl		printf("      spans:\n");
1178196200Sscottl		for (j = 0; j < ld->params.span_depth; j++) {
1179196200Sscottl			printf("        array %u @ ", ld->span[j].array_ref);
1180196200Sscottl			printf("%ju : %ju\n",
1181196200Sscottl			    (uintmax_t)ld->span[j].start_block,
1182196200Sscottl			    (uintmax_t)ld->span[j].num_blocks);
1183196200Sscottl		}
1184196200Sscottl		p += config->log_drv_size;
1185196200Sscottl	}
1186196200Sscottl
1187196200Sscottl	for (i = 0; i < config->spares_count; i++) {
1188196200Sscottl		sp = (struct mfi_spare *)p;
1189196200Sscottl		printf("    %s spare %u ",
1190196200Sscottl		    sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" :
1191213674Srandi		    "global", sp->ref.v.device_id);
1192196200Sscottl		printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE));
1193196200Sscottl		printf(" backs:\n");
1194196200Sscottl		for (j = 0; j < sp->array_count; j++)
1195196200Sscottl			printf("        array %u\n", sp->array_ref[j]);
1196196200Sscottl		p += config->spares_size;
1197196200Sscottl	}
1198196200Sscottl}
1199196200Sscottl
1200251516Ssbruno#ifdef DEBUG
1201196200Sscottlstatic int
1202196200Sscottldebug_config(int ac, char **av)
1203196200Sscottl{
1204196200Sscottl	struct mfi_config_data *config;
1205214396Sjhb	int error, fd;
1206196200Sscottl
1207196200Sscottl	if (ac != 1) {
1208196200Sscottl		warnx("debug: extra arguments");
1209196200Sscottl		return (EINVAL);
1210196200Sscottl	}
1211196200Sscottl
1212237259Seadler	fd = mfi_open(mfi_unit, O_RDWR);
1213196200Sscottl	if (fd < 0) {
1214214396Sjhb		error = errno;
1215196200Sscottl		warn("mfi_open");
1216214396Sjhb		return (error);
1217196200Sscottl	}
1218196200Sscottl
1219196200Sscottl	/* Get the config from the controller. */
1220196200Sscottl	if (mfi_config_read(fd, &config) < 0) {
1221214396Sjhb		error = errno;
1222196200Sscottl		warn("Failed to get config");
1223222899Sbz		close(fd);
1224214396Sjhb		return (error);
1225196200Sscottl	}
1226196200Sscottl
1227196200Sscottl	/* Dump out the configuration. */
1228251516Ssbruno	dump_config(fd, config, NULL);
1229196200Sscottl	free(config);
1230196200Sscottl	close(fd);
1231196200Sscottl
1232196200Sscottl	return (0);
1233196200Sscottl}
1234196200SscottlMFI_COMMAND(top, debug, debug_config);
1235196200Sscottl
1236196200Sscottlstatic int
1237196200Sscottldump(int ac, char **av)
1238196200Sscottl{
1239196200Sscottl	struct mfi_config_data *config;
1240196200Sscottl	char buf[64];
1241196200Sscottl	size_t len;
1242214396Sjhb	int error, fd;
1243196200Sscottl
1244196200Sscottl	if (ac != 1) {
1245196200Sscottl		warnx("dump: extra arguments");
1246196200Sscottl		return (EINVAL);
1247196200Sscottl	}
1248196200Sscottl
1249237259Seadler	fd = mfi_open(mfi_unit, O_RDWR);
1250196200Sscottl	if (fd < 0) {
1251214396Sjhb		error = errno;
1252196200Sscottl		warn("mfi_open");
1253214396Sjhb		return (error);
1254196200Sscottl	}
1255196200Sscottl
1256196200Sscottl	/* Get the stashed copy of the last dcmd from the driver. */
1257196200Sscottl	snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit);
1258196200Sscottl	if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) {
1259214396Sjhb		error = errno;
1260196200Sscottl		warn("Failed to read debug command");
1261214396Sjhb		if (error == ENOENT)
1262214396Sjhb			error = EOPNOTSUPP;
1263222899Sbz		close(fd);
1264214396Sjhb		return (error);
1265196200Sscottl	}
1266196200Sscottl
1267196200Sscottl	config = malloc(len);
1268215526Sjhb	if (config == NULL) {
1269215526Sjhb		warnx("malloc failed");
1270222899Sbz		close(fd);
1271215526Sjhb		return (ENOMEM);
1272215526Sjhb	}
1273196200Sscottl	if (sysctlbyname(buf, config, &len, NULL, 0) < 0) {
1274214396Sjhb		error = errno;
1275196200Sscottl		warn("Failed to read debug command");
1276222899Sbz		free(config);
1277222899Sbz		close(fd);
1278214396Sjhb		return (error);
1279196200Sscottl	}
1280251516Ssbruno	dump_config(fd, config, NULL);
1281196200Sscottl	free(config);
1282196200Sscottl	close(fd);
1283196200Sscottl
1284196200Sscottl	return (0);
1285196200Sscottl}
1286196200SscottlMFI_COMMAND(top, dump, dump);
1287196200Sscottl#endif
1288