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