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