1196212Sscottl/*-
2196212Sscottl * Copyright (c) 2008 Yahoo!, Inc.
3196212Sscottl * All rights reserved.
4196212Sscottl * Written by: John Baldwin <jhb@FreeBSD.org>
5196212Sscottl *
6196212Sscottl * Redistribution and use in source and binary forms, with or without
7196212Sscottl * modification, are permitted provided that the following conditions
8196212Sscottl * are met:
9196212Sscottl * 1. Redistributions of source code must retain the above copyright
10196212Sscottl *    notice, this list of conditions and the following disclaimer.
11196212Sscottl * 2. Redistributions in binary form must reproduce the above copyright
12196212Sscottl *    notice, this list of conditions and the following disclaimer in the
13196212Sscottl *    documentation and/or other materials provided with the distribution.
14196212Sscottl * 3. Neither the name of the author nor the names of any co-contributors
15196212Sscottl *    may be used to endorse or promote products derived from this software
16196212Sscottl *    without specific prior written permission.
17196212Sscottl *
18196212Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19196212Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20196212Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21196212Sscottl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22196212Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23196212Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24196212Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25196212Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26196212Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27196212Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28196212Sscottl * SUCH DAMAGE.
29196212Sscottl */
30196212Sscottl
31196212Sscottl#include <sys/cdefs.h>
32196212Sscottl__RCSID("$FreeBSD: stable/11/usr.sbin/mptutil/mpt_config.c 332603 2018-04-16 16:24:36Z asomers $");
33196212Sscottl
34196212Sscottl#include <sys/param.h>
35196212Sscottl#include <sys/errno.h>
36196212Sscottl#include <err.h>
37196212Sscottl#include <fcntl.h>
38196212Sscottl#include <libutil.h>
39196212Sscottl#include <paths.h>
40196212Sscottl#ifdef DEBUG
41196212Sscottl#include <stdint.h>
42196212Sscottl#endif
43196212Sscottl#include <stdio.h>
44196212Sscottl#include <stdlib.h>
45196212Sscottl#include <string.h>
46196212Sscottl#include <unistd.h>
47196212Sscottl#include "mptutil.h"
48196212Sscottl
49196212Sscottl#ifdef DEBUG
50196212Sscottlstatic void	dump_config(CONFIG_PAGE_RAID_VOL_0 *vol);
51196212Sscottl#endif
52196212Sscottl
53196212Sscottlstatic long
54196212Sscottldehumanize(const char *value)
55196212Sscottl{
56196212Sscottl        char    *vtp;
57196212Sscottl        long    iv;
58196212Sscottl
59196212Sscottl        if (value == NULL)
60196212Sscottl                return (0);
61196212Sscottl        iv = strtoq(value, &vtp, 0);
62196212Sscottl        if (vtp == value || (vtp[0] != '\0' && vtp[1] != '\0')) {
63196212Sscottl                return (0);
64196212Sscottl        }
65196212Sscottl        switch (vtp[0]) {
66196212Sscottl        case 't': case 'T':
67196212Sscottl                iv *= 1024;
68332603Sasomers                /* FALLTHROUGH */
69196212Sscottl        case 'g': case 'G':
70196212Sscottl                iv *= 1024;
71332603Sasomers                /* FALLTHROUGH */
72196212Sscottl        case 'm': case 'M':
73196212Sscottl                iv *= 1024;
74332603Sasomers                /* FALLTHROUGH */
75196212Sscottl        case 'k': case 'K':
76196212Sscottl                iv *= 1024;
77332603Sasomers                /* FALLTHROUGH */
78196212Sscottl        case '\0':
79196212Sscottl                break;
80196212Sscottl        default:
81196212Sscottl                return (0);
82196212Sscottl        }
83196212Sscottl        return (iv);
84196212Sscottl}
85196212Sscottl
86196212Sscottl/*
87196212Sscottl * Lock the volume by opening its /dev device read/write.  This will
88196212Sscottl * only work if nothing else has it opened (including mounts).  We
89196212Sscottl * leak the fd on purpose since this application is not long-running.
90196212Sscottl */
91196212Sscottlint
92196212Sscottlmpt_lock_volume(U8 VolumeBus, U8 VolumeID)
93196212Sscottl{
94196212Sscottl	char path[MAXPATHLEN];
95196212Sscottl	struct mpt_query_disk qd;
96196212Sscottl	int error, vfd;
97196212Sscottl
98196212Sscottl	error = mpt_query_disk(VolumeBus, VolumeID, &qd);
99196212Sscottl	if (error == ENOENT)
100196212Sscottl		/*
101196212Sscottl		 * This means there isn't a CAM device associated with
102196212Sscottl		 * the volume, and thus it is already implicitly
103196212Sscottl		 * locked, so just return.
104196212Sscottl		 */
105196212Sscottl		return (0);
106196212Sscottl	if (error) {
107215046Sjhb		warnc(error, "Unable to lookup volume device name");
108215046Sjhb		return (error);
109196212Sscottl	}
110196212Sscottl	snprintf(path, sizeof(path), "%s%s", _PATH_DEV, qd.devname);
111196212Sscottl	vfd = open(path, O_RDWR);
112196212Sscottl	if (vfd < 0) {
113215046Sjhb		error = errno;
114196212Sscottl		warn("Unable to lock volume %s", qd.devname);
115215046Sjhb		return (error);
116196212Sscottl	}
117196212Sscottl	return (0);
118196212Sscottl}
119196212Sscottl
120196212Sscottlstatic int
121196212Sscottlmpt_lock_physdisk(struct mpt_standalone_disk *disk)
122196212Sscottl{
123196212Sscottl	char path[MAXPATHLEN];
124215046Sjhb	int dfd, error;
125196212Sscottl
126196212Sscottl	snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk->devname);
127196212Sscottl	dfd = open(path, O_RDWR);
128196212Sscottl	if (dfd < 0) {
129215046Sjhb		error = errno;
130196212Sscottl		warn("Unable to lock disk %s", disk->devname);
131215046Sjhb		return (error);
132196212Sscottl	}
133196212Sscottl	return (0);
134196212Sscottl}
135196212Sscottl
136196212Sscottlstatic int
137196212Sscottlmpt_lookup_standalone_disk(const char *name, struct mpt_standalone_disk *disks,
138196212Sscottl    int ndisks, int *index)
139196212Sscottl{
140196212Sscottl	char *cp;
141196212Sscottl	long bus, id;
142196212Sscottl	int i;
143196212Sscottl
144196212Sscottl	/* Check for a raw <bus>:<id> string. */
145196212Sscottl	bus = strtol(name, &cp, 0);
146196212Sscottl	if (*cp == ':') {
147196212Sscottl		id = strtol(cp + 1, &cp, 0);
148196212Sscottl		if (*cp == '\0') {
149196212Sscottl			if (bus < 0 || bus > 0xff || id < 0 || id > 0xff) {
150215046Sjhb				return (EINVAL);
151196212Sscottl			}
152196212Sscottl			for (i = 0; i < ndisks; i++) {
153196212Sscottl				if (disks[i].bus == (U8)bus &&
154196212Sscottl				    disks[i].target == (U8)id) {
155196212Sscottl					*index = i;
156196212Sscottl					return (0);
157196212Sscottl				}
158196212Sscottl			}
159215046Sjhb			return (ENOENT);
160196212Sscottl		}
161196212Sscottl	}
162196212Sscottl
163196212Sscottl	if (name[0] == 'd' && name[1] == 'a') {
164196212Sscottl		for (i = 0; i < ndisks; i++) {
165196212Sscottl			if (strcmp(name, disks[i].devname) == 0) {
166196212Sscottl				*index = i;
167196212Sscottl				return (0);
168196212Sscottl			}
169196212Sscottl		}
170215046Sjhb		return (ENOENT);
171196212Sscottl	}
172196212Sscottl
173215046Sjhb	return (EINVAL);
174196212Sscottl}
175196212Sscottl
176196212Sscottl/*
177196212Sscottl * Mark a standalone disk as being a physical disk.
178196212Sscottl */
179196212Sscottlstatic int
180196212Sscottlmpt_create_physdisk(int fd, struct mpt_standalone_disk *disk, U8 *PhysDiskNum)
181196212Sscottl{
182196212Sscottl	CONFIG_PAGE_HEADER header;
183196212Sscottl	CONFIG_PAGE_RAID_PHYS_DISK_0 *config_page;
184215046Sjhb	int error;
185196212Sscottl	U32 ActionData;
186196212Sscottl
187215046Sjhb	error = mpt_read_config_page_header(fd, MPI_CONFIG_PAGETYPE_RAID_PHYSDISK,
188215046Sjhb	    0, 0, &header, NULL);
189215046Sjhb	if (error)
190215046Sjhb		return (error);
191196212Sscottl	if (header.PageVersion > MPI_RAIDPHYSDISKPAGE0_PAGEVERSION) {
192196212Sscottl		warnx("Unsupported RAID physdisk page 0 version %d",
193196212Sscottl		    header.PageVersion);
194215046Sjhb		return (EOPNOTSUPP);
195196212Sscottl	}
196196212Sscottl	config_page = calloc(1, sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0));
197196212Sscottl	config_page->Header.PageType = MPI_CONFIG_PAGETYPE_RAID_PHYSDISK;
198196212Sscottl	config_page->Header.PageNumber = 0;
199196212Sscottl	config_page->Header.PageLength = sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0) /
200196212Sscottl	    4;
201196212Sscottl	config_page->PhysDiskIOC = 0;	/* XXX */
202196212Sscottl	config_page->PhysDiskBus = disk->bus;
203196212Sscottl	config_page->PhysDiskID = disk->target;
204196212Sscottl
205196212Sscottl	/* XXX: Enclosure info for PhysDiskSettings? */
206215046Sjhb	error = mpt_raid_action(fd, MPI_RAID_ACTION_CREATE_PHYSDISK, 0, 0, 0, 0,
207196212Sscottl	    config_page, sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0), NULL,
208215046Sjhb	    &ActionData, sizeof(ActionData), NULL, NULL, 1);
209215046Sjhb	if (error)
210215046Sjhb		return (error);
211196212Sscottl	*PhysDiskNum = ActionData & 0xff;
212196212Sscottl	return (0);
213196212Sscottl}
214196212Sscottl
215196212Sscottlstatic int
216196212Sscottlmpt_delete_physdisk(int fd, U8 PhysDiskNum)
217196212Sscottl{
218196212Sscottl
219196212Sscottl	return (mpt_raid_action(fd, MPI_RAID_ACTION_DELETE_PHYSDISK, 0, 0,
220196212Sscottl	    PhysDiskNum, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0));
221196212Sscottl}
222196212Sscottl
223196212Sscottl/*
224196212Sscottl * MPT's firmware does not have a clear command.  Instead, we
225196212Sscottl * implement it by deleting each array and disk by hand.
226196212Sscottl */
227196212Sscottlstatic int
228196212Sscottlclear_config(int ac, char **av)
229196212Sscottl{
230196212Sscottl	CONFIG_PAGE_IOC_2 *ioc2;
231196212Sscottl	CONFIG_PAGE_IOC_2_RAID_VOL *vol;
232196212Sscottl	CONFIG_PAGE_IOC_3 *ioc3;
233196212Sscottl	IOC_3_PHYS_DISK *disk;
234196212Sscottl	CONFIG_PAGE_IOC_5 *ioc5;
235196212Sscottl	IOC_5_HOT_SPARE *spare;
236215046Sjhb	int ch, error, fd, i;
237196212Sscottl
238196212Sscottl	fd = mpt_open(mpt_unit);
239196212Sscottl	if (fd < 0) {
240215046Sjhb		error = errno;
241196212Sscottl		warn("mpt_open");
242215046Sjhb		return (error);
243196212Sscottl	}
244196212Sscottl
245196212Sscottl	ioc2 = mpt_read_ioc_page(fd, 2, NULL);
246196212Sscottl	if (ioc2 == NULL) {
247215046Sjhb		error = errno;
248196212Sscottl		warn("Failed to fetch volume list");
249332603Sasomers		close(fd);
250215046Sjhb		return (error);
251196212Sscottl	}
252196212Sscottl
253196212Sscottl	/* Lock all the volumes first. */
254196212Sscottl	vol = ioc2->RaidVolume;
255196212Sscottl	for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
256196212Sscottl		if (mpt_lock_volume(vol->VolumeBus, vol->VolumeID) < 0) {
257196212Sscottl			warnx("Volume %s is busy and cannot be deleted",
258196212Sscottl			    mpt_volume_name(vol->VolumeBus, vol->VolumeID));
259332603Sasomers			free(ioc2);
260332603Sasomers			close(fd);
261196212Sscottl			return (EBUSY);
262196212Sscottl		}
263196212Sscottl	}
264196212Sscottl
265196212Sscottl	printf(
266196212Sscottl	    "Are you sure you wish to clear the configuration on mpt%u? [y/N] ",
267196212Sscottl	    mpt_unit);
268196212Sscottl	ch = getchar();
269196212Sscottl	if (ch != 'y' && ch != 'Y') {
270196212Sscottl		printf("\nAborting\n");
271332603Sasomers		free(ioc2);
272332603Sasomers		close(fd);
273196212Sscottl		return (0);
274196212Sscottl	}
275196212Sscottl
276196212Sscottl	/* Delete all the volumes. */
277196212Sscottl	vol = ioc2->RaidVolume;
278215046Sjhb	for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
279215046Sjhb		error = mpt_raid_action(fd, MPI_RAID_ACTION_DELETE_VOLUME,
280196212Sscottl		    vol->VolumeBus, vol->VolumeID, 0,
281196212Sscottl		    MPI_RAID_ACTION_ADATA_DEL_PHYS_DISKS |
282196212Sscottl		    MPI_RAID_ACTION_ADATA_ZERO_LBA0, NULL, 0, NULL, NULL, 0,
283215046Sjhb		    NULL, NULL, 0);
284215046Sjhb		if (error)
285215046Sjhb			warnc(error, "Failed to delete volume %s",
286196212Sscottl			    mpt_volume_name(vol->VolumeBus, vol->VolumeID));
287215046Sjhb	}
288196212Sscottl	free(ioc2);
289196212Sscottl
290196212Sscottl	/* Delete all the spares. */
291196212Sscottl	ioc5 = mpt_read_ioc_page(fd, 5, NULL);
292196212Sscottl	if (ioc5 == NULL)
293196212Sscottl		warn("Failed to fetch spare list");
294196212Sscottl	else {
295196212Sscottl		spare = ioc5->HotSpare;
296196212Sscottl		for (i = 0; i < ioc5->NumHotSpares; spare++, i++)
297196212Sscottl			if (mpt_delete_physdisk(fd, spare->PhysDiskNum) < 0)
298196212Sscottl				warn("Failed to delete physical disk %d",
299196212Sscottl				    spare->PhysDiskNum);
300196212Sscottl		free(ioc5);
301196212Sscottl	}
302196212Sscottl
303196212Sscottl	/* Delete any RAID physdisks that may be left. */
304196212Sscottl	ioc3 = mpt_read_ioc_page(fd, 3, NULL);
305196212Sscottl	if (ioc3 == NULL)
306196212Sscottl		warn("Failed to fetch drive list");
307196212Sscottl	else {
308196212Sscottl		disk = ioc3->PhysDisk;
309196212Sscottl		for (i = 0; i < ioc3->NumPhysDisks; disk++, i++)
310196212Sscottl			if (mpt_delete_physdisk(fd, disk->PhysDiskNum) < 0)
311196212Sscottl				warn("Failed to delete physical disk %d",
312196212Sscottl				    disk->PhysDiskNum);
313196212Sscottl		free(ioc3);
314196212Sscottl	}
315196212Sscottl
316196212Sscottl	printf("mpt%d: Configuration cleared\n", mpt_unit);
317196212Sscottl	mpt_rescan_bus(-1, -1);
318196212Sscottl	close(fd);
319196212Sscottl
320196212Sscottl	return (0);
321196212Sscottl}
322196212SscottlMPT_COMMAND(top, clear, clear_config);
323196212Sscottl
324196212Sscottl#define	RT_RAID0	0
325196212Sscottl#define	RT_RAID1	1
326196212Sscottl#define	RT_RAID1E	2
327196212Sscottl
328196212Sscottlstatic struct raid_type_entry {
329196212Sscottl	const char *name;
330196212Sscottl	int	raid_type;
331196212Sscottl} raid_type_table[] = {
332196212Sscottl	{ "raid0",	RT_RAID0 },
333196212Sscottl	{ "raid-0",	RT_RAID0 },
334196212Sscottl	{ "raid1",	RT_RAID1 },
335196212Sscottl	{ "raid-1",	RT_RAID1 },
336196212Sscottl	{ "mirror",	RT_RAID1 },
337196212Sscottl	{ "raid1e",	RT_RAID1E },
338196212Sscottl	{ "raid-1e",	RT_RAID1E },
339196212Sscottl	{ NULL,		0 },
340196212Sscottl};
341196212Sscottl
342196212Sscottlstruct config_id_state {
343196212Sscottl	struct mpt_standalone_disk *sdisks;
344196212Sscottl	struct mpt_drive_list *list;
345196212Sscottl	CONFIG_PAGE_IOC_2 *ioc2;
346196212Sscottl	U8	target_id;
347196212Sscottl	int	nsdisks;
348196212Sscottl};
349196212Sscottl
350196212Sscottlstruct drive_info {
351196212Sscottl	CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
352196212Sscottl	struct mpt_standalone_disk *sdisk;
353196212Sscottl};
354196212Sscottl
355196212Sscottlstruct volume_info {
356196212Sscottl	int	drive_count;
357196212Sscottl	struct drive_info *drives;
358196212Sscottl};
359196212Sscottl
360196212Sscottl/* Parse a comma-separated list of drives for a volume. */
361196212Sscottlstatic int
362196212Sscottlparse_volume(int fd, int raid_type, struct config_id_state *state,
363196212Sscottl    char *volume_str, struct volume_info *info)
364196212Sscottl{
365196212Sscottl	struct drive_info *dinfo;
366196212Sscottl	U8 PhysDiskNum;
367196212Sscottl	char *cp;
368196212Sscottl	int count, error, i;
369196212Sscottl
370196212Sscottl	cp = volume_str;
371196212Sscottl	for (count = 0; cp != NULL; count++) {
372196212Sscottl		cp = strchr(cp, ',');
373196212Sscottl		if (cp != NULL) {
374196212Sscottl			cp++;
375196212Sscottl			if (*cp == ',') {
376196212Sscottl				warnx("Invalid drive list '%s'", volume_str);
377196212Sscottl				return (EINVAL);
378196212Sscottl			}
379196212Sscottl		}
380196212Sscottl	}
381196212Sscottl
382196212Sscottl	/* Validate the number of drives for this volume. */
383196212Sscottl	switch (raid_type) {
384196212Sscottl	case RT_RAID0:
385196212Sscottl		if (count < 2) {
386196212Sscottl			warnx("RAID0 requires at least 2 drives in each "
387196212Sscottl			    "array");
388196212Sscottl			return (EINVAL);
389196212Sscottl		}
390196212Sscottl		break;
391196212Sscottl	case RT_RAID1:
392196212Sscottl		if (count != 2) {
393196212Sscottl			warnx("RAID1 requires exactly 2 drives in each "
394196212Sscottl			    "array");
395196212Sscottl			return (EINVAL);
396196212Sscottl		}
397196212Sscottl		break;
398196212Sscottl	case RT_RAID1E:
399196212Sscottl		if (count < 3) {
400196212Sscottl			warnx("RAID1E requires at least 3 drives in each "
401196212Sscottl			    "array");
402196212Sscottl			return (EINVAL);
403196212Sscottl		}
404196212Sscottl		break;
405196212Sscottl	}
406196212Sscottl
407196212Sscottl	/* Validate each drive. */
408196212Sscottl	info->drives = calloc(count, sizeof(struct drive_info));
409196212Sscottl	info->drive_count = count;
410196212Sscottl	for (dinfo = info->drives; (cp = strsep(&volume_str, ",")) != NULL;
411196212Sscottl	     dinfo++) {
412196212Sscottl		/* If this drive is already a RAID phys just fetch the info. */
413196212Sscottl		error = mpt_lookup_drive(state->list, cp, &PhysDiskNum);
414196212Sscottl		if (error == 0) {
415196212Sscottl			dinfo->info = mpt_pd_info(fd, PhysDiskNum, NULL);
416196212Sscottl			if (dinfo->info == NULL)
417196212Sscottl				return (errno);
418196212Sscottl			continue;
419196212Sscottl		}
420196212Sscottl
421196212Sscottl		/* See if it is a standalone disk. */
422196212Sscottl		if (mpt_lookup_standalone_disk(cp, state->sdisks,
423196212Sscottl		    state->nsdisks, &i) < 0) {
424215046Sjhb			error = errno;
425196212Sscottl			warn("Unable to lookup drive %s", cp);
426215046Sjhb			return (error);
427196212Sscottl		}
428196212Sscottl		dinfo->sdisk = &state->sdisks[i];
429196212Sscottl
430196212Sscottl		/* Lock the disk, we will create phys disk pages later. */
431196212Sscottl		if (mpt_lock_physdisk(dinfo->sdisk) < 0)
432196212Sscottl			return (errno);
433196212Sscottl	}
434196212Sscottl
435196212Sscottl	return (0);
436196212Sscottl}
437196212Sscottl
438196212Sscottl/*
439196212Sscottl * Add RAID physdisk pages for any standalone disks that a volume is
440196212Sscottl * going to use.
441196212Sscottl */
442196212Sscottlstatic int
443196212Sscottladd_drives(int fd, struct volume_info *info, int verbose)
444196212Sscottl{
445196212Sscottl	struct drive_info *dinfo;
446196212Sscottl	U8 PhysDiskNum;
447215046Sjhb	int error, i;
448196212Sscottl
449196212Sscottl	for (i = 0, dinfo = info->drives; i < info->drive_count;
450196212Sscottl	     i++, dinfo++) {
451196212Sscottl		if (dinfo->info == NULL) {
452196212Sscottl			if (mpt_create_physdisk(fd, dinfo->sdisk,
453196212Sscottl			    &PhysDiskNum) < 0) {
454215046Sjhb				error = errno;
455196212Sscottl				warn(
456196212Sscottl			    "Failed to create physical disk page for %s",
457196212Sscottl				    dinfo->sdisk->devname);
458215046Sjhb				return (error);
459196212Sscottl			}
460196212Sscottl			if (verbose)
461196212Sscottl				printf("Added drive %s with PhysDiskNum %u\n",
462196212Sscottl				    dinfo->sdisk->devname, PhysDiskNum);
463196212Sscottl
464196212Sscottl			dinfo->info = mpt_pd_info(fd, PhysDiskNum, NULL);
465196212Sscottl			if (dinfo->info == NULL)
466196212Sscottl				return (errno);
467196212Sscottl		}
468196212Sscottl	}
469196212Sscottl	return (0);
470196212Sscottl}
471196212Sscottl
472196212Sscottl/*
473196212Sscottl * Find the next free target ID assuming that 'target_id' is the last
474196212Sscottl * one used.  'target_id' should be 0xff for the initial test.
475196212Sscottl */
476196212Sscottlstatic U8
477196212Sscottlfind_next_volume(struct config_id_state *state)
478196212Sscottl{
479196212Sscottl	CONFIG_PAGE_IOC_2_RAID_VOL *vol;
480196212Sscottl	int i;
481196212Sscottl
482196212Sscottlrestart:
483196212Sscottl	/* Assume the current one is used. */
484196212Sscottl	state->target_id++;
485196212Sscottl
486196212Sscottl	/* Search drives first. */
487196212Sscottl	for (i = 0; i < state->nsdisks; i++)
488196212Sscottl		if (state->sdisks[i].target == state->target_id)
489196212Sscottl			goto restart;
490196212Sscottl	for (i = 0; i < state->list->ndrives; i++)
491196212Sscottl		if (state->list->drives[i]->PhysDiskID == state->target_id)
492196212Sscottl			goto restart;
493196212Sscottl
494228990Suqs	/* Search volumes second. */
495196212Sscottl	vol = state->ioc2->RaidVolume;
496196212Sscottl	for (i = 0; i < state->ioc2->NumActiveVolumes; vol++, i++)
497196212Sscottl		if (vol->VolumeID == state->target_id)
498196212Sscottl			goto restart;
499196212Sscottl
500196212Sscottl	return (state->target_id);
501196212Sscottl}
502196212Sscottl
503196212Sscottl/* Create a volume and populate it with drives. */
504196212Sscottlstatic CONFIG_PAGE_RAID_VOL_0 *
505196212Sscottlbuild_volume(int fd, struct volume_info *info, int raid_type, long stripe_size,
506196212Sscottl    struct config_id_state *state, int verbose)
507196212Sscottl{
508196212Sscottl	CONFIG_PAGE_HEADER header;
509196212Sscottl	CONFIG_PAGE_RAID_VOL_0 *vol;
510196212Sscottl	RAID_VOL0_PHYS_DISK *rdisk;
511196212Sscottl	struct drive_info *dinfo;
512196212Sscottl        U32 MinLBA;
513196212Sscottl	uint64_t MaxLBA;
514196212Sscottl	size_t page_size;
515215046Sjhb	int error, i;
516196212Sscottl
517215046Sjhb	error = mpt_read_config_page_header(fd, MPI_CONFIG_PAGETYPE_RAID_VOLUME,
518215046Sjhb	    0, 0, &header, NULL);
519215046Sjhb	if (error) {
520215046Sjhb		errno = error;
521196212Sscottl		return (NULL);
522215046Sjhb	}
523196212Sscottl	if (header.PageVersion > MPI_RAIDVOLPAGE0_PAGEVERSION) {
524196212Sscottl		warnx("Unsupported RAID volume page 0 version %d",
525196212Sscottl		    header.PageVersion);
526196212Sscottl		errno = EOPNOTSUPP;
527196212Sscottl		return (NULL);
528196212Sscottl	}
529196212Sscottl	page_size = sizeof(CONFIG_PAGE_RAID_VOL_0) +
530196212Sscottl	    sizeof(RAID_VOL0_PHYS_DISK) * (info->drive_count - 1);
531196212Sscottl	vol = calloc(1, page_size);
532215046Sjhb	if (vol == NULL)
533215046Sjhb		return (NULL);
534196212Sscottl
535196212Sscottl	/* Header */
536196212Sscottl	vol->Header.PageType = MPI_CONFIG_PAGETYPE_RAID_VOLUME;
537196212Sscottl	vol->Header.PageNumber = 0;
538196212Sscottl	vol->Header.PageLength = page_size / 4;
539196212Sscottl
540196212Sscottl	/* Properties */
541196212Sscottl	vol->VolumeID = find_next_volume(state);
542196212Sscottl	vol->VolumeBus = 0;
543196212Sscottl	vol->VolumeIOC = 0;	/* XXX */
544196212Sscottl	vol->VolumeStatus.Flags = MPI_RAIDVOL0_STATUS_FLAG_ENABLED;
545196212Sscottl	vol->VolumeStatus.State = MPI_RAIDVOL0_STATUS_STATE_OPTIMAL;
546196212Sscottl	vol->VolumeSettings.Settings = MPI_RAIDVOL0_SETTING_USE_DEFAULTS;
547196212Sscottl	vol->VolumeSettings.HotSparePool = MPI_RAID_HOT_SPARE_POOL_0;
548196212Sscottl	vol->NumPhysDisks = info->drive_count;
549196212Sscottl
550196212Sscottl	/* Find the smallest drive. */
551196212Sscottl	MinLBA = info->drives[0].info->MaxLBA;
552196212Sscottl	for (i = 1; i < info->drive_count; i++)
553196212Sscottl		if (info->drives[i].info->MaxLBA < MinLBA)
554196212Sscottl			MinLBA = info->drives[i].info->MaxLBA;
555196212Sscottl
556196212Sscottl	/*
557196212Sscottl	 * Now chop off 512MB at the end to leave room for the
558196212Sscottl	 * metadata.  The controller might only use 64MB, but we just
559196212Sscottl	 * chop off the max to be simple.
560196212Sscottl	 */
561196212Sscottl	MinLBA -= (512 * 1024 * 1024) / 512;
562196212Sscottl
563196212Sscottl	switch (raid_type) {
564196212Sscottl	case RT_RAID0:
565196212Sscottl		vol->VolumeType = MPI_RAID_VOL_TYPE_IS;
566196212Sscottl		vol->StripeSize = stripe_size / 512;
567332603Sasomers		MaxLBA = (uint64_t)MinLBA * info->drive_count;
568196212Sscottl		break;
569196212Sscottl	case RT_RAID1:
570196212Sscottl		vol->VolumeType = MPI_RAID_VOL_TYPE_IM;
571332603Sasomers		MaxLBA = (uint64_t)MinLBA * (info->drive_count / 2);
572196212Sscottl		break;
573196212Sscottl	case RT_RAID1E:
574196212Sscottl		vol->VolumeType = MPI_RAID_VOL_TYPE_IME;
575196212Sscottl		vol->StripeSize = stripe_size / 512;
576332603Sasomers		MaxLBA = (uint64_t)MinLBA * info->drive_count / 2;
577196212Sscottl		break;
578196212Sscottl	default:
579196212Sscottl		/* Pacify gcc. */
580196212Sscottl		abort();
581196212Sscottl	}
582196212Sscottl
583196212Sscottl	/*
584196212Sscottl	 * If the controller doesn't support 64-bit addressing and the
585196212Sscottl	 * new volume is larger than 2^32 blocks, warn the user and
586196212Sscottl	 * truncate the volume.
587196212Sscottl	 */
588196212Sscottl	if (MaxLBA >> 32 != 0 &&
589196212Sscottl	    !(state->ioc2->CapabilitiesFlags &
590196212Sscottl	    MPI_IOCPAGE2_CAP_FLAGS_RAID_64_BIT_ADDRESSING)) {
591196212Sscottl		warnx(
592196212Sscottl	    "Controller does not support volumes > 2TB, truncating volume.");
593196212Sscottl		MaxLBA = 0xffffffff;
594196212Sscottl	}
595196212Sscottl	vol->MaxLBA = MaxLBA;
596196212Sscottl	vol->MaxLBAHigh = MaxLBA >> 32;
597196212Sscottl
598196212Sscottl	/* Populate drives. */
599196212Sscottl	for (i = 0, dinfo = info->drives, rdisk = vol->PhysDisk;
600196212Sscottl	     i < info->drive_count; i++, dinfo++, rdisk++) {
601196212Sscottl		if (verbose)
602196212Sscottl			printf("Adding drive %u (%u:%u) to volume %u:%u\n",
603196212Sscottl			    dinfo->info->PhysDiskNum, dinfo->info->PhysDiskBus,
604196212Sscottl			    dinfo->info->PhysDiskID, vol->VolumeBus,
605196212Sscottl			    vol->VolumeID);
606196212Sscottl		if (raid_type == RT_RAID1) {
607196212Sscottl			if (i == 0)
608196212Sscottl				rdisk->PhysDiskMap =
609196212Sscottl				    MPI_RAIDVOL0_PHYSDISK_PRIMARY;
610196212Sscottl			else
611196212Sscottl				rdisk->PhysDiskMap =
612196212Sscottl				    MPI_RAIDVOL0_PHYSDISK_SECONDARY;
613196212Sscottl		} else
614196212Sscottl			rdisk->PhysDiskMap = i;
615196212Sscottl		rdisk->PhysDiskNum = dinfo->info->PhysDiskNum;
616196212Sscottl	}
617196212Sscottl
618196212Sscottl	return (vol);
619196212Sscottl}
620196212Sscottl
621196212Sscottlstatic int
622196212Sscottlcreate_volume(int ac, char **av)
623196212Sscottl{
624196212Sscottl	CONFIG_PAGE_RAID_VOL_0 *vol;
625196212Sscottl	struct config_id_state state;
626196212Sscottl	struct volume_info *info;
627196212Sscottl	long stripe_size;
628215046Sjhb	int ch, error, fd, i, quick, raid_type, verbose;
629196212Sscottl#ifdef DEBUG
630196212Sscottl	int dump;
631196212Sscottl#endif
632196212Sscottl
633196212Sscottl	if (ac < 2) {
634196212Sscottl		warnx("create: volume type required");
635196212Sscottl		return (EINVAL);
636196212Sscottl	}
637196212Sscottl
638196212Sscottl	fd = mpt_open(mpt_unit);
639196212Sscottl	if (fd < 0) {
640215046Sjhb		error = errno;
641196212Sscottl		warn("mpt_open");
642215046Sjhb		return (error);
643196212Sscottl	}
644196212Sscottl
645196212Sscottl	/* Lookup the RAID type first. */
646196212Sscottl	raid_type = -1;
647196212Sscottl	for (i = 0; raid_type_table[i].name != NULL; i++)
648196212Sscottl		if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
649196212Sscottl			raid_type = raid_type_table[i].raid_type;
650196212Sscottl			break;
651196212Sscottl		}
652196212Sscottl
653196212Sscottl	if (raid_type == -1) {
654196212Sscottl		warnx("Unknown or unsupported volume type %s", av[1]);
655332603Sasomers		close(fd);
656196212Sscottl		return (EINVAL);
657196212Sscottl	}
658196212Sscottl
659196212Sscottl	/* Parse any options. */
660196212Sscottl	optind = 2;
661196212Sscottl#ifdef DEBUG
662196212Sscottl	dump = 0;
663196212Sscottl#endif
664196212Sscottl	quick = 0;
665196212Sscottl	verbose = 0;
666196212Sscottl	stripe_size = 64 * 1024;
667196212Sscottl
668196212Sscottl	while ((ch = getopt(ac, av, "dqs:v")) != -1) {
669196212Sscottl		switch (ch) {
670196212Sscottl#ifdef DEBUG
671196212Sscottl		case 'd':
672196212Sscottl			dump = 1;
673196212Sscottl			break;
674196212Sscottl#endif
675196212Sscottl		case 'q':
676196212Sscottl			quick = 1;
677196212Sscottl			break;
678196212Sscottl		case 's':
679196212Sscottl			stripe_size = dehumanize(optarg);
680196212Sscottl			if ((stripe_size < 512) || (!powerof2(stripe_size))) {
681196212Sscottl				warnx("Invalid stripe size %s", optarg);
682332603Sasomers				close(fd);
683196212Sscottl				return (EINVAL);
684196212Sscottl			}
685196212Sscottl			break;
686196212Sscottl		case 'v':
687196212Sscottl			verbose = 1;
688196212Sscottl			break;
689196212Sscottl		case '?':
690196212Sscottl		default:
691332603Sasomers			close(fd);
692196212Sscottl			return (EINVAL);
693196212Sscottl		}
694196212Sscottl	}
695196212Sscottl	ac -= optind;
696196212Sscottl	av += optind;
697196212Sscottl
698196212Sscottl	/* Fetch existing config data. */
699196212Sscottl	state.ioc2 = mpt_read_ioc_page(fd, 2, NULL);
700196212Sscottl	if (state.ioc2 == NULL) {
701215046Sjhb		error = errno;
702196212Sscottl		warn("Failed to read volume list");
703332603Sasomers		close(fd);
704215046Sjhb		return (error);
705196212Sscottl	}
706196212Sscottl	state.list = mpt_pd_list(fd);
707332603Sasomers	if (state.list == NULL) {
708332603Sasomers		close(fd);
709196212Sscottl		return (errno);
710332603Sasomers	}
711196212Sscottl	error = mpt_fetch_disks(fd, &state.nsdisks, &state.sdisks);
712196212Sscottl	if (error) {
713196212Sscottl		warn("Failed to fetch standalone disk list");
714332603Sasomers		close(fd);
715196212Sscottl		return (error);
716196212Sscottl	}
717196212Sscottl	state.target_id = 0xff;
718196212Sscottl
719196212Sscottl	/* Parse the drive list. */
720196212Sscottl	if (ac != 1) {
721196212Sscottl		warnx("Exactly one drive list is required");
722332603Sasomers		close(fd);
723196212Sscottl		return (EINVAL);
724196212Sscottl	}
725196212Sscottl	info = calloc(1, sizeof(*info));
726332603Sasomers	if (info == NULL) {
727332603Sasomers		close(fd);
728215046Sjhb		return (ENOMEM);
729332603Sasomers	}
730196212Sscottl	error = parse_volume(fd, raid_type, &state, av[0], info);
731332603Sasomers	if (error) {
732332603Sasomers		free(info);
733332603Sasomers		close(fd);
734196212Sscottl		return (error);
735332603Sasomers	}
736196212Sscottl
737196212Sscottl	/* Create RAID physdisk pages for standalone disks. */
738196212Sscottl	error = add_drives(fd, info, verbose);
739332603Sasomers	if (error) {
740332603Sasomers		free(info);
741332603Sasomers		close(fd);
742196212Sscottl		return (error);
743332603Sasomers	}
744196212Sscottl
745196212Sscottl	/* Build the volume. */
746196212Sscottl	vol = build_volume(fd, info, raid_type, stripe_size, &state, verbose);
747332603Sasomers	if (vol == NULL) {
748332603Sasomers		free(info);
749332603Sasomers		close(fd);
750215046Sjhb		return (errno);
751332603Sasomers	}
752196212Sscottl
753196212Sscottl#ifdef DEBUG
754196212Sscottl	if (dump) {
755196212Sscottl		dump_config(vol);
756196212Sscottl		goto skip;
757196212Sscottl	}
758196212Sscottl#endif
759196212Sscottl
760196212Sscottl	/* Send the new volume to the controller. */
761215046Sjhb	error = mpt_raid_action(fd, MPI_RAID_ACTION_CREATE_VOLUME, vol->VolumeBus,
762196212Sscottl	    vol->VolumeID, 0, quick ? MPI_RAID_ACTION_ADATA_DO_NOT_SYNC : 0,
763215046Sjhb	    vol, vol->Header.PageLength * 4, NULL, NULL, 0, NULL, NULL, 1);
764215046Sjhb	if (error) {
765215046Sjhb		errno = error;
766196212Sscottl		warn("Failed to add volume");
767332603Sasomers		free(info);
768332603Sasomers		close(fd);
769215046Sjhb		return (error);
770196212Sscottl	}
771196212Sscottl
772196212Sscottl#ifdef DEBUG
773196212Sscottlskip:
774196212Sscottl#endif
775196212Sscottl	mpt_rescan_bus(vol->VolumeBus, vol->VolumeID);
776196212Sscottl
777196212Sscottl	/* Clean up. */
778196212Sscottl	free(vol);
779196212Sscottl	free(info);
780196212Sscottl	free(state.sdisks);
781196212Sscottl	mpt_free_pd_list(state.list);
782196212Sscottl	free(state.ioc2);
783196212Sscottl	close(fd);
784196212Sscottl
785196212Sscottl	return (0);
786196212Sscottl}
787196212SscottlMPT_COMMAND(top, create, create_volume);
788196212Sscottl
789196212Sscottlstatic int
790196212Sscottldelete_volume(int ac, char **av)
791196212Sscottl{
792196212Sscottl	U8 VolumeBus, VolumeID;
793215046Sjhb	int error, fd;
794196212Sscottl
795196212Sscottl	if (ac != 2) {
796196212Sscottl		warnx("delete: volume required");
797196212Sscottl		return (EINVAL);
798196212Sscottl	}
799196212Sscottl
800196212Sscottl	fd = mpt_open(mpt_unit);
801196212Sscottl	if (fd < 0) {
802215046Sjhb		error = errno;
803196212Sscottl		warn("mpt_open");
804215046Sjhb		return (error);
805196212Sscottl	}
806196212Sscottl
807215046Sjhb	error = mpt_lookup_volume(fd, av[1], &VolumeBus, &VolumeID);
808215046Sjhb	if (error) {
809215046Sjhb		warnc(error, "Invalid volume %s", av[1]);
810332603Sasomers		close(fd);
811215046Sjhb		return (error);
812196212Sscottl	}
813196212Sscottl
814332603Sasomers	if (mpt_lock_volume(VolumeBus, VolumeID) < 0) {
815332603Sasomers		close(fd);
816196212Sscottl		return (errno);
817332603Sasomers	}
818196212Sscottl
819215046Sjhb	error = mpt_raid_action(fd, MPI_RAID_ACTION_DELETE_VOLUME, VolumeBus,
820196212Sscottl	    VolumeID, 0, MPI_RAID_ACTION_ADATA_DEL_PHYS_DISKS |
821196212Sscottl	    MPI_RAID_ACTION_ADATA_ZERO_LBA0, NULL, 0, NULL, NULL, 0, NULL,
822215046Sjhb	    NULL, 0);
823215046Sjhb	if (error) {
824215046Sjhb		warnc(error, "Failed to delete volume");
825332603Sasomers		close(fd);
826215046Sjhb		return (error);
827196212Sscottl	}
828196212Sscottl
829196212Sscottl	mpt_rescan_bus(-1, -1);
830196212Sscottl	close(fd);
831196212Sscottl
832196212Sscottl	return (0);
833196212Sscottl}
834196212SscottlMPT_COMMAND(top, delete, delete_volume);
835196212Sscottl
836196212Sscottlstatic int
837196212Sscottlfind_volume_spare_pool(int fd, const char *name, int *pool)
838196212Sscottl{
839196212Sscottl	CONFIG_PAGE_RAID_VOL_0 *info;
840196212Sscottl	CONFIG_PAGE_IOC_2 *ioc2;
841196212Sscottl	CONFIG_PAGE_IOC_2_RAID_VOL *vol;
842196212Sscottl	U8 VolumeBus, VolumeID;
843215046Sjhb	int error, i, j, new_pool, pool_count[7];
844196212Sscottl
845215046Sjhb	error = mpt_lookup_volume(fd, name, &VolumeBus, &VolumeID);
846215046Sjhb	if (error) {
847215046Sjhb		warnc(error, "Invalid volume %s", name);
848215046Sjhb		return (error);
849196212Sscottl	}
850196212Sscottl
851196212Sscottl	info = mpt_vol_info(fd, VolumeBus, VolumeID, NULL);
852196212Sscottl	if (info == NULL)
853215046Sjhb		return (errno);
854196212Sscottl
855196212Sscottl	/*
856196212Sscottl	 * Check for an existing pool other than pool 0 (used for
857196212Sscottl	 * global spares).
858196212Sscottl	 */
859196212Sscottl	if ((info->VolumeSettings.HotSparePool & ~MPI_RAID_HOT_SPARE_POOL_0) !=
860196212Sscottl	    0) {
861196212Sscottl		*pool = 1 << (ffs(info->VolumeSettings.HotSparePool &
862196212Sscottl		    ~MPI_RAID_HOT_SPARE_POOL_0) - 1);
863332603Sasomers		free(info);
864196212Sscottl		return (0);
865196212Sscottl	}
866196212Sscottl	free(info);
867196212Sscottl
868196212Sscottl	/*
869196212Sscottl	 * Try to find a free pool.  First, figure out which pools are
870196212Sscottl	 * in use.
871196212Sscottl	 */
872196212Sscottl	ioc2 = mpt_read_ioc_page(fd, 2, NULL);
873196212Sscottl	if (ioc2 == NULL) {
874215046Sjhb		error = errno;
875196212Sscottl		warn("Failed to fetch volume list");
876215046Sjhb		return (error);
877196212Sscottl	}
878196212Sscottl	bzero(pool_count, sizeof(pool_count));
879196212Sscottl	vol = ioc2->RaidVolume;
880196212Sscottl	for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
881196212Sscottl		info = mpt_vol_info(fd, vol->VolumeBus, vol->VolumeID, NULL);
882196212Sscottl		if (info == NULL)
883215046Sjhb			return (errno);
884196212Sscottl		for (j = 0; j < 7; j++)
885196212Sscottl			if (info->VolumeSettings.HotSparePool & (1 << (j + 1)))
886196212Sscottl				pool_count[j]++;
887196212Sscottl		free(info);
888196212Sscottl	}
889196212Sscottl	free(ioc2);
890196212Sscottl
891196212Sscottl	/* Find the pool with the lowest use count. */
892196212Sscottl	new_pool = 0;
893196212Sscottl	for (i = 1; i < 7; i++)
894196212Sscottl		if (pool_count[i] < pool_count[new_pool])
895196212Sscottl			new_pool = i;
896196212Sscottl	new_pool++;
897196212Sscottl
898196212Sscottl	/* Add this pool to the volume. */
899196212Sscottl	info = mpt_vol_info(fd, VolumeBus, VolumeID, NULL);
900196212Sscottl	if (info == NULL)
901215046Sjhb		return (error);
902196212Sscottl	info->VolumeSettings.HotSparePool |= (1 << new_pool);
903215046Sjhb	error = mpt_raid_action(fd, MPI_RAID_ACTION_CHANGE_VOLUME_SETTINGS,
904196212Sscottl	    VolumeBus, VolumeID, 0, *(U32 *)&info->VolumeSettings, NULL, 0,
905215046Sjhb	    NULL, NULL, 0, NULL, NULL, 0);
906215046Sjhb	if (error) {
907196212Sscottl		warnx("Failed to add spare pool %d to %s", new_pool,
908196212Sscottl		    mpt_volume_name(VolumeBus, VolumeID));
909332603Sasomers		free(info);
910215046Sjhb		return (error);
911196212Sscottl	}
912196212Sscottl	free(info);
913196212Sscottl
914196212Sscottl	*pool = (1 << new_pool);
915196212Sscottl	return (0);
916196212Sscottl}
917196212Sscottl
918196212Sscottlstatic int
919196212Sscottladd_spare(int ac, char **av)
920196212Sscottl{
921196212Sscottl	CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
922196212Sscottl	struct mpt_standalone_disk *sdisks;
923196212Sscottl	struct mpt_drive_list *list;
924196212Sscottl	U8 PhysDiskNum;
925196212Sscottl	int error, fd, i, nsdisks, pool;
926196212Sscottl
927196212Sscottl	if (ac < 2) {
928196212Sscottl		warnx("add spare: drive required");
929196212Sscottl		return (EINVAL);
930196212Sscottl	}
931196212Sscottl	if (ac > 3) {
932196212Sscottl		warnx("add spare: extra arguments");
933196212Sscottl		return (EINVAL);
934196212Sscottl	}
935196212Sscottl
936196212Sscottl	fd = mpt_open(mpt_unit);
937196212Sscottl	if (fd < 0) {
938215046Sjhb		error = errno;
939196212Sscottl		warn("mpt_open");
940215046Sjhb		return (error);
941196212Sscottl	}
942196212Sscottl
943196212Sscottl	if (ac == 3) {
944215046Sjhb		error = find_volume_spare_pool(fd, av[2], &pool);
945332603Sasomers		if (error) {
946332603Sasomers			close(fd);
947215046Sjhb			return (error);
948332603Sasomers		}
949196212Sscottl	} else
950196212Sscottl		pool = MPI_RAID_HOT_SPARE_POOL_0;
951196212Sscottl
952196212Sscottl	list = mpt_pd_list(fd);
953196212Sscottl	if (list == NULL)
954196212Sscottl		return (errno);
955196212Sscottl
956196212Sscottl	error = mpt_lookup_drive(list, av[1], &PhysDiskNum);
957196212Sscottl	if (error) {
958196212Sscottl		error = mpt_fetch_disks(fd, &nsdisks, &sdisks);
959196212Sscottl		if (error != 0) {
960196212Sscottl			warn("Failed to fetch standalone disk list");
961332603Sasomers			mpt_free_pd_list(list);
962332603Sasomers			close(fd);
963196212Sscottl			return (error);
964196212Sscottl		}
965196212Sscottl
966196212Sscottl		if (mpt_lookup_standalone_disk(av[1], sdisks, nsdisks, &i) <
967196212Sscottl		    0) {
968215046Sjhb			error = errno;
969196212Sscottl			warn("Unable to lookup drive %s", av[1]);
970332603Sasomers			mpt_free_pd_list(list);
971332603Sasomers			close(fd);
972215046Sjhb			return (error);
973196212Sscottl		}
974196212Sscottl
975332603Sasomers		if (mpt_lock_physdisk(&sdisks[i]) < 0) {
976332603Sasomers			mpt_free_pd_list(list);
977332603Sasomers			close(fd);
978196212Sscottl			return (errno);
979332603Sasomers		}
980196212Sscottl
981196212Sscottl		if (mpt_create_physdisk(fd, &sdisks[i], &PhysDiskNum) < 0) {
982215046Sjhb			error = errno;
983196212Sscottl			warn("Failed to create physical disk page");
984332603Sasomers			mpt_free_pd_list(list);
985332603Sasomers			close(fd);
986215046Sjhb			return (error);
987196212Sscottl		}
988196212Sscottl		free(sdisks);
989196212Sscottl	}
990196212Sscottl	mpt_free_pd_list(list);
991196212Sscottl
992196212Sscottl	info = mpt_pd_info(fd, PhysDiskNum, NULL);
993196212Sscottl	if (info == NULL) {
994215046Sjhb		error = errno;
995196212Sscottl		warn("Failed to fetch drive info");
996332603Sasomers		close(fd);
997215046Sjhb		return (error);
998196212Sscottl	}
999196212Sscottl
1000196212Sscottl	info->PhysDiskSettings.HotSparePool = pool;
1001196212Sscottl	error = mpt_raid_action(fd, MPI_RAID_ACTION_CHANGE_PHYSDISK_SETTINGS, 0,
1002196212Sscottl	    0, PhysDiskNum, *(U32 *)&info->PhysDiskSettings, NULL, 0, NULL,
1003196212Sscottl	    NULL, 0, NULL, NULL, 0);
1004196212Sscottl	if (error) {
1005215046Sjhb		warnc(error, "Failed to assign spare");
1006332603Sasomers		close(fd);
1007215046Sjhb		return (error);
1008196212Sscottl	}
1009196212Sscottl
1010196212Sscottl	free(info);
1011196212Sscottl	close(fd);
1012196212Sscottl
1013196212Sscottl	return (0);
1014196212Sscottl}
1015196212SscottlMPT_COMMAND(top, add, add_spare);
1016196212Sscottl
1017196212Sscottlstatic int
1018196212Sscottlremove_spare(int ac, char **av)
1019196212Sscottl{
1020196212Sscottl	CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
1021196212Sscottl	struct mpt_drive_list *list;
1022196212Sscottl	U8 PhysDiskNum;
1023196212Sscottl	int error, fd;
1024196212Sscottl
1025196212Sscottl	if (ac != 2) {
1026196212Sscottl		warnx("remove spare: drive required");
1027196212Sscottl		return (EINVAL);
1028196212Sscottl	}
1029196212Sscottl
1030196212Sscottl	fd = mpt_open(mpt_unit);
1031196212Sscottl	if (fd < 0) {
1032215046Sjhb		error = errno;
1033196212Sscottl		warn("mpt_open");
1034215046Sjhb		return (error);
1035196212Sscottl	}
1036196212Sscottl
1037196212Sscottl	list = mpt_pd_list(fd);
1038332603Sasomers	if (list == NULL) {
1039332603Sasomers		close(fd);
1040196212Sscottl		return (errno);
1041332603Sasomers	}
1042196212Sscottl
1043196212Sscottl	error = mpt_lookup_drive(list, av[1], &PhysDiskNum);
1044196212Sscottl	if (error) {
1045196212Sscottl		warn("Failed to find drive %s", av[1]);
1046332603Sasomers		close(fd);
1047196212Sscottl		return (error);
1048196212Sscottl	}
1049196212Sscottl	mpt_free_pd_list(list);
1050196212Sscottl
1051196212Sscottl
1052196212Sscottl	info = mpt_pd_info(fd, PhysDiskNum, NULL);
1053196212Sscottl	if (info == NULL) {
1054215046Sjhb		error = errno;
1055196212Sscottl		warn("Failed to fetch drive info");
1056332603Sasomers		close(fd);
1057215046Sjhb		return (error);
1058196212Sscottl	}
1059196212Sscottl
1060196212Sscottl	if (info->PhysDiskSettings.HotSparePool == 0) {
1061196212Sscottl		warnx("Drive %u is not a hot spare", PhysDiskNum);
1062332603Sasomers		free(info);
1063332603Sasomers		close(fd);
1064196212Sscottl		return (EINVAL);
1065196212Sscottl	}
1066196212Sscottl
1067196212Sscottl	if (mpt_delete_physdisk(fd, PhysDiskNum) < 0) {
1068215046Sjhb		error = errno;
1069196212Sscottl		warn("Failed to delete physical disk page");
1070332603Sasomers		free(info);
1071332603Sasomers		close(fd);
1072215046Sjhb		return (error);
1073196212Sscottl	}
1074196212Sscottl
1075196212Sscottl	mpt_rescan_bus(info->PhysDiskBus, info->PhysDiskID);
1076196212Sscottl	free(info);
1077196212Sscottl	close(fd);
1078196212Sscottl
1079196212Sscottl	return (0);
1080196212Sscottl}
1081196212SscottlMPT_COMMAND(top, remove, remove_spare);
1082196212Sscottl
1083196212Sscottl#ifdef DEBUG
1084196212SscottlMPT_TABLE(top, pd);
1085196212Sscottl
1086196212Sscottlstatic int
1087196212Sscottlpd_create(int ac, char **av)
1088196212Sscottl{
1089196212Sscottl	struct mpt_standalone_disk *disks;
1090196212Sscottl	int error, fd, i, ndisks;
1091196212Sscottl	U8 PhysDiskNum;
1092196212Sscottl
1093196212Sscottl	if (ac != 2) {
1094196212Sscottl		warnx("pd create: drive required");
1095196212Sscottl		return (EINVAL);
1096196212Sscottl	}
1097196212Sscottl
1098196212Sscottl	fd = mpt_open(mpt_unit);
1099196212Sscottl	if (fd < 0) {
1100215046Sjhb		error = errno;
1101196212Sscottl		warn("mpt_open");
1102215046Sjhb		return (error);
1103196212Sscottl	}
1104196212Sscottl
1105196212Sscottl	error = mpt_fetch_disks(fd, &ndisks, &disks);
1106196212Sscottl	if (error != 0) {
1107196212Sscottl		warn("Failed to fetch standalone disk list");
1108196212Sscottl		return (error);
1109196212Sscottl	}
1110196212Sscottl
1111196212Sscottl	if (mpt_lookup_standalone_disk(av[1], disks, ndisks, &i) < 0) {
1112215046Sjhb		error = errno;
1113196212Sscottl		warn("Unable to lookup drive");
1114215046Sjhb		return (error);
1115196212Sscottl	}
1116196212Sscottl
1117196212Sscottl	if (mpt_lock_physdisk(&disks[i]) < 0)
1118196212Sscottl		return (errno);
1119196212Sscottl
1120196212Sscottl	if (mpt_create_physdisk(fd, &disks[i], &PhysDiskNum) < 0) {
1121215046Sjhb		error = errno;
1122196212Sscottl		warn("Failed to create physical disk page");
1123215046Sjhb		return (error);
1124196212Sscottl	}
1125196212Sscottl	free(disks);
1126196212Sscottl
1127196212Sscottl	printf("Added drive %s with PhysDiskNum %u\n", av[1], PhysDiskNum);
1128196212Sscottl
1129196212Sscottl	close(fd);
1130196212Sscottl
1131196212Sscottl	return (0);
1132196212Sscottl}
1133196212SscottlMPT_COMMAND(pd, create, pd_create);
1134196212Sscottl
1135196212Sscottlstatic int
1136196212Sscottlpd_delete(int ac, char **av)
1137196212Sscottl{
1138196212Sscottl	CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
1139196212Sscottl	struct mpt_drive_list *list;
1140215046Sjhb	int error, fd;
1141196212Sscottl	U8 PhysDiskNum;
1142196212Sscottl
1143196212Sscottl	if (ac != 2) {
1144196212Sscottl		warnx("pd delete: drive required");
1145196212Sscottl		return (EINVAL);
1146196212Sscottl	}
1147196212Sscottl
1148196212Sscottl	fd = mpt_open(mpt_unit);
1149196212Sscottl	if (fd < 0) {
1150215046Sjhb		error = errno;
1151196212Sscottl		warn("mpt_open");
1152215046Sjhb		return (error);
1153196212Sscottl	}
1154196212Sscottl
1155196212Sscottl	list = mpt_pd_list(fd);
1156196212Sscottl	if (list == NULL)
1157196212Sscottl		return (errno);
1158196212Sscottl
1159196212Sscottl	if (mpt_lookup_drive(list, av[1], &PhysDiskNum) < 0) {
1160215046Sjhb		error = errno;
1161196212Sscottl		warn("Failed to find drive %s", av[1]);
1162215046Sjhb		return (error);
1163196212Sscottl	}
1164196212Sscottl	mpt_free_pd_list(list);
1165196212Sscottl
1166196212Sscottl	info = mpt_pd_info(fd, PhysDiskNum, NULL);
1167196212Sscottl	if (info == NULL) {
1168215046Sjhb		error = errno;
1169196212Sscottl		warn("Failed to fetch drive info");
1170215046Sjhb		return (error);
1171196212Sscottl	}
1172196212Sscottl
1173196212Sscottl	if (mpt_delete_physdisk(fd, PhysDiskNum) < 0) {
1174215046Sjhb		error = errno;
1175196212Sscottl		warn("Failed to delete physical disk page");
1176215046Sjhb		return (error);
1177196212Sscottl	}
1178196212Sscottl
1179196212Sscottl	mpt_rescan_bus(info->PhysDiskBus, info->PhysDiskID);
1180196212Sscottl	free(info);
1181196212Sscottl	close(fd);
1182196212Sscottl
1183196212Sscottl	return (0);
1184196212Sscottl}
1185196212SscottlMPT_COMMAND(pd, delete, pd_delete);
1186196212Sscottl
1187196212Sscottl/* Display raw data about a volume config. */
1188196212Sscottlstatic void
1189196212Sscottldump_config(CONFIG_PAGE_RAID_VOL_0 *vol)
1190196212Sscottl{
1191196212Sscottl	int i;
1192196212Sscottl
1193196212Sscottl	printf("Volume Configuration (Debug):\n");
1194196212Sscottl	printf(
1195196212Sscottl   " Page Header: Type 0x%02x Number 0x%02x Length 0x%02x(%u) Version 0x%02x\n",
1196196212Sscottl	    vol->Header.PageType, vol->Header.PageNumber,
1197196212Sscottl	    vol->Header.PageLength, vol->Header.PageLength * 4,
1198196212Sscottl	    vol->Header.PageVersion);
1199196212Sscottl	printf("     Address: %d:%d IOC %d\n", vol->VolumeBus, vol->VolumeID,
1200196212Sscottl	    vol->VolumeIOC);
1201196212Sscottl	printf("        Type: %d (%s)\n", vol->VolumeType,
1202196212Sscottl	    mpt_raid_level(vol->VolumeType));
1203196212Sscottl	printf("      Status: %s (Flags 0x%02x)\n",
1204196212Sscottl	    mpt_volstate(vol->VolumeStatus.State), vol->VolumeStatus.Flags);
1205196212Sscottl	printf("    Settings: 0x%04x (Spare Pools 0x%02x)\n",
1206196212Sscottl	    vol->VolumeSettings.Settings, vol->VolumeSettings.HotSparePool);
1207196212Sscottl	printf("      MaxLBA: %ju\n", (uintmax_t)vol->MaxLBAHigh << 32 |
1208196212Sscottl	    vol->MaxLBA);
1209196212Sscottl	printf(" Stripe Size: %ld\n", (long)vol->StripeSize * 512);
1210196212Sscottl	printf(" %d Disks:\n", vol->NumPhysDisks);
1211196212Sscottl
1212196212Sscottl	for (i = 0; i < vol->NumPhysDisks; i++)
1213196212Sscottl		printf("    Disk %d: Num 0x%02x Map 0x%02x\n", i,
1214196212Sscottl		    vol->PhysDisk[i].PhysDiskNum, vol->PhysDisk[i].PhysDiskMap);
1215196212Sscottl}
1216196212Sscottl
1217196212Sscottlstatic int
1218196212Sscottldebug_config(int ac, char **av)
1219196212Sscottl{
1220196212Sscottl	CONFIG_PAGE_RAID_VOL_0 *vol;
1221196212Sscottl	U8 VolumeBus, VolumeID;
1222215046Sjhb	int error, fd;
1223196212Sscottl
1224196212Sscottl	if (ac != 2) {
1225196212Sscottl		warnx("debug: volume required");
1226196212Sscottl		return (EINVAL);
1227196212Sscottl	}
1228196212Sscottl
1229196212Sscottl	fd = mpt_open(mpt_unit);
1230196212Sscottl	if (fd < 0) {
1231215046Sjhb		error = errno;
1232196212Sscottl		warn("mpt_open");
1233215046Sjhb		return (error);
1234196212Sscottl	}
1235196212Sscottl
1236215046Sjhb	error = mpt_lookup_volume(fd, av[1], &VolumeBus, &VolumeID);
1237215046Sjhb	if (error) {
1238215046Sjhb		warnc(error, "Invalid volume: %s", av[1]);
1239215046Sjhb		return (error);
1240196212Sscottl	}
1241196212Sscottl
1242196212Sscottl	vol = mpt_vol_info(fd, VolumeBus, VolumeID, NULL);
1243196212Sscottl	if (vol == NULL) {
1244215046Sjhb		error = errno;
1245196212Sscottl		warn("Failed to get volume info");
1246215046Sjhb		return (error);
1247196212Sscottl	}
1248196212Sscottl
1249196212Sscottl	dump_config(vol);
1250196212Sscottl	free(vol);
1251196212Sscottl	close(fd);
1252196212Sscottl
1253196212Sscottl	return (0);
1254196212Sscottl}
1255196212SscottlMPT_COMMAND(top, debug, debug_config);
1256196212Sscottl#endif
1257