freebsd-src/usr.sbin/mptutil/mpt_cam.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

566 lines
14 KiB
C
Raw Normal View History

/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 2008 Yahoo!, Inc.
* All rights reserved.
* Written by: John Baldwin <jhb@FreeBSD.org>
*
* 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 <sys/param.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <camlib.h>
#include <cam/scsi/scsi_message.h>
#include <cam/scsi/scsi_pass.h>
#include "mptutil.h"
static int xptfd;
static int
xpt_open(void)
{
if (xptfd == 0)
xptfd = open(XPT_DEVICE, O_RDWR);
return (xptfd);
}
/* Fetch the path id of bus 0 for the opened mpt controller. */
static int
fetch_path_id(path_id_t *path_id)
{
struct bus_match_pattern *b;
union ccb ccb;
size_t bufsize;
int error;
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) {
error = errno;
free(ccb.cdm.matches);
free(ccb.cdm.patterns);
return (error);
}
free(ccb.cdm.patterns);
if (((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) ||
(ccb.cdm.status != CAM_DEV_MATCH_LAST)) {
warnx("fetch_path_id 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);
return (0);
}
int
mpt_query_disk(U8 VolumeBus, U8 VolumeID, struct mpt_query_disk *qd)
{
struct periph_match_pattern *p;
struct periph_match_result *r;
union ccb ccb;
path_id_t path_id;
size_t bufsize;
int error;
/* mpt(4) only handles devices on bus 0. */
if (VolumeBus != 0)
return (ENXIO);
if (xpt_open() < 0)
return (ENXIO);
/* Find the path ID of bus 0. */
error = fetch_path_id(&path_id);
if (error)
return (error);
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) * 1;
ccb.cdm.num_patterns = 1;
ccb.cdm.pattern_buf_len = bufsize;
ccb.cdm.patterns = calloc(1, bufsize);
/* Look for a "da" device at the specified target and lun. */
ccb.cdm.patterns[0].type = DEV_MATCH_PERIPH;
p = &ccb.cdm.patterns[0].pattern.periph_pattern;
p->path_id = path_id;
snprintf(p->periph_name, sizeof(p->periph_name), "da");
p->target_id = VolumeID;
p->flags = PERIPH_MATCH_PATH | PERIPH_MATCH_NAME | PERIPH_MATCH_TARGET;
if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) {
error = errno;
free(ccb.cdm.matches);
free(ccb.cdm.patterns);
return (error);
}
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 1 match for the peripheral.
* However, if we don't get a match, don't print an error
* message and return ENOENT.
*/
if (ccb.cdm.num_matches == 0) {
free(ccb.cdm.matches);
return (ENOENT);
}
if (ccb.cdm.num_matches != 1) {
warnx("mpt_query_disk got %d matches, expected 1",
ccb.cdm.num_matches);
free(ccb.cdm.matches);
return (EIO);
}
if (ccb.cdm.matches[0].type != DEV_MATCH_PERIPH) {
warnx("mpt_query_disk got wrong CAM match");
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. */
Fix multiple Coverity Out-of-bounds access false postive issues in CAM The currently used idiom for clearing the part of a ccb after its header generates one or two Coverity errors for each time it is used. All instances generate an Out-of-bounds access (ARRAY_VS_SINGLETON) error because of the treatment of the header as a two element array, with a pointer to the non-existent second element being passed as the starting address to bzero(). Some instances also alsp generate Out-of-bounds access (OVERRUN) errors, probably because the space being cleared is larger than the sizeofstruct ccb_hdr). In addition, this idiom is difficult for humans to understand and it is error prone. The user has to chose the proper struct ccb_* type (which does not appear in the surrounding code) for the sizeof() in the length calculation. I found several instances where the length was incorrect, which could cause either an actual out of bounds write, or incompletely clear the ccb. A better way is to write the code to clear the ccb itself starting at sizeof(ccb_hdr) bytes from the start of the ccb, and calculate the length based on the specific type of struct ccb_* being cleared as specified by the union ccb member being used. The latter can normally be seen in the nearby code. This is friendlier for Coverity and other static analysis tools because they will see that the intent is to clear the trailing part of the ccb. Wrap all of the boilerplate code in a convenient macro that only requires a pointer to the desired union ccb member (or a pointer to the union ccb itself) as an argument. Reported by: Coverity CID: 1007578, 1008684, 1009724, 1009773, 1011304, 1011306 CID: 1011307, 1011308, 1011309, 1011310, 1011311, 1011312 CID: 1011313, 1011314, 1011315, 1011316, 1011317, 1011318 CID: 1011319, 1011320, 1011321, 1011322, 1011324, 1011325 CID: 1011326, 1011327, 1011328, 1011329, 1011330, 1011374 CID: 1011390, 1011391, 1011392, 1011393, 1011394, 1011395 CID: 1011396, 1011397, 1011398, 1011399, 1011400, 1011401 CID: 1011402, 1011403, 1011404, 1011405, 1011406, 1011408 CID: 1011409, 1011410, 1011411, 1011412, 1011413, 1011414 CID: 1017461, 1018387, 1086860, 1086874, 1194257, 1229897 CID: 1229968, 1306229, 1306234, 1331282, 1331283, 1331294 CID: 1331295, 1331535, 1331536, 1331539, 1331540, 1341623 CID: 1341624, 1341637, 1341638, 1355264, 1355324 Reviewed by: scottl, ken, delphij, imp MFH: 1 month Differential Revision: https://reviews.freebsd.org/D6496
2016-05-24 00:57:11 +00:00
CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
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);
}
/*
* 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);
cam_freeccb(ccb);
return (0);
}
/* Zero the rest of the ccb. */
Fix multiple Coverity Out-of-bounds access false postive issues in CAM The currently used idiom for clearing the part of a ccb after its header generates one or two Coverity errors for each time it is used. All instances generate an Out-of-bounds access (ARRAY_VS_SINGLETON) error because of the treatment of the header as a two element array, with a pointer to the non-existent second element being passed as the starting address to bzero(). Some instances also alsp generate Out-of-bounds access (OVERRUN) errors, probably because the space being cleared is larger than the sizeofstruct ccb_hdr). In addition, this idiom is difficult for humans to understand and it is error prone. The user has to chose the proper struct ccb_* type (which does not appear in the surrounding code) for the sizeof() in the length calculation. I found several instances where the length was incorrect, which could cause either an actual out of bounds write, or incompletely clear the ccb. A better way is to write the code to clear the ccb itself starting at sizeof(ccb_hdr) bytes from the start of the ccb, and calculate the length based on the specific type of struct ccb_* being cleared as specified by the union ccb member being used. The latter can normally be seen in the nearby code. This is friendlier for Coverity and other static analysis tools because they will see that the intent is to clear the trailing part of the ccb. Wrap all of the boilerplate code in a convenient macro that only requires a pointer to the desired union ccb member (or a pointer to the union ccb itself) as an argument. Reported by: Coverity CID: 1007578, 1008684, 1009724, 1009773, 1011304, 1011306 CID: 1011307, 1011308, 1011309, 1011310, 1011311, 1011312 CID: 1011313, 1011314, 1011315, 1011316, 1011317, 1011318 CID: 1011319, 1011320, 1011321, 1011322, 1011324, 1011325 CID: 1011326, 1011327, 1011328, 1011329, 1011330, 1011374 CID: 1011390, 1011391, 1011392, 1011393, 1011394, 1011395 CID: 1011396, 1011397, 1011398, 1011399, 1011400, 1011401 CID: 1011402, 1011403, 1011404, 1011405, 1011406, 1011408 CID: 1011409, 1011410, 1011411, 1011412, 1011413, 1011414 CID: 1017461, 1018387, 1086860, 1086874, 1194257, 1229897 CID: 1229968, 1306229, 1306234, 1331282, 1331283, 1331294 CID: 1331295, 1331535, 1331536, 1331539, 1331540, 1341623 CID: 1341624, 1341637, 1341638, 1355264, 1355324 Reviewed by: scottl, ken, delphij, imp MFH: 1 month Differential Revision: https://reviews.freebsd.org/D6496
2016-05-24 00:57:11 +00:00
CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
scsi_read_capacity_16(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, 0, 0, 0,
Add CAM infrastructure to allow reporting when a drive's long read capacity data changes. cam_ccb.h: Add a new advanced information type, CDAI_TYPE_RCAPLONG, for long read capacity data. cam_xpt_internal.h: Add a read capacity data pointer and length to struct cam_ed. cam_xpt.c: Free the read capacity buffer when a device goes away. While we're here, make sure we don't leak memory for other malloced fields in struct cam_ed. scsi_all.c: Update the scsi_read_capacity_16() to take a uint8_t * and a length instead of just a pointer to the parameter data structure. This will hopefully make this function somewhat immune to future changes in the parameter data. scsi_all.h: Add some extra bit definitions to struct scsi_read_capacity_data_long, and bump up the structure size to the full size specified by SBC-3. Change the prototype for scsi_read_capacity_16(). scsi_da.c: Register changes in read capacity data with the transport layer. This allows the transport layer to send out an async notification to interested parties. Update the dasetgeom() API. Use scsi_extract_sense_len() instead of scsi_extract_sense(). scsi_xpt.c: Add support for the new CDAI_TYPE_RCAPLONG advanced information type. Make sure we set the physpath pointer to NULL after freeing it. This allows blindly freeing it in the struct cam_ed destructor. sys/param.h: Bump __FreeBSD_version from 1000005 to 1000006 to make it easier for third party drivers to determine that the read capacity data async notification is available. camcontrol.c, mptutil/mpt_cam.c: Update these for the new scsi_read_capacity_16() argument structure. Sponsored by: Spectra Logic
2012-01-26 18:09:28 +00:00
(uint8_t *)&rcaplong, sizeof(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. */
Fix multiple Coverity Out-of-bounds access false postive issues in CAM The currently used idiom for clearing the part of a ccb after its header generates one or two Coverity errors for each time it is used. All instances generate an Out-of-bounds access (ARRAY_VS_SINGLETON) error because of the treatment of the header as a two element array, with a pointer to the non-existent second element being passed as the starting address to bzero(). Some instances also alsp generate Out-of-bounds access (OVERRUN) errors, probably because the space being cleared is larger than the sizeofstruct ccb_hdr). In addition, this idiom is difficult for humans to understand and it is error prone. The user has to chose the proper struct ccb_* type (which does not appear in the surrounding code) for the sizeof() in the length calculation. I found several instances where the length was incorrect, which could cause either an actual out of bounds write, or incompletely clear the ccb. A better way is to write the code to clear the ccb itself starting at sizeof(ccb_hdr) bytes from the start of the ccb, and calculate the length based on the specific type of struct ccb_* being cleared as specified by the union ccb member being used. The latter can normally be seen in the nearby code. This is friendlier for Coverity and other static analysis tools because they will see that the intent is to clear the trailing part of the ccb. Wrap all of the boilerplate code in a convenient macro that only requires a pointer to the desired union ccb member (or a pointer to the union ccb itself) as an argument. Reported by: Coverity CID: 1007578, 1008684, 1009724, 1009773, 1011304, 1011306 CID: 1011307, 1011308, 1011309, 1011310, 1011311, 1011312 CID: 1011313, 1011314, 1011315, 1011316, 1011317, 1011318 CID: 1011319, 1011320, 1011321, 1011322, 1011324, 1011325 CID: 1011326, 1011327, 1011328, 1011329, 1011330, 1011374 CID: 1011390, 1011391, 1011392, 1011393, 1011394, 1011395 CID: 1011396, 1011397, 1011398, 1011399, 1011400, 1011401 CID: 1011402, 1011403, 1011404, 1011405, 1011406, 1011408 CID: 1011409, 1011410, 1011411, 1011412, 1011413, 1011414 CID: 1017461, 1018387, 1086860, 1086874, 1194257, 1229897 CID: 1229968, 1306229, 1306234, 1331282, 1331283, 1331294 CID: 1331295, 1331535, 1331536, 1331539, 1331540, 1341623 CID: 1341624, 1341637, 1341638, 1355264, 1355324 Reviewed by: scottl, ken, delphij, imp MFH: 1 month Differential Revision: https://reviews.freebsd.org/D6496
2016-05-24 00:57:11 +00:00
CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
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 periph_match_pattern *p;
struct periph_match_result *r;
struct cam_device *dev;
union ccb ccb;
path_id_t path_id;
size_t bufsize;
int count, error;
uint32_t i;
if (xpt_open() < 0)
return (ENXIO);
error = fetch_path_id(&path_id);
if (error)
return (error);
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 + 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 any "da" peripherals. */
ccb.cdm.patterns[0].type = DEV_MATCH_PERIPH;
p = &ccb.cdm.patterns[0].pattern.periph_pattern;
p->path_id = path_id;
snprintf(p->periph_name, sizeof(p->periph_name), "da");
p->flags = PERIPH_MATCH_PATH | PERIPH_MATCH_NAME;
if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) {
error = errno;
free(ccb.cdm.matches);
free(ccb.cdm.patterns);
return (error);
}
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;
}
/* Shortcut if we don't have any "da" devices. */
if (ccb.cdm.num_matches == 0) {
free(ccb.cdm.matches);
*ndisks = 0;
*disksp = NULL;
return (0);
}
/* We should have N matches, 1 for each "da" device. */
for (i = 0; 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);
}
}
/*
* 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);
if (ioc2 == NULL)
return (errno);
disks = calloc(ccb.cdm.num_matches, sizeof(*disks));
count = 0;
for (i = 0; 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 buses 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 buses are scanned instead of the specified bus.
* Note that currently, only bus 0 is supported.
*/
int
mpt_rescan_bus(int bus, int id)
{
union ccb ccb;
path_id_t path_id;
int error;
/* mpt(4) only handles devices on bus 0. */
if (bus != -1 && bus != 0)
return (EINVAL);
if (xpt_open() < 0)
return (ENXIO);
error = fetch_path_id(&path_id);
if (error)
return (error);
/* Perform the actual rescan. */
bzero(&ccb, sizeof(ccb));
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);
}