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