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$");
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;
68196212Sscottl        case 'g': case 'G':
69196212Sscottl                iv *= 1024;
70196212Sscottl        case 'm': case 'M':
71196212Sscottl                iv *= 1024;
72196212Sscottl        case 'k': case 'K':
73196212Sscottl                iv *= 1024;
74196212Sscottl        case '\0':
75196212Sscottl                break;
76196212Sscottl        default:
77196212Sscottl                return (0);
78196212Sscottl        }
79196212Sscottl        return (iv);
80196212Sscottl}
81196212Sscottl
82196212Sscottl/*
83196212Sscottl * Lock the volume by opening its /dev device read/write.  This will
84196212Sscottl * only work if nothing else has it opened (including mounts).  We
85196212Sscottl * leak the fd on purpose since this application is not long-running.
86196212Sscottl */
87196212Sscottlint
88196212Sscottlmpt_lock_volume(U8 VolumeBus, U8 VolumeID)
89196212Sscottl{
90196212Sscottl	char path[MAXPATHLEN];
91196212Sscottl	struct mpt_query_disk qd;
92196212Sscottl	int error, vfd;
93196212Sscottl
94196212Sscottl	error = mpt_query_disk(VolumeBus, VolumeID, &qd);
95196212Sscottl	if (error == ENOENT)
96196212Sscottl		/*
97196212Sscottl		 * This means there isn't a CAM device associated with
98196212Sscottl		 * the volume, and thus it is already implicitly
99196212Sscottl		 * locked, so just return.
100196212Sscottl		 */
101196212Sscottl		return (0);
102196212Sscottl	if (error) {
103215046Sjhb		warnc(error, "Unable to lookup volume device name");
104215046Sjhb		return (error);
105196212Sscottl	}
106196212Sscottl	snprintf(path, sizeof(path), "%s%s", _PATH_DEV, qd.devname);
107196212Sscottl	vfd = open(path, O_RDWR);
108196212Sscottl	if (vfd < 0) {
109215046Sjhb		error = errno;
110196212Sscottl		warn("Unable to lock volume %s", qd.devname);
111215046Sjhb		return (error);
112196212Sscottl	}
113196212Sscottl	return (0);
114196212Sscottl}
115196212Sscottl
116196212Sscottlstatic int
117196212Sscottlmpt_lock_physdisk(struct mpt_standalone_disk *disk)
118196212Sscottl{
119196212Sscottl	char path[MAXPATHLEN];
120215046Sjhb	int dfd, error;
121196212Sscottl
122196212Sscottl	snprintf(path, sizeof(path), "%s%s", _PATH_DEV, disk->devname);
123196212Sscottl	dfd = open(path, O_RDWR);
124196212Sscottl	if (dfd < 0) {
125215046Sjhb		error = errno;
126196212Sscottl		warn("Unable to lock disk %s", disk->devname);
127215046Sjhb		return (error);
128196212Sscottl	}
129196212Sscottl	return (0);
130196212Sscottl}
131196212Sscottl
132196212Sscottlstatic int
133196212Sscottlmpt_lookup_standalone_disk(const char *name, struct mpt_standalone_disk *disks,
134196212Sscottl    int ndisks, int *index)
135196212Sscottl{
136196212Sscottl	char *cp;
137196212Sscottl	long bus, id;
138196212Sscottl	int i;
139196212Sscottl
140196212Sscottl	/* Check for a raw <bus>:<id> string. */
141196212Sscottl	bus = strtol(name, &cp, 0);
142196212Sscottl	if (*cp == ':') {
143196212Sscottl		id = strtol(cp + 1, &cp, 0);
144196212Sscottl		if (*cp == '\0') {
145196212Sscottl			if (bus < 0 || bus > 0xff || id < 0 || id > 0xff) {
146215046Sjhb				return (EINVAL);
147196212Sscottl			}
148196212Sscottl			for (i = 0; i < ndisks; i++) {
149196212Sscottl				if (disks[i].bus == (U8)bus &&
150196212Sscottl				    disks[i].target == (U8)id) {
151196212Sscottl					*index = i;
152196212Sscottl					return (0);
153196212Sscottl				}
154196212Sscottl			}
155215046Sjhb			return (ENOENT);
156196212Sscottl		}
157196212Sscottl	}
158196212Sscottl
159196212Sscottl	if (name[0] == 'd' && name[1] == 'a') {
160196212Sscottl		for (i = 0; i < ndisks; i++) {
161196212Sscottl			if (strcmp(name, disks[i].devname) == 0) {
162196212Sscottl				*index = i;
163196212Sscottl				return (0);
164196212Sscottl			}
165196212Sscottl		}
166215046Sjhb		return (ENOENT);
167196212Sscottl	}
168196212Sscottl
169215046Sjhb	return (EINVAL);
170196212Sscottl}
171196212Sscottl
172196212Sscottl/*
173196212Sscottl * Mark a standalone disk as being a physical disk.
174196212Sscottl */
175196212Sscottlstatic int
176196212Sscottlmpt_create_physdisk(int fd, struct mpt_standalone_disk *disk, U8 *PhysDiskNum)
177196212Sscottl{
178196212Sscottl	CONFIG_PAGE_HEADER header;
179196212Sscottl	CONFIG_PAGE_RAID_PHYS_DISK_0 *config_page;
180215046Sjhb	int error;
181196212Sscottl	U32 ActionData;
182196212Sscottl
183215046Sjhb	error = mpt_read_config_page_header(fd, MPI_CONFIG_PAGETYPE_RAID_PHYSDISK,
184215046Sjhb	    0, 0, &header, NULL);
185215046Sjhb	if (error)
186215046Sjhb		return (error);
187196212Sscottl	if (header.PageVersion > MPI_RAIDPHYSDISKPAGE0_PAGEVERSION) {
188196212Sscottl		warnx("Unsupported RAID physdisk page 0 version %d",
189196212Sscottl		    header.PageVersion);
190215046Sjhb		return (EOPNOTSUPP);
191196212Sscottl	}
192196212Sscottl	config_page = calloc(1, sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0));
193196212Sscottl	config_page->Header.PageType = MPI_CONFIG_PAGETYPE_RAID_PHYSDISK;
194196212Sscottl	config_page->Header.PageNumber = 0;
195196212Sscottl	config_page->Header.PageLength = sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0) /
196196212Sscottl	    4;
197196212Sscottl	config_page->PhysDiskIOC = 0;	/* XXX */
198196212Sscottl	config_page->PhysDiskBus = disk->bus;
199196212Sscottl	config_page->PhysDiskID = disk->target;
200196212Sscottl
201196212Sscottl	/* XXX: Enclosure info for PhysDiskSettings? */
202215046Sjhb	error = mpt_raid_action(fd, MPI_RAID_ACTION_CREATE_PHYSDISK, 0, 0, 0, 0,
203196212Sscottl	    config_page, sizeof(CONFIG_PAGE_RAID_PHYS_DISK_0), NULL,
204215046Sjhb	    &ActionData, sizeof(ActionData), NULL, NULL, 1);
205215046Sjhb	if (error)
206215046Sjhb		return (error);
207196212Sscottl	*PhysDiskNum = ActionData & 0xff;
208196212Sscottl	return (0);
209196212Sscottl}
210196212Sscottl
211196212Sscottlstatic int
212196212Sscottlmpt_delete_physdisk(int fd, U8 PhysDiskNum)
213196212Sscottl{
214196212Sscottl
215196212Sscottl	return (mpt_raid_action(fd, MPI_RAID_ACTION_DELETE_PHYSDISK, 0, 0,
216196212Sscottl	    PhysDiskNum, 0, NULL, 0, NULL, NULL, 0, NULL, NULL, 0));
217196212Sscottl}
218196212Sscottl
219196212Sscottl/*
220196212Sscottl * MPT's firmware does not have a clear command.  Instead, we
221196212Sscottl * implement it by deleting each array and disk by hand.
222196212Sscottl */
223196212Sscottlstatic int
224196212Sscottlclear_config(int ac, char **av)
225196212Sscottl{
226196212Sscottl	CONFIG_PAGE_IOC_2 *ioc2;
227196212Sscottl	CONFIG_PAGE_IOC_2_RAID_VOL *vol;
228196212Sscottl	CONFIG_PAGE_IOC_3 *ioc3;
229196212Sscottl	IOC_3_PHYS_DISK *disk;
230196212Sscottl	CONFIG_PAGE_IOC_5 *ioc5;
231196212Sscottl	IOC_5_HOT_SPARE *spare;
232215046Sjhb	int ch, error, fd, i;
233196212Sscottl
234196212Sscottl	fd = mpt_open(mpt_unit);
235196212Sscottl	if (fd < 0) {
236215046Sjhb		error = errno;
237196212Sscottl		warn("mpt_open");
238215046Sjhb		return (error);
239196212Sscottl	}
240196212Sscottl
241196212Sscottl	ioc2 = mpt_read_ioc_page(fd, 2, NULL);
242196212Sscottl	if (ioc2 == NULL) {
243215046Sjhb		error = errno;
244196212Sscottl		warn("Failed to fetch volume list");
245215046Sjhb		return (error);
246196212Sscottl	}
247196212Sscottl
248196212Sscottl	/* Lock all the volumes first. */
249196212Sscottl	vol = ioc2->RaidVolume;
250196212Sscottl	for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
251196212Sscottl		if (mpt_lock_volume(vol->VolumeBus, vol->VolumeID) < 0) {
252196212Sscottl			warnx("Volume %s is busy and cannot be deleted",
253196212Sscottl			    mpt_volume_name(vol->VolumeBus, vol->VolumeID));
254196212Sscottl			return (EBUSY);
255196212Sscottl		}
256196212Sscottl	}
257196212Sscottl
258196212Sscottl	printf(
259196212Sscottl	    "Are you sure you wish to clear the configuration on mpt%u? [y/N] ",
260196212Sscottl	    mpt_unit);
261196212Sscottl	ch = getchar();
262196212Sscottl	if (ch != 'y' && ch != 'Y') {
263196212Sscottl		printf("\nAborting\n");
264196212Sscottl		return (0);
265196212Sscottl	}
266196212Sscottl
267196212Sscottl	/* Delete all the volumes. */
268196212Sscottl	vol = ioc2->RaidVolume;
269215046Sjhb	for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
270215046Sjhb		error = mpt_raid_action(fd, MPI_RAID_ACTION_DELETE_VOLUME,
271196212Sscottl		    vol->VolumeBus, vol->VolumeID, 0,
272196212Sscottl		    MPI_RAID_ACTION_ADATA_DEL_PHYS_DISKS |
273196212Sscottl		    MPI_RAID_ACTION_ADATA_ZERO_LBA0, NULL, 0, NULL, NULL, 0,
274215046Sjhb		    NULL, NULL, 0);
275215046Sjhb		if (error)
276215046Sjhb			warnc(error, "Failed to delete volume %s",
277196212Sscottl			    mpt_volume_name(vol->VolumeBus, vol->VolumeID));
278215046Sjhb	}
279196212Sscottl	free(ioc2);
280196212Sscottl
281196212Sscottl	/* Delete all the spares. */
282196212Sscottl	ioc5 = mpt_read_ioc_page(fd, 5, NULL);
283196212Sscottl	if (ioc5 == NULL)
284196212Sscottl		warn("Failed to fetch spare list");
285196212Sscottl	else {
286196212Sscottl		spare = ioc5->HotSpare;
287196212Sscottl		for (i = 0; i < ioc5->NumHotSpares; spare++, i++)
288196212Sscottl			if (mpt_delete_physdisk(fd, spare->PhysDiskNum) < 0)
289196212Sscottl				warn("Failed to delete physical disk %d",
290196212Sscottl				    spare->PhysDiskNum);
291196212Sscottl		free(ioc5);
292196212Sscottl	}
293196212Sscottl
294196212Sscottl	/* Delete any RAID physdisks that may be left. */
295196212Sscottl	ioc3 = mpt_read_ioc_page(fd, 3, NULL);
296196212Sscottl	if (ioc3 == NULL)
297196212Sscottl		warn("Failed to fetch drive list");
298196212Sscottl	else {
299196212Sscottl		disk = ioc3->PhysDisk;
300196212Sscottl		for (i = 0; i < ioc3->NumPhysDisks; disk++, i++)
301196212Sscottl			if (mpt_delete_physdisk(fd, disk->PhysDiskNum) < 0)
302196212Sscottl				warn("Failed to delete physical disk %d",
303196212Sscottl				    disk->PhysDiskNum);
304196212Sscottl		free(ioc3);
305196212Sscottl	}
306196212Sscottl
307196212Sscottl	printf("mpt%d: Configuration cleared\n", mpt_unit);
308196212Sscottl	mpt_rescan_bus(-1, -1);
309196212Sscottl	close(fd);
310196212Sscottl
311196212Sscottl	return (0);
312196212Sscottl}
313196212SscottlMPT_COMMAND(top, clear, clear_config);
314196212Sscottl
315196212Sscottl#define	RT_RAID0	0
316196212Sscottl#define	RT_RAID1	1
317196212Sscottl#define	RT_RAID1E	2
318196212Sscottl
319196212Sscottlstatic struct raid_type_entry {
320196212Sscottl	const char *name;
321196212Sscottl	int	raid_type;
322196212Sscottl} raid_type_table[] = {
323196212Sscottl	{ "raid0",	RT_RAID0 },
324196212Sscottl	{ "raid-0",	RT_RAID0 },
325196212Sscottl	{ "raid1",	RT_RAID1 },
326196212Sscottl	{ "raid-1",	RT_RAID1 },
327196212Sscottl	{ "mirror",	RT_RAID1 },
328196212Sscottl	{ "raid1e",	RT_RAID1E },
329196212Sscottl	{ "raid-1e",	RT_RAID1E },
330196212Sscottl	{ NULL,		0 },
331196212Sscottl};
332196212Sscottl
333196212Sscottlstruct config_id_state {
334196212Sscottl	struct mpt_standalone_disk *sdisks;
335196212Sscottl	struct mpt_drive_list *list;
336196212Sscottl	CONFIG_PAGE_IOC_2 *ioc2;
337196212Sscottl	U8	target_id;
338196212Sscottl	int	nsdisks;
339196212Sscottl};
340196212Sscottl
341196212Sscottlstruct drive_info {
342196212Sscottl	CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
343196212Sscottl	struct mpt_standalone_disk *sdisk;
344196212Sscottl};
345196212Sscottl
346196212Sscottlstruct volume_info {
347196212Sscottl	int	drive_count;
348196212Sscottl	struct drive_info *drives;
349196212Sscottl};
350196212Sscottl
351196212Sscottl/* Parse a comma-separated list of drives for a volume. */
352196212Sscottlstatic int
353196212Sscottlparse_volume(int fd, int raid_type, struct config_id_state *state,
354196212Sscottl    char *volume_str, struct volume_info *info)
355196212Sscottl{
356196212Sscottl	struct drive_info *dinfo;
357196212Sscottl	U8 PhysDiskNum;
358196212Sscottl	char *cp;
359196212Sscottl	int count, error, i;
360196212Sscottl
361196212Sscottl	cp = volume_str;
362196212Sscottl	for (count = 0; cp != NULL; count++) {
363196212Sscottl		cp = strchr(cp, ',');
364196212Sscottl		if (cp != NULL) {
365196212Sscottl			cp++;
366196212Sscottl			if (*cp == ',') {
367196212Sscottl				warnx("Invalid drive list '%s'", volume_str);
368196212Sscottl				return (EINVAL);
369196212Sscottl			}
370196212Sscottl		}
371196212Sscottl	}
372196212Sscottl
373196212Sscottl	/* Validate the number of drives for this volume. */
374196212Sscottl	switch (raid_type) {
375196212Sscottl	case RT_RAID0:
376196212Sscottl		if (count < 2) {
377196212Sscottl			warnx("RAID0 requires at least 2 drives in each "
378196212Sscottl			    "array");
379196212Sscottl			return (EINVAL);
380196212Sscottl		}
381196212Sscottl		break;
382196212Sscottl	case RT_RAID1:
383196212Sscottl		if (count != 2) {
384196212Sscottl			warnx("RAID1 requires exactly 2 drives in each "
385196212Sscottl			    "array");
386196212Sscottl			return (EINVAL);
387196212Sscottl		}
388196212Sscottl		break;
389196212Sscottl	case RT_RAID1E:
390196212Sscottl		if (count < 3) {
391196212Sscottl			warnx("RAID1E requires at least 3 drives in each "
392196212Sscottl			    "array");
393196212Sscottl			return (EINVAL);
394196212Sscottl		}
395196212Sscottl		break;
396196212Sscottl	}
397196212Sscottl
398196212Sscottl	/* Validate each drive. */
399196212Sscottl	info->drives = calloc(count, sizeof(struct drive_info));
400196212Sscottl	info->drive_count = count;
401196212Sscottl	for (dinfo = info->drives; (cp = strsep(&volume_str, ",")) != NULL;
402196212Sscottl	     dinfo++) {
403196212Sscottl		/* If this drive is already a RAID phys just fetch the info. */
404196212Sscottl		error = mpt_lookup_drive(state->list, cp, &PhysDiskNum);
405196212Sscottl		if (error == 0) {
406196212Sscottl			dinfo->info = mpt_pd_info(fd, PhysDiskNum, NULL);
407196212Sscottl			if (dinfo->info == NULL)
408196212Sscottl				return (errno);
409196212Sscottl			continue;
410196212Sscottl		}
411196212Sscottl
412196212Sscottl		/* See if it is a standalone disk. */
413196212Sscottl		if (mpt_lookup_standalone_disk(cp, state->sdisks,
414196212Sscottl		    state->nsdisks, &i) < 0) {
415215046Sjhb			error = errno;
416196212Sscottl			warn("Unable to lookup drive %s", cp);
417215046Sjhb			return (error);
418196212Sscottl		}
419196212Sscottl		dinfo->sdisk = &state->sdisks[i];
420196212Sscottl
421196212Sscottl		/* Lock the disk, we will create phys disk pages later. */
422196212Sscottl		if (mpt_lock_physdisk(dinfo->sdisk) < 0)
423196212Sscottl			return (errno);
424196212Sscottl	}
425196212Sscottl
426196212Sscottl	return (0);
427196212Sscottl}
428196212Sscottl
429196212Sscottl/*
430196212Sscottl * Add RAID physdisk pages for any standalone disks that a volume is
431196212Sscottl * going to use.
432196212Sscottl */
433196212Sscottlstatic int
434196212Sscottladd_drives(int fd, struct volume_info *info, int verbose)
435196212Sscottl{
436196212Sscottl	struct drive_info *dinfo;
437196212Sscottl	U8 PhysDiskNum;
438215046Sjhb	int error, i;
439196212Sscottl
440196212Sscottl	for (i = 0, dinfo = info->drives; i < info->drive_count;
441196212Sscottl	     i++, dinfo++) {
442196212Sscottl		if (dinfo->info == NULL) {
443196212Sscottl			if (mpt_create_physdisk(fd, dinfo->sdisk,
444196212Sscottl			    &PhysDiskNum) < 0) {
445215046Sjhb				error = errno;
446196212Sscottl				warn(
447196212Sscottl			    "Failed to create physical disk page for %s",
448196212Sscottl				    dinfo->sdisk->devname);
449215046Sjhb				return (error);
450196212Sscottl			}
451196212Sscottl			if (verbose)
452196212Sscottl				printf("Added drive %s with PhysDiskNum %u\n",
453196212Sscottl				    dinfo->sdisk->devname, PhysDiskNum);
454196212Sscottl
455196212Sscottl			dinfo->info = mpt_pd_info(fd, PhysDiskNum, NULL);
456196212Sscottl			if (dinfo->info == NULL)
457196212Sscottl				return (errno);
458196212Sscottl		}
459196212Sscottl	}
460196212Sscottl	return (0);
461196212Sscottl}
462196212Sscottl
463196212Sscottl/*
464196212Sscottl * Find the next free target ID assuming that 'target_id' is the last
465196212Sscottl * one used.  'target_id' should be 0xff for the initial test.
466196212Sscottl */
467196212Sscottlstatic U8
468196212Sscottlfind_next_volume(struct config_id_state *state)
469196212Sscottl{
470196212Sscottl	CONFIG_PAGE_IOC_2_RAID_VOL *vol;
471196212Sscottl	int i;
472196212Sscottl
473196212Sscottlrestart:
474196212Sscottl	/* Assume the current one is used. */
475196212Sscottl	state->target_id++;
476196212Sscottl
477196212Sscottl	/* Search drives first. */
478196212Sscottl	for (i = 0; i < state->nsdisks; i++)
479196212Sscottl		if (state->sdisks[i].target == state->target_id)
480196212Sscottl			goto restart;
481196212Sscottl	for (i = 0; i < state->list->ndrives; i++)
482196212Sscottl		if (state->list->drives[i]->PhysDiskID == state->target_id)
483196212Sscottl			goto restart;
484196212Sscottl
485228990Suqs	/* Search volumes second. */
486196212Sscottl	vol = state->ioc2->RaidVolume;
487196212Sscottl	for (i = 0; i < state->ioc2->NumActiveVolumes; vol++, i++)
488196212Sscottl		if (vol->VolumeID == state->target_id)
489196212Sscottl			goto restart;
490196212Sscottl
491196212Sscottl	return (state->target_id);
492196212Sscottl}
493196212Sscottl
494196212Sscottl/* Create a volume and populate it with drives. */
495196212Sscottlstatic CONFIG_PAGE_RAID_VOL_0 *
496196212Sscottlbuild_volume(int fd, struct volume_info *info, int raid_type, long stripe_size,
497196212Sscottl    struct config_id_state *state, int verbose)
498196212Sscottl{
499196212Sscottl	CONFIG_PAGE_HEADER header;
500196212Sscottl	CONFIG_PAGE_RAID_VOL_0 *vol;
501196212Sscottl	RAID_VOL0_PHYS_DISK *rdisk;
502196212Sscottl	struct drive_info *dinfo;
503196212Sscottl        U32 MinLBA;
504196212Sscottl	uint64_t MaxLBA;
505196212Sscottl	size_t page_size;
506215046Sjhb	int error, i;
507196212Sscottl
508215046Sjhb	error = mpt_read_config_page_header(fd, MPI_CONFIG_PAGETYPE_RAID_VOLUME,
509215046Sjhb	    0, 0, &header, NULL);
510215046Sjhb	if (error) {
511215046Sjhb		errno = error;
512196212Sscottl		return (NULL);
513215046Sjhb	}
514196212Sscottl	if (header.PageVersion > MPI_RAIDVOLPAGE0_PAGEVERSION) {
515196212Sscottl		warnx("Unsupported RAID volume page 0 version %d",
516196212Sscottl		    header.PageVersion);
517196212Sscottl		errno = EOPNOTSUPP;
518196212Sscottl		return (NULL);
519196212Sscottl	}
520196212Sscottl	page_size = sizeof(CONFIG_PAGE_RAID_VOL_0) +
521196212Sscottl	    sizeof(RAID_VOL0_PHYS_DISK) * (info->drive_count - 1);
522196212Sscottl	vol = calloc(1, page_size);
523215046Sjhb	if (vol == NULL)
524215046Sjhb		return (NULL);
525196212Sscottl
526196212Sscottl	/* Header */
527196212Sscottl	vol->Header.PageType = MPI_CONFIG_PAGETYPE_RAID_VOLUME;
528196212Sscottl	vol->Header.PageNumber = 0;
529196212Sscottl	vol->Header.PageLength = page_size / 4;
530196212Sscottl
531196212Sscottl	/* Properties */
532196212Sscottl	vol->VolumeID = find_next_volume(state);
533196212Sscottl	vol->VolumeBus = 0;
534196212Sscottl	vol->VolumeIOC = 0;	/* XXX */
535196212Sscottl	vol->VolumeStatus.Flags = MPI_RAIDVOL0_STATUS_FLAG_ENABLED;
536196212Sscottl	vol->VolumeStatus.State = MPI_RAIDVOL0_STATUS_STATE_OPTIMAL;
537196212Sscottl	vol->VolumeSettings.Settings = MPI_RAIDVOL0_SETTING_USE_DEFAULTS;
538196212Sscottl	vol->VolumeSettings.HotSparePool = MPI_RAID_HOT_SPARE_POOL_0;
539196212Sscottl	vol->NumPhysDisks = info->drive_count;
540196212Sscottl
541196212Sscottl	/* Find the smallest drive. */
542196212Sscottl	MinLBA = info->drives[0].info->MaxLBA;
543196212Sscottl	for (i = 1; i < info->drive_count; i++)
544196212Sscottl		if (info->drives[i].info->MaxLBA < MinLBA)
545196212Sscottl			MinLBA = info->drives[i].info->MaxLBA;
546196212Sscottl
547196212Sscottl	/*
548196212Sscottl	 * Now chop off 512MB at the end to leave room for the
549196212Sscottl	 * metadata.  The controller might only use 64MB, but we just
550196212Sscottl	 * chop off the max to be simple.
551196212Sscottl	 */
552196212Sscottl	MinLBA -= (512 * 1024 * 1024) / 512;
553196212Sscottl
554196212Sscottl	switch (raid_type) {
555196212Sscottl	case RT_RAID0:
556196212Sscottl		vol->VolumeType = MPI_RAID_VOL_TYPE_IS;
557196212Sscottl		vol->StripeSize = stripe_size / 512;
558196212Sscottl		MaxLBA = MinLBA * info->drive_count;
559196212Sscottl		break;
560196212Sscottl	case RT_RAID1:
561196212Sscottl		vol->VolumeType = MPI_RAID_VOL_TYPE_IM;
562196212Sscottl		MaxLBA = MinLBA * (info->drive_count / 2);
563196212Sscottl		break;
564196212Sscottl	case RT_RAID1E:
565196212Sscottl		vol->VolumeType = MPI_RAID_VOL_TYPE_IME;
566196212Sscottl		vol->StripeSize = stripe_size / 512;
567196212Sscottl		MaxLBA = MinLBA * info->drive_count / 2;
568196212Sscottl		break;
569196212Sscottl	default:
570196212Sscottl		/* Pacify gcc. */
571196212Sscottl		abort();
572196212Sscottl	}
573196212Sscottl
574196212Sscottl	/*
575196212Sscottl	 * If the controller doesn't support 64-bit addressing and the
576196212Sscottl	 * new volume is larger than 2^32 blocks, warn the user and
577196212Sscottl	 * truncate the volume.
578196212Sscottl	 */
579196212Sscottl	if (MaxLBA >> 32 != 0 &&
580196212Sscottl	    !(state->ioc2->CapabilitiesFlags &
581196212Sscottl	    MPI_IOCPAGE2_CAP_FLAGS_RAID_64_BIT_ADDRESSING)) {
582196212Sscottl		warnx(
583196212Sscottl	    "Controller does not support volumes > 2TB, truncating volume.");
584196212Sscottl		MaxLBA = 0xffffffff;
585196212Sscottl	}
586196212Sscottl	vol->MaxLBA = MaxLBA;
587196212Sscottl	vol->MaxLBAHigh = MaxLBA >> 32;
588196212Sscottl
589196212Sscottl	/* Populate drives. */
590196212Sscottl	for (i = 0, dinfo = info->drives, rdisk = vol->PhysDisk;
591196212Sscottl	     i < info->drive_count; i++, dinfo++, rdisk++) {
592196212Sscottl		if (verbose)
593196212Sscottl			printf("Adding drive %u (%u:%u) to volume %u:%u\n",
594196212Sscottl			    dinfo->info->PhysDiskNum, dinfo->info->PhysDiskBus,
595196212Sscottl			    dinfo->info->PhysDiskID, vol->VolumeBus,
596196212Sscottl			    vol->VolumeID);
597196212Sscottl		if (raid_type == RT_RAID1) {
598196212Sscottl			if (i == 0)
599196212Sscottl				rdisk->PhysDiskMap =
600196212Sscottl				    MPI_RAIDVOL0_PHYSDISK_PRIMARY;
601196212Sscottl			else
602196212Sscottl				rdisk->PhysDiskMap =
603196212Sscottl				    MPI_RAIDVOL0_PHYSDISK_SECONDARY;
604196212Sscottl		} else
605196212Sscottl			rdisk->PhysDiskMap = i;
606196212Sscottl		rdisk->PhysDiskNum = dinfo->info->PhysDiskNum;
607196212Sscottl	}
608196212Sscottl
609196212Sscottl	return (vol);
610196212Sscottl}
611196212Sscottl
612196212Sscottlstatic int
613196212Sscottlcreate_volume(int ac, char **av)
614196212Sscottl{
615196212Sscottl	CONFIG_PAGE_RAID_VOL_0 *vol;
616196212Sscottl	struct config_id_state state;
617196212Sscottl	struct volume_info *info;
618196212Sscottl	long stripe_size;
619215046Sjhb	int ch, error, fd, i, quick, raid_type, verbose;
620196212Sscottl#ifdef DEBUG
621196212Sscottl	int dump;
622196212Sscottl#endif
623196212Sscottl
624196212Sscottl	if (ac < 2) {
625196212Sscottl		warnx("create: volume type required");
626196212Sscottl		return (EINVAL);
627196212Sscottl	}
628196212Sscottl
629196212Sscottl	fd = mpt_open(mpt_unit);
630196212Sscottl	if (fd < 0) {
631215046Sjhb		error = errno;
632196212Sscottl		warn("mpt_open");
633215046Sjhb		return (error);
634196212Sscottl	}
635196212Sscottl
636196212Sscottl	/* Lookup the RAID type first. */
637196212Sscottl	raid_type = -1;
638196212Sscottl	for (i = 0; raid_type_table[i].name != NULL; i++)
639196212Sscottl		if (strcasecmp(raid_type_table[i].name, av[1]) == 0) {
640196212Sscottl			raid_type = raid_type_table[i].raid_type;
641196212Sscottl			break;
642196212Sscottl		}
643196212Sscottl
644196212Sscottl	if (raid_type == -1) {
645196212Sscottl		warnx("Unknown or unsupported volume type %s", av[1]);
646196212Sscottl		return (EINVAL);
647196212Sscottl	}
648196212Sscottl
649196212Sscottl	/* Parse any options. */
650196212Sscottl	optind = 2;
651196212Sscottl#ifdef DEBUG
652196212Sscottl	dump = 0;
653196212Sscottl#endif
654196212Sscottl	quick = 0;
655196212Sscottl	verbose = 0;
656196212Sscottl	stripe_size = 64 * 1024;
657196212Sscottl
658196212Sscottl	while ((ch = getopt(ac, av, "dqs:v")) != -1) {
659196212Sscottl		switch (ch) {
660196212Sscottl#ifdef DEBUG
661196212Sscottl		case 'd':
662196212Sscottl			dump = 1;
663196212Sscottl			break;
664196212Sscottl#endif
665196212Sscottl		case 'q':
666196212Sscottl			quick = 1;
667196212Sscottl			break;
668196212Sscottl		case 's':
669196212Sscottl			stripe_size = dehumanize(optarg);
670196212Sscottl			if ((stripe_size < 512) || (!powerof2(stripe_size))) {
671196212Sscottl				warnx("Invalid stripe size %s", optarg);
672196212Sscottl				return (EINVAL);
673196212Sscottl			}
674196212Sscottl			break;
675196212Sscottl		case 'v':
676196212Sscottl			verbose = 1;
677196212Sscottl			break;
678196212Sscottl		case '?':
679196212Sscottl		default:
680196212Sscottl			return (EINVAL);
681196212Sscottl		}
682196212Sscottl	}
683196212Sscottl	ac -= optind;
684196212Sscottl	av += optind;
685196212Sscottl
686196212Sscottl	/* Fetch existing config data. */
687196212Sscottl	state.ioc2 = mpt_read_ioc_page(fd, 2, NULL);
688196212Sscottl	if (state.ioc2 == NULL) {
689215046Sjhb		error = errno;
690196212Sscottl		warn("Failed to read volume list");
691215046Sjhb		return (error);
692196212Sscottl	}
693196212Sscottl	state.list = mpt_pd_list(fd);
694196212Sscottl	if (state.list == NULL)
695196212Sscottl		return (errno);
696196212Sscottl	error = mpt_fetch_disks(fd, &state.nsdisks, &state.sdisks);
697196212Sscottl	if (error) {
698196212Sscottl		warn("Failed to fetch standalone disk list");
699196212Sscottl		return (error);
700196212Sscottl	}
701196212Sscottl	state.target_id = 0xff;
702196212Sscottl
703196212Sscottl	/* Parse the drive list. */
704196212Sscottl	if (ac != 1) {
705196212Sscottl		warnx("Exactly one drive list is required");
706196212Sscottl		return (EINVAL);
707196212Sscottl	}
708196212Sscottl	info = calloc(1, sizeof(*info));
709215046Sjhb	if (info == NULL)
710215046Sjhb		return (ENOMEM);
711196212Sscottl	error = parse_volume(fd, raid_type, &state, av[0], info);
712196212Sscottl	if (error)
713196212Sscottl		return (error);
714196212Sscottl
715196212Sscottl	/* Create RAID physdisk pages for standalone disks. */
716196212Sscottl	error = add_drives(fd, info, verbose);
717196212Sscottl	if (error)
718196212Sscottl		return (error);
719196212Sscottl
720196212Sscottl	/* Build the volume. */
721196212Sscottl	vol = build_volume(fd, info, raid_type, stripe_size, &state, verbose);
722215046Sjhb	if (vol == NULL)
723215046Sjhb		return (errno);
724196212Sscottl
725196212Sscottl#ifdef DEBUG
726196212Sscottl	if (dump) {
727196212Sscottl		dump_config(vol);
728196212Sscottl		goto skip;
729196212Sscottl	}
730196212Sscottl#endif
731196212Sscottl
732196212Sscottl	/* Send the new volume to the controller. */
733215046Sjhb	error = mpt_raid_action(fd, MPI_RAID_ACTION_CREATE_VOLUME, vol->VolumeBus,
734196212Sscottl	    vol->VolumeID, 0, quick ? MPI_RAID_ACTION_ADATA_DO_NOT_SYNC : 0,
735215046Sjhb	    vol, vol->Header.PageLength * 4, NULL, NULL, 0, NULL, NULL, 1);
736215046Sjhb	if (error) {
737215046Sjhb		errno = error;
738196212Sscottl		warn("Failed to add volume");
739215046Sjhb		return (error);
740196212Sscottl	}
741196212Sscottl
742196212Sscottl#ifdef DEBUG
743196212Sscottlskip:
744196212Sscottl#endif
745196212Sscottl	mpt_rescan_bus(vol->VolumeBus, vol->VolumeID);
746196212Sscottl
747196212Sscottl	/* Clean up. */
748196212Sscottl	free(vol);
749196212Sscottl	free(info);
750196212Sscottl	free(state.sdisks);
751196212Sscottl	mpt_free_pd_list(state.list);
752196212Sscottl	free(state.ioc2);
753196212Sscottl	close(fd);
754196212Sscottl
755196212Sscottl	return (0);
756196212Sscottl}
757196212SscottlMPT_COMMAND(top, create, create_volume);
758196212Sscottl
759196212Sscottlstatic int
760196212Sscottldelete_volume(int ac, char **av)
761196212Sscottl{
762196212Sscottl	U8 VolumeBus, VolumeID;
763215046Sjhb	int error, fd;
764196212Sscottl
765196212Sscottl	if (ac != 2) {
766196212Sscottl		warnx("delete: volume required");
767196212Sscottl		return (EINVAL);
768196212Sscottl	}
769196212Sscottl
770196212Sscottl	fd = mpt_open(mpt_unit);
771196212Sscottl	if (fd < 0) {
772215046Sjhb		error = errno;
773196212Sscottl		warn("mpt_open");
774215046Sjhb		return (error);
775196212Sscottl	}
776196212Sscottl
777215046Sjhb	error = mpt_lookup_volume(fd, av[1], &VolumeBus, &VolumeID);
778215046Sjhb	if (error) {
779215046Sjhb		warnc(error, "Invalid volume %s", av[1]);
780215046Sjhb		return (error);
781196212Sscottl	}
782196212Sscottl
783196212Sscottl	if (mpt_lock_volume(VolumeBus, VolumeID) < 0)
784196212Sscottl		return (errno);
785196212Sscottl
786215046Sjhb	error = mpt_raid_action(fd, MPI_RAID_ACTION_DELETE_VOLUME, VolumeBus,
787196212Sscottl	    VolumeID, 0, MPI_RAID_ACTION_ADATA_DEL_PHYS_DISKS |
788196212Sscottl	    MPI_RAID_ACTION_ADATA_ZERO_LBA0, NULL, 0, NULL, NULL, 0, NULL,
789215046Sjhb	    NULL, 0);
790215046Sjhb	if (error) {
791215046Sjhb		warnc(error, "Failed to delete volume");
792215046Sjhb		return (error);
793196212Sscottl	}
794196212Sscottl
795196212Sscottl	mpt_rescan_bus(-1, -1);
796196212Sscottl	close(fd);
797196212Sscottl
798196212Sscottl	return (0);
799196212Sscottl}
800196212SscottlMPT_COMMAND(top, delete, delete_volume);
801196212Sscottl
802196212Sscottlstatic int
803196212Sscottlfind_volume_spare_pool(int fd, const char *name, int *pool)
804196212Sscottl{
805196212Sscottl	CONFIG_PAGE_RAID_VOL_0 *info;
806196212Sscottl	CONFIG_PAGE_IOC_2 *ioc2;
807196212Sscottl	CONFIG_PAGE_IOC_2_RAID_VOL *vol;
808196212Sscottl	U8 VolumeBus, VolumeID;
809215046Sjhb	int error, i, j, new_pool, pool_count[7];
810196212Sscottl
811215046Sjhb	error = mpt_lookup_volume(fd, name, &VolumeBus, &VolumeID);
812215046Sjhb	if (error) {
813215046Sjhb		warnc(error, "Invalid volume %s", name);
814215046Sjhb		return (error);
815196212Sscottl	}
816196212Sscottl
817196212Sscottl	info = mpt_vol_info(fd, VolumeBus, VolumeID, NULL);
818196212Sscottl	if (info == NULL)
819215046Sjhb		return (errno);
820196212Sscottl
821196212Sscottl	/*
822196212Sscottl	 * Check for an existing pool other than pool 0 (used for
823196212Sscottl	 * global spares).
824196212Sscottl	 */
825196212Sscottl	if ((info->VolumeSettings.HotSparePool & ~MPI_RAID_HOT_SPARE_POOL_0) !=
826196212Sscottl	    0) {
827196212Sscottl		*pool = 1 << (ffs(info->VolumeSettings.HotSparePool &
828196212Sscottl		    ~MPI_RAID_HOT_SPARE_POOL_0) - 1);
829196212Sscottl		return (0);
830196212Sscottl	}
831196212Sscottl	free(info);
832196212Sscottl
833196212Sscottl	/*
834196212Sscottl	 * Try to find a free pool.  First, figure out which pools are
835196212Sscottl	 * in use.
836196212Sscottl	 */
837196212Sscottl	ioc2 = mpt_read_ioc_page(fd, 2, NULL);
838196212Sscottl	if (ioc2 == NULL) {
839215046Sjhb		error = errno;
840196212Sscottl		warn("Failed to fetch volume list");
841215046Sjhb		return (error);
842196212Sscottl	}
843196212Sscottl	bzero(pool_count, sizeof(pool_count));
844196212Sscottl	vol = ioc2->RaidVolume;
845196212Sscottl	for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) {
846196212Sscottl		info = mpt_vol_info(fd, vol->VolumeBus, vol->VolumeID, NULL);
847196212Sscottl		if (info == NULL)
848215046Sjhb			return (errno);
849196212Sscottl		for (j = 0; j < 7; j++)
850196212Sscottl			if (info->VolumeSettings.HotSparePool & (1 << (j + 1)))
851196212Sscottl				pool_count[j]++;
852196212Sscottl		free(info);
853196212Sscottl	}
854196212Sscottl	free(ioc2);
855196212Sscottl
856196212Sscottl	/* Find the pool with the lowest use count. */
857196212Sscottl	new_pool = 0;
858196212Sscottl	for (i = 1; i < 7; i++)
859196212Sscottl		if (pool_count[i] < pool_count[new_pool])
860196212Sscottl			new_pool = i;
861196212Sscottl	new_pool++;
862196212Sscottl
863196212Sscottl	/* Add this pool to the volume. */
864196212Sscottl	info = mpt_vol_info(fd, VolumeBus, VolumeID, NULL);
865196212Sscottl	if (info == NULL)
866215046Sjhb		return (error);
867196212Sscottl	info->VolumeSettings.HotSparePool |= (1 << new_pool);
868215046Sjhb	error = mpt_raid_action(fd, MPI_RAID_ACTION_CHANGE_VOLUME_SETTINGS,
869196212Sscottl	    VolumeBus, VolumeID, 0, *(U32 *)&info->VolumeSettings, NULL, 0,
870215046Sjhb	    NULL, NULL, 0, NULL, NULL, 0);
871215046Sjhb	if (error) {
872196212Sscottl		warnx("Failed to add spare pool %d to %s", new_pool,
873196212Sscottl		    mpt_volume_name(VolumeBus, VolumeID));
874215046Sjhb		return (error);
875196212Sscottl	}
876196212Sscottl	free(info);
877196212Sscottl
878196212Sscottl	*pool = (1 << new_pool);
879196212Sscottl	return (0);
880196212Sscottl}
881196212Sscottl
882196212Sscottlstatic int
883196212Sscottladd_spare(int ac, char **av)
884196212Sscottl{
885196212Sscottl	CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
886196212Sscottl	struct mpt_standalone_disk *sdisks;
887196212Sscottl	struct mpt_drive_list *list;
888196212Sscottl	U8 PhysDiskNum;
889196212Sscottl	int error, fd, i, nsdisks, pool;
890196212Sscottl
891196212Sscottl	if (ac < 2) {
892196212Sscottl		warnx("add spare: drive required");
893196212Sscottl		return (EINVAL);
894196212Sscottl	}
895196212Sscottl	if (ac > 3) {
896196212Sscottl		warnx("add spare: extra arguments");
897196212Sscottl		return (EINVAL);
898196212Sscottl	}
899196212Sscottl
900196212Sscottl	fd = mpt_open(mpt_unit);
901196212Sscottl	if (fd < 0) {
902215046Sjhb		error = errno;
903196212Sscottl		warn("mpt_open");
904215046Sjhb		return (error);
905196212Sscottl	}
906196212Sscottl
907196212Sscottl	if (ac == 3) {
908215046Sjhb		error = find_volume_spare_pool(fd, av[2], &pool);
909215046Sjhb		if (error)
910215046Sjhb			return (error);
911196212Sscottl	} else
912196212Sscottl		pool = MPI_RAID_HOT_SPARE_POOL_0;
913196212Sscottl
914196212Sscottl	list = mpt_pd_list(fd);
915196212Sscottl	if (list == NULL)
916196212Sscottl		return (errno);
917196212Sscottl
918196212Sscottl	error = mpt_lookup_drive(list, av[1], &PhysDiskNum);
919196212Sscottl	if (error) {
920196212Sscottl		error = mpt_fetch_disks(fd, &nsdisks, &sdisks);
921196212Sscottl		if (error != 0) {
922196212Sscottl			warn("Failed to fetch standalone disk list");
923196212Sscottl			return (error);
924196212Sscottl		}
925196212Sscottl
926196212Sscottl		if (mpt_lookup_standalone_disk(av[1], sdisks, nsdisks, &i) <
927196212Sscottl		    0) {
928215046Sjhb			error = errno;
929196212Sscottl			warn("Unable to lookup drive %s", av[1]);
930215046Sjhb			return (error);
931196212Sscottl		}
932196212Sscottl
933196212Sscottl		if (mpt_lock_physdisk(&sdisks[i]) < 0)
934196212Sscottl			return (errno);
935196212Sscottl
936196212Sscottl		if (mpt_create_physdisk(fd, &sdisks[i], &PhysDiskNum) < 0) {
937215046Sjhb			error = errno;
938196212Sscottl			warn("Failed to create physical disk page");
939215046Sjhb			return (error);
940196212Sscottl		}
941196212Sscottl		free(sdisks);
942196212Sscottl	}
943196212Sscottl	mpt_free_pd_list(list);
944196212Sscottl
945196212Sscottl	info = mpt_pd_info(fd, PhysDiskNum, NULL);
946196212Sscottl	if (info == NULL) {
947215046Sjhb		error = errno;
948196212Sscottl		warn("Failed to fetch drive info");
949215046Sjhb		return (error);
950196212Sscottl	}
951196212Sscottl
952196212Sscottl	info->PhysDiskSettings.HotSparePool = pool;
953196212Sscottl	error = mpt_raid_action(fd, MPI_RAID_ACTION_CHANGE_PHYSDISK_SETTINGS, 0,
954196212Sscottl	    0, PhysDiskNum, *(U32 *)&info->PhysDiskSettings, NULL, 0, NULL,
955196212Sscottl	    NULL, 0, NULL, NULL, 0);
956196212Sscottl	if (error) {
957215046Sjhb		warnc(error, "Failed to assign spare");
958215046Sjhb		return (error);
959196212Sscottl	}
960196212Sscottl
961196212Sscottl	free(info);
962196212Sscottl	close(fd);
963196212Sscottl
964196212Sscottl	return (0);
965196212Sscottl}
966196212SscottlMPT_COMMAND(top, add, add_spare);
967196212Sscottl
968196212Sscottlstatic int
969196212Sscottlremove_spare(int ac, char **av)
970196212Sscottl{
971196212Sscottl	CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
972196212Sscottl	struct mpt_drive_list *list;
973196212Sscottl	U8 PhysDiskNum;
974196212Sscottl	int error, fd;
975196212Sscottl
976196212Sscottl	if (ac != 2) {
977196212Sscottl		warnx("remove spare: drive required");
978196212Sscottl		return (EINVAL);
979196212Sscottl	}
980196212Sscottl
981196212Sscottl	fd = mpt_open(mpt_unit);
982196212Sscottl	if (fd < 0) {
983215046Sjhb		error = errno;
984196212Sscottl		warn("mpt_open");
985215046Sjhb		return (error);
986196212Sscottl	}
987196212Sscottl
988196212Sscottl	list = mpt_pd_list(fd);
989196212Sscottl	if (list == NULL)
990196212Sscottl		return (errno);
991196212Sscottl
992196212Sscottl	error = mpt_lookup_drive(list, av[1], &PhysDiskNum);
993196212Sscottl	if (error) {
994196212Sscottl		warn("Failed to find drive %s", av[1]);
995196212Sscottl		return (error);
996196212Sscottl	}
997196212Sscottl	mpt_free_pd_list(list);
998196212Sscottl
999196212Sscottl
1000196212Sscottl	info = mpt_pd_info(fd, PhysDiskNum, NULL);
1001196212Sscottl	if (info == NULL) {
1002215046Sjhb		error = errno;
1003196212Sscottl		warn("Failed to fetch drive info");
1004215046Sjhb		return (error);
1005196212Sscottl	}
1006196212Sscottl
1007196212Sscottl	if (info->PhysDiskSettings.HotSparePool == 0) {
1008196212Sscottl		warnx("Drive %u is not a hot spare", PhysDiskNum);
1009196212Sscottl		return (EINVAL);
1010196212Sscottl	}
1011196212Sscottl
1012196212Sscottl	if (mpt_delete_physdisk(fd, PhysDiskNum) < 0) {
1013215046Sjhb		error = errno;
1014196212Sscottl		warn("Failed to delete physical disk page");
1015215046Sjhb		return (error);
1016196212Sscottl	}
1017196212Sscottl
1018196212Sscottl	mpt_rescan_bus(info->PhysDiskBus, info->PhysDiskID);
1019196212Sscottl	free(info);
1020196212Sscottl	close(fd);
1021196212Sscottl
1022196212Sscottl	return (0);
1023196212Sscottl}
1024196212SscottlMPT_COMMAND(top, remove, remove_spare);
1025196212Sscottl
1026196212Sscottl#ifdef DEBUG
1027196212SscottlMPT_TABLE(top, pd);
1028196212Sscottl
1029196212Sscottlstatic int
1030196212Sscottlpd_create(int ac, char **av)
1031196212Sscottl{
1032196212Sscottl	struct mpt_standalone_disk *disks;
1033196212Sscottl	int error, fd, i, ndisks;
1034196212Sscottl	U8 PhysDiskNum;
1035196212Sscottl
1036196212Sscottl	if (ac != 2) {
1037196212Sscottl		warnx("pd create: drive required");
1038196212Sscottl		return (EINVAL);
1039196212Sscottl	}
1040196212Sscottl
1041196212Sscottl	fd = mpt_open(mpt_unit);
1042196212Sscottl	if (fd < 0) {
1043215046Sjhb		error = errno;
1044196212Sscottl		warn("mpt_open");
1045215046Sjhb		return (error);
1046196212Sscottl	}
1047196212Sscottl
1048196212Sscottl	error = mpt_fetch_disks(fd, &ndisks, &disks);
1049196212Sscottl	if (error != 0) {
1050196212Sscottl		warn("Failed to fetch standalone disk list");
1051196212Sscottl		return (error);
1052196212Sscottl	}
1053196212Sscottl
1054196212Sscottl	if (mpt_lookup_standalone_disk(av[1], disks, ndisks, &i) < 0) {
1055215046Sjhb		error = errno;
1056196212Sscottl		warn("Unable to lookup drive");
1057215046Sjhb		return (error);
1058196212Sscottl	}
1059196212Sscottl
1060196212Sscottl	if (mpt_lock_physdisk(&disks[i]) < 0)
1061196212Sscottl		return (errno);
1062196212Sscottl
1063196212Sscottl	if (mpt_create_physdisk(fd, &disks[i], &PhysDiskNum) < 0) {
1064215046Sjhb		error = errno;
1065196212Sscottl		warn("Failed to create physical disk page");
1066215046Sjhb		return (error);
1067196212Sscottl	}
1068196212Sscottl	free(disks);
1069196212Sscottl
1070196212Sscottl	printf("Added drive %s with PhysDiskNum %u\n", av[1], PhysDiskNum);
1071196212Sscottl
1072196212Sscottl	close(fd);
1073196212Sscottl
1074196212Sscottl	return (0);
1075196212Sscottl}
1076196212SscottlMPT_COMMAND(pd, create, pd_create);
1077196212Sscottl
1078196212Sscottlstatic int
1079196212Sscottlpd_delete(int ac, char **av)
1080196212Sscottl{
1081196212Sscottl	CONFIG_PAGE_RAID_PHYS_DISK_0 *info;
1082196212Sscottl	struct mpt_drive_list *list;
1083215046Sjhb	int error, fd;
1084196212Sscottl	U8 PhysDiskNum;
1085196212Sscottl
1086196212Sscottl	if (ac != 2) {
1087196212Sscottl		warnx("pd delete: drive required");
1088196212Sscottl		return (EINVAL);
1089196212Sscottl	}
1090196212Sscottl
1091196212Sscottl	fd = mpt_open(mpt_unit);
1092196212Sscottl	if (fd < 0) {
1093215046Sjhb		error = errno;
1094196212Sscottl		warn("mpt_open");
1095215046Sjhb		return (error);
1096196212Sscottl	}
1097196212Sscottl
1098196212Sscottl	list = mpt_pd_list(fd);
1099196212Sscottl	if (list == NULL)
1100196212Sscottl		return (errno);
1101196212Sscottl
1102196212Sscottl	if (mpt_lookup_drive(list, av[1], &PhysDiskNum) < 0) {
1103215046Sjhb		error = errno;
1104196212Sscottl		warn("Failed to find drive %s", av[1]);
1105215046Sjhb		return (error);
1106196212Sscottl	}
1107196212Sscottl	mpt_free_pd_list(list);
1108196212Sscottl
1109196212Sscottl	info = mpt_pd_info(fd, PhysDiskNum, NULL);
1110196212Sscottl	if (info == NULL) {
1111215046Sjhb		error = errno;
1112196212Sscottl		warn("Failed to fetch drive info");
1113215046Sjhb		return (error);
1114196212Sscottl	}
1115196212Sscottl
1116196212Sscottl	if (mpt_delete_physdisk(fd, PhysDiskNum) < 0) {
1117215046Sjhb		error = errno;
1118196212Sscottl		warn("Failed to delete physical disk page");
1119215046Sjhb		return (error);
1120196212Sscottl	}
1121196212Sscottl
1122196212Sscottl	mpt_rescan_bus(info->PhysDiskBus, info->PhysDiskID);
1123196212Sscottl	free(info);
1124196212Sscottl	close(fd);
1125196212Sscottl
1126196212Sscottl	return (0);
1127196212Sscottl}
1128196212SscottlMPT_COMMAND(pd, delete, pd_delete);
1129196212Sscottl
1130196212Sscottl/* Display raw data about a volume config. */
1131196212Sscottlstatic void
1132196212Sscottldump_config(CONFIG_PAGE_RAID_VOL_0 *vol)
1133196212Sscottl{
1134196212Sscottl	int i;
1135196212Sscottl
1136196212Sscottl	printf("Volume Configuration (Debug):\n");
1137196212Sscottl	printf(
1138196212Sscottl   " Page Header: Type 0x%02x Number 0x%02x Length 0x%02x(%u) Version 0x%02x\n",
1139196212Sscottl	    vol->Header.PageType, vol->Header.PageNumber,
1140196212Sscottl	    vol->Header.PageLength, vol->Header.PageLength * 4,
1141196212Sscottl	    vol->Header.PageVersion);
1142196212Sscottl	printf("     Address: %d:%d IOC %d\n", vol->VolumeBus, vol->VolumeID,
1143196212Sscottl	    vol->VolumeIOC);
1144196212Sscottl	printf("        Type: %d (%s)\n", vol->VolumeType,
1145196212Sscottl	    mpt_raid_level(vol->VolumeType));
1146196212Sscottl	printf("      Status: %s (Flags 0x%02x)\n",
1147196212Sscottl	    mpt_volstate(vol->VolumeStatus.State), vol->VolumeStatus.Flags);
1148196212Sscottl	printf("    Settings: 0x%04x (Spare Pools 0x%02x)\n",
1149196212Sscottl	    vol->VolumeSettings.Settings, vol->VolumeSettings.HotSparePool);
1150196212Sscottl	printf("      MaxLBA: %ju\n", (uintmax_t)vol->MaxLBAHigh << 32 |
1151196212Sscottl	    vol->MaxLBA);
1152196212Sscottl	printf(" Stripe Size: %ld\n", (long)vol->StripeSize * 512);
1153196212Sscottl	printf(" %d Disks:\n", vol->NumPhysDisks);
1154196212Sscottl
1155196212Sscottl	for (i = 0; i < vol->NumPhysDisks; i++)
1156196212Sscottl		printf("    Disk %d: Num 0x%02x Map 0x%02x\n", i,
1157196212Sscottl		    vol->PhysDisk[i].PhysDiskNum, vol->PhysDisk[i].PhysDiskMap);
1158196212Sscottl}
1159196212Sscottl
1160196212Sscottlstatic int
1161196212Sscottldebug_config(int ac, char **av)
1162196212Sscottl{
1163196212Sscottl	CONFIG_PAGE_RAID_VOL_0 *vol;
1164196212Sscottl	U8 VolumeBus, VolumeID;
1165215046Sjhb	int error, fd;
1166196212Sscottl
1167196212Sscottl	if (ac != 2) {
1168196212Sscottl		warnx("debug: volume required");
1169196212Sscottl		return (EINVAL);
1170196212Sscottl	}
1171196212Sscottl
1172196212Sscottl	fd = mpt_open(mpt_unit);
1173196212Sscottl	if (fd < 0) {
1174215046Sjhb		error = errno;
1175196212Sscottl		warn("mpt_open");
1176215046Sjhb		return (error);
1177196212Sscottl	}
1178196212Sscottl
1179215046Sjhb	error = mpt_lookup_volume(fd, av[1], &VolumeBus, &VolumeID);
1180215046Sjhb	if (error) {
1181215046Sjhb		warnc(error, "Invalid volume: %s", av[1]);
1182215046Sjhb		return (error);
1183196212Sscottl	}
1184196212Sscottl
1185196212Sscottl	vol = mpt_vol_info(fd, VolumeBus, VolumeID, NULL);
1186196212Sscottl	if (vol == NULL) {
1187215046Sjhb		error = errno;
1188196212Sscottl		warn("Failed to get volume info");
1189215046Sjhb		return (error);
1190196212Sscottl	}
1191196212Sscottl
1192196212Sscottl	dump_config(vol);
1193196212Sscottl	free(vol);
1194196212Sscottl	close(fd);
1195196212Sscottl
1196196212Sscottl	return (0);
1197196212Sscottl}
1198196212SscottlMPT_COMMAND(top, debug, debug_config);
1199196212Sscottl#endif
1200