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