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