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