xref: /freebsd/usr.sbin/mptutil/mpt_config.c (revision cfd6422a5217410fbd66f7a7a8a64d9d85e61229)
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
1259