1763fae79SScott Long /*-
21de7b4b8SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
31de7b4b8SPedro F. Giffuni *
4763fae79SScott Long * Copyright (c) 2008, 2009 Yahoo!, Inc.
5763fae79SScott Long * All rights reserved.
6763fae79SScott Long *
7763fae79SScott Long * Redistribution and use in source and binary forms, with or without
8763fae79SScott Long * modification, are permitted provided that the following conditions
9763fae79SScott Long * are met:
10763fae79SScott Long * 1. Redistributions of source code must retain the above copyright
11763fae79SScott Long * notice, this list of conditions and the following disclaimer.
12763fae79SScott Long * 2. Redistributions in binary form must reproduce the above copyright
13763fae79SScott Long * notice, this list of conditions and the following disclaimer in the
14763fae79SScott Long * documentation and/or other materials provided with the distribution.
15763fae79SScott Long * 3. The names of the authors may not be used to endorse or promote
16763fae79SScott Long * products derived from this software without specific prior written
17763fae79SScott Long * permission.
18763fae79SScott Long *
19763fae79SScott Long * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20763fae79SScott Long * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21763fae79SScott Long * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22763fae79SScott Long * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23763fae79SScott Long * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24763fae79SScott Long * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25763fae79SScott Long * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26763fae79SScott Long * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27763fae79SScott Long * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28763fae79SScott Long * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29763fae79SScott Long * SUCH DAMAGE.
30763fae79SScott Long */
31763fae79SScott Long
32c02999d9SJohn Baldwin #include <sys/param.h>
33763fae79SScott Long #ifdef DEBUG
34763fae79SScott Long #include <sys/sysctl.h>
35763fae79SScott Long #endif
36*7e0f8b79SDoug Ambrisko #include <ctype.h>
37763fae79SScott Long #include <err.h>
38c02999d9SJohn Baldwin #include <errno.h>
39bf4ec4dfSEitan Adler #include <fcntl.h>
40763fae79SScott Long #include <libutil.h>
41763fae79SScott Long #include <stdint.h>
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 static int add_spare(int ac, char **av);
49763fae79SScott Long static int remove_spare(int ac, char **av);
50763fae79SScott Long
51763fae79SScott Long static long
dehumanize(const char * value)52763fae79SScott Long dehumanize(const char *value)
53763fae79SScott Long {
54763fae79SScott Long char *vtp;
55763fae79SScott Long long iv;
56763fae79SScott Long
57763fae79SScott Long if (value == NULL)
58763fae79SScott Long return (0);
59763fae79SScott Long iv = strtoq(value, &vtp, 0);
60763fae79SScott Long if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
61763fae79SScott Long return (0);
62763fae79SScott Long }
63763fae79SScott Long switch (vtp[0]) {
64763fae79SScott Long case 't': case 'T':
65763fae79SScott Long iv *= 1024;
66763fae79SScott Long case 'g': case 'G':
67763fae79SScott Long iv *= 1024;
68763fae79SScott Long case 'm': case 'M':
69763fae79SScott Long iv *= 1024;
70763fae79SScott Long case 'k': case 'K':
71763fae79SScott Long iv *= 1024;
72763fae79SScott Long case '\0':
73763fae79SScott Long break;
74763fae79SScott Long default:
75763fae79SScott Long return (0);
76763fae79SScott Long }
77763fae79SScott Long return (iv);
78763fae79SScott Long }
7906f1884fSSean Bruno
80763fae79SScott Long int
mfi_config_read(int fd,struct mfi_config_data ** configp)81763fae79SScott Long mfi_config_read(int fd, struct mfi_config_data **configp)
82763fae79SScott Long {
8306f1884fSSean Bruno return mfi_config_read_opcode(fd, MFI_DCMD_CFG_READ, configp, NULL, 0);
8406f1884fSSean Bruno }
8506f1884fSSean Bruno
8606f1884fSSean Bruno int
mfi_config_read_opcode(int fd,uint32_t opcode,struct mfi_config_data ** configp,uint8_t * mbox,size_t mboxlen)8706f1884fSSean Bruno mfi_config_read_opcode(int fd, uint32_t opcode, struct mfi_config_data **configp,
8806f1884fSSean Bruno uint8_t *mbox, size_t mboxlen)
8906f1884fSSean Bruno {
90763fae79SScott Long struct mfi_config_data *config;
91763fae79SScott Long uint32_t config_size;
92375c4656SBjoern A. Zeeb int error;
93763fae79SScott Long
94763fae79SScott Long /*
95763fae79SScott Long * Keep fetching the config in a loop until we have a large enough
96763fae79SScott Long * buffer to hold the entire configuration.
97763fae79SScott Long */
98763fae79SScott Long config = NULL;
99763fae79SScott Long config_size = 1024;
100763fae79SScott Long fetch:
101763fae79SScott Long config = reallocf(config, config_size);
102763fae79SScott Long if (config == NULL)
103763fae79SScott Long return (-1);
10406f1884fSSean Bruno if (mfi_dcmd_command(fd, opcode, config,
10506f1884fSSean Bruno config_size, mbox, mboxlen, NULL) < 0) {
106375c4656SBjoern A. Zeeb error = errno;
107375c4656SBjoern A. Zeeb free(config);
108375c4656SBjoern A. Zeeb errno = error;
109763fae79SScott Long return (-1);
110375c4656SBjoern A. Zeeb }
111763fae79SScott Long
112763fae79SScott Long if (config->size > config_size) {
113763fae79SScott Long config_size = config->size;
114763fae79SScott Long goto fetch;
115763fae79SScott Long }
116763fae79SScott Long
117763fae79SScott Long *configp = config;
118763fae79SScott Long return (0);
119763fae79SScott Long }
120763fae79SScott Long
121763fae79SScott Long static struct mfi_array *
mfi_config_lookup_array(struct mfi_config_data * config,uint16_t array_ref)122763fae79SScott Long mfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
123763fae79SScott Long {
124763fae79SScott Long struct mfi_array *ar;
125763fae79SScott Long char *p;
126763fae79SScott Long int i;
127763fae79SScott Long
128763fae79SScott Long p = (char *)config->array;
129763fae79SScott Long for (i = 0; i < config->array_count; i++) {
130763fae79SScott Long ar = (struct mfi_array *)p;
131763fae79SScott Long if (ar->array_ref == array_ref)
132763fae79SScott Long return (ar);
133763fae79SScott Long p += config->array_size;
134763fae79SScott Long }
135763fae79SScott Long
136763fae79SScott Long return (NULL);
137763fae79SScott Long }
138763fae79SScott Long
139763fae79SScott Long static struct mfi_ld_config *
mfi_config_lookup_volume(struct mfi_config_data * config,uint8_t target_id)140763fae79SScott Long mfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
141763fae79SScott Long {
142763fae79SScott Long struct mfi_ld_config *ld;
143763fae79SScott Long char *p;
144763fae79SScott Long int i;
145763fae79SScott Long
146763fae79SScott Long p = (char *)config->array + config->array_count * config->array_size;
147763fae79SScott Long for (i = 0; i < config->log_drv_count; i++) {
148763fae79SScott Long ld = (struct mfi_ld_config *)p;
149763fae79SScott Long if (ld->properties.ld.v.target_id == target_id)
150763fae79SScott Long return (ld);
151763fae79SScott Long p += config->log_drv_size;
152763fae79SScott Long }
153763fae79SScott Long
154763fae79SScott Long return (NULL);
155763fae79SScott Long }
156763fae79SScott Long
157763fae79SScott Long static int
clear_config(int ac __unused,char ** av __unused)15841b8cbdaSEitan Adler clear_config(int ac __unused, char **av __unused)
159763fae79SScott Long {
160763fae79SScott Long struct mfi_ld_list list;
161c02999d9SJohn Baldwin int ch, error, fd;
162763fae79SScott Long u_int i;
163763fae79SScott Long
164*7e0f8b79SDoug Ambrisko fd = mfi_open(mfi_device, O_RDWR);
165763fae79SScott Long if (fd < 0) {
166c02999d9SJohn Baldwin error = errno;
167763fae79SScott Long warn("mfi_open");
168c02999d9SJohn Baldwin return (error);
169763fae79SScott Long }
170763fae79SScott Long
171*7e0f8b79SDoug Ambrisko if (!mfi_reconfig_supported(mfi_device)) {
172*7e0f8b79SDoug Ambrisko warnx("The current %s driver does not support "
173*7e0f8b79SDoug Ambrisko "configuration changes.", mfi_device);
174375c4656SBjoern A. Zeeb close(fd);
175763fae79SScott Long return (EOPNOTSUPP);
176763fae79SScott Long }
177763fae79SScott Long
178763fae79SScott Long if (mfi_ld_get_list(fd, &list, NULL) < 0) {
179c02999d9SJohn Baldwin error = errno;
180763fae79SScott Long warn("Failed to get volume list");
181375c4656SBjoern A. Zeeb close(fd);
182c02999d9SJohn Baldwin return (error);
183763fae79SScott Long }
184763fae79SScott Long
185763fae79SScott Long for (i = 0; i < list.ld_count; i++) {
186763fae79SScott Long if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) {
187763fae79SScott Long warnx("Volume %s is busy and cannot be deleted",
188763fae79SScott Long mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
189375c4656SBjoern A. Zeeb close(fd);
190763fae79SScott Long return (EBUSY);
191763fae79SScott Long }
192763fae79SScott Long }
193763fae79SScott Long
194763fae79SScott Long printf(
195*7e0f8b79SDoug Ambrisko "Are you sure you wish to clear the configuration on %s? [y/N] ",
196*7e0f8b79SDoug Ambrisko mfi_device);
197763fae79SScott Long ch = getchar();
198763fae79SScott Long if (ch != 'y' && ch != 'Y') {
199763fae79SScott Long printf("\nAborting\n");
200375c4656SBjoern A. Zeeb close(fd);
201763fae79SScott Long return (0);
202763fae79SScott Long }
203763fae79SScott Long
204763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) {
205c02999d9SJohn Baldwin error = errno;
206763fae79SScott Long warn("Failed to clear configuration");
207375c4656SBjoern A. Zeeb close(fd);
208c02999d9SJohn Baldwin return (error);
209763fae79SScott Long }
210763fae79SScott Long
211*7e0f8b79SDoug Ambrisko printf("%s: Configuration cleared\n", mfi_device);
212763fae79SScott Long close(fd);
213763fae79SScott Long
214763fae79SScott Long return (0);
215763fae79SScott Long }
216763fae79SScott Long MFI_COMMAND(top, clear, clear_config);
217763fae79SScott Long
2181d8f043aSDoug Ambrisko #define MAX_DRIVES_PER_ARRAY MFI_MAX_ROW_SIZE
2191d8f043aSDoug Ambrisko #define MFI_ARRAY_SIZE sizeof(struct mfi_array)
220763fae79SScott Long
221763fae79SScott Long #define RT_RAID0 0
222763fae79SScott Long #define RT_RAID1 1
223763fae79SScott Long #define RT_RAID5 2
224763fae79SScott Long #define RT_RAID6 3
225763fae79SScott Long #define RT_JBOD 4
226763fae79SScott Long #define RT_CONCAT 5
227763fae79SScott Long #define RT_RAID10 6
228763fae79SScott Long #define RT_RAID50 7
229763fae79SScott Long #define RT_RAID60 8
230763fae79SScott Long
231763fae79SScott Long static int
compare_int(const void * one,const void * two)232763fae79SScott Long compare_int(const void *one, const void *two)
233763fae79SScott Long {
234763fae79SScott Long int first, second;
235763fae79SScott Long
236763fae79SScott Long first = *(const int *)one;
237763fae79SScott Long second = *(const int *)two;
238763fae79SScott Long
239763fae79SScott Long return (first - second);
240763fae79SScott Long }
241763fae79SScott Long
242763fae79SScott Long static struct raid_type_entry {
243763fae79SScott Long const char *name;
244763fae79SScott Long int raid_type;
245763fae79SScott Long } raid_type_table[] = {
246763fae79SScott Long { "raid0", RT_RAID0 },
247763fae79SScott Long { "raid-0", RT_RAID0 },
248763fae79SScott Long { "raid1", RT_RAID1 },
249763fae79SScott Long { "raid-1", RT_RAID1 },
250763fae79SScott Long { "mirror", RT_RAID1 },
251763fae79SScott Long { "raid5", RT_RAID5 },
252763fae79SScott Long { "raid-5", RT_RAID5 },
253763fae79SScott Long { "raid6", RT_RAID6 },
254763fae79SScott Long { "raid-6", RT_RAID6 },
255763fae79SScott Long { "jbod", RT_JBOD },
256763fae79SScott Long { "concat", RT_CONCAT },
257763fae79SScott Long { "raid10", RT_RAID10 },
258763fae79SScott Long { "raid1+0", RT_RAID10 },
259763fae79SScott Long { "raid-10", RT_RAID10 },
260763fae79SScott Long { "raid-1+0", RT_RAID10 },
261763fae79SScott Long { "raid50", RT_RAID50 },
262763fae79SScott Long { "raid5+0", RT_RAID50 },
263763fae79SScott Long { "raid-50", RT_RAID50 },
264763fae79SScott Long { "raid-5+0", RT_RAID50 },
265763fae79SScott Long { "raid60", RT_RAID60 },
266763fae79SScott Long { "raid6+0", RT_RAID60 },
267763fae79SScott Long { "raid-60", RT_RAID60 },
268763fae79SScott Long { "raid-6+0", RT_RAID60 },
269763fae79SScott Long { NULL, 0 },
270763fae79SScott Long };
271763fae79SScott Long
272763fae79SScott Long struct config_id_state {
273763fae79SScott Long int array_count;
274763fae79SScott Long int log_drv_count;
275763fae79SScott Long int *arrays;
276763fae79SScott Long int *volumes;
277763fae79SScott Long uint16_t array_ref;
278763fae79SScott Long uint8_t target_id;
279763fae79SScott Long };
280763fae79SScott Long
281763fae79SScott Long struct array_info {
282763fae79SScott Long int drive_count;
283763fae79SScott Long struct mfi_pd_info *drives;
284763fae79SScott Long struct mfi_array *array;
285763fae79SScott Long };
286763fae79SScott Long
287763fae79SScott Long /* Parse a comma-separated list of drives for an array. */
288763fae79SScott Long static int
parse_array(int fd,int raid_type,char * array_str,struct array_info * info)289763fae79SScott Long parse_array(int fd, int raid_type, char *array_str, struct array_info *info)
290763fae79SScott Long {
291763fae79SScott Long struct mfi_pd_info *pinfo;
292763fae79SScott Long uint16_t device_id;
293763fae79SScott Long char *cp;
294763fae79SScott Long u_int count;
295763fae79SScott Long int error;
296763fae79SScott Long
297763fae79SScott Long cp = array_str;
298763fae79SScott Long for (count = 0; cp != NULL; count++) {
299763fae79SScott Long cp = strchr(cp, ',');
300763fae79SScott Long if (cp != NULL) {
301763fae79SScott Long cp++;
302763fae79SScott Long if (*cp == ',') {
303763fae79SScott Long warnx("Invalid drive list '%s'", array_str);
304763fae79SScott Long return (EINVAL);
305763fae79SScott Long }
306763fae79SScott Long }
307763fae79SScott Long }
308763fae79SScott Long
309763fae79SScott Long /* Validate the number of drives for this array. */
310763fae79SScott Long if (count >= MAX_DRIVES_PER_ARRAY) {
3111d8f043aSDoug Ambrisko warnx("Too many drives for a single array: max is %d",
312763fae79SScott Long MAX_DRIVES_PER_ARRAY);
313763fae79SScott Long return (EINVAL);
314763fae79SScott Long }
315763fae79SScott Long switch (raid_type) {
316763fae79SScott Long case RT_RAID1:
317763fae79SScott Long case RT_RAID10:
318763fae79SScott Long if (count % 2 != 0) {
319763fae79SScott Long warnx("RAID1 and RAID10 require an even number of "
320763fae79SScott Long "drives in each array");
321763fae79SScott Long return (EINVAL);
322763fae79SScott Long }
323763fae79SScott Long break;
324763fae79SScott Long case RT_RAID5:
325763fae79SScott Long case RT_RAID50:
326763fae79SScott Long if (count < 3) {
327763fae79SScott Long warnx("RAID5 and RAID50 require at least 3 drives in "
328763fae79SScott Long "each array");
329763fae79SScott Long return (EINVAL);
330763fae79SScott Long }
331763fae79SScott Long break;
332763fae79SScott Long case RT_RAID6:
333763fae79SScott Long case RT_RAID60:
334763fae79SScott Long if (count < 4) {
335763fae79SScott Long warnx("RAID6 and RAID60 require at least 4 drives in "
336763fae79SScott Long "each array");
337763fae79SScott Long return (EINVAL);
338763fae79SScott Long }
339763fae79SScott Long break;
340763fae79SScott Long }
341763fae79SScott Long
342763fae79SScott Long /* Validate each drive. */
343763fae79SScott Long info->drives = calloc(count, sizeof(struct mfi_pd_info));
3443c22a809SJohn Baldwin if (info->drives == NULL) {
3453c22a809SJohn Baldwin warnx("malloc failed");
3463c22a809SJohn Baldwin return (ENOMEM);
3473c22a809SJohn Baldwin }
348763fae79SScott Long info->drive_count = count;
349763fae79SScott Long for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL;
350763fae79SScott Long pinfo++) {
351763fae79SScott Long error = mfi_lookup_drive(fd, cp, &device_id);
352375c4656SBjoern A. Zeeb if (error) {
353375c4656SBjoern A. Zeeb free(info->drives);
354f1e7af36SEd Maste info->drives = NULL;
355763fae79SScott Long return (error);
356375c4656SBjoern A. Zeeb }
357763fae79SScott Long
358763fae79SScott Long if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) {
359c02999d9SJohn Baldwin error = errno;
360763fae79SScott Long warn("Failed to fetch drive info for drive %s", cp);
361375c4656SBjoern A. Zeeb free(info->drives);
362f1e7af36SEd Maste info->drives = NULL;
363c02999d9SJohn Baldwin return (error);
364763fae79SScott Long }
365763fae79SScott Long
366763fae79SScott Long if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
367763fae79SScott Long warnx("Drive %u is not available", device_id);
368375c4656SBjoern A. Zeeb free(info->drives);
369f1e7af36SEd Maste info->drives = NULL;
370763fae79SScott Long return (EINVAL);
371763fae79SScott Long }
37206f1884fSSean Bruno
37306f1884fSSean Bruno if (pinfo->state.ddf.v.pd_type.is_foreign) {
37406f1884fSSean Bruno warnx("Drive %u is foreign", device_id);
37506f1884fSSean Bruno free(info->drives);
37606f1884fSSean Bruno info->drives = NULL;
37706f1884fSSean Bruno return (EINVAL);
37806f1884fSSean Bruno }
379763fae79SScott Long }
380763fae79SScott Long
381763fae79SScott Long return (0);
382763fae79SScott Long }
383763fae79SScott Long
384763fae79SScott Long /*
385763fae79SScott Long * Find the next free array ref assuming that 'array_ref' is the last
386763fae79SScott Long * one used. 'array_ref' should be 0xffff for the initial test.
387763fae79SScott Long */
388763fae79SScott Long static uint16_t
find_next_array(struct config_id_state * state)389763fae79SScott Long find_next_array(struct config_id_state *state)
390763fae79SScott Long {
391763fae79SScott Long int i;
392763fae79SScott Long
393763fae79SScott Long /* Assume the current one is used. */
394763fae79SScott Long state->array_ref++;
395763fae79SScott Long
396763fae79SScott Long /* Find the next free one. */
397763fae79SScott Long for (i = 0; i < state->array_count; i++)
398763fae79SScott Long if (state->arrays[i] == state->array_ref)
399763fae79SScott Long state->array_ref++;
400763fae79SScott Long return (state->array_ref);
401763fae79SScott Long }
402763fae79SScott Long
403763fae79SScott Long /*
404763fae79SScott Long * Find the next free volume ID assuming that 'target_id' is the last
405763fae79SScott Long * one used. 'target_id' should be 0xff for the initial test.
406763fae79SScott Long */
407763fae79SScott Long static uint8_t
find_next_volume(struct config_id_state * state)408763fae79SScott Long find_next_volume(struct config_id_state *state)
409763fae79SScott Long {
410763fae79SScott Long int i;
411763fae79SScott Long
412763fae79SScott Long /* Assume the current one is used. */
413763fae79SScott Long state->target_id++;
414763fae79SScott Long
415763fae79SScott Long /* Find the next free one. */
416763fae79SScott Long for (i = 0; i < state->log_drv_count; i++)
417763fae79SScott Long if (state->volumes[i] == state->target_id)
418763fae79SScott Long state->target_id++;
419763fae79SScott Long return (state->target_id);
420763fae79SScott Long }
421763fae79SScott Long
422763fae79SScott Long /* Populate an array with drives. */
423763fae79SScott Long static void
build_array(int fd __unused,char * arrayp,struct array_info * array_info,struct config_id_state * state,int verbose)42441b8cbdaSEitan Adler build_array(int fd __unused, char *arrayp, struct array_info *array_info,
425763fae79SScott Long struct config_id_state *state, int verbose)
426763fae79SScott Long {
427763fae79SScott Long struct mfi_array *ar = (struct mfi_array *)arrayp;
428763fae79SScott Long int i;
429763fae79SScott Long
430763fae79SScott Long ar->size = array_info->drives[0].coerced_size;
431763fae79SScott Long ar->num_drives = array_info->drive_count;
432763fae79SScott Long ar->array_ref = find_next_array(state);
433763fae79SScott Long for (i = 0; i < array_info->drive_count; i++) {
434763fae79SScott Long if (verbose)
4357bbae305SBjoern A. Zeeb printf("Adding drive %s to array %u\n",
4367bbae305SBjoern A. Zeeb mfi_drive_name(NULL,
437763fae79SScott Long array_info->drives[i].ref.v.device_id,
4387bbae305SBjoern A. Zeeb MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS),
439763fae79SScott Long ar->array_ref);
440763fae79SScott Long if (ar->size > array_info->drives[i].coerced_size)
441763fae79SScott Long ar->size = array_info->drives[i].coerced_size;
442763fae79SScott Long ar->pd[i].ref = array_info->drives[i].ref;
443763fae79SScott Long ar->pd[i].fw_state = MFI_PD_STATE_ONLINE;
444763fae79SScott Long }
445763fae79SScott Long array_info->array = ar;
446763fae79SScott Long }
447763fae79SScott Long
448763fae79SScott Long /*
449763fae79SScott Long * Create a volume that spans one or more arrays.
450763fae79SScott Long */
451763fae79SScott Long static void
build_volume(char * volumep,int narrays,struct array_info * arrays,int raid_type,long stripe_size,struct config_id_state * state,int verbose)452763fae79SScott Long build_volume(char *volumep, int narrays, struct array_info *arrays,
453763fae79SScott Long int raid_type, long stripe_size, struct config_id_state *state, int verbose)
454763fae79SScott Long {
455763fae79SScott Long struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
456763fae79SScott Long struct mfi_array *ar;
457763fae79SScott Long int i;
458763fae79SScott Long
459763fae79SScott Long /* properties */
460763fae79SScott Long ld->properties.ld.v.target_id = find_next_volume(state);
461763fae79SScott Long ld->properties.ld.v.seq = 0;
462763fae79SScott Long ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
463763fae79SScott Long MR_LD_CACHE_WRITE_BACK;
464763fae79SScott Long ld->properties.access_policy = MFI_LD_ACCESS_RW;
465763fae79SScott Long ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED;
466763fae79SScott Long ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
467763fae79SScott Long MR_LD_CACHE_WRITE_BACK;
468763fae79SScott Long ld->properties.no_bgi = 0;
469763fae79SScott Long
470763fae79SScott Long /* params */
471763fae79SScott Long switch (raid_type) {
472763fae79SScott Long case RT_RAID0:
473763fae79SScott Long case RT_JBOD:
474763fae79SScott Long ld->params.primary_raid_level = DDF_RAID0;
475763fae79SScott Long ld->params.raid_level_qualifier = 0;
476763fae79SScott Long ld->params.secondary_raid_level = 0;
477763fae79SScott Long break;
478763fae79SScott Long case RT_RAID1:
479763fae79SScott Long ld->params.primary_raid_level = DDF_RAID1;
480763fae79SScott Long ld->params.raid_level_qualifier = 0;
481763fae79SScott Long ld->params.secondary_raid_level = 0;
482763fae79SScott Long break;
483763fae79SScott Long case RT_RAID5:
484763fae79SScott Long ld->params.primary_raid_level = DDF_RAID5;
485763fae79SScott Long ld->params.raid_level_qualifier = 3;
486763fae79SScott Long ld->params.secondary_raid_level = 0;
487763fae79SScott Long break;
488763fae79SScott Long case RT_RAID6:
489763fae79SScott Long ld->params.primary_raid_level = DDF_RAID6;
490763fae79SScott Long ld->params.raid_level_qualifier = 3;
491763fae79SScott Long ld->params.secondary_raid_level = 0;
492763fae79SScott Long break;
493763fae79SScott Long case RT_CONCAT:
494763fae79SScott Long ld->params.primary_raid_level = DDF_CONCAT;
495763fae79SScott Long ld->params.raid_level_qualifier = 0;
496763fae79SScott Long ld->params.secondary_raid_level = 0;
497763fae79SScott Long break;
498763fae79SScott Long case RT_RAID10:
499763fae79SScott Long ld->params.primary_raid_level = DDF_RAID1;
500763fae79SScott Long ld->params.raid_level_qualifier = 0;
501763fae79SScott Long ld->params.secondary_raid_level = 3; /* XXX? */
502763fae79SScott Long break;
503763fae79SScott Long case RT_RAID50:
504763fae79SScott Long /*
505763fae79SScott Long * XXX: This appears to work though the card's BIOS
506763fae79SScott Long * complains that the configuration is foreign. The
507763fae79SScott Long * BIOS setup does not allow for creation of RAID-50
508763fae79SScott Long * or RAID-60 arrays. The only nested array
509763fae79SScott Long * configuration it allows for is RAID-10.
510763fae79SScott Long */
511763fae79SScott Long ld->params.primary_raid_level = DDF_RAID5;
512763fae79SScott Long ld->params.raid_level_qualifier = 3;
513763fae79SScott Long ld->params.secondary_raid_level = 3; /* XXX? */
514763fae79SScott Long break;
515763fae79SScott Long case RT_RAID60:
516763fae79SScott Long ld->params.primary_raid_level = DDF_RAID6;
517763fae79SScott Long ld->params.raid_level_qualifier = 3;
518763fae79SScott Long ld->params.secondary_raid_level = 3; /* XXX? */
519763fae79SScott Long break;
520763fae79SScott Long }
521763fae79SScott Long
522763fae79SScott Long /*
523763fae79SScott Long * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use
524763fae79SScott Long * ffs() to simulate log2(stripe_size).
525763fae79SScott Long */
526763fae79SScott Long ld->params.stripe_size = ffs(stripe_size) - 1 - 9;
527763fae79SScott Long ld->params.num_drives = arrays[0].array->num_drives;
528763fae79SScott Long ld->params.span_depth = narrays;
529763fae79SScott Long ld->params.state = MFI_LD_STATE_OPTIMAL;
530763fae79SScott Long ld->params.init_state = MFI_LD_PARAMS_INIT_NO;
531763fae79SScott Long ld->params.is_consistent = 0;
532763fae79SScott Long
533763fae79SScott Long /* spans */
534763fae79SScott Long for (i = 0; i < narrays; i++) {
535763fae79SScott Long ar = arrays[i].array;
536763fae79SScott Long if (verbose)
537763fae79SScott Long printf("Adding array %u to volume %u\n", ar->array_ref,
538763fae79SScott Long ld->properties.ld.v.target_id);
539763fae79SScott Long ld->span[i].start_block = 0;
540763fae79SScott Long ld->span[i].num_blocks = ar->size;
541763fae79SScott Long ld->span[i].array_ref = ar->array_ref;
542763fae79SScott Long }
543763fae79SScott Long }
544763fae79SScott Long
545763fae79SScott Long static int
create_volume(int ac,char ** av)546763fae79SScott Long create_volume(int ac, char **av)
547763fae79SScott Long {
548763fae79SScott Long struct mfi_config_data *config;
549763fae79SScott Long struct mfi_array *ar;
550763fae79SScott Long struct mfi_ld_config *ld;
551763fae79SScott Long struct config_id_state state;
552763fae79SScott Long size_t config_size;
553763fae79SScott Long char *p, *cfg_arrays, *cfg_volumes;
554763fae79SScott Long int error, fd, i, raid_type;
555763fae79SScott Long int narrays, nvolumes, arrays_per_volume;
556763fae79SScott Long struct array_info *arrays;
557763fae79SScott Long long stripe_size;
558763fae79SScott Long #ifdef DEBUG
559763fae79SScott Long int dump;
560763fae79SScott Long #endif
561763fae79SScott Long int ch, verbose;
562763fae79SScott Long
563763fae79SScott Long /*
564763fae79SScott Long * Backwards compat. Map 'create volume' to 'create' and
565763fae79SScott Long * 'create spare' to 'add'.
566763fae79SScott Long */
567763fae79SScott Long if (ac > 1) {
568763fae79SScott Long if (strcmp(av[1], "volume") == 0) {
569763fae79SScott Long av++;
570763fae79SScott Long ac--;
571763fae79SScott Long } else if (strcmp(av[1], "spare") == 0) {
572763fae79SScott Long av++;
573763fae79SScott Long ac--;
574763fae79SScott Long return (add_spare(ac, av));
575763fae79SScott Long }
576763fae79SScott Long }
577763fae79SScott Long
578763fae79SScott Long if (ac < 2) {
579763fae79SScott Long warnx("create volume: volume type required");
580763fae79SScott Long return (EINVAL);
581763fae79SScott Long }
582763fae79SScott Long
583375c4656SBjoern A. Zeeb bzero(&state, sizeof(state));
584375c4656SBjoern A. Zeeb config = NULL;
585375c4656SBjoern A. Zeeb arrays = NULL;
586375c4656SBjoern A. Zeeb narrays = 0;
587375c4656SBjoern A. Zeeb error = 0;
588763fae79SScott Long
589*7e0f8b79SDoug Ambrisko fd = mfi_open(mfi_device, O_RDWR);
590763fae79SScott Long if (fd < 0) {
591c02999d9SJohn Baldwin error = errno;
592763fae79SScott Long warn("mfi_open");
593c02999d9SJohn Baldwin return (error);
594763fae79SScott Long }
595763fae79SScott Long
596*7e0f8b79SDoug Ambrisko if (!mfi_reconfig_supported(mfi_device)) {
597*7e0f8b79SDoug Ambrisko warnx("The current %s(4) driver does not support "
598*7e0f8b79SDoug Ambrisko "configuration changes.", mfi_device);
599375c4656SBjoern A. Zeeb error = EOPNOTSUPP;
600375c4656SBjoern A. Zeeb goto error;
601763fae79SScott Long }
602763fae79SScott Long
603763fae79SScott Long /* Lookup the RAID type first. */
604763fae79SScott Long raid_type = -1;
605763fae79SScott Long for (i = 0; raid_type_table[i].name != NULL; i++)
606763fae79SScott Long if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
607763fae79SScott Long raid_type = raid_type_table[i].raid_type;
608763fae79SScott Long break;
609763fae79SScott Long }
610763fae79SScott Long
611763fae79SScott Long if (raid_type == -1) {
612763fae79SScott Long warnx("Unknown or unsupported volume type %s", av[1]);
613375c4656SBjoern A. Zeeb error = EINVAL;
614375c4656SBjoern A. Zeeb goto error;
615763fae79SScott Long }
616763fae79SScott Long
617763fae79SScott Long /* Parse any options. */
618763fae79SScott Long optind = 2;
619763fae79SScott Long #ifdef DEBUG
620763fae79SScott Long dump = 0;
621763fae79SScott Long #endif
622763fae79SScott Long verbose = 0;
623763fae79SScott Long stripe_size = 64 * 1024;
624763fae79SScott Long
625763fae79SScott Long while ((ch = getopt(ac, av, "ds:v")) != -1) {
626763fae79SScott Long switch (ch) {
627763fae79SScott Long #ifdef DEBUG
628763fae79SScott Long case 'd':
629763fae79SScott Long dump = 1;
630763fae79SScott Long break;
631763fae79SScott Long #endif
632763fae79SScott Long case 's':
633763fae79SScott Long stripe_size = dehumanize(optarg);
634763fae79SScott Long if ((stripe_size < 512) || (!powerof2(stripe_size)))
635763fae79SScott Long stripe_size = 64 * 1024;
636763fae79SScott Long break;
637763fae79SScott Long case 'v':
638763fae79SScott Long verbose = 1;
639763fae79SScott Long break;
640763fae79SScott Long case '?':
641763fae79SScott Long default:
642375c4656SBjoern A. Zeeb error = EINVAL;
643375c4656SBjoern A. Zeeb goto error;
644763fae79SScott Long }
645763fae79SScott Long }
646763fae79SScott Long ac -= optind;
647763fae79SScott Long av += optind;
648763fae79SScott Long
649763fae79SScott Long /* Parse all the arrays. */
650763fae79SScott Long narrays = ac;
651763fae79SScott Long if (narrays == 0) {
652763fae79SScott Long warnx("At least one drive list is required");
653375c4656SBjoern A. Zeeb error = EINVAL;
654375c4656SBjoern A. Zeeb goto error;
655763fae79SScott Long }
656763fae79SScott Long switch (raid_type) {
657763fae79SScott Long case RT_RAID0:
658763fae79SScott Long case RT_RAID1:
659763fae79SScott Long case RT_RAID5:
660763fae79SScott Long case RT_RAID6:
661763fae79SScott Long case RT_CONCAT:
662763fae79SScott Long if (narrays != 1) {
663763fae79SScott Long warnx("Only one drive list can be specified");
664375c4656SBjoern A. Zeeb error = EINVAL;
665375c4656SBjoern A. Zeeb goto error;
666763fae79SScott Long }
667763fae79SScott Long break;
668763fae79SScott Long case RT_RAID10:
669763fae79SScott Long case RT_RAID50:
670763fae79SScott Long case RT_RAID60:
671763fae79SScott Long if (narrays < 1) {
672763fae79SScott Long warnx("RAID10, RAID50, and RAID60 require at least "
673763fae79SScott Long "two drive lists");
674375c4656SBjoern A. Zeeb error = EINVAL;
675375c4656SBjoern A. Zeeb goto error;
676763fae79SScott Long }
677763fae79SScott Long if (narrays > MFI_MAX_SPAN_DEPTH) {
678763fae79SScott Long warnx("Volume spans more than %d arrays",
679763fae79SScott Long MFI_MAX_SPAN_DEPTH);
680375c4656SBjoern A. Zeeb error = EINVAL;
681375c4656SBjoern A. Zeeb goto error;
682763fae79SScott Long }
683763fae79SScott Long break;
684763fae79SScott Long }
685763fae79SScott Long arrays = calloc(narrays, sizeof(*arrays));
6863c22a809SJohn Baldwin if (arrays == NULL) {
6873c22a809SJohn Baldwin warnx("malloc failed");
688375c4656SBjoern A. Zeeb error = ENOMEM;
689375c4656SBjoern A. Zeeb goto error;
6903c22a809SJohn Baldwin }
691763fae79SScott Long for (i = 0; i < narrays; i++) {
692763fae79SScott Long error = parse_array(fd, raid_type, av[i], &arrays[i]);
693763fae79SScott Long if (error)
694375c4656SBjoern A. Zeeb goto error;
695763fae79SScott Long }
696763fae79SScott Long
697763fae79SScott Long switch (raid_type) {
698763fae79SScott Long case RT_RAID10:
699763fae79SScott Long case RT_RAID50:
700763fae79SScott Long case RT_RAID60:
701763fae79SScott Long for (i = 1; i < narrays; i++) {
702763fae79SScott Long if (arrays[i].drive_count != arrays[0].drive_count) {
703763fae79SScott Long warnx("All arrays must contain the same "
704763fae79SScott Long "number of drives");
705375c4656SBjoern A. Zeeb error = EINVAL;
706375c4656SBjoern A. Zeeb goto error;
707763fae79SScott Long }
708763fae79SScott Long }
709763fae79SScott Long break;
710763fae79SScott Long }
711763fae79SScott Long
712763fae79SScott Long /*
713763fae79SScott Long * Fetch the current config and build sorted lists of existing
714763fae79SScott Long * array and volume identifiers.
715763fae79SScott Long */
716763fae79SScott Long if (mfi_config_read(fd, &config) < 0) {
717c02999d9SJohn Baldwin error = errno;
718763fae79SScott Long warn("Failed to read configuration");
719375c4656SBjoern A. Zeeb goto error;
720763fae79SScott Long }
721763fae79SScott Long p = (char *)config->array;
722763fae79SScott Long state.array_ref = 0xffff;
723763fae79SScott Long state.target_id = 0xff;
724763fae79SScott Long state.array_count = config->array_count;
725763fae79SScott Long if (config->array_count > 0) {
726763fae79SScott Long state.arrays = calloc(config->array_count, sizeof(int));
7273c22a809SJohn Baldwin if (state.arrays == NULL) {
7283c22a809SJohn Baldwin warnx("malloc failed");
729375c4656SBjoern A. Zeeb error = ENOMEM;
730375c4656SBjoern A. Zeeb goto error;
7313c22a809SJohn Baldwin }
732763fae79SScott Long for (i = 0; i < config->array_count; i++) {
733763fae79SScott Long ar = (struct mfi_array *)p;
734763fae79SScott Long state.arrays[i] = ar->array_ref;
735763fae79SScott Long p += config->array_size;
736763fae79SScott Long }
737763fae79SScott Long qsort(state.arrays, config->array_count, sizeof(int),
738763fae79SScott Long compare_int);
739763fae79SScott Long } else
740763fae79SScott Long state.arrays = NULL;
741763fae79SScott Long state.log_drv_count = config->log_drv_count;
742763fae79SScott Long if (config->log_drv_count) {
743763fae79SScott Long state.volumes = calloc(config->log_drv_count, sizeof(int));
7443c22a809SJohn Baldwin if (state.volumes == NULL) {
7453c22a809SJohn Baldwin warnx("malloc failed");
746375c4656SBjoern A. Zeeb error = ENOMEM;
747375c4656SBjoern A. Zeeb goto error;
7483c22a809SJohn Baldwin }
749763fae79SScott Long for (i = 0; i < config->log_drv_count; i++) {
750763fae79SScott Long ld = (struct mfi_ld_config *)p;
751763fae79SScott Long state.volumes[i] = ld->properties.ld.v.target_id;
752763fae79SScott Long p += config->log_drv_size;
753763fae79SScott Long }
754763fae79SScott Long qsort(state.volumes, config->log_drv_count, sizeof(int),
755763fae79SScott Long compare_int);
756763fae79SScott Long } else
757763fae79SScott Long state.volumes = NULL;
758763fae79SScott Long free(config);
759763fae79SScott Long
760763fae79SScott Long /* Determine the size of the configuration we will build. */
761763fae79SScott Long switch (raid_type) {
762763fae79SScott Long case RT_RAID0:
763763fae79SScott Long case RT_RAID1:
764763fae79SScott Long case RT_RAID5:
765763fae79SScott Long case RT_RAID6:
766763fae79SScott Long case RT_CONCAT:
767763fae79SScott Long case RT_JBOD:
768763fae79SScott Long /* Each volume spans a single array. */
769763fae79SScott Long nvolumes = narrays;
770763fae79SScott Long break;
771763fae79SScott Long case RT_RAID10:
772763fae79SScott Long case RT_RAID50:
773763fae79SScott Long case RT_RAID60:
774763fae79SScott Long /* A single volume spans multiple arrays. */
775763fae79SScott Long nvolumes = 1;
776763fae79SScott Long break;
777763fae79SScott Long default:
778763fae79SScott Long /* Pacify gcc. */
779763fae79SScott Long abort();
780763fae79SScott Long }
781763fae79SScott Long
782763fae79SScott Long config_size = sizeof(struct mfi_config_data) +
783763fae79SScott Long sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays;
784763fae79SScott Long config = calloc(1, config_size);
7853c22a809SJohn Baldwin if (config == NULL) {
7863c22a809SJohn Baldwin warnx("malloc failed");
787375c4656SBjoern A. Zeeb error = ENOMEM;
788375c4656SBjoern A. Zeeb goto error;
7893c22a809SJohn Baldwin }
790763fae79SScott Long config->size = config_size;
791763fae79SScott Long config->array_count = narrays;
792763fae79SScott Long config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */
793763fae79SScott Long config->log_drv_count = nvolumes;
794763fae79SScott Long config->log_drv_size = sizeof(struct mfi_ld_config);
795763fae79SScott Long config->spares_count = 0;
796763fae79SScott Long config->spares_size = 40; /* XXX: Firmware hardcode */
797763fae79SScott Long cfg_arrays = (char *)config->array;
798763fae79SScott Long cfg_volumes = cfg_arrays + config->array_size * narrays;
799763fae79SScott Long
800763fae79SScott Long /* Build the arrays. */
801763fae79SScott Long for (i = 0; i < narrays; i++) {
802763fae79SScott Long build_array(fd, cfg_arrays, &arrays[i], &state, verbose);
803763fae79SScott Long cfg_arrays += config->array_size;
804763fae79SScott Long }
805763fae79SScott Long
806763fae79SScott Long /* Now build the volume(s). */
807763fae79SScott Long arrays_per_volume = narrays / nvolumes;
808763fae79SScott Long for (i = 0; i < nvolumes; i++) {
809763fae79SScott Long build_volume(cfg_volumes, arrays_per_volume,
810763fae79SScott Long &arrays[i * arrays_per_volume], raid_type, stripe_size,
811763fae79SScott Long &state, verbose);
812763fae79SScott Long cfg_volumes += config->log_drv_size;
813763fae79SScott Long }
814763fae79SScott Long
815763fae79SScott Long #ifdef DEBUG
816763fae79SScott Long if (dump)
81706f1884fSSean Bruno dump_config(fd, config, NULL);
818763fae79SScott Long #endif
819763fae79SScott Long
820763fae79SScott Long /* Send the new config to the controller. */
821763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size,
822763fae79SScott Long NULL, 0, NULL) < 0) {
823c02999d9SJohn Baldwin error = errno;
824763fae79SScott Long warn("Failed to add volume");
825375c4656SBjoern A. Zeeb /* FALLTHROUGH */
826763fae79SScott Long }
827763fae79SScott Long
828375c4656SBjoern A. Zeeb error:
829763fae79SScott Long /* Clean up. */
830763fae79SScott Long free(config);
831763fae79SScott Long free(state.volumes);
832763fae79SScott Long free(state.arrays);
8336dc3afaeSXin LI if (arrays != NULL) {
834763fae79SScott Long for (i = 0; i < narrays; i++)
835763fae79SScott Long free(arrays[i].drives);
836763fae79SScott Long free(arrays);
8376dc3afaeSXin LI }
838763fae79SScott Long close(fd);
839763fae79SScott Long
840375c4656SBjoern A. Zeeb return (error);
841763fae79SScott Long }
842763fae79SScott Long MFI_COMMAND(top, create, create_volume);
843763fae79SScott Long
844763fae79SScott Long static int
delete_volume(int ac,char ** av)845763fae79SScott Long delete_volume(int ac, char **av)
846763fae79SScott Long {
847763fae79SScott Long struct mfi_ld_info info;
848c02999d9SJohn Baldwin int error, fd;
849763fae79SScott Long uint8_t target_id, mbox[4];
850763fae79SScott Long
851763fae79SScott Long /*
852763fae79SScott Long * Backwards compat. Map 'delete volume' to 'delete' and
853763fae79SScott Long * 'delete spare' to 'remove'.
854763fae79SScott Long */
855763fae79SScott Long if (ac > 1) {
856763fae79SScott Long if (strcmp(av[1], "volume") == 0) {
857763fae79SScott Long av++;
858763fae79SScott Long ac--;
859763fae79SScott Long } else if (strcmp(av[1], "spare") == 0) {
860763fae79SScott Long av++;
861763fae79SScott Long ac--;
862763fae79SScott Long return (remove_spare(ac, av));
863763fae79SScott Long }
864763fae79SScott Long }
865763fae79SScott Long
866763fae79SScott Long if (ac != 2) {
867763fae79SScott Long warnx("delete volume: volume required");
868763fae79SScott Long return (EINVAL);
869763fae79SScott Long }
870763fae79SScott Long
871*7e0f8b79SDoug Ambrisko fd = mfi_open(mfi_device, O_RDWR);
872763fae79SScott Long if (fd < 0) {
873c02999d9SJohn Baldwin error = errno;
874763fae79SScott Long warn("mfi_open");
875c02999d9SJohn Baldwin return (error);
876763fae79SScott Long }
877763fae79SScott Long
878*7e0f8b79SDoug Ambrisko if (!mfi_reconfig_supported(mfi_device)) {
879*7e0f8b79SDoug Ambrisko warnx("The current %s(4) driver does not support "
880*7e0f8b79SDoug Ambrisko "configuration changes.", mfi_device);
881375c4656SBjoern A. Zeeb close(fd);
882763fae79SScott Long return (EOPNOTSUPP);
883763fae79SScott Long }
884763fae79SScott Long
885763fae79SScott Long if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
886c02999d9SJohn Baldwin error = errno;
887763fae79SScott Long warn("Invalid volume %s", av[1]);
888375c4656SBjoern A. Zeeb close(fd);
889c02999d9SJohn Baldwin return (error);
890763fae79SScott Long }
891763fae79SScott Long
892763fae79SScott Long if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
893c02999d9SJohn Baldwin error = errno;
894763fae79SScott Long warn("Failed to get info for volume %d", target_id);
895375c4656SBjoern A. Zeeb close(fd);
896c02999d9SJohn Baldwin return (error);
897763fae79SScott Long }
898763fae79SScott Long
899763fae79SScott Long if (mfi_volume_busy(fd, target_id)) {
900763fae79SScott Long warnx("Volume %s is busy and cannot be deleted",
901763fae79SScott Long mfi_volume_name(fd, target_id));
902375c4656SBjoern A. Zeeb close(fd);
903763fae79SScott Long return (EBUSY);
904763fae79SScott Long }
905763fae79SScott Long
906763fae79SScott Long mbox_store_ldref(mbox, &info.ld_config.properties.ld);
907763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox,
908763fae79SScott Long sizeof(mbox), NULL) < 0) {
909c02999d9SJohn Baldwin error = errno;
910763fae79SScott Long warn("Failed to delete volume");
911375c4656SBjoern A. Zeeb close(fd);
912c02999d9SJohn Baldwin return (error);
913763fae79SScott Long }
914763fae79SScott Long
915763fae79SScott Long close(fd);
916763fae79SScott Long
917763fae79SScott Long return (0);
918763fae79SScott Long }
919763fae79SScott Long MFI_COMMAND(top, delete, delete_volume);
920763fae79SScott Long
921763fae79SScott Long static int
add_spare(int ac,char ** av)922763fae79SScott Long add_spare(int ac, char **av)
923763fae79SScott Long {
924763fae79SScott Long struct mfi_pd_info info;
925763fae79SScott Long struct mfi_config_data *config;
926763fae79SScott Long struct mfi_array *ar;
927763fae79SScott Long struct mfi_ld_config *ld;
928763fae79SScott Long struct mfi_spare *spare;
929763fae79SScott Long uint16_t device_id;
930763fae79SScott Long uint8_t target_id;
931763fae79SScott Long char *p;
932763fae79SScott Long int error, fd, i;
933763fae79SScott Long
934763fae79SScott Long if (ac < 2) {
935763fae79SScott Long warnx("add spare: drive required");
936763fae79SScott Long return (EINVAL);
937763fae79SScott Long }
938763fae79SScott Long
939*7e0f8b79SDoug Ambrisko fd = mfi_open(mfi_device, O_RDWR);
940763fae79SScott Long if (fd < 0) {
941c02999d9SJohn Baldwin error = errno;
942763fae79SScott Long warn("mfi_open");
943c02999d9SJohn Baldwin return (error);
944763fae79SScott Long }
945763fae79SScott Long
946375c4656SBjoern A. Zeeb config = NULL;
947375c4656SBjoern A. Zeeb spare = NULL;
948763fae79SScott Long error = mfi_lookup_drive(fd, av[1], &device_id);
949763fae79SScott Long if (error)
950375c4656SBjoern A. Zeeb goto error;
951763fae79SScott Long
952763fae79SScott Long if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
953c02999d9SJohn Baldwin error = errno;
954763fae79SScott Long warn("Failed to fetch drive info");
955375c4656SBjoern A. Zeeb goto error;
956763fae79SScott Long }
957763fae79SScott Long
958763fae79SScott Long if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
959763fae79SScott Long warnx("Drive %u is not available", device_id);
960375c4656SBjoern A. Zeeb error = EINVAL;
961375c4656SBjoern A. Zeeb goto error;
962763fae79SScott Long }
963763fae79SScott Long
964763fae79SScott Long if (ac > 2) {
965763fae79SScott Long if (mfi_lookup_volume(fd, av[2], &target_id) < 0) {
966c02999d9SJohn Baldwin error = errno;
967763fae79SScott Long warn("Invalid volume %s", av[2]);
968375c4656SBjoern A. Zeeb goto error;
969763fae79SScott Long }
970763fae79SScott Long }
971763fae79SScott Long
972763fae79SScott Long if (mfi_config_read(fd, &config) < 0) {
973c02999d9SJohn Baldwin error = errno;
974763fae79SScott Long warn("Failed to read configuration");
975375c4656SBjoern A. Zeeb goto error;
976763fae79SScott Long }
977763fae79SScott Long
978763fae79SScott Long spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) *
979763fae79SScott Long config->array_count);
9803c22a809SJohn Baldwin if (spare == NULL) {
9813c22a809SJohn Baldwin warnx("malloc failed");
982375c4656SBjoern A. Zeeb error = ENOMEM;
983375c4656SBjoern A. Zeeb goto error;
9843c22a809SJohn Baldwin }
985763fae79SScott Long bzero(spare, sizeof(struct mfi_spare));
986763fae79SScott Long spare->ref = info.ref;
987763fae79SScott Long
988763fae79SScott Long if (ac == 2) {
989763fae79SScott Long /* Global spare backs all arrays. */
990763fae79SScott Long p = (char *)config->array;
991763fae79SScott Long for (i = 0; i < config->array_count; i++) {
992763fae79SScott Long ar = (struct mfi_array *)p;
993763fae79SScott Long if (ar->size > info.coerced_size) {
994763fae79SScott Long warnx("Spare isn't large enough for array %u",
995763fae79SScott Long ar->array_ref);
996375c4656SBjoern A. Zeeb error = EINVAL;
997375c4656SBjoern A. Zeeb goto error;
998763fae79SScott Long }
999763fae79SScott Long p += config->array_size;
1000763fae79SScott Long }
1001763fae79SScott Long spare->array_count = 0;
1002763fae79SScott Long } else {
1003763fae79SScott Long /*
1004763fae79SScott Long * Dedicated spares only back the arrays for a
1005763fae79SScott Long * specific volume.
1006763fae79SScott Long */
1007763fae79SScott Long ld = mfi_config_lookup_volume(config, target_id);
1008763fae79SScott Long if (ld == NULL) {
1009763fae79SScott Long warnx("Did not find volume %d", target_id);
1010375c4656SBjoern A. Zeeb error = EINVAL;
1011375c4656SBjoern A. Zeeb goto error;
1012763fae79SScott Long }
1013763fae79SScott Long
1014763fae79SScott Long spare->spare_type |= MFI_SPARE_DEDICATED;
1015763fae79SScott Long spare->array_count = ld->params.span_depth;
1016763fae79SScott Long for (i = 0; i < ld->params.span_depth; i++) {
1017763fae79SScott Long ar = mfi_config_lookup_array(config,
1018763fae79SScott Long ld->span[i].array_ref);
1019763fae79SScott Long if (ar == NULL) {
1020763fae79SScott Long warnx("Missing array; inconsistent config?");
1021375c4656SBjoern A. Zeeb error = ENXIO;
1022375c4656SBjoern A. Zeeb goto error;
1023763fae79SScott Long }
1024763fae79SScott Long if (ar->size > info.coerced_size) {
1025763fae79SScott Long warnx("Spare isn't large enough for array %u",
1026763fae79SScott Long ar->array_ref);
1027375c4656SBjoern A. Zeeb error = EINVAL;
1028375c4656SBjoern A. Zeeb goto error;
1029763fae79SScott Long }
1030763fae79SScott Long spare->array_ref[i] = ar->array_ref;
1031763fae79SScott Long }
1032763fae79SScott Long }
1033763fae79SScott Long
1034763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare,
1035763fae79SScott Long sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count,
1036763fae79SScott Long NULL, 0, NULL) < 0) {
1037c02999d9SJohn Baldwin error = errno;
1038763fae79SScott Long warn("Failed to assign spare");
1039375c4656SBjoern A. Zeeb /* FALLTHROUGH. */
1040763fae79SScott Long }
1041763fae79SScott Long
1042375c4656SBjoern A. Zeeb error:
1043375c4656SBjoern A. Zeeb free(spare);
1044375c4656SBjoern A. Zeeb free(config);
1045763fae79SScott Long close(fd);
1046763fae79SScott Long
1047375c4656SBjoern A. Zeeb return (error);
1048763fae79SScott Long }
1049763fae79SScott Long MFI_COMMAND(top, add, add_spare);
1050763fae79SScott Long
1051763fae79SScott Long static int
remove_spare(int ac,char ** av)1052763fae79SScott Long remove_spare(int ac, char **av)
1053763fae79SScott Long {
1054763fae79SScott Long struct mfi_pd_info info;
1055763fae79SScott Long int error, fd;
1056763fae79SScott Long uint16_t device_id;
1057763fae79SScott Long uint8_t mbox[4];
1058763fae79SScott Long
1059763fae79SScott Long if (ac != 2) {
1060763fae79SScott Long warnx("remove spare: drive required");
1061763fae79SScott Long return (EINVAL);
1062763fae79SScott Long }
1063763fae79SScott Long
1064*7e0f8b79SDoug Ambrisko fd = mfi_open(mfi_device, O_RDWR);
1065763fae79SScott Long if (fd < 0) {
1066c02999d9SJohn Baldwin error = errno;
1067763fae79SScott Long warn("mfi_open");
1068c02999d9SJohn Baldwin return (error);
1069763fae79SScott Long }
1070763fae79SScott Long
1071763fae79SScott Long error = mfi_lookup_drive(fd, av[1], &device_id);
1072375c4656SBjoern A. Zeeb if (error) {
1073375c4656SBjoern A. Zeeb close(fd);
1074763fae79SScott Long return (error);
1075375c4656SBjoern A. Zeeb }
1076763fae79SScott Long
1077763fae79SScott Long /* Get the info for this drive. */
1078763fae79SScott Long if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
1079c02999d9SJohn Baldwin error = errno;
1080763fae79SScott Long warn("Failed to fetch info for drive %u", device_id);
1081375c4656SBjoern A. Zeeb close(fd);
1082c02999d9SJohn Baldwin return (error);
1083763fae79SScott Long }
1084763fae79SScott Long
1085763fae79SScott Long if (info.fw_state != MFI_PD_STATE_HOT_SPARE) {
1086763fae79SScott Long warnx("Drive %u is not a hot spare", device_id);
1087375c4656SBjoern A. Zeeb close(fd);
1088763fae79SScott Long return (EINVAL);
1089763fae79SScott Long }
1090763fae79SScott Long
1091763fae79SScott Long mbox_store_pdref(mbox, &info.ref);
1092763fae79SScott Long if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox,
1093763fae79SScott Long sizeof(mbox), NULL) < 0) {
1094c02999d9SJohn Baldwin error = errno;
1095763fae79SScott Long warn("Failed to delete spare");
1096375c4656SBjoern A. Zeeb close(fd);
1097c02999d9SJohn Baldwin return (error);
1098763fae79SScott Long }
1099763fae79SScott Long
1100763fae79SScott Long close(fd);
1101763fae79SScott Long
1102763fae79SScott Long return (0);
1103763fae79SScott Long }
1104763fae79SScott Long MFI_COMMAND(top, remove, remove_spare);
1105763fae79SScott Long
1106763fae79SScott Long /* Display raw data about a config. */
110706f1884fSSean Bruno void
dump_config(int fd,struct mfi_config_data * config,const char * msg_prefix)110806f1884fSSean Bruno dump_config(int fd, struct mfi_config_data *config, const char *msg_prefix)
1109763fae79SScott Long {
1110763fae79SScott Long struct mfi_array *ar;
1111763fae79SScott Long struct mfi_ld_config *ld;
1112763fae79SScott Long struct mfi_spare *sp;
1113763fae79SScott Long struct mfi_pd_info pinfo;
1114763fae79SScott Long uint16_t device_id;
1115763fae79SScott Long char *p;
1116763fae79SScott Long int i, j;
1117763fae79SScott Long
111806f1884fSSean Bruno if (NULL == msg_prefix)
111906f1884fSSean Bruno msg_prefix = "Configuration (Debug)";
112006f1884fSSean Bruno
1121763fae79SScott Long printf(
1122*7e0f8b79SDoug Ambrisko "%s %s: %d arrays, %d volumes, %d spares\n", mfi_device,
112306f1884fSSean Bruno msg_prefix, config->array_count, config->log_drv_count,
1124763fae79SScott Long config->spares_count);
1125763fae79SScott Long printf(" array size: %u\n", config->array_size);
1126763fae79SScott Long printf(" volume size: %u\n", config->log_drv_size);
1127763fae79SScott Long printf(" spare size: %u\n", config->spares_size);
1128763fae79SScott Long p = (char *)config->array;
1129763fae79SScott Long
1130763fae79SScott Long for (i = 0; i < config->array_count; i++) {
1131763fae79SScott Long ar = (struct mfi_array *)p;
1132763fae79SScott Long printf(" array %u of %u drives:\n", ar->array_ref,
1133763fae79SScott Long ar->num_drives);
1134763fae79SScott Long printf(" size = %ju\n", (uintmax_t)ar->size);
1135763fae79SScott Long for (j = 0; j < ar->num_drives; j++) {
11360c019d8cSRandi Harper device_id = ar->pd[j].ref.v.device_id;
1137763fae79SScott Long if (device_id == 0xffff)
1138763fae79SScott Long printf(" drive MISSING\n");
1139763fae79SScott Long else {
1140763fae79SScott Long printf(" drive %u %s\n", device_id,
1141763fae79SScott Long mfi_pdstate(ar->pd[j].fw_state));
1142763fae79SScott Long if (mfi_pd_get_info(fd, device_id, &pinfo,
1143763fae79SScott Long NULL) >= 0) {
1144763fae79SScott Long printf(" raw size: %ju\n",
1145763fae79SScott Long (uintmax_t)pinfo.raw_size);
1146763fae79SScott Long printf(" non-coerced size: %ju\n",
1147763fae79SScott Long (uintmax_t)pinfo.non_coerced_size);
1148763fae79SScott Long printf(" coerced size: %ju\n",
1149763fae79SScott Long (uintmax_t)pinfo.coerced_size);
1150763fae79SScott Long }
1151763fae79SScott Long }
1152763fae79SScott Long }
1153763fae79SScott Long p += config->array_size;
1154763fae79SScott Long }
1155763fae79SScott Long
1156763fae79SScott Long for (i = 0; i < config->log_drv_count; i++) {
1157763fae79SScott Long ld = (struct mfi_ld_config *)p;
1158763fae79SScott Long printf(" volume %s ",
1159763fae79SScott Long mfi_volume_name(fd, ld->properties.ld.v.target_id));
1160763fae79SScott Long printf("%s %s",
1161763fae79SScott Long mfi_raid_level(ld->params.primary_raid_level,
1162763fae79SScott Long ld->params.secondary_raid_level),
1163763fae79SScott Long mfi_ldstate(ld->params.state));
1164763fae79SScott Long if (ld->properties.name[0] != '\0')
1165763fae79SScott Long printf(" <%s>", ld->properties.name);
1166763fae79SScott Long printf("\n");
1167763fae79SScott Long printf(" primary raid level: %u\n",
1168763fae79SScott Long ld->params.primary_raid_level);
1169763fae79SScott Long printf(" raid level qualifier: %u\n",
1170763fae79SScott Long ld->params.raid_level_qualifier);
1171763fae79SScott Long printf(" secondary raid level: %u\n",
1172763fae79SScott Long ld->params.secondary_raid_level);
1173763fae79SScott Long printf(" stripe size: %u\n", ld->params.stripe_size);
1174763fae79SScott Long printf(" num drives: %u\n", ld->params.num_drives);
1175763fae79SScott Long printf(" init state: %u\n", ld->params.init_state);
1176763fae79SScott Long printf(" consistent: %u\n", ld->params.is_consistent);
1177763fae79SScott Long printf(" no bgi: %u\n", ld->properties.no_bgi);
1178763fae79SScott Long printf(" spans:\n");
1179763fae79SScott Long for (j = 0; j < ld->params.span_depth; j++) {
1180763fae79SScott Long printf(" array %u @ ", ld->span[j].array_ref);
1181763fae79SScott Long printf("%ju : %ju\n",
1182763fae79SScott Long (uintmax_t)ld->span[j].start_block,
1183763fae79SScott Long (uintmax_t)ld->span[j].num_blocks);
1184763fae79SScott Long }
1185763fae79SScott Long p += config->log_drv_size;
1186763fae79SScott Long }
1187763fae79SScott Long
1188763fae79SScott Long for (i = 0; i < config->spares_count; i++) {
1189763fae79SScott Long sp = (struct mfi_spare *)p;
1190763fae79SScott Long printf(" %s spare %u ",
1191763fae79SScott Long sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" :
11920c019d8cSRandi Harper "global", sp->ref.v.device_id);
1193763fae79SScott Long printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE));
1194763fae79SScott Long printf(" backs:\n");
1195763fae79SScott Long for (j = 0; j < sp->array_count; j++)
1196763fae79SScott Long printf(" array %u\n", sp->array_ref[j]);
1197763fae79SScott Long p += config->spares_size;
1198763fae79SScott Long }
1199763fae79SScott Long }
1200763fae79SScott Long
120106f1884fSSean Bruno #ifdef DEBUG
1202763fae79SScott Long static int
debug_config(int ac,char ** av)1203763fae79SScott Long debug_config(int ac, char **av)
1204763fae79SScott Long {
1205763fae79SScott Long struct mfi_config_data *config;
1206c02999d9SJohn Baldwin int error, fd;
1207763fae79SScott Long
1208763fae79SScott Long if (ac != 1) {
1209763fae79SScott Long warnx("debug: extra arguments");
1210763fae79SScott Long return (EINVAL);
1211763fae79SScott Long }
1212763fae79SScott Long
1213*7e0f8b79SDoug Ambrisko fd = mfi_open(mfi_device, O_RDWR);
1214763fae79SScott Long if (fd < 0) {
1215c02999d9SJohn Baldwin error = errno;
1216763fae79SScott Long warn("mfi_open");
1217c02999d9SJohn Baldwin return (error);
1218763fae79SScott Long }
1219763fae79SScott Long
1220763fae79SScott Long /* Get the config from the controller. */
1221763fae79SScott Long if (mfi_config_read(fd, &config) < 0) {
1222c02999d9SJohn Baldwin error = errno;
1223763fae79SScott Long warn("Failed to get config");
1224375c4656SBjoern A. Zeeb close(fd);
1225c02999d9SJohn Baldwin return (error);
1226763fae79SScott Long }
1227763fae79SScott Long
1228763fae79SScott Long /* Dump out the configuration. */
122906f1884fSSean Bruno dump_config(fd, config, NULL);
1230763fae79SScott Long free(config);
1231763fae79SScott Long close(fd);
1232763fae79SScott Long
1233763fae79SScott Long return (0);
1234763fae79SScott Long }
1235763fae79SScott Long MFI_COMMAND(top, debug, debug_config);
1236763fae79SScott Long
1237763fae79SScott Long static int
dump(int ac,char ** av)1238763fae79SScott Long dump(int ac, char **av)
1239763fae79SScott Long {
1240763fae79SScott Long struct mfi_config_data *config;
1241763fae79SScott Long char buf[64];
1242763fae79SScott Long size_t len;
1243c02999d9SJohn Baldwin int error, fd;
1244763fae79SScott Long
1245763fae79SScott Long if (ac != 1) {
1246763fae79SScott Long warnx("dump: extra arguments");
1247763fae79SScott Long return (EINVAL);
1248763fae79SScott Long }
1249763fae79SScott Long
1250*7e0f8b79SDoug Ambrisko fd = mfi_open(mfi_device, O_RDWR);
1251763fae79SScott Long if (fd < 0) {
1252c02999d9SJohn Baldwin error = errno;
1253763fae79SScott Long warn("mfi_open");
1254c02999d9SJohn Baldwin return (error);
1255763fae79SScott Long }
1256763fae79SScott Long
1257763fae79SScott Long /* Get the stashed copy of the last dcmd from the driver. */
1258763fae79SScott Long snprintf(buf, sizeof(buf), "dev.mfi.%d.debug_command", mfi_unit);
1259763fae79SScott Long if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) {
1260c02999d9SJohn Baldwin error = errno;
1261763fae79SScott Long warn("Failed to read debug command");
1262c02999d9SJohn Baldwin if (error == ENOENT)
1263c02999d9SJohn Baldwin error = EOPNOTSUPP;
1264375c4656SBjoern A. Zeeb close(fd);
1265c02999d9SJohn Baldwin return (error);
1266763fae79SScott Long }
1267763fae79SScott Long
1268763fae79SScott Long config = malloc(len);
12693c22a809SJohn Baldwin if (config == NULL) {
12703c22a809SJohn Baldwin warnx("malloc failed");
1271375c4656SBjoern A. Zeeb close(fd);
12723c22a809SJohn Baldwin return (ENOMEM);
12733c22a809SJohn Baldwin }
1274763fae79SScott Long if (sysctlbyname(buf, config, &len, NULL, 0) < 0) {
1275c02999d9SJohn Baldwin error = errno;
1276763fae79SScott Long warn("Failed to read debug command");
1277375c4656SBjoern A. Zeeb free(config);
1278375c4656SBjoern A. Zeeb close(fd);
1279c02999d9SJohn Baldwin return (error);
1280763fae79SScott Long }
128106f1884fSSean Bruno dump_config(fd, config, NULL);
1282763fae79SScott Long free(config);
1283763fae79SScott Long close(fd);
1284763fae79SScott Long
1285763fae79SScott Long return (0);
1286763fae79SScott Long }
1287763fae79SScott Long MFI_COMMAND(top, dump, dump);
1288763fae79SScott Long #endif
1289