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