From owner-svn-src-all@FreeBSD.ORG Fri Aug 14 13:13:13 2009 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 57BD2106568D; Fri, 14 Aug 2009 13:13:13 +0000 (UTC) (envelope-from scottl@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 446C98FC57; Fri, 14 Aug 2009 13:13:13 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n7EDDDwe034949; Fri, 14 Aug 2009 13:13:13 GMT (envelope-from scottl@svn.freebsd.org) Received: (from scottl@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n7EDDDC5034943; Fri, 14 Aug 2009 13:13:13 GMT (envelope-from scottl@svn.freebsd.org) Message-Id: <200908141313.n7EDDDC5034943@svn.freebsd.org> From: Scott Long Date: Fri, 14 Aug 2009 13:13:13 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r196212 - in head/usr.sbin: . mptutil X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 14 Aug 2009 13:13:13 -0000 Author: scottl Date: Fri Aug 14 13:13:12 2009 New Revision: 196212 URL: http://svn.freebsd.org/changeset/base/196212 Log: Add mptutil, a basic utility for managing MPT SCSI/SATA/SAS controllers. Drive and controller status can be reported, basic attributes changed, and arrays and spares can be created and deleted. Approved by: re Obtained from: Yahoo! Inc. Added: head/usr.sbin/mptutil/ head/usr.sbin/mptutil/Makefile (contents, props changed) head/usr.sbin/mptutil/mpt_cam.c (contents, props changed) head/usr.sbin/mptutil/mpt_cmd.c (contents, props changed) head/usr.sbin/mptutil/mpt_config.c (contents, props changed) head/usr.sbin/mptutil/mpt_drive.c (contents, props changed) head/usr.sbin/mptutil/mpt_evt.c (contents, props changed) head/usr.sbin/mptutil/mpt_show.c (contents, props changed) head/usr.sbin/mptutil/mpt_volume.c (contents, props changed) head/usr.sbin/mptutil/mptutil.8 (contents, props changed) head/usr.sbin/mptutil/mptutil.c (contents, props changed) head/usr.sbin/mptutil/mptutil.h (contents, props changed) Modified: head/usr.sbin/Makefile Modified: head/usr.sbin/Makefile ============================================================================== --- head/usr.sbin/Makefile Fri Aug 14 12:30:10 2009 (r196211) +++ head/usr.sbin/Makefile Fri Aug 14 13:13:12 2009 (r196212) @@ -104,6 +104,7 @@ SUBDIR= ${_ac} \ ${_mount_smbfs} \ ${_moused} \ ${_mptable} \ + mptutil \ mtest \ mtree \ ${_named} \ Added: head/usr.sbin/mptutil/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.sbin/mptutil/Makefile Fri Aug 14 13:13:12 2009 (r196212) @@ -0,0 +1,19 @@ +# $FreeBSD$ + +PROG= mptutil +SRCS= mptutil.c mpt_cam.c mpt_cmd.c mpt_config.c mpt_drive.c mpt_evt.c \ + mpt_show.c mpt_volume.c +# mpt_flash.c +MAN= mptutil.8 + +WARNS?= 3 + +DPADD+= ${LIBCAM} ${LIBUTIL} +LDADD+= -lcam -lutil + +# Here be dragons +.ifdef DEBUG +CFLAGS+= -DDEBUG +.endif + +.include Added: head/usr.sbin/mptutil/mpt_cam.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.sbin/mptutil/mpt_cam.c Fri Aug 14 13:13:12 2009 (r196212) @@ -0,0 +1,569 @@ +/*- + * Copyright (c) 2008 Yahoo!, Inc. + * All rights reserved. + * Written by: John Baldwin + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__RCSID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mptutil.h" + +static int xptfd; + +static int +xpt_open(void) +{ + + if (xptfd == 0) + xptfd = open(XPT_DEVICE, O_RDWR); + return (xptfd); +} + +int +mpt_query_disk(U8 VolumeBus, U8 VolumeID, struct mpt_query_disk *qd) +{ + struct bus_match_pattern *b; + struct periph_match_pattern *p; + struct periph_match_result *r; + union ccb ccb; + size_t bufsize; + int i; + + /* mpt(4) only handles devices on bus 0. */ + if (VolumeBus != 0) + return (ENXIO); + + if (xpt_open() < 0) + return (ENXIO); + + bzero(&ccb, sizeof(ccb)); + + ccb.ccb_h.func_code = XPT_DEV_MATCH; + ccb.ccb_h.path_id = CAM_XPT_PATH_ID; + ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; + ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; + + bufsize = sizeof(struct dev_match_result) * 5; + ccb.cdm.num_matches = 0; + ccb.cdm.match_buf_len = bufsize; + ccb.cdm.matches = calloc(1, bufsize); + + bufsize = sizeof(struct dev_match_pattern) * 2; + ccb.cdm.num_patterns = 2; + ccb.cdm.pattern_buf_len = bufsize; + ccb.cdm.patterns = calloc(1, bufsize); + + /* Match mptX bus 0. */ + ccb.cdm.patterns[0].type = DEV_MATCH_BUS; + b = &ccb.cdm.patterns[0].pattern.bus_pattern; + snprintf(b->dev_name, sizeof(b->dev_name), "mpt"); + b->unit_number = mpt_unit; + b->bus_id = 0; + b->flags = BUS_MATCH_NAME | BUS_MATCH_UNIT | BUS_MATCH_BUS_ID; + + /* Look for a "da" device at the specified target and lun. */ + ccb.cdm.patterns[1].type = DEV_MATCH_PERIPH; + p = &ccb.cdm.patterns[1].pattern.periph_pattern; + snprintf(p->periph_name, sizeof(p->periph_name), "da"); + p->target_id = VolumeID; + p->flags = PERIPH_MATCH_NAME | PERIPH_MATCH_TARGET; + + if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) { + i = errno; + free(ccb.cdm.matches); + free(ccb.cdm.patterns); + return (i); + } + free(ccb.cdm.patterns); + + if (((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) || + (ccb.cdm.status != CAM_DEV_MATCH_LAST)) { + warnx("mpt_query_disk got CAM error %#x, CDM error %d\n", + ccb.ccb_h.status, ccb.cdm.status); + free(ccb.cdm.matches); + return (EIO); + } + + /* + * We should have exactly 2 matches, 1 for the bus and 1 for + * the peripheral. However, if we only have 1 match and it is + * for the bus, don't print an error message and return + * ENOENT. + */ + if (ccb.cdm.num_matches == 1 && + ccb.cdm.matches[0].type == DEV_MATCH_BUS) { + free(ccb.cdm.matches); + return (ENOENT); + } + if (ccb.cdm.num_matches != 2) { + warnx("mpt_query_disk got %d matches, expected 2", + ccb.cdm.num_matches); + free(ccb.cdm.matches); + return (EIO); + } + if (ccb.cdm.matches[0].type != DEV_MATCH_BUS || + ccb.cdm.matches[1].type != DEV_MATCH_PERIPH) { + warnx("mpt_query_disk got wrong CAM matches"); + free(ccb.cdm.matches); + return (EIO); + } + + /* Copy out the data. */ + r = &ccb.cdm.matches[1].result.periph_result; + snprintf(qd->devname, sizeof(qd->devname), "%s%d", r->periph_name, + r->unit_number); + free(ccb.cdm.matches); + + return (0); +} + +static int +periph_is_volume(CONFIG_PAGE_IOC_2 *ioc2, struct periph_match_result *r) +{ + CONFIG_PAGE_IOC_2_RAID_VOL *vol; + int i; + + if (ioc2 == NULL) + return (0); + vol = ioc2->RaidVolume; + for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { + if (vol->VolumeBus == 0 && vol->VolumeID == r->target_id) + return (1); + } + return (0); +} + +/* Much borrowed from scsireadcapacity() in src/sbin/camcontrol/camcontrol.c. */ +static int +fetch_scsi_capacity(struct cam_device *dev, struct mpt_standalone_disk *disk) +{ + struct scsi_read_capacity_data rcap; + struct scsi_read_capacity_data_long rcaplong; + union ccb *ccb; + int error; + + ccb = cam_getccb(dev); + if (ccb == NULL) + return (ENOMEM); + + /* Zero the rest of the ccb. */ + bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - + sizeof(struct ccb_hdr)); + + scsi_read_capacity(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, &rcap, + SSD_FULL_SIZE, 5000); + + /* Disable freezing the device queue */ + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (cam_send_ccb(dev, ccb) < 0) { + error = errno; + cam_freeccb(ccb); + return (error); + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + cam_freeccb(ccb); + return (EIO); + } + cam_freeccb(ccb); + + /* + * A last block of 2^32-1 means that the true capacity is over 2TB, + * and we need to issue the long READ CAPACITY to get the real + * capacity. Otherwise, we're all set. + */ + if (scsi_4btoul(rcap.addr) != 0xffffffff) { + disk->maxlba = scsi_4btoul(rcap.addr); + return (0); + } + + /* Zero the rest of the ccb. */ + bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - + sizeof(struct ccb_hdr)); + + scsi_read_capacity_16(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, 0, 0, 0, + &rcaplong, SSD_FULL_SIZE, 5000); + + /* Disable freezing the device queue */ + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (cam_send_ccb(dev, ccb) < 0) { + error = errno; + cam_freeccb(ccb); + return (error); + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + cam_freeccb(ccb); + return (EIO); + } + cam_freeccb(ccb); + + disk->maxlba = scsi_8btou64(rcaplong.addr); + return (0); +} + +/* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */ +static void +format_scsi_inquiry(struct mpt_standalone_disk *disk, + struct scsi_inquiry_data *inq_data) +{ + char vendor[16], product[48], revision[16], rstr[12]; + + if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data)) + return; + if (SID_TYPE(inq_data) != T_DIRECT) + return; + if (SID_QUAL(inq_data) != SID_QUAL_LU_CONNECTED) + return; + + cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor), + sizeof(vendor)); + cam_strvis(product, inq_data->product, sizeof(inq_data->product), + sizeof(product)); + cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision), + sizeof(revision)); + + /* Hack for SATA disks, no idea how to tell speed. */ + if (strcmp(vendor, "ATA") == 0) { + snprintf(disk->inqstring, sizeof(disk->inqstring), + "<%s %s> SATA", product, revision); + return; + } + + switch (SID_ANSI_REV(inq_data)) { + case SCSI_REV_CCS: + strcpy(rstr, "SCSI-CCS"); + break; + case 5: + strcpy(rstr, "SAS"); + break; + default: + snprintf(rstr, sizeof (rstr), "SCSI-%d", + SID_ANSI_REV(inq_data)); + break; + } + snprintf(disk->inqstring, sizeof(disk->inqstring), "<%s %s %s> %s", + vendor, product, revision, rstr); +} + +/* Much borrowed from scsiinquiry() in src/sbin/camcontrol/camcontrol.c. */ +static int +fetch_scsi_inquiry(struct cam_device *dev, struct mpt_standalone_disk *disk) +{ + struct scsi_inquiry_data *inq_buf; + union ccb *ccb; + int error; + + ccb = cam_getccb(dev); + if (ccb == NULL) + return (ENOMEM); + + /* Zero the rest of the ccb. */ + bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - + sizeof(struct ccb_hdr)); + + inq_buf = calloc(1, sizeof(*inq_buf)); + if (inq_buf == NULL) { + cam_freeccb(ccb); + return (ENOMEM); + } + scsi_inquiry(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, (void *)inq_buf, + SHORT_INQUIRY_LENGTH, 0, 0, SSD_FULL_SIZE, 5000); + + /* Disable freezing the device queue */ + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (cam_send_ccb(dev, ccb) < 0) { + error = errno; + free(inq_buf); + cam_freeccb(ccb); + return (error); + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + free(inq_buf); + cam_freeccb(ccb); + return (EIO); + } + + cam_freeccb(ccb); + format_scsi_inquiry(disk, inq_buf); + free(inq_buf); + return (0); +} + +int +mpt_fetch_disks(int fd, int *ndisks, struct mpt_standalone_disk **disksp) +{ + CONFIG_PAGE_IOC_2 *ioc2; + struct mpt_standalone_disk *disks; + struct bus_match_pattern *b; + struct periph_match_pattern *p; + struct periph_match_result *r; + struct cam_device *dev; + union ccb ccb; + size_t bufsize; + u_int i; + int count; + + if (xpt_open() < 0) + return (ENXIO); + + for (count = 100;; count+= 100) { + /* Try to fetch 'count' disks in one go. */ + bzero(&ccb, sizeof(ccb)); + + ccb.ccb_h.func_code = XPT_DEV_MATCH; + + bufsize = sizeof(struct dev_match_result) * (count + 2); + ccb.cdm.num_matches = 0; + ccb.cdm.match_buf_len = bufsize; + ccb.cdm.matches = calloc(1, bufsize); + + bufsize = sizeof(struct dev_match_pattern) * 2; + ccb.cdm.num_patterns = 2; + ccb.cdm.pattern_buf_len = bufsize; + ccb.cdm.patterns = calloc(1, bufsize); + + /* Match mptX bus 0. */ + ccb.cdm.patterns[0].type = DEV_MATCH_BUS; + b = &ccb.cdm.patterns[0].pattern.bus_pattern; + snprintf(b->dev_name, sizeof(b->dev_name), "mpt"); + b->unit_number = mpt_unit; + b->bus_id = 0; + b->flags = BUS_MATCH_NAME | BUS_MATCH_UNIT | BUS_MATCH_BUS_ID; + + /* Match any "da" peripherals. */ + ccb.cdm.patterns[1].type = DEV_MATCH_PERIPH; + p = &ccb.cdm.patterns[1].pattern.periph_pattern; + snprintf(p->periph_name, sizeof(p->periph_name), "da"); + p->flags = PERIPH_MATCH_NAME; + + if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) { + i = errno; + free(ccb.cdm.matches); + free(ccb.cdm.patterns); + return (i); + } + free(ccb.cdm.patterns); + + /* Check for CCB errors. */ + if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + free(ccb.cdm.matches); + return (EIO); + } + + /* If we need a longer list, try again. */ + if (ccb.cdm.status == CAM_DEV_MATCH_MORE) { + free(ccb.cdm.matches); + continue; + } + + /* If we got an error, abort. */ + if (ccb.cdm.status != CAM_DEV_MATCH_LAST) { + free(ccb.cdm.matches); + return (EIO); + } + break; + } + + /* + * We should have N + 1 matches, 1 for the bus and 1 for each + * "da" device. + */ + if (ccb.cdm.num_matches < 1) { + warnx("mpt_fetch_disks didn't get any matches"); + free(ccb.cdm.matches); + return (EIO); + } + if (ccb.cdm.matches[0].type != DEV_MATCH_BUS) { + warnx("mpt_fetch_disks got wrong CAM matches"); + free(ccb.cdm.matches); + return (EIO); + } + for (i = 1; i < ccb.cdm.num_matches; i++) { + if (ccb.cdm.matches[i].type != DEV_MATCH_PERIPH) { + warnx("mpt_fetch_disks got wrong CAM matches"); + free(ccb.cdm.matches); + return (EIO); + } + } + + /* Shortcut if we don't have any "da" devices. */ + if (ccb.cdm.num_matches == 1) { + free(ccb.cdm.matches); + *ndisks = 0; + *disksp = NULL; + return (0); + } + + /* + * Some of the "da" peripherals may be for RAID volumes, so + * fetch the IOC 2 page (list of RAID volumes) so we can + * exclude them from the list. + */ + ioc2 = mpt_read_ioc_page(fd, 2, NULL); + disks = calloc(ccb.cdm.num_matches, sizeof(*disks)); + count = 0; + for (i = 1; i < ccb.cdm.num_matches; i++) { + r = &ccb.cdm.matches[i].result.periph_result; + if (periph_is_volume(ioc2, r)) + continue; + disks[count].bus = 0; + disks[count].target = r->target_id; + snprintf(disks[count].devname, sizeof(disks[count].devname), + "%s%d", r->periph_name, r->unit_number); + + dev = cam_open_device(disks[count].devname, O_RDWR); + if (dev != NULL) { + fetch_scsi_capacity(dev, &disks[count]); + fetch_scsi_inquiry(dev, &disks[count]); + cam_close_device(dev); + } + count++; + } + free(ccb.cdm.matches); + free(ioc2); + + *ndisks = count; + *disksp = disks; + return (0); +} + +/* + * Instruct the mpt(4) device to rescan its busses to find new devices + * such as disks whose RAID physdisk page was removed or volumes that + * were created. If id is -1, the entire bus is rescanned. + * Otherwise, only devices at the specified ID are rescanned. If bus + * is -1, then all busses are scanned instead of the specified bus. + * Note that currently, only bus 0 is supported. + */ +int +mpt_rescan_bus(int bus, int id) +{ + struct bus_match_pattern *b; + union ccb ccb; + path_id_t path_id; + size_t bufsize; + + /* mpt(4) only handles devices on bus 0. */ + if (bus != -1 && bus != 0) + return (EINVAL); + + if (xpt_open() < 0) + return (ENXIO); + + /* First, find the path id of bus 0 for this mpt controller. */ + bzero(&ccb, sizeof(ccb)); + + ccb.ccb_h.func_code = XPT_DEV_MATCH; + + bufsize = sizeof(struct dev_match_result) * 1; + ccb.cdm.num_matches = 0; + ccb.cdm.match_buf_len = bufsize; + ccb.cdm.matches = calloc(1, bufsize); + + bufsize = sizeof(struct dev_match_pattern) * 1; + ccb.cdm.num_patterns = 1; + ccb.cdm.pattern_buf_len = bufsize; + ccb.cdm.patterns = calloc(1, bufsize); + + /* Match mptX bus 0. */ + ccb.cdm.patterns[0].type = DEV_MATCH_BUS; + b = &ccb.cdm.patterns[0].pattern.bus_pattern; + snprintf(b->dev_name, sizeof(b->dev_name), "mpt"); + b->unit_number = mpt_unit; + b->bus_id = 0; + b->flags = BUS_MATCH_NAME | BUS_MATCH_UNIT | BUS_MATCH_BUS_ID; + + if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) { + free(ccb.cdm.matches); + free(ccb.cdm.patterns); + return (errno); + } + free(ccb.cdm.patterns); + + if (((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) || + (ccb.cdm.status != CAM_DEV_MATCH_LAST)) { + warnx("mpt_rescan_bus got CAM error %#x, CDM error %d\n", + ccb.ccb_h.status, ccb.cdm.status); + free(ccb.cdm.matches); + return (EIO); + } + + /* We should have exactly 1 match for the bus. */ + if (ccb.cdm.num_matches != 1 || + ccb.cdm.matches[0].type != DEV_MATCH_BUS) { + free(ccb.cdm.matches); + return (ENOENT); + } + path_id = ccb.cdm.matches[0].result.bus_result.path_id; + free(ccb.cdm.matches); + + /* Now perform the actual rescan. */ + ccb.ccb_h.path_id = path_id; + if (id == -1) { + ccb.ccb_h.func_code = XPT_SCAN_BUS; + ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; + ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; + ccb.ccb_h.timeout = 5000; + } else { + ccb.ccb_h.func_code = XPT_SCAN_LUN; + ccb.ccb_h.target_id = id; + ccb.ccb_h.target_lun = 0; + } + ccb.crcn.flags = CAM_FLAG_NONE; + + /* Run this at a low priority. */ + ccb.ccb_h.pinfo.priority = 5; + + if (ioctl(xptfd, CAMIOCOMMAND, &ccb) == -1) + return (errno); + + if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + warnx("mpt_rescan_bus rescan got CAM error %#x\n", + ccb.ccb_h.status & CAM_STATUS_MASK); + return (EIO); + } + + return (0); +} Added: head/usr.sbin/mptutil/mpt_cmd.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.sbin/mptutil/mpt_cmd.c Fri Aug 14 13:13:12 2009 (r196212) @@ -0,0 +1,639 @@ +/*- + * Copyright (c) 2008 Yahoo!, Inc. + * All rights reserved. + * Written by: John Baldwin + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__RCSID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "mptutil.h" + +static const char *mpt_ioc_status_codes[] = { + "Success", /* 0x0000 */ + "Invalid function", + "Busy", + "Invalid scatter-gather list", + "Internal error", + "Reserved", + "Insufficient resources", + "Invalid field", + "Invalid state", /* 0x0008 */ + "Operation state not supported", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, /* 0x0010 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, /* 0x0018 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "Invalid configuration action", /* 0x0020 */ + "Invalid configuration type", + "Invalid configuration page", + "Invalid configuration data", + "No configuration defaults", + "Unable to commit configuration change", + NULL, + NULL, + NULL, /* 0x0028 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, /* 0x0030 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, /* 0x0038 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "Recovered SCSI error", /* 0x0040 */ + "Invalid SCSI bus", + "Invalid SCSI target ID", + "SCSI device not there", + "SCSI data overrun", + "SCSI data underrun", + "SCSI I/O error", + "SCSI protocol error", + "SCSI task terminated", /* 0x0048 */ + "SCSI residual mismatch", + "SCSI task management failed", + "SCSI I/O controller terminated", + "SCSI external controller terminated", + "EEDP guard error", + "EEDP reference tag error", + "EEDP application tag error", + NULL, /* 0x0050 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, /* 0x0058 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "SCSI target priority I/O", /* 0x0060 */ + "Invalid SCSI target port", + "Invalid SCSI target I/O index", + "SCSI target aborted", + "No connection retryable", + "No connection", + "FC aborted", + "Invalid FC receive ID", + "FC did invalid", /* 0x0068 */ + "FC node logged out", + "Transfer count mismatch", + "STS data not set", + "FC exchange canceled", + "Data offset error", + "Too much write data", + "IU too short", + "ACK NAK timeout", /* 0x0070 */ + "NAK received", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, /* 0x0078 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "LAN device not found", /* 0x0080 */ + "LAN device failure", + "LAN transmit error", + "LAN transmit aborted", + "LAN receive error", + "LAN receive aborted", + "LAN partial packet", + "LAN canceled", + NULL, /* 0x0088 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "SAS SMP request failed", /* 0x0090 */ + "SAS SMP data overrun", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "Inband aborted", /* 0x0098 */ + "No inband connection", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "Diagnostic released", /* 0x00A0 */ +}; + +static const char *mpt_raid_action_status_codes[] = { + "Success", + "Invalid action", + "Failure", + "Operation in progress", +}; + +const char * +mpt_ioc_status(U16 IOCStatus) +{ + static char buffer[16]; + + IOCStatus &= MPI_IOCSTATUS_MASK; + if (IOCStatus < sizeof(mpt_ioc_status_codes) / sizeof(char *) && + mpt_ioc_status_codes[IOCStatus] != NULL) + return (mpt_ioc_status_codes[IOCStatus]); + snprintf(buffer, sizeof(buffer), "Status: 0x%04x", IOCStatus); + return (buffer); +} + +const char * +mpt_raid_status(U16 ActionStatus) +{ + static char buffer[16]; + + if (ActionStatus < sizeof(mpt_raid_action_status_codes) / + sizeof(char *)) + return (mpt_raid_action_status_codes[ActionStatus]); + snprintf(buffer, sizeof(buffer), "Status: 0x%04x", ActionStatus); + return (buffer); +} + +const char * +mpt_raid_level(U8 VolumeType) +{ + static char buf[16]; + + switch (VolumeType) { + case MPI_RAID_VOL_TYPE_IS: + return ("RAID-0"); + case MPI_RAID_VOL_TYPE_IM: + return ("RAID-1"); + case MPI_RAID_VOL_TYPE_IME: + return ("RAID-1E"); + case MPI_RAID_VOL_TYPE_RAID_5: + return ("RAID-5"); + case MPI_RAID_VOL_TYPE_RAID_6: + return ("RAID-6"); + case MPI_RAID_VOL_TYPE_RAID_10: + return ("RAID-10"); + case MPI_RAID_VOL_TYPE_RAID_50: + return ("RAID-50"); + default: + sprintf(buf, "LVL 0x%02x", VolumeType); + return (buf); + } +} + +const char * +mpt_volume_name(U8 VolumeBus, U8 VolumeID) +{ + static struct mpt_query_disk info; + static char buf[16]; + + if (mpt_query_disk(VolumeBus, VolumeID, &info) != 0) { + /* + * We only print out the bus number if it is non-zero + * since mpt(4) only supports devices on bus zero + * anyway. + */ + if (VolumeBus == 0) + snprintf(buf, sizeof(buf), "%d", VolumeID); + else + snprintf(buf, sizeof(buf), "%d:%d", VolumeBus, + VolumeID); + return (buf); + } + return (info.devname); +} + +int +mpt_lookup_volume(int fd, const char *name, U8 *VolumeBus, U8 *VolumeID) +{ + CONFIG_PAGE_IOC_2 *ioc2; + CONFIG_PAGE_IOC_2_RAID_VOL *vol; + struct mpt_query_disk info; + char *cp; + long bus, id; + int i; + + /* + * Check for a raw [:] string. If the bus is not + * specified, assume bus 0. + */ + bus = strtol(name, &cp, 0); + if (*cp == ':') { + id = strtol(cp + 1, &cp, 0); + if (*cp == '\0') { + if (bus < 0 || bus > 0xff || id < 0 || id > 0xff) { + errno = EINVAL; + return (-1); + } + *VolumeBus = bus; + *VolumeID = id; + return (0); + } + } else if (*cp == '\0') { + if (bus < 0 || bus > 0xff) { + errno = EINVAL; + return (-1); + } + *VolumeBus = 0; + *VolumeID = bus; + return (0); + } + + ioc2 = mpt_read_ioc_page(fd, 2, NULL); + if (ioc2 == NULL) + return (-1); + + vol = ioc2->RaidVolume; + for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { + if (mpt_query_disk(vol->VolumeBus, vol->VolumeID, &info) != 0) + continue; + if (strcmp(name, info.devname) == 0) { + *VolumeBus = vol->VolumeBus; + *VolumeID = vol->VolumeID; + free(ioc2); + return (0); + } + } + free(ioc2); + errno = EINVAL; + return (-1); +} + +int +mpt_read_config_page_header(int fd, U8 PageType, U8 PageNumber, U32 PageAddress, + CONFIG_PAGE_HEADER *header, U16 *IOCStatus) +{ + struct mpt_cfg_page_req req; + + if (IOCStatus != NULL) + *IOCStatus = MPI_IOCSTATUS_SUCCESS; + bzero(&req, sizeof(req)); + req.header.PageType = PageType; + req.header.PageNumber = PageNumber; + req.page_address = PageAddress; + if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0) + return (-1); + if (!IOC_STATUS_SUCCESS(req.ioc_status)) { + if (IOCStatus != NULL) + *IOCStatus = req.ioc_status; + else + warnx("Reading config page header failed: %s", + mpt_ioc_status(req.ioc_status)); + errno = EIO; + return (-1); + } + *header = req.header; + return (0); +} + +void * +mpt_read_config_page(int fd, U8 PageType, U8 PageNumber, U32 PageAddress, + U16 *IOCStatus) +{ + struct mpt_cfg_page_req req; + void *buf; + int save_errno; + + if (IOCStatus != NULL) + *IOCStatus = MPI_IOCSTATUS_SUCCESS; + bzero(&req, sizeof(req)); + req.header.PageType = PageType; + req.header.PageNumber = PageNumber; + req.page_address = PageAddress; + if (ioctl(fd, MPTIO_READ_CFG_HEADER, &req) < 0) + return (NULL); + if (!IOC_STATUS_SUCCESS(req.ioc_status)) { *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***