xref: /freebsd/usr.sbin/mfiutil/mfi_config.c (revision f1e7af36c0bfebfaaaf4e330f7441c148a7d71b7)
1763fae79SScott Long /*-
2763fae79SScott Long  * Copyright (c) 2008, 2009 Yahoo!, Inc.
3763fae79SScott Long  * All rights reserved.
4763fae79SScott Long  *
5763fae79SScott Long  * Redistribution and use in source and binary forms, with or without
6763fae79SScott Long  * modification, are permitted provided that the following conditions
7763fae79SScott Long  * are met:
8763fae79SScott Long  * 1. Redistributions of source code must retain the above copyright
9763fae79SScott Long  *    notice, this list of conditions and the following disclaimer.
10763fae79SScott Long  * 2. Redistributions in binary form must reproduce the above copyright
11763fae79SScott Long  *    notice, this list of conditions and the following disclaimer in the
12763fae79SScott Long  *    documentation and/or other materials provided with the distribution.
13763fae79SScott Long  * 3. The names of the authors may not be used to endorse or promote
14763fae79SScott Long  *    products derived from this software without specific prior written
15763fae79SScott Long  *    permission.
16763fae79SScott Long  *
17763fae79SScott Long  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18763fae79SScott Long  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19763fae79SScott Long  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20763fae79SScott Long  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21763fae79SScott Long  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22763fae79SScott Long  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23763fae79SScott Long  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24763fae79SScott Long  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25763fae79SScott Long  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26763fae79SScott Long  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27763fae79SScott Long  * SUCH DAMAGE.
28763fae79SScott Long  *
29763fae79SScott Long  * $FreeBSD$
30763fae79SScott Long  */
31763fae79SScott Long 
32c02999d9SJohn Baldwin #include <sys/param.h>
33763fae79SScott Long #ifdef DEBUG
34763fae79SScott Long #include <sys/sysctl.h>
35763fae79SScott Long #endif
36763fae79SScott Long #include <err.h>
37c02999d9SJohn Baldwin #include <errno.h>
38763fae79SScott Long #include <libutil.h>
39763fae79SScott Long #ifdef DEBUG
40763fae79SScott Long #include <stdint.h>
41763fae79SScott Long #endif
42763fae79SScott Long #include <stdio.h>
43763fae79SScott Long #include <stdlib.h>
44763fae79SScott Long #include <string.h>
45763fae79SScott Long #include <unistd.h>
46763fae79SScott Long #include "mfiutil.h"
47763fae79SScott Long 
48763fae79SScott Long #ifdef DEBUG
49763fae79SScott Long static void	dump_config(int fd, struct mfi_config_data *config);
50763fae79SScott Long #endif
51763fae79SScott Long 
52763fae79SScott Long static int	add_spare(int ac, char **av);
53763fae79SScott Long static int	remove_spare(int ac, char **av);
54763fae79SScott Long 
55763fae79SScott Long static long
56763fae79SScott Long dehumanize(const char *value)
57763fae79SScott Long {
58763fae79SScott Long         char    *vtp;
59763fae79SScott Long         long    iv;
60763fae79SScott Long 
61763fae79SScott Long         if (value == NULL)
62763fae79SScott Long                 return (0);
63763fae79SScott Long         iv = strtoq(value, &vtp, 0);
64763fae79SScott Long         if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
65763fae79SScott Long                 return (0);
66763fae79SScott Long         }
67763fae79SScott Long         switch (vtp[0]) {
68763fae79SScott Long         case 't': case 'T':
69763fae79SScott Long                 iv *= 1024;
70763fae79SScott Long         case 'g': case 'G':
71763fae79SScott Long                 iv *= 1024;
72763fae79SScott Long         case 'm': case 'M':
73763fae79SScott Long                 iv *= 1024;
74763fae79SScott Long         case 'k': case 'K':
75763fae79SScott Long                 iv *= 1024;
76763fae79SScott Long         case '\0':
77763fae79SScott Long                 break;
78763fae79SScott Long         default:
79763fae79SScott Long                 return (0);
80763fae79SScott Long         }
81763fae79SScott Long         return (iv);
82763fae79SScott Long }
83763fae79SScott Long int
84763fae79SScott Long mfi_config_read(int fd, struct mfi_config_data **configp)
85763fae79SScott Long {
86763fae79SScott Long 	struct mfi_config_data *config;
87763fae79SScott Long 	uint32_t config_size;
88375c4656SBjoern A. Zeeb 	int error;
89763fae79SScott Long 
90763fae79SScott Long 	/*
91763fae79SScott Long 	 * Keep fetching the config in a loop until we have a large enough
92763fae79SScott Long 	 * buffer to hold the entire configuration.
93763fae79SScott Long 	 */
94763fae79SScott Long 	config = NULL;
95763fae79SScott Long 	config_size = 1024;
96763fae79SScott Long fetch:
97763fae79SScott Long 	config = reallocf(config, config_size);
98763fae79SScott Long 	if (config == NULL)
99763fae79SScott Long 		return (-1);
100763fae79SScott Long 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_READ, config,
101375c4656SBjoern A. Zeeb 	    config_size, NULL, 0, NULL) < 0) {
102375c4656SBjoern A. Zeeb 		error = errno;
103375c4656SBjoern A. Zeeb 		free(config);
104375c4656SBjoern A. Zeeb 		errno = error;
105763fae79SScott Long 		return (-1);
106375c4656SBjoern A. Zeeb 	}
107763fae79SScott Long 
108763fae79SScott Long 	if (config->size > config_size) {
109763fae79SScott Long 		config_size = config->size;
110763fae79SScott Long 		goto fetch;
111763fae79SScott Long 	}
112763fae79SScott Long 
113763fae79SScott Long 	*configp = config;
114763fae79SScott Long 	return (0);
115763fae79SScott Long }
116763fae79SScott Long 
117763fae79SScott Long static struct mfi_array *
118763fae79SScott Long mfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
119763fae79SScott Long {
120763fae79SScott Long 	struct mfi_array *ar;
121763fae79SScott Long 	char *p;
122763fae79SScott Long 	int i;
123763fae79SScott Long 
124763fae79SScott Long 	p = (char *)config->array;
125763fae79SScott Long 	for (i = 0; i < config->array_count; i++) {
126763fae79SScott Long 		ar = (struct mfi_array *)p;
127763fae79SScott Long 		if (ar->array_ref == array_ref)
128763fae79SScott Long 			return (ar);
129763fae79SScott Long 		p += config->array_size;
130763fae79SScott Long 	}
131763fae79SScott Long 
132763fae79SScott Long 	return (NULL);
133763fae79SScott Long }
134763fae79SScott Long 
135763fae79SScott Long static struct mfi_ld_config *
136763fae79SScott Long mfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
137763fae79SScott Long {
138763fae79SScott Long 	struct mfi_ld_config *ld;
139763fae79SScott Long 	char *p;
140763fae79SScott Long 	int i;
141763fae79SScott Long 
142763fae79SScott Long 	p = (char *)config->array + config->array_count * config->array_size;
143763fae79SScott Long 	for (i = 0; i < config->log_drv_count; i++) {
144763fae79SScott Long 		ld = (struct mfi_ld_config *)p;
145763fae79SScott Long 		if (ld->properties.ld.v.target_id == target_id)
146763fae79SScott Long 			return (ld);
147763fae79SScott Long 		p += config->log_drv_size;
148763fae79SScott Long 	}
149763fae79SScott Long 
150763fae79SScott Long 	return (NULL);
151763fae79SScott Long }
152763fae79SScott Long 
153763fae79SScott Long static int
154763fae79SScott Long clear_config(int ac, char **av)
155763fae79SScott Long {
156763fae79SScott Long 	struct mfi_ld_list list;
157c02999d9SJohn Baldwin 	int ch, error, fd;
158763fae79SScott Long 	u_int i;
159763fae79SScott Long 
160763fae79SScott Long 	fd = mfi_open(mfi_unit);
161763fae79SScott Long 	if (fd < 0) {
162c02999d9SJohn Baldwin 		error = errno;
163763fae79SScott Long 		warn("mfi_open");
164c02999d9SJohn Baldwin 		return (error);
165763fae79SScott Long 	}
166763fae79SScott Long 
167763fae79SScott Long 	if (!mfi_reconfig_supported()) {
168763fae79SScott Long 		warnx("The current mfi(4) driver does not support "
169763fae79SScott Long 		    "configuration changes.");
170375c4656SBjoern A. Zeeb 		close(fd);
171763fae79SScott Long 		return (EOPNOTSUPP);
172763fae79SScott Long 	}
173763fae79SScott Long 
174763fae79SScott Long 	if (mfi_ld_get_list(fd, &list, NULL) < 0) {
175c02999d9SJohn Baldwin 		error = errno;
176763fae79SScott Long 		warn("Failed to get volume list");
177375c4656SBjoern A. Zeeb 		close(fd);
178c02999d9SJohn Baldwin 		return (error);
179763fae79SScott Long 	}
180763fae79SScott Long 
181763fae79SScott Long 	for (i = 0; i < list.ld_count; i++) {
182763fae79SScott Long 		if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) {
183763fae79SScott Long 			warnx("Volume %s is busy and cannot be deleted",
184763fae79SScott Long 			    mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
185375c4656SBjoern A. Zeeb 			close(fd);
186763fae79SScott Long 			return (EBUSY);
187763fae79SScott Long 		}
188763fae79SScott Long 	}
189763fae79SScott Long 
190763fae79SScott Long 	printf(
191763fae79SScott Long 	    "Are you sure you wish to clear the configuration on mfi%u? [y/N] ",
192763fae79SScott Long 	    mfi_unit);
193763fae79SScott Long 	ch = getchar();
194763fae79SScott Long 	if (ch != 'y' && ch != 'Y') {
195763fae79SScott Long 		printf("\nAborting\n");
196375c4656SBjoern A. Zeeb 		close(fd);
197763fae79SScott Long 		return (0);
198763fae79SScott Long 	}
199763fae79SScott Long 
200763fae79SScott Long 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) {
201c02999d9SJohn Baldwin 		error = errno;
202763fae79SScott Long 		warn("Failed to clear configuration");
203375c4656SBjoern A. Zeeb 		close(fd);
204c02999d9SJohn Baldwin 		return (error);
205763fae79SScott Long 	}
206763fae79SScott Long 
207763fae79SScott Long 	printf("mfi%d: Configuration cleared\n", mfi_unit);
208763fae79SScott Long 	close(fd);
209763fae79SScott Long 
210763fae79SScott Long 	return (0);
211763fae79SScott Long }
212763fae79SScott Long MFI_COMMAND(top, clear, clear_config);
213763fae79SScott Long 
214763fae79SScott Long #define	MFI_ARRAY_SIZE		288
215763fae79SScott Long #define	MAX_DRIVES_PER_ARRAY						\
216763fae79SScott Long 	((MFI_ARRAY_SIZE - sizeof(struct mfi_array)) / 8)
217763fae79SScott Long 
218763fae79SScott Long #define	RT_RAID0	0
219763fae79SScott Long #define	RT_RAID1	1
220763fae79SScott Long #define	RT_RAID5	2
221763fae79SScott Long #define	RT_RAID6	3
222763fae79SScott Long #define	RT_JBOD		4
223763fae79SScott Long #define	RT_CONCAT	5
224763fae79SScott Long #define	RT_RAID10	6
225763fae79SScott Long #define	RT_RAID50	7
226763fae79SScott Long #define	RT_RAID60	8
227763fae79SScott Long 
228763fae79SScott Long static int
229763fae79SScott Long compare_int(const void *one, const void *two)
230763fae79SScott Long {
231763fae79SScott Long 	int first, second;
232763fae79SScott Long 
233763fae79SScott Long 	first = *(const int *)one;
234763fae79SScott Long 	second = *(const int *)two;
235763fae79SScott Long 
236763fae79SScott Long 	return (first - second);
237763fae79SScott Long }
238763fae79SScott Long 
239763fae79SScott Long static struct raid_type_entry {
240763fae79SScott Long 	const char *name;
241763fae79SScott Long 	int	raid_type;
242763fae79SScott Long } raid_type_table[] = {
243763fae79SScott Long 	{ "raid0",	RT_RAID0 },
244763fae79SScott Long 	{ "raid-0",	RT_RAID0 },
245763fae79SScott Long 	{ "raid1",	RT_RAID1 },
246763fae79SScott Long 	{ "raid-1",	RT_RAID1 },
247763fae79SScott Long 	{ "mirror",	RT_RAID1 },
248763fae79SScott Long 	{ "raid5",	RT_RAID5 },
249763fae79SScott Long 	{ "raid-5",	RT_RAID5 },
250763fae79SScott Long 	{ "raid6",	RT_RAID6 },
251763fae79SScott Long 	{ "raid-6",	RT_RAID6 },
252763fae79SScott Long 	{ "jbod",	RT_JBOD },
253763fae79SScott Long 	{ "concat",	RT_CONCAT },
254763fae79SScott Long 	{ "raid10",	RT_RAID10 },
255763fae79SScott Long 	{ "raid1+0",	RT_RAID10 },
256763fae79SScott Long 	{ "raid-10",	RT_RAID10 },
257763fae79SScott Long 	{ "raid-1+0",	RT_RAID10 },
258763fae79SScott Long 	{ "raid50",	RT_RAID50 },
259763fae79SScott Long 	{ "raid5+0",	RT_RAID50 },
260763fae79SScott Long 	{ "raid-50",	RT_RAID50 },
261763fae79SScott Long 	{ "raid-5+0",	RT_RAID50 },
262763fae79SScott Long 	{ "raid60",	RT_RAID60 },
263763fae79SScott Long 	{ "raid6+0",	RT_RAID60 },
264763fae79SScott Long 	{ "raid-60",	RT_RAID60 },
265763fae79SScott Long 	{ "raid-6+0",	RT_RAID60 },
266763fae79SScott Long 	{ NULL,		0 },
267763fae79SScott Long };
268763fae79SScott Long 
269763fae79SScott Long struct config_id_state {
270763fae79SScott Long 	int	array_count;
271763fae79SScott Long 	int	log_drv_count;
272763fae79SScott Long 	int	*arrays;
273763fae79SScott Long 	int	*volumes;
274763fae79SScott Long 	uint16_t array_ref;
275763fae79SScott Long 	uint8_t	target_id;
276763fae79SScott Long };
277763fae79SScott Long 
278763fae79SScott Long struct array_info {
279763fae79SScott Long 	int	drive_count;
280763fae79SScott Long 	struct mfi_pd_info *drives;
281763fae79SScott Long 	struct mfi_array *array;
282763fae79SScott Long };
283763fae79SScott Long 
284763fae79SScott Long /* Parse a comma-separated list of drives for an array. */
285763fae79SScott Long static int
286763fae79SScott Long parse_array(int fd, int raid_type, char *array_str, struct array_info *info)
287763fae79SScott Long {
288763fae79SScott Long 	struct mfi_pd_info *pinfo;
289763fae79SScott Long 	uint16_t device_id;
290763fae79SScott Long 	char *cp;
291763fae79SScott Long 	u_int count;
292763fae79SScott Long 	int error;
293763fae79SScott Long 
294763fae79SScott Long 	cp = array_str;
295763fae79SScott Long 	for (count = 0; cp != NULL; count++) {
296763fae79SScott Long 		cp = strchr(cp, ',');
297763fae79SScott Long 		if (cp != NULL) {
298763fae79SScott Long 			cp++;
299763fae79SScott Long 			if (*cp == ',') {
300763fae79SScott Long 				warnx("Invalid drive list '%s'", array_str);
301763fae79SScott Long 				return (EINVAL);
302763fae79SScott Long 			}
303763fae79SScott Long 		}
304763fae79SScott Long 	}
305763fae79SScott Long 
306763fae79SScott Long 	/* Validate the number of drives for this array. */
307763fae79SScott Long 	if (count >= MAX_DRIVES_PER_ARRAY) {
308763fae79SScott Long 		warnx("Too many drives for a single array: max is %zu",
309763fae79SScott Long 		    MAX_DRIVES_PER_ARRAY);
310763fae79SScott Long 		return (EINVAL);
311763fae79SScott Long 	}
312763fae79SScott Long 	switch (raid_type) {
313763fae79SScott Long 	case RT_RAID1:
314763fae79SScott Long 	case RT_RAID10:
315763fae79SScott Long 		if (count % 2 != 0) {
316763fae79SScott Long 			warnx("RAID1 and RAID10 require an even number of "
317763fae79SScott Long 			    "drives in each array");
318763fae79SScott Long 			return (EINVAL);
319763fae79SScott Long 		}
320763fae79SScott Long 		break;
321763fae79SScott Long 	case RT_RAID5:
322763fae79SScott Long 	case RT_RAID50:
323763fae79SScott Long 		if (count < 3) {
324763fae79SScott Long 			warnx("RAID5 and RAID50 require at least 3 drives in "
325763fae79SScott Long 			    "each array");
326763fae79SScott Long 			return (EINVAL);
327763fae79SScott Long 		}
328763fae79SScott Long 		break;
329763fae79SScott Long 	case RT_RAID6:
330763fae79SScott Long 	case RT_RAID60:
331763fae79SScott Long 		if (count < 4) {
332763fae79SScott Long 			warnx("RAID6 and RAID60 require at least 4 drives in "
333763fae79SScott Long 			    "each array");
334763fae79SScott Long 			return (EINVAL);
335763fae79SScott Long 		}
336763fae79SScott Long 		break;
337763fae79SScott Long 	}
338763fae79SScott Long 
339763fae79SScott Long 	/* Validate each drive. */
340763fae79SScott Long 	info->drives = calloc(count, sizeof(struct mfi_pd_info));
3413c22a809SJohn Baldwin 	if (info->drives == NULL) {
3423c22a809SJohn Baldwin 		warnx("malloc failed");
3433c22a809SJohn Baldwin 		return (ENOMEM);
3443c22a809SJohn Baldwin 	}
345763fae79SScott Long 	info->drive_count = count;
346763fae79SScott Long 	for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL;
347763fae79SScott Long 	     pinfo++) {
348763fae79SScott Long 		error = mfi_lookup_drive(fd, cp, &device_id);
349375c4656SBjoern A. Zeeb 		if (error) {
350375c4656SBjoern A. Zeeb 			free(info->drives);
351*f1e7af36SEd Maste 			info->drives = NULL;
352763fae79SScott Long 			return (error);
353375c4656SBjoern A. Zeeb 		}
354763fae79SScott Long 
355763fae79SScott Long 		if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) {
356c02999d9SJohn Baldwin 			error = errno;
357763fae79SScott Long 			warn("Failed to fetch drive info for drive %s", cp);
358375c4656SBjoern A. Zeeb 			free(info->drives);
359*f1e7af36SEd Maste 			info->drives = NULL;
360c02999d9SJohn Baldwin 			return (error);
361763fae79SScott Long 		}
362763fae79SScott Long 
363763fae79SScott Long 		if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
364763fae79SScott Long 			warnx("Drive %u is not available", device_id);
365375c4656SBjoern A. Zeeb 			free(info->drives);
366*f1e7af36SEd Maste 			info->drives = NULL;
367763fae79SScott Long 			return (EINVAL);
368763fae79SScott Long 		}
369763fae79SScott Long 	}
370763fae79SScott Long 
371763fae79SScott Long 	return (0);
372763fae79SScott Long }
373763fae79SScott Long 
374763fae79SScott Long /*
375763fae79SScott Long  * Find the next free array ref assuming that 'array_ref' is the last
376763fae79SScott Long  * one used.  'array_ref' should be 0xffff for the initial test.
377763fae79SScott Long  */
378763fae79SScott Long static uint16_t
379763fae79SScott Long find_next_array(struct config_id_state *state)
380763fae79SScott Long {
381763fae79SScott Long 	int i;
382763fae79SScott Long 
383763fae79SScott Long 	/* Assume the current one is used. */
384763fae79SScott Long 	state->array_ref++;
385763fae79SScott Long 
386763fae79SScott Long 	/* Find the next free one. */
387763fae79SScott Long 	for (i = 0; i < state->array_count; i++)
388763fae79SScott Long 		if (state->arrays[i] == state->array_ref)
389763fae79SScott Long 			state->array_ref++;
390763fae79SScott Long 	return (state->array_ref);
391763fae79SScott Long }
392763fae79SScott Long 
393763fae79SScott Long /*
394763fae79SScott Long  * Find the next free volume ID assuming that 'target_id' is the last
395763fae79SScott Long  * one used.  'target_id' should be 0xff for the initial test.
396763fae79SScott Long  */
397763fae79SScott Long static uint8_t
398763fae79SScott Long find_next_volume(struct config_id_state *state)
399763fae79SScott Long {
400763fae79SScott Long 	int i;
401763fae79SScott Long 
402763fae79SScott Long 	/* Assume the current one is used. */
403763fae79SScott Long 	state->target_id++;
404763fae79SScott Long 
405763fae79SScott Long 	/* Find the next free one. */
406763fae79SScott Long 	for (i = 0; i < state->log_drv_count; i++)
407763fae79SScott Long 		if (state->volumes[i] == state->target_id)
408763fae79SScott Long 			state->target_id++;
409763fae79SScott Long 	return (state->target_id);
410763fae79SScott Long }
411763fae79SScott Long 
412763fae79SScott Long /* Populate an array with drives. */
413763fae79SScott Long static void
414763fae79SScott Long build_array(int fd, char *arrayp, struct array_info *array_info,
415763fae79SScott Long     struct config_id_state *state, int verbose)
416763fae79SScott Long {
417763fae79SScott Long 	struct mfi_array *ar = (struct mfi_array *)arrayp;
418763fae79SScott Long 	int i;
419763fae79SScott Long 
420763fae79SScott Long 	ar->size = array_info->drives[0].coerced_size;
421763fae79SScott Long 	ar->num_drives = array_info->drive_count;
422763fae79SScott Long 	ar->array_ref = find_next_array(state);
423763fae79SScott Long 	for (i = 0; i < array_info->drive_count; i++) {
424763fae79SScott Long 		if (verbose)
4257bbae305SBjoern A. Zeeb 			printf("Adding drive %s to array %u\n",
4267bbae305SBjoern A. Zeeb 			    mfi_drive_name(NULL,
427763fae79SScott Long 			    array_info->drives[i].ref.v.device_id,
4287bbae305SBjoern A. Zeeb 			    MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS),
429763fae79SScott Long 			    ar->array_ref);
430763fae79SScott Long 		if (ar->size > array_info->drives[i].coerced_size)
431763fae79SScott Long 			ar->size = array_info->drives[i].coerced_size;
432763fae79SScott Long 		ar->pd[i].ref = array_info->drives[i].ref;
433763fae79SScott Long 		ar->pd[i].fw_state = MFI_PD_STATE_ONLINE;
434763fae79SScott Long 	}
435763fae79SScott Long 	array_info->array = ar;
436763fae79SScott Long }
437763fae79SScott Long 
438763fae79SScott Long /*
439763fae79SScott Long  * Create a volume that spans one or more arrays.
440763fae79SScott Long  */
441763fae79SScott Long static void
442763fae79SScott Long build_volume(char *volumep, int narrays, struct array_info *arrays,
443763fae79SScott Long     int raid_type, long stripe_size, struct config_id_state *state, int verbose)
444763fae79SScott Long {
445763fae79SScott Long 	struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
446763fae79SScott Long 	struct mfi_array *ar;
447763fae79SScott Long 	int i;
448763fae79SScott Long 
449763fae79SScott Long 	/* properties */
450763fae79SScott Long 	ld->properties.ld.v.target_id = find_next_volume(state);
451763fae79SScott Long 	ld->properties.ld.v.seq = 0;
452763fae79SScott Long 	ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
453763fae79SScott Long 	    MR_LD_CACHE_WRITE_BACK;
454763fae79SScott Long 	ld->properties.access_policy = MFI_LD_ACCESS_RW;
455763fae79SScott Long 	ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED;
456763fae79SScott Long 	ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
457763fae79SScott Long 	    MR_LD_CACHE_WRITE_BACK;
458763fae79SScott Long 	ld->properties.no_bgi = 0;
459763fae79SScott Long 
460763fae79SScott Long 	/* params */
461763fae79SScott Long 	switch (raid_type) {
462763fae79SScott Long 	case RT_RAID0:
463763fae79SScott Long 	case RT_JBOD:
464763fae79SScott Long 		ld->params.primary_raid_level = DDF_RAID0;
465763fae79SScott Long 		ld->params.raid_level_qualifier = 0;
466763fae79SScott Long 		ld->params.secondary_raid_level = 0;
467763fae79SScott Long 		break;
468763fae79SScott Long 	case RT_RAID1:
469763fae79SScott Long 		ld->params.primary_raid_level = DDF_RAID1;
470763fae79SScott Long 		ld->params.raid_level_qualifier = 0;
471763fae79SScott Long 		ld->params.secondary_raid_level = 0;
472763fae79SScott Long 		break;
473763fae79SScott Long 	case RT_RAID5:
474763fae79SScott Long 		ld->params.primary_raid_level = DDF_RAID5;
475763fae79SScott Long 		ld->params.raid_level_qualifier = 3;
476763fae79SScott Long 		ld->params.secondary_raid_level = 0;
477763fae79SScott Long 		break;
478763fae79SScott Long 	case RT_RAID6:
479763fae79SScott Long 		ld->params.primary_raid_level = DDF_RAID6;
480763fae79SScott Long 		ld->params.raid_level_qualifier = 3;
481763fae79SScott Long 		ld->params.secondary_raid_level = 0;
482763fae79SScott Long 		break;
483763fae79SScott Long 	case RT_CONCAT:
484763fae79SScott Long 		ld->params.primary_raid_level = DDF_CONCAT;
485763fae79SScott Long 		ld->params.raid_level_qualifier = 0;
486763fae79SScott Long 		ld->params.secondary_raid_level = 0;
487763fae79SScott Long 		break;
488763fae79SScott Long 	case RT_RAID10:
489763fae79SScott Long 		ld->params.primary_raid_level = DDF_RAID1;
490763fae79SScott Long 		ld->params.raid_level_qualifier = 0;
491763fae79SScott Long 		ld->params.secondary_raid_level = 3; /* XXX? */
492763fae79SScott Long 		break;
493763fae79SScott Long 	case RT_RAID50:
494763fae79SScott Long 		/*
495763fae79SScott Long 		 * XXX: This appears to work though the card's BIOS
496763fae79SScott Long 		 * complains that the configuration is foreign.  The
497763fae79SScott Long 		 * BIOS setup does not allow for creation of RAID-50
498763fae79SScott Long 		 * or RAID-60 arrays.  The only nested array
499763fae79SScott Long 		 * configuration it allows for is RAID-10.
500763fae79SScott Long 		 */
501763fae79SScott Long 		ld->params.primary_raid_level = DDF_RAID5;
502763fae79SScott Long 		ld->params.raid_level_qualifier = 3;
503763fae79SScott Long 		ld->params.secondary_raid_level = 3; /* XXX? */
504763fae79SScott Long 		break;
505763fae79SScott Long 	case RT_RAID60:
506763fae79SScott Long 		ld->params.primary_raid_level = DDF_RAID6;
507763fae79SScott Long 		ld->params.raid_level_qualifier = 3;
508763fae79SScott Long 		ld->params.secondary_raid_level = 3; /* XXX? */
509763fae79SScott Long 		break;
510763fae79SScott Long 	}
511763fae79SScott Long 
512763fae79SScott Long 	/*
513763fae79SScott Long 	 * Stripe size is encoded as (2 ^ N) * 512 = stripe_size.  Use
514763fae79SScott Long 	 * ffs() to simulate log2(stripe_size).
515763fae79SScott Long 	 */
516763fae79SScott Long 	ld->params.stripe_size = ffs(stripe_size) - 1 - 9;
517763fae79SScott Long 	ld->params.num_drives = arrays[0].array->num_drives;
518763fae79SScott Long 	ld->params.span_depth = narrays;
519763fae79SScott Long 	ld->params.state = MFI_LD_STATE_OPTIMAL;
520763fae79SScott Long 	ld->params.init_state = MFI_LD_PARAMS_INIT_NO;
521763fae79SScott Long 	ld->params.is_consistent = 0;
522763fae79SScott Long 
523763fae79SScott Long 	/* spans */
524763fae79SScott Long 	for (i = 0; i < narrays; i++) {
525763fae79SScott Long 		ar = arrays[i].array;
526763fae79SScott Long 		if (verbose)
527763fae79SScott Long 			printf("Adding array %u to volume %u\n", ar->array_ref,
528763fae79SScott Long 			    ld->properties.ld.v.target_id);
529763fae79SScott Long 		ld->span[i].start_block = 0;
530763fae79SScott Long 		ld->span[i].num_blocks = ar->size;
531763fae79SScott Long 		ld->span[i].array_ref = ar->array_ref;
532763fae79SScott Long 	}
533763fae79SScott Long }
534763fae79SScott Long 
535763fae79SScott Long static int
536763fae79SScott Long create_volume(int ac, char **av)
537763fae79SScott Long {
538763fae79SScott Long 	struct mfi_config_data *config;
539763fae79SScott Long 	struct mfi_array *ar;
540763fae79SScott Long 	struct mfi_ld_config *ld;
541763fae79SScott Long 	struct config_id_state state;
542763fae79SScott Long 	size_t config_size;
543763fae79SScott Long 	char *p, *cfg_arrays, *cfg_volumes;
544763fae79SScott Long 	int error, fd, i, raid_type;
545763fae79SScott Long 	int narrays, nvolumes, arrays_per_volume;
546763fae79SScott Long 	struct array_info *arrays;
547763fae79SScott Long 	long stripe_size;
548763fae79SScott Long #ifdef DEBUG
549763fae79SScott Long 	int dump;
550763fae79SScott Long #endif
551763fae79SScott Long 	int ch, verbose;
552763fae79SScott Long 
553763fae79SScott Long 	/*
554763fae79SScott Long 	 * Backwards compat.  Map 'create volume' to 'create' and
555763fae79SScott Long 	 * 'create spare' to 'add'.
556763fae79SScott Long 	 */
557763fae79SScott Long 	if (ac > 1) {
558763fae79SScott Long 		if (strcmp(av[1], "volume") == 0) {
559763fae79SScott Long 			av++;
560763fae79SScott Long 			ac--;
561763fae79SScott Long 		} else if (strcmp(av[1], "spare") == 0) {
562763fae79SScott Long 			av++;
563763fae79SScott Long 			ac--;
564763fae79SScott Long 			return (add_spare(ac, av));
565763fae79SScott Long 		}
566763fae79SScott Long 	}
567763fae79SScott Long 
568763fae79SScott Long 	if (ac < 2) {
569763fae79SScott Long 		warnx("create volume: volume type required");
570763fae79SScott Long 		return (EINVAL);
571763fae79SScott Long 	}
572763fae79SScott Long 
573375c4656SBjoern A. Zeeb 	bzero(&state, sizeof(state));
574375c4656SBjoern A. Zeeb 	config = NULL;
575375c4656SBjoern A. Zeeb 	arrays = NULL;
576375c4656SBjoern A. Zeeb 	narrays = 0;
577375c4656SBjoern A. Zeeb 	error = 0;
578763fae79SScott Long 
579763fae79SScott Long 	fd = mfi_open(mfi_unit);
580763fae79SScott Long 	if (fd < 0) {
581c02999d9SJohn Baldwin 		error = errno;
582763fae79SScott Long 		warn("mfi_open");
583c02999d9SJohn Baldwin 		return (error);
584763fae79SScott Long 	}
585763fae79SScott Long 
586763fae79SScott Long 	if (!mfi_reconfig_supported()) {
587763fae79SScott Long 		warnx("The current mfi(4) driver does not support "
588763fae79SScott Long 		    "configuration changes.");
589375c4656SBjoern A. Zeeb 		error = EOPNOTSUPP;
590375c4656SBjoern A. Zeeb 		goto error;
591763fae79SScott Long 	}
592763fae79SScott Long 
593763fae79SScott Long 	/* Lookup the RAID type first. */
594763fae79SScott Long 	raid_type = -1;
595763fae79SScott Long 	for (i = 0; raid_type_table[i].name != NULL; i++)
596763fae79SScott Long 		if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
597763fae79SScott Long 			raid_type = raid_type_table[i].raid_type;
598763fae79SScott Long 			break;
599763fae79SScott Long 		}
600763fae79SScott Long 
601763fae79SScott Long 	if (raid_type == -1) {
602763fae79SScott Long 		warnx("Unknown or unsupported volume type %s", av[1]);
603375c4656SBjoern A. Zeeb 		error = EINVAL;
604375c4656SBjoern A. Zeeb 		goto error;
605763fae79SScott Long 	}
606763fae79SScott Long 
607763fae79SScott Long 	/* Parse any options. */
608763fae79SScott Long 	optind = 2;
609763fae79SScott Long #ifdef DEBUG
610763fae79SScott Long 	dump = 0;
611763fae79SScott Long #endif
612763fae79SScott Long 	verbose = 0;
613763fae79SScott Long 	stripe_size = 64 * 1024;
614763fae79SScott Long 
615763fae79SScott Long 	while ((ch = getopt(ac, av, "ds:v")) != -1) {
616763fae79SScott Long 		switch (ch) {
617763fae79SScott Long #ifdef DEBUG
618763fae79SScott Long 		case 'd':
619763fae79SScott Long 			dump = 1;
620763fae79SScott Long 			break;
621763fae79SScott Long #endif
622763fae79SScott Long 		case 's':
623763fae79SScott Long 			stripe_size = dehumanize(optarg);
624763fae79SScott Long 			if ((stripe_size < 512) || (!powerof2(stripe_size)))
625763fae79SScott Long 				stripe_size = 64 * 1024;
626763fae79SScott Long 			break;
627763fae79SScott Long 		case 'v':
628763fae79SScott Long 			verbose = 1;
629763fae79SScott Long 			break;
630763fae79SScott Long 		case '?':
631763fae79SScott Long 		default:
632375c4656SBjoern A. Zeeb 			error = EINVAL;
633375c4656SBjoern A. Zeeb 			goto error;
634763fae79SScott Long 		}
635763fae79SScott Long 	}
636763fae79SScott Long 	ac -= optind;
637763fae79SScott Long 	av += optind;
638763fae79SScott Long 
639763fae79SScott Long 	/* Parse all the arrays. */
640763fae79SScott Long 	narrays = ac;
641763fae79SScott Long 	if (narrays == 0) {
642763fae79SScott Long 		warnx("At least one drive list is required");
643375c4656SBjoern A. Zeeb 		error = EINVAL;
644375c4656SBjoern A. Zeeb 		goto error;
645763fae79SScott Long 	}
646763fae79SScott Long 	switch (raid_type) {
647763fae79SScott Long 	case RT_RAID0:
648763fae79SScott Long 	case RT_RAID1:
649763fae79SScott Long 	case RT_RAID5:
650763fae79SScott Long 	case RT_RAID6:
651763fae79SScott Long 	case RT_CONCAT:
652763fae79SScott Long 		if (narrays != 1) {
653763fae79SScott Long 			warnx("Only one drive list can be specified");
654375c4656SBjoern A. Zeeb 			error = EINVAL;
655375c4656SBjoern A. Zeeb 			goto error;
656763fae79SScott Long 		}
657763fae79SScott Long 		break;
658763fae79SScott Long 	case RT_RAID10:
659763fae79SScott Long 	case RT_RAID50:
660763fae79SScott Long 	case RT_RAID60:
661763fae79SScott Long 		if (narrays < 1) {
662763fae79SScott Long 			warnx("RAID10, RAID50, and RAID60 require at least "
663763fae79SScott Long 			    "two drive lists");
664375c4656SBjoern A. Zeeb 			error = EINVAL;
665375c4656SBjoern A. Zeeb 			goto error;
666763fae79SScott Long 		}
667763fae79SScott Long 		if (narrays > MFI_MAX_SPAN_DEPTH) {
668763fae79SScott Long 			warnx("Volume spans more than %d arrays",
669763fae79SScott Long 			    MFI_MAX_SPAN_DEPTH);
670375c4656SBjoern A. Zeeb 			error = EINVAL;
671375c4656SBjoern A. Zeeb 			goto error;
672763fae79SScott Long 		}
673763fae79SScott Long 		break;
674763fae79SScott Long 	}
675763fae79SScott Long 	arrays = calloc(narrays, sizeof(*arrays));
6763c22a809SJohn Baldwin 	if (arrays == NULL) {
6773c22a809SJohn Baldwin 		warnx("malloc failed");
678375c4656SBjoern A. Zeeb 		error = ENOMEM;
679375c4656SBjoern A. Zeeb 		goto error;
6803c22a809SJohn Baldwin 	}
681763fae79SScott Long 	for (i = 0; i < narrays; i++) {
682763fae79SScott Long 		error = parse_array(fd, raid_type, av[i], &arrays[i]);
683763fae79SScott Long 		if (error)
684375c4656SBjoern A. Zeeb 			goto error;
685763fae79SScott Long 	}
686763fae79SScott Long 
687763fae79SScott Long 	switch (raid_type) {
688763fae79SScott Long 	case RT_RAID10:
689763fae79SScott Long 	case RT_RAID50:
690763fae79SScott Long 	case RT_RAID60:
691763fae79SScott Long 		for (i = 1; i < narrays; i++) {
692763fae79SScott Long 			if (arrays[i].drive_count != arrays[0].drive_count) {
693763fae79SScott Long 				warnx("All arrays must contain the same "
694763fae79SScott Long 				    "number of drives");
695375c4656SBjoern A. Zeeb 				error = EINVAL;
696375c4656SBjoern A. Zeeb 				goto error;
697763fae79SScott Long 			}
698763fae79SScott Long 		}
699763fae79SScott Long 		break;
700763fae79SScott Long 	}
701763fae79SScott Long 
702763fae79SScott Long 	/*
703763fae79SScott Long 	 * Fetch the current config and build sorted lists of existing
704763fae79SScott Long 	 * array and volume identifiers.
705763fae79SScott Long 	 */
706763fae79SScott Long 	if (mfi_config_read(fd, &config) < 0) {
707c02999d9SJohn Baldwin 		error = errno;
708763fae79SScott Long 		warn("Failed to read configuration");
709375c4656SBjoern A. Zeeb 		goto error;
710763fae79SScott Long 	}
711763fae79SScott Long 	p = (char *)config->array;
712763fae79SScott Long 	state.array_ref = 0xffff;
713763fae79SScott Long 	state.target_id = 0xff;
714763fae79SScott Long 	state.array_count = config->array_count;
715763fae79SScott Long 	if (config->array_count > 0) {
716763fae79SScott Long 		state.arrays = calloc(config->array_count, sizeof(int));
7173c22a809SJohn Baldwin 		if (state.arrays == NULL) {
7183c22a809SJohn Baldwin 			warnx("malloc failed");
719375c4656SBjoern A. Zeeb 			error = ENOMEM;
720375c4656SBjoern A. Zeeb 			goto error;
7213c22a809SJohn Baldwin 		}
722763fae79SScott Long 		for (i = 0; i < config->array_count; i++) {
723763fae79SScott Long 			ar = (struct mfi_array *)p;
724763fae79SScott Long 			state.arrays[i] = ar->array_ref;
725763fae79SScott Long 			p += config->array_size;
726763fae79SScott Long 		}
727763fae79SScott Long 		qsort(state.arrays, config->array_count, sizeof(int),
728763fae79SScott Long 		    compare_int);
729763fae79SScott Long 	} else
730763fae79SScott Long 		state.arrays = NULL;
731763fae79SScott Long 	state.log_drv_count = config->log_drv_count;
732763fae79SScott Long 	if (config->log_drv_count) {
733763fae79SScott Long 		state.volumes = calloc(config->log_drv_count, sizeof(int));
7343c22a809SJohn Baldwin 		if (state.volumes == NULL) {
7353c22a809SJohn Baldwin 			warnx("malloc failed");
736375c4656SBjoern A. Zeeb 			error = ENOMEM;
737375c4656SBjoern A. Zeeb 			goto error;
7383c22a809SJohn Baldwin 		}
739763fae79SScott Long 		for (i = 0; i < config->log_drv_count; i++) {
740763fae79SScott Long 			ld = (struct mfi_ld_config *)p;
741763fae79SScott Long 			state.volumes[i] = ld->properties.ld.v.target_id;
742763fae79SScott Long 			p += config->log_drv_size;
743763fae79SScott Long 		}
744763fae79SScott Long 		qsort(state.volumes, config->log_drv_count, sizeof(int),
745763fae79SScott Long 		    compare_int);
746763fae79SScott Long 	} else
747763fae79SScott Long 		state.volumes = NULL;
748763fae79SScott Long 	free(config);
749763fae79SScott Long 
750763fae79SScott Long 	/* Determine the size of the configuration we will build. */
751763fae79SScott Long 	switch (raid_type) {
752763fae79SScott Long 	case RT_RAID0:
753763fae79SScott Long 	case RT_RAID1:
754763fae79SScott Long 	case RT_RAID5:
755763fae79SScott Long 	case RT_RAID6:
756763fae79SScott Long 	case RT_CONCAT:
757763fae79SScott Long 	case RT_JBOD:
758763fae79SScott Long 		/* Each volume spans a single array. */
759763fae79SScott Long 		nvolumes = narrays;
760763fae79SScott Long 		break;
761763fae79SScott Long 	case RT_RAID10:
762763fae79SScott Long 	case RT_RAID50:
763763fae79SScott Long 	case RT_RAID60:
764763fae79SScott Long 		/* A single volume spans multiple arrays. */
765763fae79SScott Long 		nvolumes = 1;
766763fae79SScott Long 		break;
767763fae79SScott Long 	default:
768763fae79SScott Long 		/* Pacify gcc. */
769763fae79SScott Long 		abort();
770763fae79SScott Long 	}
771763fae79SScott Long 
772763fae79SScott Long 	config_size = sizeof(struct mfi_config_data) +
773763fae79SScott Long 	    sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays;
774763fae79SScott Long 	config = calloc(1, config_size);
7753c22a809SJohn Baldwin 	if (config == NULL) {
7763c22a809SJohn Baldwin 		warnx("malloc failed");
777375c4656SBjoern A. Zeeb 		error = ENOMEM;
778375c4656SBjoern A. Zeeb 		goto error;
7793c22a809SJohn Baldwin 	}
780763fae79SScott Long 	config->size = config_size;
781763fae79SScott Long 	config->array_count = narrays;
782763fae79SScott Long 	config->array_size = MFI_ARRAY_SIZE;	/* XXX: Firmware hardcode */
783763fae79SScott Long 	config->log_drv_count = nvolumes;
784763fae79SScott Long 	config->log_drv_size = sizeof(struct mfi_ld_config);
785763fae79SScott Long 	config->spares_count = 0;
786763fae79SScott Long 	config->spares_size = 40;		/* XXX: Firmware hardcode */
787763fae79SScott Long 	cfg_arrays = (char *)config->array;
788763fae79SScott Long 	cfg_volumes = cfg_arrays + config->array_size * narrays;
789763fae79SScott Long 
790763fae79SScott Long 	/* Build the arrays. */
791763fae79SScott Long 	for (i = 0; i < narrays; i++) {
792763fae79SScott Long 		build_array(fd, cfg_arrays, &arrays[i], &state, verbose);
793763fae79SScott Long 		cfg_arrays += config->array_size;
794763fae79SScott Long 	}
795763fae79SScott Long 
796763fae79SScott Long 	/* Now build the volume(s). */
797763fae79SScott Long 	arrays_per_volume = narrays / nvolumes;
798763fae79SScott Long 	for (i = 0; i < nvolumes; i++) {
799763fae79SScott Long 		build_volume(cfg_volumes, arrays_per_volume,
800763fae79SScott Long 		    &arrays[i * arrays_per_volume], raid_type, stripe_size,
801763fae79SScott Long 		    &state, verbose);
802763fae79SScott Long 		cfg_volumes += config->log_drv_size;
803763fae79SScott Long 	}
804763fae79SScott Long 
805763fae79SScott Long #ifdef DEBUG
806763fae79SScott Long 	if (dump)
807763fae79SScott Long 		dump_config(fd, config);
808763fae79SScott Long #endif
809763fae79SScott Long 
810763fae79SScott Long 	/* Send the new config to the controller. */
811763fae79SScott Long 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size,
812763fae79SScott Long 	    NULL, 0, NULL) < 0) {
813c02999d9SJohn Baldwin 		error = errno;
814763fae79SScott Long 		warn("Failed to add volume");
815375c4656SBjoern A. Zeeb 		/* FALLTHROUGH */
816763fae79SScott Long 	}
817763fae79SScott Long 
818375c4656SBjoern A. Zeeb error:
819763fae79SScott Long 	/* Clean up. */
820763fae79SScott Long 	free(config);
821763fae79SScott Long 	free(state.volumes);
822763fae79SScott Long 	free(state.arrays);
823763fae79SScott Long 	for (i = 0; i < narrays; i++)
824763fae79SScott Long 		free(arrays[i].drives);
825763fae79SScott Long 	free(arrays);
826763fae79SScott Long 	close(fd);
827763fae79SScott Long 
828375c4656SBjoern A. Zeeb 	return (error);
829763fae79SScott Long }
830763fae79SScott Long MFI_COMMAND(top, create, create_volume);
831763fae79SScott Long 
832763fae79SScott Long static int
833763fae79SScott Long delete_volume(int ac, char **av)
834763fae79SScott Long {
835763fae79SScott Long 	struct mfi_ld_info info;
836c02999d9SJohn Baldwin 	int error, fd;
837763fae79SScott Long 	uint8_t target_id, mbox[4];
838763fae79SScott Long 
839763fae79SScott Long 	/*
840763fae79SScott Long 	 * Backwards compat.  Map 'delete volume' to 'delete' and
841763fae79SScott Long 	 * 'delete spare' to 'remove'.
842763fae79SScott Long 	 */
843763fae79SScott Long 	if (ac > 1) {
844763fae79SScott Long 		if (strcmp(av[1], "volume") == 0) {
845763fae79SScott Long 			av++;
846763fae79SScott Long 			ac--;
847763fae79SScott Long 		} else if (strcmp(av[1], "spare") == 0) {
848763fae79SScott Long 			av++;
849763fae79SScott Long 			ac--;
850763fae79SScott Long 			return (remove_spare(ac, av));
851763fae79SScott Long 		}
852763fae79SScott Long 	}
853763fae79SScott Long 
854763fae79SScott Long 	if (ac != 2) {
855763fae79SScott Long 		warnx("delete volume: volume required");
856763fae79SScott Long 		return (EINVAL);
857763fae79SScott Long 	}
858763fae79SScott Long 
859763fae79SScott Long 	fd = mfi_open(mfi_unit);
860763fae79SScott Long 	if (fd < 0) {
861c02999d9SJohn Baldwin 		error = errno;
862763fae79SScott Long 		warn("mfi_open");
863c02999d9SJohn Baldwin 		return (error);
864763fae79SScott Long 	}
865763fae79SScott Long 
866763fae79SScott Long 	if (!mfi_reconfig_supported()) {
867763fae79SScott Long 		warnx("The current mfi(4) driver does not support "
868763fae79SScott Long 		    "configuration changes.");
869375c4656SBjoern A. Zeeb 		close(fd);
870763fae79SScott Long 		return (EOPNOTSUPP);
871763fae79SScott Long 	}
872763fae79SScott Long 
873763fae79SScott Long 	if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
874c02999d9SJohn Baldwin 		error = errno;
875763fae79SScott Long 		warn("Invalid volume %s", av[1]);
876375c4656SBjoern A. Zeeb 		close(fd);
877c02999d9SJohn Baldwin 		return (error);
878763fae79SScott Long 	}
879763fae79SScott Long 
880763fae79SScott Long 	if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
881c02999d9SJohn Baldwin 		error = errno;
882763fae79SScott Long 		warn("Failed to get info for volume %d", target_id);
883375c4656SBjoern A. Zeeb 		close(fd);
884c02999d9SJohn Baldwin 		return (error);
885763fae79SScott Long 	}
886763fae79SScott Long 
887763fae79SScott Long 	if (mfi_volume_busy(fd, target_id)) {
888763fae79SScott Long 		warnx("Volume %s is busy and cannot be deleted",
889763fae79SScott Long 		    mfi_volume_name(fd, target_id));
890375c4656SBjoern A. Zeeb 		close(fd);
891763fae79SScott Long 		return (EBUSY);
892763fae79SScott Long 	}
893763fae79SScott Long 
894763fae79SScott Long 	mbox_store_ldref(mbox, &info.ld_config.properties.ld);
895763fae79SScott Long 	if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox,
896763fae79SScott Long 	    sizeof(mbox), NULL) < 0) {
897c02999d9SJohn Baldwin 		error = errno;
898763fae79SScott Long 		warn("Failed to delete volume");
899375c4656SBjoern A. Zeeb 		close(fd);
900c02999d9SJohn Baldwin 		return (error);
901763fae79SScott Long 	}
902763fae79SScott Long 
903763fae79SScott Long 	close(fd);
904763fae79SScott Long 
905763fae79SScott Long 	return (0);
906763fae79SScott Long }
907763fae79SScott Long MFI_COMMAND(top, delete, delete_volume);
908763fae79SScott Long 
909763fae79SScott Long static int
910763fae79SScott Long add_spare(int ac, char **av)
911763fae79SScott Long {
912763fae79SScott Long 	struct mfi_pd_info info;
913763fae79SScott Long 	struct mfi_config_data *config;
914763fae79SScott Long 	struct mfi_array *ar;
915763fae79SScott Long 	struct mfi_ld_config *ld;
916763fae79SScott Long 	struct mfi_spare *spare;
917763fae79SScott Long 	uint16_t device_id;
918763fae79SScott Long 	uint8_t target_id;
919763fae79SScott Long 	char *p;
920763fae79SScott Long 	int error, fd, i;
921763fae79SScott Long 
922763fae79SScott Long 	if (ac < 2) {
923763fae79SScott Long 		warnx("add spare: drive required");
924763fae79SScott Long 		return (EINVAL);
925763fae79SScott Long 	}
926763fae79SScott Long 
927763fae79SScott Long 	fd = mfi_open(mfi_unit);
928763fae79SScott Long 	if (fd < 0) {
929c02999d9SJohn Baldwin 		error = errno;
930763fae79SScott Long 		warn("mfi_open");
931c02999d9SJohn Baldwin 		return (error);
932763fae79SScott Long 	}
933763fae79SScott Long 
934375c4656SBjoern A. Zeeb 	config = NULL;
935375c4656SBjoern A. Zeeb 	spare = NULL;
936763fae79SScott Long 	error = mfi_lookup_drive(fd, av[1], &device_id);
937763fae79SScott Long 	if (error)
938375c4656SBjoern A. Zeeb 		goto error;
939763fae79SScott Long 
940763fae79SScott Long 	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
941c02999d9SJohn Baldwin 		error = errno;
942763fae79SScott Long 		warn("Failed to fetch drive info");
943375c4656SBjoern A. Zeeb 		goto error;
944763fae79SScott Long 	}
945763fae79SScott Long 
946763fae79SScott Long 	if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
947763fae79SScott Long 		warnx("Drive %u is not available", device_id);
948375c4656SBjoern A. Zeeb 		error = EINVAL;
949375c4656SBjoern A. Zeeb 		goto error;
950763fae79SScott Long 	}
951763fae79SScott Long 
952763fae79SScott Long 	if (ac > 2) {
953763fae79SScott Long 		if (mfi_lookup_volume(fd, av[2], &target_id) < 0) {
954c02999d9SJohn Baldwin 			error = errno;
955763fae79SScott Long 			warn("Invalid volume %s", av[2]);
956375c4656SBjoern A. Zeeb 			goto error;
957763fae79SScott Long 		}
958763fae79SScott Long 	}
959763fae79SScott Long 
960763fae79SScott Long 	if (mfi_config_read(fd, &config) < 0) {
961c02999d9SJohn Baldwin 		error = errno;
962763fae79SScott Long 		warn("Failed to read configuration");
963375c4656SBjoern A. Zeeb 		goto error;
964763fae79SScott Long 	}
965763fae79SScott Long 
966763fae79SScott Long 	spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) *
967763fae79SScott Long 	    config->array_count);
9683c22a809SJohn Baldwin 	if (spare == NULL) {
9693c22a809SJohn Baldwin 		warnx("malloc failed");
970375c4656SBjoern A. Zeeb 		error = ENOMEM;
971375c4656SBjoern A. Zeeb 		goto error;
9723c22a809SJohn Baldwin 	}
973763fae79SScott Long 	bzero(spare, sizeof(struct mfi_spare));
974763fae79SScott Long 	spare->ref = info.ref;
975763fae79SScott Long 
976763fae79SScott Long 	if (ac == 2) {
977763fae79SScott Long 		/* Global spare backs all arrays. */
978763fae79SScott Long 		p = (char *)config->array;
979763fae79SScott Long 		for (i = 0; i < config->array_count; i++) {
980763fae79SScott Long 			ar = (struct mfi_array *)p;
981763fae79SScott Long 			if (ar->size > info.coerced_size) {
982763fae79SScott Long 				warnx("Spare isn't large enough for array %u",
983763fae79SScott Long 				    ar->array_ref);
984375c4656SBjoern A. Zeeb 				error = EINVAL;
985375c4656SBjoern A. Zeeb 				goto error;
986763fae79SScott Long 			}
987763fae79SScott Long 			p += config->array_size;
988763fae79SScott Long 		}
989763fae79SScott Long 		spare->array_count = 0;
990763fae79SScott Long 	} else  {
991763fae79SScott Long 		/*
992763fae79SScott Long 		 * Dedicated spares only back the arrays for a
993763fae79SScott Long 		 * specific volume.
994763fae79SScott Long 		 */
995763fae79SScott Long 		ld = mfi_config_lookup_volume(config, target_id);
996763fae79SScott Long 		if (ld == NULL) {
997763fae79SScott Long 			warnx("Did not find volume %d", target_id);
998375c4656SBjoern A. Zeeb 			error = EINVAL;
999375c4656SBjoern A. Zeeb 			goto error;
1000763fae79SScott Long 		}
1001763fae79SScott Long 
1002763fae79SScott Long 		spare->spare_type |= MFI_SPARE_DEDICATED;
1003763fae79SScott Long 		spare->array_count = ld->params.span_depth;
1004763fae79SScott Long 		for (i = 0; i < ld->params.span_depth; i++) {
1005763fae79SScott Long 			ar = mfi_config_lookup_array(config,
1006763fae79SScott Long 			    ld->span[i].array_ref);
1007763fae79SScott Long 			if (ar == NULL) {
1008763fae79SScott Long 				warnx("Missing array; inconsistent config?");
1009375c4656SBjoern A. Zeeb 				error = ENXIO;
1010375c4656SBjoern A. Zeeb 				goto error;
1011763fae79SScott Long 			}
1012763fae79SScott Long 			if (ar->size > info.coerced_size) {
1013763fae79SScott Long 				warnx("Spare isn't large enough for array %u",
1014763fae79SScott Long 				    ar->array_ref);
1015375c4656SBjoern A. Zeeb 				error = EINVAL;
1016375c4656SBjoern A. Zeeb 				goto error;
1017763fae79SScott Long 			}
1018763fae79SScott Long 			spare->array_ref[i] = ar->array_ref;
1019763fae79SScott Long 		}
1020763fae79SScott Long 	}
1021763fae79SScott Long 
1022763fae79SScott Long 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare,
1023763fae79SScott Long 	    sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count,
1024763fae79SScott Long 	    NULL, 0, NULL) < 0) {
1025c02999d9SJohn Baldwin 		error = errno;
1026763fae79SScott Long 		warn("Failed to assign spare");
1027375c4656SBjoern A. Zeeb 		/* FALLTHROUGH. */
1028763fae79SScott Long 	}
1029763fae79SScott Long 
1030375c4656SBjoern A. Zeeb error:
1031375c4656SBjoern A. Zeeb 	free(spare);
1032375c4656SBjoern A. Zeeb 	free(config);
1033763fae79SScott Long 	close(fd);
1034763fae79SScott Long 
1035375c4656SBjoern A. Zeeb 	return (error);
1036763fae79SScott Long }
1037763fae79SScott Long MFI_COMMAND(top, add, add_spare);
1038763fae79SScott Long 
1039763fae79SScott Long static int
1040763fae79SScott Long remove_spare(int ac, char **av)
1041763fae79SScott Long {
1042763fae79SScott Long 	struct mfi_pd_info info;
1043763fae79SScott Long 	int error, fd;
1044763fae79SScott Long 	uint16_t device_id;
1045763fae79SScott Long 	uint8_t mbox[4];
1046763fae79SScott Long 
1047763fae79SScott Long 	if (ac != 2) {
1048763fae79SScott Long 		warnx("remove spare: drive required");
1049763fae79SScott Long 		return (EINVAL);
1050763fae79SScott Long 	}
1051763fae79SScott Long 
1052763fae79SScott Long 	fd = mfi_open(mfi_unit);
1053763fae79SScott Long 	if (fd < 0) {
1054c02999d9SJohn Baldwin 		error = errno;
1055763fae79SScott Long 		warn("mfi_open");
1056c02999d9SJohn Baldwin 		return (error);
1057763fae79SScott Long 	}
1058763fae79SScott Long 
1059763fae79SScott Long 	error = mfi_lookup_drive(fd, av[1], &device_id);
1060375c4656SBjoern A. Zeeb 	if (error) {
1061375c4656SBjoern A. Zeeb 		close(fd);
1062763fae79SScott Long 		return (error);
1063375c4656SBjoern A. Zeeb 	}
1064763fae79SScott Long 
1065763fae79SScott Long 	/* Get the info for this drive. */
1066763fae79SScott Long 	if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
1067c02999d9SJohn Baldwin 		error = errno;
1068763fae79SScott Long 		warn("Failed to fetch info for drive %u", device_id);
1069375c4656SBjoern A. Zeeb 		close(fd);
1070c02999d9SJohn Baldwin 		return (error);
1071763fae79SScott Long 	}
1072763fae79SScott Long 
1073763fae79SScott Long 	if (info.fw_state != MFI_PD_STATE_HOT_SPARE) {
1074763fae79SScott Long 		warnx("Drive %u is not a hot spare", device_id);
1075375c4656SBjoern A. Zeeb 		close(fd);
1076763fae79SScott Long 		return (EINVAL);
1077763fae79SScott Long 	}
1078763fae79SScott Long 
1079763fae79SScott Long 	mbox_store_pdref(mbox, &info.ref);
1080763fae79SScott Long 	if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox,
1081763fae79SScott Long 	    sizeof(mbox), NULL) < 0) {
1082c02999d9SJohn Baldwin 		error = errno;
1083763fae79SScott Long 		warn("Failed to delete spare");
1084375c4656SBjoern A. Zeeb 		close(fd);
1085c02999d9SJohn Baldwin 		return (error);
1086763fae79SScott Long 	}
1087763fae79SScott Long 
1088763fae79SScott Long 	close(fd);
1089763fae79SScott Long 
1090763fae79SScott Long 	return (0);
1091763fae79SScott Long }
1092763fae79SScott Long MFI_COMMAND(top, remove, remove_spare);
1093763fae79SScott Long 
1094763fae79SScott Long #ifdef DEBUG
1095763fae79SScott Long /* Display raw data about a config. */
1096763fae79SScott Long static void
1097763fae79SScott Long dump_config(int fd, struct mfi_config_data *config)
1098763fae79SScott Long {
1099763fae79SScott Long 	struct mfi_array *ar;
1100763fae79SScott Long 	struct mfi_ld_config *ld;
1101763fae79SScott Long 	struct mfi_spare *sp;
1102763fae79SScott Long 	struct mfi_pd_info pinfo;
1103763fae79SScott Long 	uint16_t device_id;
1104763fae79SScott Long 	char *p;
1105763fae79SScott Long 	int i, j;
1106763fae79SScott Long 
1107763fae79SScott Long 	printf(
1108763fae79SScott Long 	    "mfi%d Configuration (Debug): %d arrays, %d volumes, %d spares\n",
1109763fae79SScott Long 	    mfi_unit, config->array_count, config->log_drv_count,
1110763fae79SScott Long 	    config->spares_count);
1111763fae79SScott Long 	printf("  array size: %u\n", config->array_size);
1112763fae79SScott Long 	printf("  volume size: %u\n", config->log_drv_size);
1113763fae79SScott Long 	printf("  spare size: %u\n", config->spares_size);
1114763fae79SScott Long 	p = (char *)config->array;
1115763fae79SScott Long 
1116763fae79SScott Long 	for (i = 0; i < config->array_count; i++) {
1117763fae79SScott Long 		ar = (struct mfi_array *)p;
1118763fae79SScott Long 		printf("    array %u of %u drives:\n", ar->array_ref,
1119763fae79SScott Long 		    ar->num_drives);
1120763fae79SScott Long 		printf("      size = %ju\n", (uintmax_t)ar->size);
1121763fae79SScott Long 		for (j = 0; j < ar->num_drives; j++) {
11220c019d8cSRandi Harper 			device_id = ar->pd[j].ref.v.device_id;
1123763fae79SScott Long 			if (device_id == 0xffff)
1124763fae79SScott Long 				printf("        drive MISSING\n");
1125763fae79SScott Long 			else {
1126763fae79SScott Long 				printf("        drive %u %s\n", device_id,
1127763fae79SScott Long 				    mfi_pdstate(ar->pd[j].fw_state));
1128763fae79SScott Long 				if (mfi_pd_get_info(fd, device_id, &pinfo,
1129763fae79SScott Long 				    NULL) >= 0) {
1130763fae79SScott Long 					printf("          raw size: %ju\n",
1131763fae79SScott Long 					    (uintmax_t)pinfo.raw_size);
1132763fae79SScott Long 					printf("          non-coerced size: %ju\n",
1133763fae79SScott Long 					    (uintmax_t)pinfo.non_coerced_size);
1134763fae79SScott Long 					printf("          coerced size: %ju\n",
1135763fae79SScott Long 					    (uintmax_t)pinfo.coerced_size);
1136763fae79SScott Long 				}
1137763fae79SScott Long 			}
1138763fae79SScott Long 		}
1139763fae79SScott Long 		p += config->array_size;
1140763fae79SScott Long 	}
1141763fae79SScott Long 
1142763fae79SScott Long 	for (i = 0; i < config->log_drv_count; i++) {
1143763fae79SScott Long 		ld = (struct mfi_ld_config *)p;
1144763fae79SScott Long 		printf("    volume %s ",
1145763fae79SScott Long 		    mfi_volume_name(fd, ld->properties.ld.v.target_id));
1146763fae79SScott Long 		printf("%s %s",
1147763fae79SScott Long 		    mfi_raid_level(ld->params.primary_raid_level,
1148763fae79SScott Long 			ld->params.secondary_raid_level),
1149763fae79SScott Long 		    mfi_ldstate(ld->params.state));
1150763fae79SScott Long 		if (ld->properties.name[0] != '\0')
1151763fae79SScott Long 			printf(" <%s>", ld->properties.name);
1152763fae79SScott Long 		printf("\n");
1153763fae79SScott Long 		printf("      primary raid level: %u\n",
1154763fae79SScott Long 		    ld->params.primary_raid_level);
1155763fae79SScott Long 		printf("      raid level qualifier: %u\n",
1156763fae79SScott Long 		    ld->params.raid_level_qualifier);
1157763fae79SScott Long 		printf("      secondary raid level: %u\n",
1158763fae79SScott Long 		    ld->params.secondary_raid_level);
1159763fae79SScott Long 		printf("      stripe size: %u\n", ld->params.stripe_size);
1160763fae79SScott Long 		printf("      num drives: %u\n", ld->params.num_drives);
1161763fae79SScott Long 		printf("      init state: %u\n", ld->params.init_state);
1162763fae79SScott Long 		printf("      consistent: %u\n", ld->params.is_consistent);
1163763fae79SScott Long 		printf("      no bgi: %u\n", ld->properties.no_bgi);
1164763fae79SScott Long 		printf("      spans:\n");
1165763fae79SScott Long 		for (j = 0; j < ld->params.span_depth; j++) {
1166763fae79SScott Long 			printf("        array %u @ ", ld->span[j].array_ref);
1167763fae79SScott Long 			printf("%ju : %ju\n",
1168763fae79SScott Long 			    (uintmax_t)ld->span[j].start_block,
1169763fae79SScott Long 			    (uintmax_t)ld->span[j].num_blocks);
1170763fae79SScott Long 		}
1171763fae79SScott Long 		p += config->log_drv_size;
1172763fae79SScott Long 	}
1173763fae79SScott Long 
1174763fae79SScott Long 	for (i = 0; i < config->spares_count; i++) {
1175763fae79SScott Long 		sp = (struct mfi_spare *)p;
1176763fae79SScott Long 		printf("    %s spare %u ",
1177763fae79SScott Long 		    sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" :
11780c019d8cSRandi Harper 		    "global", sp->ref.v.device_id);
1179763fae79SScott Long 		printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE));
1180763fae79SScott Long 		printf(" backs:\n");
1181763fae79SScott Long 		for (j = 0; j < sp->array_count; j++)
1182763fae79SScott Long 			printf("        array %u\n", sp->array_ref[j]);
1183763fae79SScott Long 		p += config->spares_size;
1184763fae79SScott Long 	}
1185763fae79SScott Long }
1186763fae79SScott Long 
1187763fae79SScott Long static int
1188763fae79SScott Long debug_config(int ac, char **av)
1189763fae79SScott Long {
1190763fae79SScott Long 	struct mfi_config_data *config;
1191c02999d9SJohn Baldwin 	int error, fd;
1192763fae79SScott Long 
1193763fae79SScott Long 	if (ac != 1) {
1194763fae79SScott Long 		warnx("debug: extra arguments");
1195763fae79SScott Long 		return (EINVAL);
1196763fae79SScott Long 	}
1197763fae79SScott Long 
1198763fae79SScott Long 	fd = mfi_open(mfi_unit);
1199763fae79SScott Long 	if (fd < 0) {
1200c02999d9SJohn Baldwin 		error = errno;
1201763fae79SScott Long 		warn("mfi_open");
1202c02999d9SJohn Baldwin 		return (error);
1203763fae79SScott Long 	}
1204763fae79SScott Long 
1205763fae79SScott Long 	/* Get the config from the controller. */
1206763fae79SScott Long 	if (mfi_config_read(fd, &config) < 0) {
1207c02999d9SJohn Baldwin 		error = errno;
1208763fae79SScott Long 		warn("Failed to get config");
1209375c4656SBjoern A. Zeeb 		close(fd);
1210c02999d9SJohn Baldwin 		return (error);
1211763fae79SScott Long 	}
1212763fae79SScott Long 
1213763fae79SScott Long 	/* Dump out the configuration. */
1214763fae79SScott Long 	dump_config(fd, config);
1215763fae79SScott Long 	free(config);
1216763fae79SScott Long 	close(fd);
1217763fae79SScott Long 
1218763fae79SScott Long 	return (0);
1219763fae79SScott Long }
1220763fae79SScott Long MFI_COMMAND(top, debug, debug_config);
1221763fae79SScott Long 
1222763fae79SScott Long static int
1223763fae79SScott Long dump(int ac, char **av)
1224763fae79SScott Long {
1225763fae79SScott Long 	struct mfi_config_data *config;
1226763fae79SScott Long 	char buf[64];
1227763fae79SScott Long 	size_t len;
1228c02999d9SJohn Baldwin 	int error, fd;
1229763fae79SScott Long 
1230763fae79SScott Long 	if (ac != 1) {
1231763fae79SScott Long 		warnx("dump: extra arguments");
1232763fae79SScott Long 		return (EINVAL);
1233763fae79SScott Long 	}
1234763fae79SScott Long 
1235763fae79SScott Long 	fd = mfi_open(mfi_unit);
1236763fae79SScott Long 	if (fd < 0) {
1237c02999d9SJohn Baldwin 		error = errno;
1238763fae79SScott Long 		warn("mfi_open");
1239c02999d9SJohn Baldwin 		return (error);
1240763fae79SScott Long 	}
1241763fae79SScott Long 
1242763fae79SScott Long 	/* Get the stashed copy of the last dcmd from the driver. */
1243763fae79SScott Long 	snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit);
1244763fae79SScott Long 	if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) {
1245c02999d9SJohn Baldwin 		error = errno;
1246763fae79SScott Long 		warn("Failed to read debug command");
1247c02999d9SJohn Baldwin 		if (error == ENOENT)
1248c02999d9SJohn Baldwin 			error = EOPNOTSUPP;
1249375c4656SBjoern A. Zeeb 		close(fd);
1250c02999d9SJohn Baldwin 		return (error);
1251763fae79SScott Long 	}
1252763fae79SScott Long 
1253763fae79SScott Long 	config = malloc(len);
12543c22a809SJohn Baldwin 	if (config == NULL) {
12553c22a809SJohn Baldwin 		warnx("malloc failed");
1256375c4656SBjoern A. Zeeb 		close(fd);
12573c22a809SJohn Baldwin 		return (ENOMEM);
12583c22a809SJohn Baldwin 	}
1259763fae79SScott Long 	if (sysctlbyname(buf, config, &len, NULL, 0) < 0) {
1260c02999d9SJohn Baldwin 		error = errno;
1261763fae79SScott Long 		warn("Failed to read debug command");
1262375c4656SBjoern A. Zeeb 		free(config);
1263375c4656SBjoern A. Zeeb 		close(fd);
1264c02999d9SJohn Baldwin 		return (error);
1265763fae79SScott Long 	}
1266763fae79SScott Long 	dump_config(fd, config);
1267763fae79SScott Long 	free(config);
1268763fae79SScott Long 	close(fd);
1269763fae79SScott Long 
1270763fae79SScott Long 	return (0);
1271763fae79SScott Long }
1272763fae79SScott Long MFI_COMMAND(top, dump, dump);
1273763fae79SScott Long #endif
1274