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