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