Merge remote-tracking branch 'origin/master'
[dragonfly.git] / usr.sbin / mfiutil / mfi_config.c
1 /*-
2  * Copyright (c) 2008, 2009 Yahoo!, Inc.
3  * All rights reserved.
4  *
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.
16  *
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.
28  *
29  * $FreeBSD: src/usr.sbin/mfiutil/mfi_config.c,v 1.8 2011/11/29 08:16:14 delphij Exp $
30  */
31
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"
47
48 #ifdef DEBUG
49 static void     dump_config(int fd, struct mfi_config_data *config);
50 #endif
51
52 static int      add_spare(int ac, char **av);
53 static int      remove_spare(int ac, char **av);
54
55 int
56 mfi_config_read(int fd, struct mfi_config_data **configp)
57 {
58         struct mfi_config_data *config;
59         uint32_t config_size;
60         int error;
61
62         /*
63          * Keep fetching the config in a loop until we have a large enough
64          * buffer to hold the entire configuration.
65          */
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);
78         }
79
80         if (config->size > config_size) {
81                 config_size = config->size;
82                 goto fetch;
83         }
84
85         *configp = config;
86         return (0);
87 }
88
89 static struct mfi_array *
90 mfi_config_lookup_array(struct mfi_config_data *config, uint16_t array_ref)
91 {
92         struct mfi_array *ar;
93         char *p;
94         int i;
95
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;
102         }
103
104         return (NULL);
105 }
106
107 static struct mfi_ld_config *
108 mfi_config_lookup_volume(struct mfi_config_data *config, uint8_t target_id)
109 {
110         struct mfi_ld_config *ld;
111         char *p;
112         int i;
113
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;
120         }
121
122         return (NULL);
123 }
124
125 static int
126 clear_config(__unused int ac, __unused char **av)
127 {
128         struct mfi_ld_list list;
129         int ch, error, fd;
130         u_int i;
131
132         fd = mfi_open(mfi_unit);
133         if (fd < 0) {
134                 error = errno;
135                 warn("mfi_open");
136                 return (error);
137         }
138
139         if (!mfi_reconfig_supported()) {
140                 warnx("The current mfi(4) driver does not support "
141                     "configuration changes.");
142                 close(fd);
143                 return (EOPNOTSUPP);
144         }
145
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);
151         }
152
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);
159                 }
160         }
161
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);
170         }
171
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);
177         }
178
179         printf("mfi%d: Configuration cleared\n", mfi_unit);
180         close(fd);
181
182         return (0);
183 }
184 MFI_COMMAND(top, clear, clear_config);
185
186 #define MFI_ARRAY_SIZE          288
187 #define MAX_DRIVES_PER_ARRAY                                            \
188         ((MFI_ARRAY_SIZE - sizeof(struct mfi_array)) / 8)
189
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
199
200 static int
201 compare_int(const void *one, const void *two)
202 {
203         int first, second;
204
205         first = *(const int *)one;
206         second = *(const int *)two;
207
208         return (first - second);
209 }
210
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 },
239 };
240
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;
248 };
249
250 struct array_info {
251         int     drive_count;
252         struct mfi_pd_info *drives;
253         struct mfi_array *array;
254 };
255
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)
259 {
260         struct mfi_pd_info *pinfo;
261         uint16_t device_id;
262         char *cp;
263         u_int count;
264         int error;
265
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);
274                         }
275                 }
276         }
277
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);
283         }
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);
291                 }
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);
299                 }
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);
307                 }
308                 break;
309         }
310
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);
316         }
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);
325                 }
326
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);
333                 }
334
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);
340                 }
341         }
342
343         return (0);
344 }
345
346 /*
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.
349  */
350 static uint16_t
351 find_next_array(struct config_id_state *state)
352 {
353         int i;
354
355         /* Assume the current one is used. */
356         state->array_ref++;
357
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);
363 }
364
365 /*
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.
368  */
369 static uint8_t
370 find_next_volume(struct config_id_state *state)
371 {
372         int i;
373
374         /* Assume the current one is used. */
375         state->target_id++;
376
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);
382 }
383
384 /* Populate an array with drives. */
385 static void
386 build_array(__unused int fd, char *arrayp, struct array_info *array_info,
387     struct config_id_state *state, int verbose)
388 {
389         struct mfi_array *ar = (struct mfi_array *)arrayp;
390         int i;
391
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;
406         }
407         array_info->array = ar;
408 }
409
410 /*
411  * Create a volume that spans one or more arrays.
412  */
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)
416 {
417         struct mfi_ld_config *ld = (struct mfi_ld_config *)volumep;
418         struct mfi_array *ar;
419         int i;
420
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;
431
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:
466                 /*
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.
472                  */
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;
482         }
483
484         /*
485          * Stripe size is encoded as (2 ^ N) * 512 = stripe_size.  Use
486          * ffs() to simulate log2(stripe_size).
487          */
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;
494
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;
504         }
505 }
506
507 static int
508 create_volume(int ac, char **av)
509 {
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;
524
525         /*
526          * Backwards compat.  Map 'create volume' to 'create' and
527          * 'create spare' to 'add'.
528          */
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));
537                 }
538         }
539
540         if (ac < 2) {
541                 warnx("create volume: volume type required");
542                 return (EINVAL);
543         }
544
545         bzero(&state, sizeof(state));
546         config = NULL;
547         arrays = NULL;
548         narrays = 0;
549         error = 0;
550
551         fd = mfi_open(mfi_unit);
552         if (fd < 0) {
553                 error = errno;
554                 warn("mfi_open");
555                 return (error);
556         }
557
558         if (!mfi_reconfig_supported()) {
559                 warnx("The current mfi(4) driver does not support "
560                     "configuration changes.");
561                 error = EOPNOTSUPP;
562                 goto error;
563         }
564
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;
571                 }
572
573         if (raid_type == -1) {
574                 warnx("Unknown or unsupported volume type %s", av[1]);
575                 error = EINVAL;
576                 goto error;
577         }
578
579         /* Parse any options. */
580         optind = 2;
581 #ifdef DEBUG
582         dump = 0;
583 #endif
584         verbose = 0;
585         stripe_size = 64 * 1024;
586
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;
599                         }
600                         if ((stripe_size < 512) || (!powerof2(stripe_size))) {
601                                 warnx("Illegal stripe size, using 64K");
602                                 stripe_size = 64 * 1024;
603                         }
604                         break;
605                 case 'v':
606                         verbose = 1;
607                         break;
608                 case '?':
609                 default:
610                         error = EINVAL;
611                         goto error;
612                 }
613         }
614         ac -= optind;
615         av += optind;
616
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;
623         }
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;
634                 }
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;
644                 }
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;
650                 }
651                 break;
652         }
653         arrays = calloc(narrays, sizeof(*arrays));
654         if (arrays == NULL) {
655                 warnx("malloc failed");
656                 error = ENOMEM;
657                 goto error;
658         }
659         for (i = 0; i < narrays; i++) {
660                 error = parse_array(fd, raid_type, av[i], &arrays[i]);
661                 if (error)
662                         goto error;
663         }
664
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;
675                         }
676                 }
677                 break;
678         }
679
680         /*
681          * Fetch the current config and build sorted lists of existing
682          * array and volume identifiers.
683          */
684         if (mfi_config_read(fd, &config) < 0) {
685                 error = errno;
686                 warn("Failed to read configuration");
687                 goto error;
688         }
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;
699                 }
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;
704                 }
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;
716                 }
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;
721                 }
722                 qsort(state.volumes, config->log_drv_count, sizeof(int),
723                     compare_int);
724         } else
725                 state.volumes = NULL;
726         free(config);
727
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();
748         }
749
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;
757         }
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;
767
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;
772         }
773
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;
781         }
782
783 #ifdef DEBUG
784         if (dump)
785                 dump_config(fd, config);
786 #endif
787
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 */
794         }
795
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);
805         }
806         close(fd);
807
808         return (error);
809 }
810 MFI_COMMAND(top, create, create_volume);
811
812 static int
813 delete_volume(int ac, char **av)
814 {
815         struct mfi_ld_info info;
816         int error, fd;
817         uint8_t target_id, mbox[4];
818
819         /*
820          * Backwards compat.  Map 'delete volume' to 'delete' and
821          * 'delete spare' to 'remove'.
822          */
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));
831                 }
832         }
833
834         if (ac != 2) {
835                 warnx("delete volume: volume required");
836                 return (EINVAL);
837         }
838
839         fd = mfi_open(mfi_unit);
840         if (fd < 0) {
841                 error = errno;
842                 warn("mfi_open");
843                 return (error);
844         }
845
846         if (!mfi_reconfig_supported()) {
847                 warnx("The current mfi(4) driver does not support "
848                     "configuration changes.");
849                 close(fd);
850                 return (EOPNOTSUPP);
851         }
852
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);
858         }
859
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);
865         }
866
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);
872         }
873
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);
881         }
882
883         close(fd);
884
885         return (0);
886 }
887 MFI_COMMAND(top, delete, delete_volume);
888
889 static int
890 add_spare(int ac, char **av)
891 {
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;
901
902         if (ac < 2) {
903                 warnx("add spare: drive required");
904                 return (EINVAL);
905         }
906
907         fd = mfi_open(mfi_unit);
908         if (fd < 0) {
909                 error = errno;
910                 warn("mfi_open");
911                 return (error);
912         }
913
914         config = NULL;
915         spare = NULL;
916         error = mfi_lookup_drive(fd, av[1], &device_id);
917         if (error)
918                 goto error;
919
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;
924         }
925
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;
930         }
931
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;
937                 }
938         }
939
940         if (mfi_config_read(fd, &config) < 0) {
941                 error = errno;
942                 warn("Failed to read configuration");
943                 goto error;
944         }
945
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;
952         }
953         bzero(spare, sizeof(struct mfi_spare));
954         spare->ref = info.ref;
955
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;
966                         }
967                         p += config->array_size;
968                 }
969                 spare->array_count = 0;
970         } else  {
971                 /*
972                  * Dedicated spares only back the arrays for a
973                  * specific volume.
974                  */
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;
980                 }
981
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;
991                         }
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;
997                         }
998                         spare->array_ref[i] = ar->array_ref;
999                 }
1000         }
1001
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. */
1008         }
1009
1010 error:
1011         free(spare);
1012         free(config);
1013         close(fd);
1014
1015         return (error);
1016 }
1017 MFI_COMMAND(top, add, add_spare);
1018
1019 static int
1020 remove_spare(int ac, char **av)
1021 {
1022         struct mfi_pd_info info;
1023         int error, fd;
1024         uint16_t device_id;
1025         uint8_t mbox[4];
1026
1027         if (ac != 2) {
1028                 warnx("remove spare: drive required");
1029                 return (EINVAL);
1030         }
1031
1032         fd = mfi_open(mfi_unit);
1033         if (fd < 0) {
1034                 error = errno;
1035                 warn("mfi_open");
1036                 return (error);
1037         }
1038
1039         error = mfi_lookup_drive(fd, av[1], &device_id);
1040         if (error) {
1041                 close(fd);
1042                 return (error);
1043         }
1044
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);
1051         }
1052
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);
1057         }
1058
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);
1066         }
1067
1068         close(fd);
1069
1070         return (0);
1071 }
1072 MFI_COMMAND(top, remove, remove_spare);
1073
1074 #ifdef DEBUG
1075 /* Display raw data about a config. */
1076 static void
1077 dump_config(int fd, struct mfi_config_data *config)
1078 {
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;
1086
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;
1095
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);
1116                                 }
1117                         }
1118                 }
1119                 p += config->array_size;
1120         }
1121
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);
1150                 }
1151                 p += config->log_drv_size;
1152         }
1153
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;
1164         }
1165 }
1166
1167 static int
1168 debug_config(int ac, char **av)
1169 {
1170         struct mfi_config_data *config;
1171         int error, fd;
1172
1173         if (ac != 1) {
1174                 warnx("debug: extra arguments");
1175                 return (EINVAL);
1176         }
1177
1178         fd = mfi_open(mfi_unit);
1179         if (fd < 0) {
1180                 error = errno;
1181                 warn("mfi_open");
1182                 return (error);
1183         }
1184
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);
1191         }
1192
1193         /* Dump out the configuration. */
1194         dump_config(fd, config);
1195         free(config);
1196         close(fd);
1197
1198         return (0);
1199 }
1200 MFI_COMMAND(top, debug, debug_config);
1201
1202 static int
1203 dump(int ac, char **av)
1204 {
1205         struct mfi_config_data *config;
1206         char buf[64];
1207         size_t len;
1208         int error, fd;
1209
1210         if (ac != 1) {
1211                 warnx("dump: extra arguments");
1212                 return (EINVAL);
1213         }
1214
1215         fd = mfi_open(mfi_unit);
1216         if (fd < 0) {
1217                 error = errno;
1218                 warn("mfi_open");
1219                 return (error);
1220         }
1221
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);
1231         }
1232
1233         config = malloc(len);
1234         if (config == NULL) {
1235                 warnx("malloc failed");
1236                 close(fd);
1237                 return (ENOMEM);
1238         }
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);
1245         }
1246         dump_config(fd, config);
1247         free(config);
1248         close(fd);
1249
1250         return (0);
1251 }
1252 MFI_COMMAND(top, dump, dump);
1253 #endif