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