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