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