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