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