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