]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mptutil/mpt_config.c
Merge branch 'releng/11.3' into releng-CDN/11.3
[FreeBSD/FreeBSD.git] / usr.sbin / mptutil / mpt_config.c
1 /*-
2  * Copyright (c) 2008 Yahoo!, Inc.
3  * All rights reserved.
4  * Written by: John Baldwin <jhb@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the author nor the names of any co-contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include <sys/cdefs.h>
32 __RCSID("$FreeBSD$");
33
34 #include <sys/param.h>
35 #include <sys/errno.h>
36 #include <err.h>
37 #include <fcntl.h>
38 #include <libutil.h>
39 #include <paths.h>
40 #ifdef DEBUG
41 #include <stdint.h>
42 #endif
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include "mptutil.h"
48
49 #ifdef DEBUG
50 static void     dump_config(CONFIG_PAGE_RAID_VOL_0 *vol);
51 #endif
52
53 static long
54 dehumanize(const char *value)
55 {
56         char    *vtp;
57         long    iv;
58  
59         if (value == NULL)
60                 return (0);
61         iv = strtoq(value, &vtp, 0);
62         if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
63                 return (0);
64         }
65         switch (vtp[0]) {
66         case 't': case 'T':
67                 iv *= 1024;
68                 /* FALLTHROUGH */
69         case 'g': case 'G':
70                 iv *= 1024;
71                 /* FALLTHROUGH */
72         case 'm': case 'M':
73                 iv *= 1024;
74                 /* FALLTHROUGH */
75         case 'k': case 'K':
76                 iv *= 1024;
77                 /* FALLTHROUGH */
78         case '\0':
79                 break;
80         default:
81                 return (0);
82         }
83         return (iv);
84 }
85
86 /*
87  * Lock the volume by opening its /dev device read/write.  This will
88  * only work if nothing else has it opened (including mounts).  We
89  * leak the fd on purpose since this application is not long-running.
90  */
91 int
92 mpt_lock_volume(U8 VolumeBus, U8 VolumeID)
93 {
94         char path[MAXPATHLEN];
95         struct mpt_query_disk qd;
96         int error, vfd;
97
98         error = mpt_query_disk(VolumeBus, VolumeID, &qd);
99         if (error == ENOENT)
100                 /*
101                  * This means there isn't a CAM device associated with
102                  * the volume, and thus it is already implicitly
103                  * locked, so just return.
104                  */
105                 return (0);
106         if (error) {
107                 warnc(error, "Unable to lookup volume device name");
108                 return (error);
109         }
110         snprintf(path, sizeof(path), "%s%s", _PATH_DEV, qd.devname);
111         vfd = open(path, O_RDWR);
112         if (vfd < 0) {
113                 error = errno;
114                 warn("Unable to lock volume %s", qd.devname);
115                 return (error);
116         }
117         return (0);
118 }
119
120 static int
121 mpt_lock_physdisk(struct mpt_standalone_disk *disk)
122 {
123         char path[MAXPATHLEN];
124         int dfd, error;
125
126         snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk->devname);
127         dfd = open(path, O_RDWR);
128         if (dfd < 0) {
129                 error = errno;
130                 warn("Unable to lock disk %s", disk->devname);
131                 return (error);
132         }
133         return (0);
134 }
135
136 static int
137 mpt_lookup_standalone_disk(const char *name, struct mpt_standalone_disk *disks,
138     int ndisks, int *index)
139 {
140         char *cp;
141         long bus, id;
142         int i;
143
144         /* Check for a raw <bus>:<id> string. */
145         bus = strtol(name, &cp, 0);
146         if (*cp == ':') {
147                 id = strtol(cp + 1, &cp, 0);
148                 if (*cp == '\0') {
149                         if (bus < 0 || bus > 0xff || id < 0 || id > 0xff) {
150                                 return (EINVAL);
151                         }
152                         for (i = 0; i < ndisks; i++) {
153                                 if (disks[i].bus == (U8)bus &&
154                                     disks[i].target == (U8)id) {
155                                         *index = i;
156                                         return (0);
157                                 }
158                         }
159                         return (ENOENT);
160                 }
161         }
162
163         if (name[0] == 'd' && name[1] == 'a') {
164                 for (i = 0; i < ndisks; i++) {
165                         if (strcmp(name, disks[i].devname) == 0) {
166                                 *index = i;
167                                 return (0);
168                         }
169                 }
170                 return (ENOENT);
171         }
172
173         return (EINVAL);
174 }
175
176 /*
177  * Mark a standalone disk as being a physical disk.
178  */
179 static int
180 mpt_create_physdisk(int fd, struct mpt_standalone_disk *disk, U8 *PhysDiskNum)
181 {
182         CONFIG_PAGE_HEADER header;
183         CONFIG_PAGE_RAID_PHYS_DISK_0 *config_page;
184         int error;
185         U32 ActionData;
186
187         error = mpt_read_config_page_header(fd, MPI_CONFIG_PAGETYPE_RAID_PHYSDISK,
188             0, 0, &header, NULL);
189         if (error)
190                 return (error);
191         if (header.PageVersion > MPI_RAIDPHYSDISKPAGE0_PAGEVERSION) {
192                 warnx("Unsupported RAID physdisk page 0 version %d",
193                     header.PageVersion);
194                 return (EOPNOTSUPP);
195         }               
196         config_page = calloc(1, sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0));
197         config_page->Header.PageType = MPI_CONFIG_PAGETYPE_RAID_PHYSDISK;
198         config_page->Header.PageNumber = 0;
199         config_page->Header.PageLength = sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0) /
200             4;
201         config_page->PhysDiskIOC = 0;   /* XXX */
202         config_page->PhysDiskBus = disk->bus;
203         config_page->PhysDiskID = disk->target;
204
205         /* XXX: Enclosure info for PhysDiskSettings? */
206         error = mpt_raid_action(fd, MPI_RAID_ACTION_CREATE_PHYSDISK, 0, 0, 0, 0,
207             config_page, sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0), NULL,
208             &ActionData, sizeof(ActionData), NULL, NULL, 1);
209         if (error)
210                 return (error);
211         *PhysDiskNum = ActionData & 0xff;
212         return (0);
213 }
214
215 static int
216 mpt_delete_physdisk(int fd, U8 PhysDiskNum)
217 {
218
219         return (mpt_raid_action(fd, MPI_RAID_ACTION_DELETE_PHYSDISK, 0, 0,
220             PhysDiskNum, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0));
221 }
222
223 /*
224  * MPT's firmware does not have a clear command.  Instead, we
225  * implement it by deleting each array and disk by hand.
226  */
227 static int
228 clear_config(int ac, char **av)
229 {
230         CONFIG_PAGE_IOC_2 *ioc2;
231         CONFIG_PAGE_IOC_2_RAID_VOL *vol;
232         CONFIG_PAGE_IOC_3 *ioc3;
233         IOC_3_PHYS_DISK *disk;
234         CONFIG_PAGE_IOC_5 *ioc5;
235         IOC_5_HOT_SPARE *spare;
236         int ch, error, fd, i;
237
238         fd = mpt_open(mpt_unit);
239         if (fd < 0) {
240                 error = errno;
241                 warn("mpt_open");
242                 return (error);
243         }
244
245         ioc2 = mpt_read_ioc_page(fd, 2, NULL);
246         if (ioc2 == NULL) {
247                 error = errno;
248                 warn("Failed to fetch volume list");
249                 close(fd);
250                 return (error);
251         }
252
253         /* Lock all the volumes first. */
254         vol = ioc2->RaidVolume;
255         for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
256                 if (mpt_lock_volume(vol->VolumeBus, vol->VolumeID) < 0) {
257                         warnx("Volume %s is busy and cannot be deleted",
258                             mpt_volume_name(vol->VolumeBus, vol->VolumeID));
259                         free(ioc2);
260                         close(fd);
261                         return (EBUSY);
262                 }
263         }
264
265         printf(
266             "Are you sure you wish to clear the configuration on mpt%u? [y/N] ",
267             mpt_unit);
268         ch = getchar();
269         if (ch != 'y' && ch != 'Y') {
270                 printf("\nAborting\n");
271                 free(ioc2);
272                 close(fd);
273                 return (0);
274         }
275
276         /* Delete all the volumes. */
277         vol = ioc2->RaidVolume;
278         for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
279                 error = mpt_raid_action(fd, MPI_RAID_ACTION_DELETE_VOLUME,
280                     vol->VolumeBus, vol->VolumeID, 0,
281                     MPI_RAID_ACTION_ADATA_DEL_PHYS_DISKS |
282                     MPI_RAID_ACTION_ADATA_ZERO_LBA0, NULL, 0, NULL, NULL, 0,
283                     NULL, NULL, 0);
284                 if (error)
285                         warnc(error, "Failed to delete volume %s",
286                             mpt_volume_name(vol->VolumeBus, vol->VolumeID));
287         }
288         free(ioc2);
289
290         /* Delete all the spares. */
291         ioc5 = mpt_read_ioc_page(fd, 5, NULL);
292         if (ioc5 == NULL)
293                 warn("Failed to fetch spare list");
294         else {
295                 spare = ioc5->HotSpare;
296                 for (i = 0; i < ioc5->NumHotSpares; spare++, i++)
297                         if (mpt_delete_physdisk(fd, spare->PhysDiskNum) < 0)
298                                 warn("Failed to delete physical disk %d",
299                                     spare->PhysDiskNum);
300                 free(ioc5);
301         }
302
303         /* Delete any RAID physdisks that may be left. */
304         ioc3 = mpt_read_ioc_page(fd, 3, NULL);
305         if (ioc3 == NULL)
306                 warn("Failed to fetch drive list");
307         else {
308                 disk = ioc3->PhysDisk;
309                 for (i = 0; i < ioc3->NumPhysDisks; disk++, i++)
310                         if (mpt_delete_physdisk(fd, disk->PhysDiskNum) < 0)
311                                 warn("Failed to delete physical disk %d",
312                                     disk->PhysDiskNum);
313                 free(ioc3);
314         }
315
316         printf("mpt%d: Configuration cleared\n", mpt_unit);
317         mpt_rescan_bus(-1, -1);
318         close(fd);
319
320         return (0);
321 }
322 MPT_COMMAND(top, clear, clear_config);
323
324 #define RT_RAID0        0
325 #define RT_RAID1        1
326 #define RT_RAID1E       2
327
328 static struct raid_type_entry {
329         const char *name;
330         int     raid_type;
331 } raid_type_table[] = {
332         { "raid0",      RT_RAID0 },
333         { "raid-0",     RT_RAID0 },
334         { "raid1",      RT_RAID1 },
335         { "raid-1",     RT_RAID1 },
336         { "mirror",     RT_RAID1 },
337         { "raid1e",     RT_RAID1E },
338         { "raid-1e",    RT_RAID1E },
339         { NULL,         0 },
340 };
341
342 struct config_id_state {
343         struct mpt_standalone_disk *sdisks;
344         struct mpt_drive_list *list;
345         CONFIG_PAGE_IOC_2 *ioc2;
346         U8      target_id;
347         int     nsdisks;
348 };
349
350 struct drive_info {
351         CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
352         struct mpt_standalone_disk *sdisk;
353 };
354
355 struct volume_info {
356         int     drive_count;
357         struct drive_info *drives;
358 };
359
360 /* Parse a comma-separated list of drives for a volume. */
361 static int
362 parse_volume(int fd, int raid_type, struct config_id_state *state,
363     char *volume_str, struct volume_info *info)
364 {
365         struct drive_info *dinfo;
366         U8 PhysDiskNum;
367         char *cp;
368         int count, error, i;
369
370         cp = volume_str;
371         for (count = 0; cp != NULL; count++) {
372                 cp = strchr(cp, ',');
373                 if (cp != NULL) {
374                         cp++;
375                         if (*cp == ',') {
376                                 warnx("Invalid drive list '%s'", volume_str);
377                                 return (EINVAL);
378                         }
379                 }
380         }
381
382         /* Validate the number of drives for this volume. */
383         switch (raid_type) {
384         case RT_RAID0:
385                 if (count < 2) {
386                         warnx("RAID0 requires at least 2 drives in each "
387                             "array");
388                         return (EINVAL);
389                 }
390                 break;
391         case RT_RAID1:
392                 if (count != 2) {
393                         warnx("RAID1 requires exactly 2 drives in each "
394                             "array");
395                         return (EINVAL);
396                 }
397                 break;
398         case RT_RAID1E:
399                 if (count < 3) {
400                         warnx("RAID1E requires at least 3 drives in each "
401                             "array");
402                         return (EINVAL);
403                 }
404                 break;
405         }
406
407         /* Validate each drive. */
408         info->drives = calloc(count, sizeof(struct drive_info));
409         info->drive_count = count;
410         for (dinfo = info->drives; (cp = strsep(&volume_str, ",")) != NULL;
411              dinfo++) {
412                 /* If this drive is already a RAID phys just fetch the info. */
413                 error = mpt_lookup_drive(state->list, cp, &PhysDiskNum);
414                 if (error == 0) {
415                         dinfo->info = mpt_pd_info(fd, PhysDiskNum, NULL);
416                         if (dinfo->info == NULL)
417                                 return (errno);
418                         continue;
419                 }
420
421                 /* See if it is a standalone disk. */
422                 if (mpt_lookup_standalone_disk(cp, state->sdisks,
423                     state->nsdisks, &i) < 0) {
424                         error = errno;
425                         warn("Unable to lookup drive %s", cp);
426                         return (error);
427                 }
428                 dinfo->sdisk = &state->sdisks[i];
429
430                 /* Lock the disk, we will create phys disk pages later. */
431                 if (mpt_lock_physdisk(dinfo->sdisk) < 0)
432                         return (errno);
433         }
434
435         return (0);
436 }
437
438 /*
439  * Add RAID physdisk pages for any standalone disks that a volume is
440  * going to use.
441  */
442 static int
443 add_drives(int fd, struct volume_info *info, int verbose)
444 {
445         struct drive_info *dinfo;
446         U8 PhysDiskNum;
447         int error, i;
448
449         for (i = 0, dinfo = info->drives; i < info->drive_count;
450              i++, dinfo++) {
451                 if (dinfo->info == NULL) {
452                         if (mpt_create_physdisk(fd, dinfo->sdisk,
453                             &PhysDiskNum) < 0) {
454                                 error = errno;
455                                 warn(
456                             "Failed to create physical disk page for %s",
457                                     dinfo->sdisk->devname);
458                                 return (error);
459                         }
460                         if (verbose)
461                                 printf("Added drive %s with PhysDiskNum %u\n",
462                                     dinfo->sdisk->devname, PhysDiskNum);
463
464                         dinfo->info = mpt_pd_info(fd, PhysDiskNum, NULL);
465                         if (dinfo->info == NULL)
466                                 return (errno);
467                 }
468         }
469         return (0);
470 }
471
472 /*
473  * Find the next free target ID assuming that 'target_id' is the last
474  * one used.  'target_id' should be 0xff for the initial test.
475  */
476 static U8
477 find_next_volume(struct config_id_state *state)
478 {
479         CONFIG_PAGE_IOC_2_RAID_VOL *vol;
480         int i;
481
482 restart:
483         /* Assume the current one is used. */
484         state->target_id++;
485
486         /* Search drives first. */
487         for (i = 0; i < state->nsdisks; i++)
488                 if (state->sdisks[i].target == state->target_id)
489                         goto restart;
490         for (i = 0; i < state->list->ndrives; i++)
491                 if (state->list->drives[i]->PhysDiskID == state->target_id)
492                         goto restart;
493
494         /* Search volumes second. */
495         vol = state->ioc2->RaidVolume;
496         for (i = 0; i < state->ioc2->NumActiveVolumes; vol++, i++)
497                 if (vol->VolumeID == state->target_id)
498                         goto restart;
499
500         return (state->target_id);
501 }
502
503 /* Create a volume and populate it with drives. */
504 static CONFIG_PAGE_RAID_VOL_0 *
505 build_volume(int fd, struct volume_info *info, int raid_type, long stripe_size,
506     struct config_id_state *state, int verbose)
507 {
508         CONFIG_PAGE_HEADER header;
509         CONFIG_PAGE_RAID_VOL_0 *vol;
510         RAID_VOL0_PHYS_DISK *rdisk;
511         struct drive_info *dinfo;
512         U32 MinLBA;
513         uint64_t MaxLBA;
514         size_t page_size;
515         int error, i;
516
517         error = mpt_read_config_page_header(fd, MPI_CONFIG_PAGETYPE_RAID_VOLUME,
518             0, 0, &header, NULL);
519         if (error) {
520                 errno = error;
521                 return (NULL);
522         }
523         if (header.PageVersion > MPI_RAIDVOLPAGE0_PAGEVERSION) {
524                 warnx("Unsupported RAID volume page 0 version %d",
525                     header.PageVersion);
526                 errno = EOPNOTSUPP;
527                 return (NULL);
528         }
529         page_size = sizeof(CONFIG_PAGE_RAID_VOL_0) +
530             sizeof(RAID_VOL0_PHYS_DISK) * (info->drive_count - 1);
531         vol = calloc(1, page_size);
532         if (vol == NULL)
533                 return (NULL);
534
535         /* Header */
536         vol->Header.PageType = MPI_CONFIG_PAGETYPE_RAID_VOLUME;
537         vol->Header.PageNumber = 0;
538         vol->Header.PageLength = page_size / 4;
539
540         /* Properties */
541         vol->VolumeID = find_next_volume(state);
542         vol->VolumeBus = 0;
543         vol->VolumeIOC = 0;     /* XXX */
544         vol->VolumeStatus.Flags = MPI_RAIDVOL0_STATUS_FLAG_ENABLED;
545         vol->VolumeStatus.State = MPI_RAIDVOL0_STATUS_STATE_OPTIMAL;
546         vol->VolumeSettings.Settings = MPI_RAIDVOL0_SETTING_USE_DEFAULTS;
547         vol->VolumeSettings.HotSparePool = MPI_RAID_HOT_SPARE_POOL_0;
548         vol->NumPhysDisks = info->drive_count;
549
550         /* Find the smallest drive. */
551         MinLBA = info->drives[0].info->MaxLBA;
552         for (i = 1; i < info->drive_count; i++)
553                 if (info->drives[i].info->MaxLBA < MinLBA)
554                         MinLBA = info->drives[i].info->MaxLBA;
555
556         /*
557          * Now chop off 512MB at the end to leave room for the
558          * metadata.  The controller might only use 64MB, but we just
559          * chop off the max to be simple.
560          */
561         MinLBA -= (512 * 1024 * 1024) / 512;
562
563         switch (raid_type) {
564         case RT_RAID0:
565                 vol->VolumeType = MPI_RAID_VOL_TYPE_IS;
566                 vol->StripeSize = stripe_size / 512;
567                 MaxLBA = (uint64_t)MinLBA * info->drive_count;
568                 break;
569         case RT_RAID1:
570                 vol->VolumeType = MPI_RAID_VOL_TYPE_IM;
571                 MaxLBA = (uint64_t)MinLBA * (info->drive_count / 2);
572                 break;
573         case RT_RAID1E:
574                 vol->VolumeType = MPI_RAID_VOL_TYPE_IME;
575                 vol->StripeSize = stripe_size / 512;
576                 MaxLBA = (uint64_t)MinLBA * info->drive_count / 2;
577                 break;
578         default:
579                 /* Pacify gcc. */
580                 abort();                
581         }
582
583         /*
584          * If the controller doesn't support 64-bit addressing and the
585          * new volume is larger than 2^32 blocks, warn the user and
586          * truncate the volume.
587          */
588         if (MaxLBA >> 32 != 0 &&
589             !(state->ioc2->CapabilitiesFlags &
590             MPI_IOCPAGE2_CAP_FLAGS_RAID_64_BIT_ADDRESSING)) {
591                 warnx(
592             "Controller does not support volumes > 2TB, truncating volume.");
593                 MaxLBA = 0xffffffff;
594         }
595         vol->MaxLBA = MaxLBA;
596         vol->MaxLBAHigh = MaxLBA >> 32;
597
598         /* Populate drives. */
599         for (i = 0, dinfo = info->drives, rdisk = vol->PhysDisk;
600              i < info->drive_count; i++, dinfo++, rdisk++) {
601                 if (verbose)
602                         printf("Adding drive %u (%u:%u) to volume %u:%u\n",
603                             dinfo->info->PhysDiskNum, dinfo->info->PhysDiskBus,
604                             dinfo->info->PhysDiskID, vol->VolumeBus,
605                             vol->VolumeID);
606                 if (raid_type == RT_RAID1) {
607                         if (i == 0)
608                                 rdisk->PhysDiskMap =
609                                     MPI_RAIDVOL0_PHYSDISK_PRIMARY;
610                         else
611                                 rdisk->PhysDiskMap =
612                                     MPI_RAIDVOL0_PHYSDISK_SECONDARY;
613                 } else
614                         rdisk->PhysDiskMap = i;
615                 rdisk->PhysDiskNum = dinfo->info->PhysDiskNum;
616         }
617
618         return (vol);
619 }
620
621 static int
622 create_volume(int ac, char **av)
623 {
624         CONFIG_PAGE_RAID_VOL_0 *vol;
625         struct config_id_state state;
626         struct volume_info *info;
627         long stripe_size;
628         int ch, error, fd, i, quick, raid_type, verbose;
629 #ifdef DEBUG
630         int dump;
631 #endif
632
633         if (ac < 2) {
634                 warnx("create: volume type required");
635                 return (EINVAL);
636         }
637         
638         fd = mpt_open(mpt_unit);
639         if (fd < 0) {
640                 error = errno;
641                 warn("mpt_open");
642                 return (error);
643         }
644
645         /* Lookup the RAID type first. */
646         raid_type = -1;
647         for (i = 0; raid_type_table[i].name != NULL; i++)
648                 if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
649                         raid_type = raid_type_table[i].raid_type;
650                         break;
651                 }
652
653         if (raid_type == -1) {
654                 warnx("Unknown or unsupported volume type %s", av[1]);
655                 close(fd);
656                 return (EINVAL);
657         }
658
659         /* Parse any options. */
660         optind = 2;
661 #ifdef DEBUG
662         dump = 0;
663 #endif
664         quick = 0;
665         verbose = 0;
666         stripe_size = 64 * 1024;
667
668         while ((ch = getopt(ac, av, "dqs:v")) != -1) {
669                 switch (ch) {
670 #ifdef DEBUG
671                 case 'd':
672                         dump = 1;
673                         break;
674 #endif
675                 case 'q':
676                         quick = 1;
677                         break;
678                 case 's':
679                         stripe_size = dehumanize(optarg);
680                         if ((stripe_size < 512) || (!powerof2(stripe_size))) {
681                                 warnx("Invalid stripe size %s", optarg);
682                                 close(fd);
683                                 return (EINVAL);
684                         }
685                         break;
686                 case 'v':
687                         verbose = 1;
688                         break;
689                 case '?':
690                 default:
691                         close(fd);
692                         return (EINVAL);
693                 }
694         }
695         ac -= optind;
696         av += optind;
697
698         /* Fetch existing config data. */
699         state.ioc2 = mpt_read_ioc_page(fd, 2, NULL);
700         if (state.ioc2 == NULL) {
701                 error = errno;
702                 warn("Failed to read volume list");
703                 close(fd);
704                 return (error);
705         }
706         state.list = mpt_pd_list(fd);
707         if (state.list == NULL) {
708                 close(fd);
709                 return (errno);
710         }
711         error = mpt_fetch_disks(fd, &state.nsdisks, &state.sdisks);
712         if (error) {
713                 warn("Failed to fetch standalone disk list");
714                 close(fd);
715                 return (error);
716         }       
717         state.target_id = 0xff;
718         
719         /* Parse the drive list. */
720         if (ac != 1) {
721                 warnx("Exactly one drive list is required");
722                 close(fd);
723                 return (EINVAL);
724         }
725         info = calloc(1, sizeof(*info));
726         if (info == NULL) {
727                 close(fd);
728                 return (ENOMEM);
729         }
730         error = parse_volume(fd, raid_type, &state, av[0], info);
731         if (error) {
732                 free(info);
733                 close(fd);
734                 return (error);
735         }
736
737         /* Create RAID physdisk pages for standalone disks. */
738         error = add_drives(fd, info, verbose);
739         if (error) {
740                 free(info);
741                 close(fd);
742                 return (error);
743         }
744
745         /* Build the volume. */
746         vol = build_volume(fd, info, raid_type, stripe_size, &state, verbose);
747         if (vol == NULL) {
748                 free(info);
749                 close(fd);
750                 return (errno);
751         }
752
753 #ifdef DEBUG
754         if (dump) {
755                 dump_config(vol);
756                 goto skip;
757         }
758 #endif
759
760         /* Send the new volume to the controller. */
761         error = mpt_raid_action(fd, MPI_RAID_ACTION_CREATE_VOLUME, vol->VolumeBus,
762             vol->VolumeID, 0, quick ? MPI_RAID_ACTION_ADATA_DO_NOT_SYNC : 0,
763             vol, vol->Header.PageLength * 4, NULL, NULL, 0, NULL, NULL, 1);
764         if (error) {
765                 errno = error;
766                 warn("Failed to add volume");
767                 free(info);
768                 close(fd);
769                 return (error);
770         }
771
772 #ifdef DEBUG
773 skip:
774 #endif
775         mpt_rescan_bus(vol->VolumeBus, vol->VolumeID);
776
777         /* Clean up. */
778         free(vol);
779         free(info);
780         free(state.sdisks);
781         mpt_free_pd_list(state.list);
782         free(state.ioc2);
783         close(fd);
784
785         return (0);
786 }
787 MPT_COMMAND(top, create, create_volume);
788
789 static int
790 delete_volume(int ac, char **av)
791 {
792         U8 VolumeBus, VolumeID;
793         int error, fd;
794
795         if (ac != 2) {
796                 warnx("delete: volume required");
797                 return (EINVAL);
798         }
799
800         fd = mpt_open(mpt_unit);
801         if (fd < 0) {
802                 error = errno;
803                 warn("mpt_open");
804                 return (error);
805         }
806
807         error = mpt_lookup_volume(fd, av[1], &VolumeBus, &VolumeID);
808         if (error) {
809                 warnc(error, "Invalid volume %s", av[1]);
810                 close(fd);
811                 return (error);
812         }
813
814         if (mpt_lock_volume(VolumeBus, VolumeID) < 0) {
815                 close(fd);
816                 return (errno);
817         }
818
819         error = mpt_raid_action(fd, MPI_RAID_ACTION_DELETE_VOLUME, VolumeBus,
820             VolumeID, 0, MPI_RAID_ACTION_ADATA_DEL_PHYS_DISKS |
821             MPI_RAID_ACTION_ADATA_ZERO_LBA0, NULL, 0, NULL, NULL, 0, NULL,
822             NULL, 0);
823         if (error) {
824                 warnc(error, "Failed to delete volume");
825                 close(fd);
826                 return (error);
827         }
828
829         mpt_rescan_bus(-1, -1);
830         close(fd);
831
832         return (0);
833 }
834 MPT_COMMAND(top, delete, delete_volume);
835
836 static int
837 find_volume_spare_pool(int fd, const char *name, int *pool)
838 {
839         CONFIG_PAGE_RAID_VOL_0 *info;
840         CONFIG_PAGE_IOC_2 *ioc2;
841         CONFIG_PAGE_IOC_2_RAID_VOL *vol;
842         U8 VolumeBus, VolumeID;
843         int error, i, j, new_pool, pool_count[7];
844
845         error = mpt_lookup_volume(fd, name, &VolumeBus, &VolumeID);
846         if (error) {
847                 warnc(error, "Invalid volume %s", name);
848                 return (error);
849         }
850
851         info = mpt_vol_info(fd, VolumeBus, VolumeID, NULL);
852         if (info == NULL)
853                 return (errno);
854
855         /*
856          * Check for an existing pool other than pool 0 (used for
857          * global spares).
858          */
859         if ((info->VolumeSettings.HotSparePool & ~MPI_RAID_HOT_SPARE_POOL_0) !=
860             0) {
861                 *pool = 1 << (ffs(info->VolumeSettings.HotSparePool &
862                     ~MPI_RAID_HOT_SPARE_POOL_0) - 1);
863                 free(info);
864                 return (0);
865         }
866         free(info);
867
868         /*
869          * Try to find a free pool.  First, figure out which pools are
870          * in use.
871          */
872         ioc2 = mpt_read_ioc_page(fd, 2, NULL);
873         if (ioc2 == NULL) {
874                 error = errno;
875                 warn("Failed to fetch volume list");
876                 return (error);
877         }
878         bzero(pool_count, sizeof(pool_count));  
879         vol = ioc2->RaidVolume;
880         for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
881                 info = mpt_vol_info(fd, vol->VolumeBus, vol->VolumeID, NULL);
882                 if (info == NULL)
883                         return (errno);
884                 for (j = 0; j < 7; j++)
885                         if (info->VolumeSettings.HotSparePool & (1 << (j + 1)))
886                                 pool_count[j]++;
887                 free(info);
888         }
889         free(ioc2);
890
891         /* Find the pool with the lowest use count. */
892         new_pool = 0;
893         for (i = 1; i < 7; i++)
894                 if (pool_count[i] < pool_count[new_pool])
895                         new_pool = i;
896         new_pool++;
897
898         /* Add this pool to the volume. */
899         info = mpt_vol_info(fd, VolumeBus, VolumeID, NULL);
900         if (info == NULL)
901                 return (error);
902         info->VolumeSettings.HotSparePool |= (1 << new_pool);
903         error = mpt_raid_action(fd, MPI_RAID_ACTION_CHANGE_VOLUME_SETTINGS,
904             VolumeBus, VolumeID, 0, *(U32 *)&info->VolumeSettings, NULL, 0,
905             NULL, NULL, 0, NULL, NULL, 0);
906         if (error) {
907                 warnx("Failed to add spare pool %d to %s", new_pool,
908                     mpt_volume_name(VolumeBus, VolumeID));
909                 free(info);
910                 return (error);
911         }
912         free(info);
913
914         *pool = (1 << new_pool);
915         return (0);
916 }
917
918 static int
919 add_spare(int ac, char **av)
920 {
921         CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
922         struct mpt_standalone_disk *sdisks;
923         struct mpt_drive_list *list;
924         U8 PhysDiskNum;
925         int error, fd, i, nsdisks, pool;
926
927         if (ac < 2) {
928                 warnx("add spare: drive required");
929                 return (EINVAL);
930         }
931         if (ac > 3) {
932                 warnx("add spare: extra arguments");
933                 return (EINVAL);
934         }
935
936         fd = mpt_open(mpt_unit);
937         if (fd < 0) {
938                 error = errno;
939                 warn("mpt_open");
940                 return (error);
941         }
942
943         if (ac == 3) {
944                 error = find_volume_spare_pool(fd, av[2], &pool);
945                 if (error) {
946                         close(fd);
947                         return (error);
948                 }
949         } else
950                 pool = MPI_RAID_HOT_SPARE_POOL_0;
951
952         list = mpt_pd_list(fd);
953         if (list == NULL)
954                 return (errno);
955
956         error = mpt_lookup_drive(list, av[1], &PhysDiskNum);
957         if (error) {
958                 error = mpt_fetch_disks(fd, &nsdisks, &sdisks);
959                 if (error != 0) {
960                         warn("Failed to fetch standalone disk list");
961                         mpt_free_pd_list(list);
962                         close(fd);
963                         return (error);
964                 }
965
966                 if (mpt_lookup_standalone_disk(av[1], sdisks, nsdisks, &i) <
967                     0) {
968                         error = errno;
969                         warn("Unable to lookup drive %s", av[1]);
970                         mpt_free_pd_list(list);
971                         close(fd);
972                         return (error);
973                 }
974
975                 if (mpt_lock_physdisk(&sdisks[i]) < 0) {
976                         mpt_free_pd_list(list);
977                         close(fd);
978                         return (errno);
979                 }
980
981                 if (mpt_create_physdisk(fd, &sdisks[i], &PhysDiskNum) < 0) {
982                         error = errno;
983                         warn("Failed to create physical disk page");
984                         mpt_free_pd_list(list);
985                         close(fd);
986                         return (error);
987                 }
988                 free(sdisks);
989         }
990         mpt_free_pd_list(list);
991
992         info = mpt_pd_info(fd, PhysDiskNum, NULL);
993         if (info == NULL) {
994                 error = errno;
995                 warn("Failed to fetch drive info");
996                 close(fd);
997                 return (error);
998         }
999
1000         info->PhysDiskSettings.HotSparePool = pool;
1001         error = mpt_raid_action(fd, MPI_RAID_ACTION_CHANGE_PHYSDISK_SETTINGS, 0,
1002             0, PhysDiskNum, *(U32 *)&info->PhysDiskSettings, NULL, 0, NULL,
1003             NULL, 0, NULL, NULL, 0);
1004         if (error) {
1005                 warnc(error, "Failed to assign spare");
1006                 close(fd);
1007                 return (error);
1008         }
1009
1010         free(info);
1011         close(fd);
1012
1013         return (0);
1014 }
1015 MPT_COMMAND(top, add, add_spare);
1016
1017 static int
1018 remove_spare(int ac, char **av)
1019 {
1020         CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
1021         struct mpt_drive_list *list;
1022         U8 PhysDiskNum;
1023         int error, fd;
1024
1025         if (ac != 2) {
1026                 warnx("remove spare: drive required");
1027                 return (EINVAL);
1028         }
1029
1030         fd = mpt_open(mpt_unit);
1031         if (fd < 0) {
1032                 error = errno;
1033                 warn("mpt_open");
1034                 return (error);
1035         }
1036
1037         list = mpt_pd_list(fd);
1038         if (list == NULL) {
1039                 close(fd);
1040                 return (errno);
1041         }
1042
1043         error = mpt_lookup_drive(list, av[1], &PhysDiskNum);
1044         if (error) {
1045                 warn("Failed to find drive %s", av[1]);
1046                 close(fd);
1047                 return (error);
1048         }
1049         mpt_free_pd_list(list);
1050
1051         
1052         info = mpt_pd_info(fd, PhysDiskNum, NULL);
1053         if (info == NULL) {
1054                 error = errno;
1055                 warn("Failed to fetch drive info");
1056                 close(fd);
1057                 return (error);
1058         }
1059
1060         if (info->PhysDiskSettings.HotSparePool == 0) {
1061                 warnx("Drive %u is not a hot spare", PhysDiskNum);
1062                 free(info);
1063                 close(fd);
1064                 return (EINVAL);
1065         }
1066
1067         if (mpt_delete_physdisk(fd, PhysDiskNum) < 0) {
1068                 error = errno;
1069                 warn("Failed to delete physical disk page");
1070                 free(info);
1071                 close(fd);
1072                 return (error);
1073         }
1074
1075         mpt_rescan_bus(info->PhysDiskBus, info->PhysDiskID);
1076         free(info);
1077         close(fd);
1078
1079         return (0);
1080 }
1081 MPT_COMMAND(top, remove, remove_spare);
1082
1083 #ifdef DEBUG
1084 MPT_TABLE(top, pd);
1085
1086 static int
1087 pd_create(int ac, char **av)
1088 {
1089         struct mpt_standalone_disk *disks;
1090         int error, fd, i, ndisks;
1091         U8 PhysDiskNum;
1092
1093         if (ac != 2) {
1094                 warnx("pd create: drive required");
1095                 return (EINVAL);
1096         }
1097
1098         fd = mpt_open(mpt_unit);
1099         if (fd < 0) {
1100                 error = errno;
1101                 warn("mpt_open");
1102                 return (error);
1103         }
1104
1105         error = mpt_fetch_disks(fd, &ndisks, &disks);
1106         if (error != 0) {
1107                 warn("Failed to fetch standalone disk list");
1108                 return (error);
1109         }
1110
1111         if (mpt_lookup_standalone_disk(av[1], disks, ndisks, &i) < 0) {
1112                 error = errno;
1113                 warn("Unable to lookup drive");
1114                 return (error);
1115         }
1116
1117         if (mpt_lock_physdisk(&disks[i]) < 0)
1118                 return (errno);
1119
1120         if (mpt_create_physdisk(fd, &disks[i], &PhysDiskNum) < 0) {
1121                 error = errno;
1122                 warn("Failed to create physical disk page");
1123                 return (error);
1124         }
1125         free(disks);
1126
1127         printf("Added drive %s with PhysDiskNum %u\n", av[1], PhysDiskNum);
1128
1129         close(fd);
1130
1131         return (0);
1132 }
1133 MPT_COMMAND(pd, create, pd_create);
1134
1135 static int
1136 pd_delete(int ac, char **av)
1137 {
1138         CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
1139         struct mpt_drive_list *list;
1140         int error, fd;
1141         U8 PhysDiskNum;
1142
1143         if (ac != 2) {
1144                 warnx("pd delete: drive required");
1145                 return (EINVAL);
1146         }
1147
1148         fd = mpt_open(mpt_unit);
1149         if (fd < 0) {
1150                 error = errno;
1151                 warn("mpt_open");
1152                 return (error);
1153         }
1154
1155         list = mpt_pd_list(fd);
1156         if (list == NULL)
1157                 return (errno);
1158
1159         if (mpt_lookup_drive(list, av[1], &PhysDiskNum) < 0) {
1160                 error = errno;
1161                 warn("Failed to find drive %s", av[1]);
1162                 return (error);
1163         }
1164         mpt_free_pd_list(list);
1165
1166         info = mpt_pd_info(fd, PhysDiskNum, NULL);
1167         if (info == NULL) {
1168                 error = errno;
1169                 warn("Failed to fetch drive info");
1170                 return (error);
1171         }
1172
1173         if (mpt_delete_physdisk(fd, PhysDiskNum) < 0) {
1174                 error = errno;
1175                 warn("Failed to delete physical disk page");
1176                 return (error);
1177         }
1178
1179         mpt_rescan_bus(info->PhysDiskBus, info->PhysDiskID);
1180         free(info);
1181         close(fd);
1182
1183         return (0);
1184 }
1185 MPT_COMMAND(pd, delete, pd_delete);
1186
1187 /* Display raw data about a volume config. */
1188 static void
1189 dump_config(CONFIG_PAGE_RAID_VOL_0 *vol)
1190 {
1191         int i;
1192
1193         printf("Volume Configuration (Debug):\n");
1194         printf(
1195    " Page Header: Type 0x%02x Number 0x%02x Length 0x%02x(%u) Version 0x%02x\n",
1196             vol->Header.PageType, vol->Header.PageNumber,
1197             vol->Header.PageLength, vol->Header.PageLength * 4,
1198             vol->Header.PageVersion);
1199         printf("     Address: %d:%d IOC %d\n", vol->VolumeBus, vol->VolumeID,
1200             vol->VolumeIOC);
1201         printf("        Type: %d (%s)\n", vol->VolumeType,
1202             mpt_raid_level(vol->VolumeType));
1203         printf("      Status: %s (Flags 0x%02x)\n",
1204             mpt_volstate(vol->VolumeStatus.State), vol->VolumeStatus.Flags);
1205         printf("    Settings: 0x%04x (Spare Pools 0x%02x)\n",
1206             vol->VolumeSettings.Settings, vol->VolumeSettings.HotSparePool);
1207         printf("      MaxLBA: %ju\n", (uintmax_t)vol->MaxLBAHigh << 32 |
1208             vol->MaxLBA);
1209         printf(" Stripe Size: %ld\n", (long)vol->StripeSize * 512);
1210         printf(" %d Disks:\n", vol->NumPhysDisks);
1211
1212         for (i = 0; i < vol->NumPhysDisks; i++)
1213                 printf("    Disk %d: Num 0x%02x Map 0x%02x\n", i,
1214                     vol->PhysDisk[i].PhysDiskNum, vol->PhysDisk[i].PhysDiskMap);
1215 }
1216
1217 static int
1218 debug_config(int ac, char **av)
1219 {
1220         CONFIG_PAGE_RAID_VOL_0 *vol;
1221         U8 VolumeBus, VolumeID;
1222         int error, fd;
1223
1224         if (ac != 2) {
1225                 warnx("debug: volume required");
1226                 return (EINVAL);
1227         }
1228
1229         fd = mpt_open(mpt_unit);
1230         if (fd < 0) {
1231                 error = errno;
1232                 warn("mpt_open");
1233                 return (error);
1234         }
1235
1236         error = mpt_lookup_volume(fd, av[1], &VolumeBus, &VolumeID);
1237         if (error) {
1238                 warnc(error, "Invalid volume: %s", av[1]);
1239                 return (error);
1240         }
1241
1242         vol = mpt_vol_info(fd, VolumeBus, VolumeID, NULL);
1243         if (vol == NULL) {
1244                 error = errno;
1245                 warn("Failed to get volume info");
1246                 return (error);
1247         }
1248
1249         dump_config(vol);
1250         free(vol);
1251         close(fd);
1252
1253         return (0);
1254 }
1255 MPT_COMMAND(top, debug, debug_config);
1256 #endif