mfiutil(8): Correct sysctl name.
[dragonfly.git] / usr.sbin / mfiutil / mfi_config.c
blobbb7b68b32df4ee3d10b236a8f238c386ffa598ef
1 /*-
2 * Copyright (c) 2008, 2009 Yahoo!, Inc.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The names of the authors may not be used to endorse or promote
14 * products derived from this software without specific prior written
15 * permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
29 * $FreeBSD: src/usr.sbin/mfiutil/mfi_config.c,v 1.8 2011/11/29 08:16:14 delphij Exp $
32 #include <sys/param.h>
33 #ifdef DEBUG
34 #include <sys/sysctl.h>
35 #endif
36 #include <err.h>
37 #include <errno.h>
38 #include <libutil.h>
39 #ifdef DEBUG
40 #include <stdint.h>
41 #endif
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include "mfiutil.h"
48 #ifdef DEBUG
49 static void dump_config(int fd, struct mfi_config_data *config);
50 #endif
52 static int add_spare(int ac, char **av);
53 static int remove_spare(int ac, char **av);
55 int
56 mfi_config_read(int fd, struct mfi_config_data **configp)
58 struct mfi_config_data *config;
59 uint32_t config_size;
60 int error;
63 * Keep fetching the config in a loop until we have a large enough
64 * buffer to hold the entire configuration.
66 config = NULL;
67 config_size = 1024;
68 fetch:
69 config = reallocf(config, config_size);
70 if (config == NULL)
71 return (-1);
72 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_READ, config,
73 config_size, NULL, 0, NULL) < 0) {
74 error = errno;
75 free(config);
76 errno = error;
77 return (-1);
80 if (config->size > config_size) {
81 config_size = config->size;
82 goto fetch;
85 *configp = config;
86 return (0);
89 static struct mfi_array *
90 mfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
92 struct mfi_array *ar;
93 char *p;
94 int i;
96 p = (char *)config->array;
97 for (i = 0; i < config->array_count; i++) {
98 ar = (struct mfi_array *)p;
99 if (ar->array_ref == array_ref)
100 return (ar);
101 p += config->array_size;
104 return (NULL);
107 static struct mfi_ld_config *
108 mfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
110 struct mfi_ld_config *ld;
111 char *p;
112 int i;
114 p = (char *)config->array + config->array_count * config->array_size;
115 for (i = 0; i < config->log_drv_count; i++) {
116 ld = (struct mfi_ld_config *)p;
117 if (ld->properties.ld.v.target_id == target_id)
118 return (ld);
119 p += config->log_drv_size;
122 return (NULL);
125 static int
126 clear_config(int ac, char **av)
128 struct mfi_ld_list list;
129 int ch, error, fd;
130 u_int i;
132 fd = mfi_open(mfi_unit);
133 if (fd < 0) {
134 error = errno;
135 warn("mfi_open");
136 return (error);
139 if (!mfi_reconfig_supported()) {
140 warnx("The current mfi(4) driver does not support "
141 "configuration changes.");
142 close(fd);
143 return (EOPNOTSUPP);
146 if (mfi_ld_get_list(fd, &list, NULL) < 0) {
147 error = errno;
148 warn("Failed to get volume list");
149 close(fd);
150 return (error);
153 for (i = 0; i < list.ld_count; i++) {
154 if (mfi_volume_busy(fd, list.ld_list[i].ld.v.target_id)) {
155 warnx("Volume %s is busy and cannot be deleted",
156 mfi_volume_name(fd, list.ld_list[i].ld.v.target_id));
157 close(fd);
158 return (EBUSY);
162 printf(
163 "Are you sure you wish to clear the configuration on mfi%u? [y/N] ",
164 mfi_unit);
165 ch = getchar();
166 if (ch != 'y' && ch != 'Y') {
167 printf("\nAborting\n");
168 close(fd);
169 return (0);
172 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_CLEAR, NULL, 0, NULL, 0, NULL) < 0) {
173 error = errno;
174 warn("Failed to clear configuration");
175 close(fd);
176 return (error);
179 printf("mfi%d: Configuration cleared\n", mfi_unit);
180 close(fd);
182 return (0);
184 MFI_COMMAND(top, clear, clear_config);
186 #define MFI_ARRAY_SIZE 288
187 #define MAX_DRIVES_PER_ARRAY \
188 ((MFI_ARRAY_SIZE - sizeof(struct mfi_array)) / 8)
190 #define RT_RAID0 0
191 #define RT_RAID1 1
192 #define RT_RAID5 2
193 #define RT_RAID6 3
194 #define RT_JBOD 4
195 #define RT_CONCAT 5
196 #define RT_RAID10 6
197 #define RT_RAID50 7
198 #define RT_RAID60 8
200 static int
201 compare_int(const void *one, const void *two)
203 int first, second;
205 first = *(const int *)one;
206 second = *(const int *)two;
208 return (first - second);
211 static struct raid_type_entry {
212 const char *name;
213 int raid_type;
214 } raid_type_table[] = {
215 { "raid0", RT_RAID0 },
216 { "raid-0", RT_RAID0 },
217 { "raid1", RT_RAID1 },
218 { "raid-1", RT_RAID1 },
219 { "mirror", RT_RAID1 },
220 { "raid5", RT_RAID5 },
221 { "raid-5", RT_RAID5 },
222 { "raid6", RT_RAID6 },
223 { "raid-6", RT_RAID6 },
224 { "jbod", RT_JBOD },
225 { "concat", RT_CONCAT },
226 { "raid10", RT_RAID10 },
227 { "raid1+0", RT_RAID10 },
228 { "raid-10", RT_RAID10 },
229 { "raid-1+0", RT_RAID10 },
230 { "raid50", RT_RAID50 },
231 { "raid5+0", RT_RAID50 },
232 { "raid-50", RT_RAID50 },
233 { "raid-5+0", RT_RAID50 },
234 { "raid60", RT_RAID60 },
235 { "raid6+0", RT_RAID60 },
236 { "raid-60", RT_RAID60 },
237 { "raid-6+0", RT_RAID60 },
238 { NULL, 0 },
241 struct config_id_state {
242 int array_count;
243 int log_drv_count;
244 int *arrays;
245 int *volumes;
246 uint16_t array_ref;
247 uint8_t target_id;
250 struct array_info {
251 int drive_count;
252 struct mfi_pd_info *drives;
253 struct mfi_array *array;
256 /* Parse a comma-separated list of drives for an array. */
257 static int
258 parse_array(int fd, int raid_type, char *array_str, struct array_info *info)
260 struct mfi_pd_info *pinfo;
261 uint16_t device_id;
262 char *cp;
263 u_int count;
264 int error;
266 cp = array_str;
267 for (count = 0; cp != NULL; count++) {
268 cp = strchr(cp, ',');
269 if (cp != NULL) {
270 cp++;
271 if (*cp == ',') {
272 warnx("Invalid drive list '%s'", array_str);
273 return (EINVAL);
278 /* Validate the number of drives for this array. */
279 if (count >= MAX_DRIVES_PER_ARRAY) {
280 warnx("Too many drives for a single array: max is %zu",
281 MAX_DRIVES_PER_ARRAY);
282 return (EINVAL);
284 switch (raid_type) {
285 case RT_RAID1:
286 case RT_RAID10:
287 if (count % 2 != 0) {
288 warnx("RAID1 and RAID10 require an even number of "
289 "drives in each array");
290 return (EINVAL);
292 break;
293 case RT_RAID5:
294 case RT_RAID50:
295 if (count < 3) {
296 warnx("RAID5 and RAID50 require at least 3 drives in "
297 "each array");
298 return (EINVAL);
300 break;
301 case RT_RAID6:
302 case RT_RAID60:
303 if (count < 4) {
304 warnx("RAID6 and RAID60 require at least 4 drives in "
305 "each array");
306 return (EINVAL);
308 break;
311 /* Validate each drive. */
312 info->drives = calloc(count, sizeof(struct mfi_pd_info));
313 if (info->drives == NULL) {
314 warnx("malloc failed");
315 return (ENOMEM);
317 info->drive_count = count;
318 for (pinfo = info->drives; (cp = strsep(&array_str, ",")) != NULL;
319 pinfo++) {
320 error = mfi_lookup_drive(fd, cp, &device_id);
321 if (error) {
322 free(info->drives);
323 info->drives = NULL;
324 return (error);
327 if (mfi_pd_get_info(fd, device_id, pinfo, NULL) < 0) {
328 error = errno;
329 warn("Failed to fetch drive info for drive %s", cp);
330 free(info->drives);
331 info->drives = NULL;
332 return (error);
335 if (pinfo->fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
336 warnx("Drive %u is not available", device_id);
337 free(info->drives);
338 info->drives = NULL;
339 return (EINVAL);
343 return (0);
347 * Find the next free array ref assuming that 'array_ref' is the last
348 * one used. 'array_ref' should be 0xffff for the initial test.
350 static uint16_t
351 find_next_array(struct config_id_state *state)
353 int i;
355 /* Assume the current one is used. */
356 state->array_ref++;
358 /* Find the next free one. */
359 for (i = 0; i < state->array_count; i++)
360 if (state->arrays[i] == state->array_ref)
361 state->array_ref++;
362 return (state->array_ref);
366 * Find the next free volume ID assuming that 'target_id' is the last
367 * one used. 'target_id' should be 0xff for the initial test.
369 static uint8_t
370 find_next_volume(struct config_id_state *state)
372 int i;
374 /* Assume the current one is used. */
375 state->target_id++;
377 /* Find the next free one. */
378 for (i = 0; i < state->log_drv_count; i++)
379 if (state->volumes[i] == state->target_id)
380 state->target_id++;
381 return (state->target_id);
384 /* Populate an array with drives. */
385 static void
386 build_array(int fd, char *arrayp, struct array_info *array_info,
387 struct config_id_state *state, int verbose)
389 struct mfi_array *ar = (struct mfi_array *)arrayp;
390 int i;
392 ar->size = array_info->drives[0].coerced_size;
393 ar->num_drives = array_info->drive_count;
394 ar->array_ref = find_next_array(state);
395 for (i = 0; i < array_info->drive_count; i++) {
396 if (verbose)
397 printf("Adding drive %s to array %u\n",
398 mfi_drive_name(NULL,
399 array_info->drives[i].ref.v.device_id,
400 MFI_DNAME_DEVICE_ID|MFI_DNAME_HONOR_OPTS),
401 ar->array_ref);
402 if (ar->size > array_info->drives[i].coerced_size)
403 ar->size = array_info->drives[i].coerced_size;
404 ar->pd[i].ref = array_info->drives[i].ref;
405 ar->pd[i].fw_state = MFI_PD_STATE_ONLINE;
407 array_info->array = ar;
411 * Create a volume that spans one or more arrays.
413 static void
414 build_volume(char *volumep, int narrays, struct array_info *arrays,
415 int raid_type, long stripe_size, struct config_id_state *state, int verbose)
417 struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
418 struct mfi_array *ar;
419 int i;
421 /* properties */
422 ld->properties.ld.v.target_id = find_next_volume(state);
423 ld->properties.ld.v.seq = 0;
424 ld->properties.default_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
425 MR_LD_CACHE_WRITE_BACK;
426 ld->properties.access_policy = MFI_LD_ACCESS_RW;
427 ld->properties.disk_cache_policy = MR_PD_CACHE_UNCHANGED;
428 ld->properties.current_cache_policy = MR_LD_CACHE_ALLOW_WRITE_CACHE |
429 MR_LD_CACHE_WRITE_BACK;
430 ld->properties.no_bgi = 0;
432 /* params */
433 switch (raid_type) {
434 case RT_RAID0:
435 case RT_JBOD:
436 ld->params.primary_raid_level = DDF_RAID0;
437 ld->params.raid_level_qualifier = 0;
438 ld->params.secondary_raid_level = 0;
439 break;
440 case RT_RAID1:
441 ld->params.primary_raid_level = DDF_RAID1;
442 ld->params.raid_level_qualifier = 0;
443 ld->params.secondary_raid_level = 0;
444 break;
445 case RT_RAID5:
446 ld->params.primary_raid_level = DDF_RAID5;
447 ld->params.raid_level_qualifier = 3;
448 ld->params.secondary_raid_level = 0;
449 break;
450 case RT_RAID6:
451 ld->params.primary_raid_level = DDF_RAID6;
452 ld->params.raid_level_qualifier = 3;
453 ld->params.secondary_raid_level = 0;
454 break;
455 case RT_CONCAT:
456 ld->params.primary_raid_level = DDF_CONCAT;
457 ld->params.raid_level_qualifier = 0;
458 ld->params.secondary_raid_level = 0;
459 break;
460 case RT_RAID10:
461 ld->params.primary_raid_level = DDF_RAID1;
462 ld->params.raid_level_qualifier = 0;
463 ld->params.secondary_raid_level = 3; /* XXX? */
464 break;
465 case RT_RAID50:
467 * XXX: This appears to work though the card's BIOS
468 * complains that the configuration is foreign. The
469 * BIOS setup does not allow for creation of RAID-50
470 * or RAID-60 arrays. The only nested array
471 * configuration it allows for is RAID-10.
473 ld->params.primary_raid_level = DDF_RAID5;
474 ld->params.raid_level_qualifier = 3;
475 ld->params.secondary_raid_level = 3; /* XXX? */
476 break;
477 case RT_RAID60:
478 ld->params.primary_raid_level = DDF_RAID6;
479 ld->params.raid_level_qualifier = 3;
480 ld->params.secondary_raid_level = 3; /* XXX? */
481 break;
485 * Stripe size is encoded as (2 ^ N) * 512 = stripe_size. Use
486 * ffs() to simulate log2(stripe_size).
488 ld->params.stripe_size = ffs(stripe_size) - 1 - 9;
489 ld->params.num_drives = arrays[0].array->num_drives;
490 ld->params.span_depth = narrays;
491 ld->params.state = MFI_LD_STATE_OPTIMAL;
492 ld->params.init_state = MFI_LD_PARAMS_INIT_NO;
493 ld->params.is_consistent = 0;
495 /* spans */
496 for (i = 0; i < narrays; i++) {
497 ar = arrays[i].array;
498 if (verbose)
499 printf("Adding array %u to volume %u\n", ar->array_ref,
500 ld->properties.ld.v.target_id);
501 ld->span[i].start_block = 0;
502 ld->span[i].num_blocks = ar->size;
503 ld->span[i].array_ref = ar->array_ref;
507 static int
508 create_volume(int ac, char **av)
510 struct mfi_config_data *config;
511 struct mfi_array *ar;
512 struct mfi_ld_config *ld;
513 struct config_id_state state;
514 size_t config_size;
515 char *p, *cfg_arrays, *cfg_volumes;
516 int error, fd, i, raid_type;
517 int narrays, nvolumes, arrays_per_volume;
518 struct array_info *arrays;
519 int64_t stripe_size;
520 #ifdef DEBUG
521 int dump;
522 #endif
523 int ch, verbose;
526 * Backwards compat. Map 'create volume' to 'create' and
527 * 'create spare' to 'add'.
529 if (ac > 1) {
530 if (strcmp(av[1], "volume") == 0) {
531 av++;
532 ac--;
533 } else if (strcmp(av[1], "spare") == 0) {
534 av++;
535 ac--;
536 return (add_spare(ac, av));
540 if (ac < 2) {
541 warnx("create volume: volume type required");
542 return (EINVAL);
545 bzero(&state, sizeof(state));
546 config = NULL;
547 arrays = NULL;
548 narrays = 0;
549 error = 0;
551 fd = mfi_open(mfi_unit);
552 if (fd < 0) {
553 error = errno;
554 warn("mfi_open");
555 return (error);
558 if (!mfi_reconfig_supported()) {
559 warnx("The current mfi(4) driver does not support "
560 "configuration changes.");
561 error = EOPNOTSUPP;
562 goto error;
565 /* Lookup the RAID type first. */
566 raid_type = -1;
567 for (i = 0; raid_type_table[i].name != NULL; i++)
568 if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
569 raid_type = raid_type_table[i].raid_type;
570 break;
573 if (raid_type == -1) {
574 warnx("Unknown or unsupported volume type %s", av[1]);
575 error = EINVAL;
576 goto error;
579 /* Parse any options. */
580 optind = 2;
581 #ifdef DEBUG
582 dump = 0;
583 #endif
584 verbose = 0;
585 stripe_size = 64 * 1024;
587 while ((ch = getopt(ac, av, "ds:v")) != -1) {
588 switch (ch) {
589 #ifdef DEBUG
590 case 'd':
591 dump = 1;
592 break;
593 #endif
594 case 's':
595 error = dehumanize_number(optarg, &stripe_size);
596 if (error != 0) {
597 warnx("Illegal stripe size");
598 goto error;
600 if ((stripe_size < 512) || (!powerof2(stripe_size))) {
601 warnx("Illegal stripe size, using 64K");
602 stripe_size = 64 * 1024;
604 break;
605 case 'v':
606 verbose = 1;
607 break;
608 case '?':
609 default:
610 error = EINVAL;
611 goto error;
614 ac -= optind;
615 av += optind;
617 /* Parse all the arrays. */
618 narrays = ac;
619 if (narrays == 0) {
620 warnx("At least one drive list is required");
621 error = EINVAL;
622 goto error;
624 switch (raid_type) {
625 case RT_RAID0:
626 case RT_RAID1:
627 case RT_RAID5:
628 case RT_RAID6:
629 case RT_CONCAT:
630 if (narrays != 1) {
631 warnx("Only one drive list can be specified");
632 error = EINVAL;
633 goto error;
635 break;
636 case RT_RAID10:
637 case RT_RAID50:
638 case RT_RAID60:
639 if (narrays < 1) {
640 warnx("RAID10, RAID50, and RAID60 require at least "
641 "two drive lists");
642 error = EINVAL;
643 goto error;
645 if (narrays > MFI_MAX_SPAN_DEPTH) {
646 warnx("Volume spans more than %d arrays",
647 MFI_MAX_SPAN_DEPTH);
648 error = EINVAL;
649 goto error;
651 break;
653 arrays = calloc(narrays, sizeof(*arrays));
654 if (arrays == NULL) {
655 warnx("malloc failed");
656 error = ENOMEM;
657 goto error;
659 for (i = 0; i < narrays; i++) {
660 error = parse_array(fd, raid_type, av[i], &arrays[i]);
661 if (error)
662 goto error;
665 switch (raid_type) {
666 case RT_RAID10:
667 case RT_RAID50:
668 case RT_RAID60:
669 for (i = 1; i < narrays; i++) {
670 if (arrays[i].drive_count != arrays[0].drive_count) {
671 warnx("All arrays must contain the same "
672 "number of drives");
673 error = EINVAL;
674 goto error;
677 break;
681 * Fetch the current config and build sorted lists of existing
682 * array and volume identifiers.
684 if (mfi_config_read(fd, &config) < 0) {
685 error = errno;
686 warn("Failed to read configuration");
687 goto error;
689 p = (char *)config->array;
690 state.array_ref = 0xffff;
691 state.target_id = 0xff;
692 state.array_count = config->array_count;
693 if (config->array_count > 0) {
694 state.arrays = calloc(config->array_count, sizeof(int));
695 if (state.arrays == NULL) {
696 warnx("malloc failed");
697 error = ENOMEM;
698 goto error;
700 for (i = 0; i < config->array_count; i++) {
701 ar = (struct mfi_array *)p;
702 state.arrays[i] = ar->array_ref;
703 p += config->array_size;
705 qsort(state.arrays, config->array_count, sizeof(int),
706 compare_int);
707 } else
708 state.arrays = NULL;
709 state.log_drv_count = config->log_drv_count;
710 if (config->log_drv_count) {
711 state.volumes = calloc(config->log_drv_count, sizeof(int));
712 if (state.volumes == NULL) {
713 warnx("malloc failed");
714 error = ENOMEM;
715 goto error;
717 for (i = 0; i < config->log_drv_count; i++) {
718 ld = (struct mfi_ld_config *)p;
719 state.volumes[i] = ld->properties.ld.v.target_id;
720 p += config->log_drv_size;
722 qsort(state.volumes, config->log_drv_count, sizeof(int),
723 compare_int);
724 } else
725 state.volumes = NULL;
726 free(config);
728 /* Determine the size of the configuration we will build. */
729 switch (raid_type) {
730 case RT_RAID0:
731 case RT_RAID1:
732 case RT_RAID5:
733 case RT_RAID6:
734 case RT_CONCAT:
735 case RT_JBOD:
736 /* Each volume spans a single array. */
737 nvolumes = narrays;
738 break;
739 case RT_RAID10:
740 case RT_RAID50:
741 case RT_RAID60:
742 /* A single volume spans multiple arrays. */
743 nvolumes = 1;
744 break;
745 default:
746 /* Pacify gcc. */
747 abort();
750 config_size = sizeof(struct mfi_config_data) +
751 sizeof(struct mfi_ld_config) * nvolumes + MFI_ARRAY_SIZE * narrays;
752 config = calloc(1, config_size);
753 if (config == NULL) {
754 warnx("malloc failed");
755 error = ENOMEM;
756 goto error;
758 config->size = config_size;
759 config->array_count = narrays;
760 config->array_size = MFI_ARRAY_SIZE; /* XXX: Firmware hardcode */
761 config->log_drv_count = nvolumes;
762 config->log_drv_size = sizeof(struct mfi_ld_config);
763 config->spares_count = 0;
764 config->spares_size = 40; /* XXX: Firmware hardcode */
765 cfg_arrays = (char *)config->array;
766 cfg_volumes = cfg_arrays + config->array_size * narrays;
768 /* Build the arrays. */
769 for (i = 0; i < narrays; i++) {
770 build_array(fd, cfg_arrays, &arrays[i], &state, verbose);
771 cfg_arrays += config->array_size;
774 /* Now build the volume(s). */
775 arrays_per_volume = narrays / nvolumes;
776 for (i = 0; i < nvolumes; i++) {
777 build_volume(cfg_volumes, arrays_per_volume,
778 &arrays[i * arrays_per_volume], raid_type, stripe_size,
779 &state, verbose);
780 cfg_volumes += config->log_drv_size;
783 #ifdef DEBUG
784 if (dump)
785 dump_config(fd, config);
786 #endif
788 /* Send the new config to the controller. */
789 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_ADD, config, config_size,
790 NULL, 0, NULL) < 0) {
791 error = errno;
792 warn("Failed to add volume");
793 /* FALLTHROUGH */
796 error:
797 /* Clean up. */
798 free(config);
799 free(state.volumes);
800 free(state.arrays);
801 if (arrays != NULL) {
802 for (i = 0; i < narrays; i++)
803 free(arrays[i].drives);
804 free(arrays);
806 close(fd);
808 return (error);
810 MFI_COMMAND(top, create, create_volume);
812 static int
813 delete_volume(int ac, char **av)
815 struct mfi_ld_info info;
816 int error, fd;
817 uint8_t target_id, mbox[4];
820 * Backwards compat. Map 'delete volume' to 'delete' and
821 * 'delete spare' to 'remove'.
823 if (ac > 1) {
824 if (strcmp(av[1], "volume") == 0) {
825 av++;
826 ac--;
827 } else if (strcmp(av[1], "spare") == 0) {
828 av++;
829 ac--;
830 return (remove_spare(ac, av));
834 if (ac != 2) {
835 warnx("delete volume: volume required");
836 return (EINVAL);
839 fd = mfi_open(mfi_unit);
840 if (fd < 0) {
841 error = errno;
842 warn("mfi_open");
843 return (error);
846 if (!mfi_reconfig_supported()) {
847 warnx("The current mfi(4) driver does not support "
848 "configuration changes.");
849 close(fd);
850 return (EOPNOTSUPP);
853 if (mfi_lookup_volume(fd, av[1], &target_id) < 0) {
854 error = errno;
855 warn("Invalid volume %s", av[1]);
856 close(fd);
857 return (error);
860 if (mfi_ld_get_info(fd, target_id, &info, NULL) < 0) {
861 error = errno;
862 warn("Failed to get info for volume %d", target_id);
863 close(fd);
864 return (error);
867 if (mfi_volume_busy(fd, target_id)) {
868 warnx("Volume %s is busy and cannot be deleted",
869 mfi_volume_name(fd, target_id));
870 close(fd);
871 return (EBUSY);
874 mbox_store_ldref(mbox, &info.ld_config.properties.ld);
875 if (mfi_dcmd_command(fd, MFI_DCMD_LD_DELETE, NULL, 0, mbox,
876 sizeof(mbox), NULL) < 0) {
877 error = errno;
878 warn("Failed to delete volume");
879 close(fd);
880 return (error);
883 close(fd);
885 return (0);
887 MFI_COMMAND(top, delete, delete_volume);
889 static int
890 add_spare(int ac, char **av)
892 struct mfi_pd_info info;
893 struct mfi_config_data *config;
894 struct mfi_array *ar;
895 struct mfi_ld_config *ld;
896 struct mfi_spare *spare;
897 uint16_t device_id;
898 uint8_t target_id;
899 char *p;
900 int error, fd, i;
902 if (ac < 2) {
903 warnx("add spare: drive required");
904 return (EINVAL);
907 fd = mfi_open(mfi_unit);
908 if (fd < 0) {
909 error = errno;
910 warn("mfi_open");
911 return (error);
914 config = NULL;
915 spare = NULL;
916 error = mfi_lookup_drive(fd, av[1], &device_id);
917 if (error)
918 goto error;
920 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
921 error = errno;
922 warn("Failed to fetch drive info");
923 goto error;
926 if (info.fw_state != MFI_PD_STATE_UNCONFIGURED_GOOD) {
927 warnx("Drive %u is not available", device_id);
928 error = EINVAL;
929 goto error;
932 if (ac > 2) {
933 if (mfi_lookup_volume(fd, av[2], &target_id) < 0) {
934 error = errno;
935 warn("Invalid volume %s", av[2]);
936 goto error;
940 if (mfi_config_read(fd, &config) < 0) {
941 error = errno;
942 warn("Failed to read configuration");
943 goto error;
946 spare = malloc(sizeof(struct mfi_spare) + sizeof(uint16_t) *
947 config->array_count);
948 if (spare == NULL) {
949 warnx("malloc failed");
950 error = ENOMEM;
951 goto error;
953 bzero(spare, sizeof(struct mfi_spare));
954 spare->ref = info.ref;
956 if (ac == 2) {
957 /* Global spare backs all arrays. */
958 p = (char *)config->array;
959 for (i = 0; i < config->array_count; i++) {
960 ar = (struct mfi_array *)p;
961 if (ar->size > info.coerced_size) {
962 warnx("Spare isn't large enough for array %u",
963 ar->array_ref);
964 error = EINVAL;
965 goto error;
967 p += config->array_size;
969 spare->array_count = 0;
970 } else {
972 * Dedicated spares only back the arrays for a
973 * specific volume.
975 ld = mfi_config_lookup_volume(config, target_id);
976 if (ld == NULL) {
977 warnx("Did not find volume %d", target_id);
978 error = EINVAL;
979 goto error;
982 spare->spare_type |= MFI_SPARE_DEDICATED;
983 spare->array_count = ld->params.span_depth;
984 for (i = 0; i < ld->params.span_depth; i++) {
985 ar = mfi_config_lookup_array(config,
986 ld->span[i].array_ref);
987 if (ar == NULL) {
988 warnx("Missing array; inconsistent config?");
989 error = ENXIO;
990 goto error;
992 if (ar->size > info.coerced_size) {
993 warnx("Spare isn't large enough for array %u",
994 ar->array_ref);
995 error = EINVAL;
996 goto error;
998 spare->array_ref[i] = ar->array_ref;
1002 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_MAKE_SPARE, spare,
1003 sizeof(struct mfi_spare) + sizeof(uint16_t) * spare->array_count,
1004 NULL, 0, NULL) < 0) {
1005 error = errno;
1006 warn("Failed to assign spare");
1007 /* FALLTHROUGH. */
1010 error:
1011 free(spare);
1012 free(config);
1013 close(fd);
1015 return (error);
1017 MFI_COMMAND(top, add, add_spare);
1019 static int
1020 remove_spare(int ac, char **av)
1022 struct mfi_pd_info info;
1023 int error, fd;
1024 uint16_t device_id;
1025 uint8_t mbox[4];
1027 if (ac != 2) {
1028 warnx("remove spare: drive required");
1029 return (EINVAL);
1032 fd = mfi_open(mfi_unit);
1033 if (fd < 0) {
1034 error = errno;
1035 warn("mfi_open");
1036 return (error);
1039 error = mfi_lookup_drive(fd, av[1], &device_id);
1040 if (error) {
1041 close(fd);
1042 return (error);
1045 /* Get the info for this drive. */
1046 if (mfi_pd_get_info(fd, device_id, &info, NULL) < 0) {
1047 error = errno;
1048 warn("Failed to fetch info for drive %u", device_id);
1049 close(fd);
1050 return (error);
1053 if (info.fw_state != MFI_PD_STATE_HOT_SPARE) {
1054 warnx("Drive %u is not a hot spare", device_id);
1055 close(fd);
1056 return (EINVAL);
1059 mbox_store_pdref(mbox, &info.ref);
1060 if (mfi_dcmd_command(fd, MFI_DCMD_CFG_REMOVE_SPARE, NULL, 0, mbox,
1061 sizeof(mbox), NULL) < 0) {
1062 error = errno;
1063 warn("Failed to delete spare");
1064 close(fd);
1065 return (error);
1068 close(fd);
1070 return (0);
1072 MFI_COMMAND(top, remove, remove_spare);
1074 #ifdef DEBUG
1075 /* Display raw data about a config. */
1076 static void
1077 dump_config(int fd, struct mfi_config_data *config)
1079 struct mfi_array *ar;
1080 struct mfi_ld_config *ld;
1081 struct mfi_spare *sp;
1082 struct mfi_pd_info pinfo;
1083 uint16_t device_id;
1084 char *p;
1085 int i, j;
1087 printf(
1088 "mfi%d Configuration (Debug): %d arrays, %d volumes, %d spares\n",
1089 mfi_unit, config->array_count, config->log_drv_count,
1090 config->spares_count);
1091 printf(" array size: %u\n", config->array_size);
1092 printf(" volume size: %u\n", config->log_drv_size);
1093 printf(" spare size: %u\n", config->spares_size);
1094 p = (char *)config->array;
1096 for (i = 0; i < config->array_count; i++) {
1097 ar = (struct mfi_array *)p;
1098 printf(" array %u of %u drives:\n", ar->array_ref,
1099 ar->num_drives);
1100 printf(" size = %ju\n", (uintmax_t)ar->size);
1101 for (j = 0; j < ar->num_drives; j++) {
1102 device_id = ar->pd[j].ref.v.device_id;
1103 if (device_id == 0xffff)
1104 printf(" drive MISSING\n");
1105 else {
1106 printf(" drive %u %s\n", device_id,
1107 mfi_pdstate(ar->pd[j].fw_state));
1108 if (mfi_pd_get_info(fd, device_id, &pinfo,
1109 NULL) >= 0) {
1110 printf(" raw size: %ju\n",
1111 (uintmax_t)pinfo.raw_size);
1112 printf(" non-coerced size: %ju\n",
1113 (uintmax_t)pinfo.non_coerced_size);
1114 printf(" coerced size: %ju\n",
1115 (uintmax_t)pinfo.coerced_size);
1119 p += config->array_size;
1122 for (i = 0; i < config->log_drv_count; i++) {
1123 ld = (struct mfi_ld_config *)p;
1124 printf(" volume %s ",
1125 mfi_volume_name(fd, ld->properties.ld.v.target_id));
1126 printf("%s %s",
1127 mfi_raid_level(ld->params.primary_raid_level,
1128 ld->params.secondary_raid_level),
1129 mfi_ldstate(ld->params.state));
1130 if (ld->properties.name[0] != '\0')
1131 printf(" <%s>", ld->properties.name);
1132 printf("\n");
1133 printf(" primary raid level: %u\n",
1134 ld->params.primary_raid_level);
1135 printf(" raid level qualifier: %u\n",
1136 ld->params.raid_level_qualifier);
1137 printf(" secondary raid level: %u\n",
1138 ld->params.secondary_raid_level);
1139 printf(" stripe size: %u\n", ld->params.stripe_size);
1140 printf(" num drives: %u\n", ld->params.num_drives);
1141 printf(" init state: %u\n", ld->params.init_state);
1142 printf(" consistent: %u\n", ld->params.is_consistent);
1143 printf(" no bgi: %u\n", ld->properties.no_bgi);
1144 printf(" spans:\n");
1145 for (j = 0; j < ld->params.span_depth; j++) {
1146 printf(" array %u @ ", ld->span[j].array_ref);
1147 printf("%ju : %ju\n",
1148 (uintmax_t)ld->span[j].start_block,
1149 (uintmax_t)ld->span[j].num_blocks);
1151 p += config->log_drv_size;
1154 for (i = 0; i < config->spares_count; i++) {
1155 sp = (struct mfi_spare *)p;
1156 printf(" %s spare %u ",
1157 sp->spare_type & MFI_SPARE_DEDICATED ? "dedicated" :
1158 "global", sp->ref.v.device_id);
1159 printf("%s", mfi_pdstate(MFI_PD_STATE_HOT_SPARE));
1160 printf(" backs:\n");
1161 for (j = 0; j < sp->array_count; j++)
1162 printf(" array %u\n", sp->array_ref[j]);
1163 p += config->spares_size;
1167 static int
1168 debug_config(int ac, char **av)
1170 struct mfi_config_data *config;
1171 int error, fd;
1173 if (ac != 1) {
1174 warnx("debug: extra arguments");
1175 return (EINVAL);
1178 fd = mfi_open(mfi_unit);
1179 if (fd < 0) {
1180 error = errno;
1181 warn("mfi_open");
1182 return (error);
1185 /* Get the config from the controller. */
1186 if (mfi_config_read(fd, &config) < 0) {
1187 error = errno;
1188 warn("Failed to get config");
1189 close(fd);
1190 return (error);
1193 /* Dump out the configuration. */
1194 dump_config(fd, config);
1195 free(config);
1196 close(fd);
1198 return (0);
1200 MFI_COMMAND(top, debug, debug_config);
1202 static int
1203 dump(int ac, char **av)
1205 struct mfi_config_data *config;
1206 char buf[64];
1207 size_t len;
1208 int error, fd;
1210 if (ac != 1) {
1211 warnx("dump: extra arguments");
1212 return (EINVAL);
1215 fd = mfi_open(mfi_unit);
1216 if (fd < 0) {
1217 error = errno;
1218 warn("mfi_open");
1219 return (error);
1222 /* Get the stashed copy of the last dcmd from the driver. */
1223 snprintf(buf, sizeof(buf), "hw.mfi%d.debug_command", mfi_unit);
1224 if (sysctlbyname(buf, NULL, &len, NULL, 0) < 0) {
1225 error = errno;
1226 warn("Failed to read debug command");
1227 if (error == ENOENT)
1228 error = EOPNOTSUPP;
1229 close(fd);
1230 return (error);
1233 config = malloc(len);
1234 if (config == NULL) {
1235 warnx("malloc failed");
1236 close(fd);
1237 return (ENOMEM);
1239 if (sysctlbyname(buf, config, &len, NULL, 0) < 0) {
1240 error = errno;
1241 warn("Failed to read debug command");
1242 free(config);
1243 close(fd);
1244 return (error);
1246 dump_config(fd, config);
1247 free(config);
1248 close(fd);
1250 return (0);
1252 MFI_COMMAND(top, dump, dump);
1253 #endif