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