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 <err.h> 36196212Sscottl#include <errno.h> 37196212Sscottl#include <fcntl.h> 38196212Sscottl#include <stdio.h> 39196212Sscottl#include <stdlib.h> 40196212Sscottl#include <string.h> 41196212Sscottl 42196212Sscottl#include <camlib.h> 43196212Sscottl#include <cam/scsi/scsi_message.h> 44196212Sscottl#include <cam/scsi/scsi_pass.h> 45196212Sscottl 46196212Sscottl#include "mptutil.h" 47196212Sscottl 48196212Sscottlstatic int xptfd; 49196212Sscottl 50196212Sscottlstatic int 51196212Sscottlxpt_open(void) 52196212Sscottl{ 53196212Sscottl 54196212Sscottl if (xptfd == 0) 55196212Sscottl xptfd = open(XPT_DEVICE, O_RDWR); 56196212Sscottl return (xptfd); 57196212Sscottl} 58196212Sscottl 59204090Sjhb/* Fetch the path id of bus 0 for the opened mpt controller. */ 60204090Sjhbstatic int 61204090Sjhbfetch_path_id(path_id_t *path_id) 62204090Sjhb{ 63204090Sjhb struct bus_match_pattern *b; 64204090Sjhb union ccb ccb; 65204090Sjhb size_t bufsize; 66215046Sjhb int error; 67204090Sjhb 68204090Sjhb if (xpt_open() < 0) 69204090Sjhb return (ENXIO); 70204090Sjhb 71204090Sjhb /* First, find the path id of bus 0 for this mpt controller. */ 72204090Sjhb bzero(&ccb, sizeof(ccb)); 73204090Sjhb 74204090Sjhb ccb.ccb_h.func_code = XPT_DEV_MATCH; 75204090Sjhb 76204090Sjhb bufsize = sizeof(struct dev_match_result) * 1; 77204090Sjhb ccb.cdm.num_matches = 0; 78204090Sjhb ccb.cdm.match_buf_len = bufsize; 79204090Sjhb ccb.cdm.matches = calloc(1, bufsize); 80204090Sjhb 81204090Sjhb bufsize = sizeof(struct dev_match_pattern) * 1; 82204090Sjhb ccb.cdm.num_patterns = 1; 83204090Sjhb ccb.cdm.pattern_buf_len = bufsize; 84204090Sjhb ccb.cdm.patterns = calloc(1, bufsize); 85204090Sjhb 86204090Sjhb /* Match mptX bus 0. */ 87204090Sjhb ccb.cdm.patterns[0].type = DEV_MATCH_BUS; 88204090Sjhb b = &ccb.cdm.patterns[0].pattern.bus_pattern; 89204090Sjhb snprintf(b->dev_name, sizeof(b->dev_name), "mpt"); 90204090Sjhb b->unit_number = mpt_unit; 91204090Sjhb b->bus_id = 0; 92204090Sjhb b->flags = BUS_MATCH_NAME | BUS_MATCH_UNIT | BUS_MATCH_BUS_ID; 93204090Sjhb 94204090Sjhb if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) { 95215046Sjhb error = errno; 96204090Sjhb free(ccb.cdm.matches); 97204090Sjhb free(ccb.cdm.patterns); 98215046Sjhb return (error); 99204090Sjhb } 100204090Sjhb free(ccb.cdm.patterns); 101204090Sjhb 102204090Sjhb if (((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) || 103204090Sjhb (ccb.cdm.status != CAM_DEV_MATCH_LAST)) { 104204090Sjhb warnx("fetch_path_id got CAM error %#x, CDM error %d\n", 105204090Sjhb ccb.ccb_h.status, ccb.cdm.status); 106204090Sjhb free(ccb.cdm.matches); 107204090Sjhb return (EIO); 108204090Sjhb } 109204090Sjhb 110204090Sjhb /* We should have exactly 1 match for the bus. */ 111204090Sjhb if (ccb.cdm.num_matches != 1 || 112204090Sjhb ccb.cdm.matches[0].type != DEV_MATCH_BUS) { 113204090Sjhb free(ccb.cdm.matches); 114204090Sjhb return (ENOENT); 115204090Sjhb } 116204090Sjhb *path_id = ccb.cdm.matches[0].result.bus_result.path_id; 117204090Sjhb free(ccb.cdm.matches); 118204090Sjhb return (0); 119204090Sjhb} 120204090Sjhb 121196212Sscottlint 122196212Sscottlmpt_query_disk(U8 VolumeBus, U8 VolumeID, struct mpt_query_disk *qd) 123196212Sscottl{ 124196212Sscottl struct periph_match_pattern *p; 125196212Sscottl struct periph_match_result *r; 126196212Sscottl union ccb ccb; 127204090Sjhb path_id_t path_id; 128196212Sscottl size_t bufsize; 129215046Sjhb int error; 130196212Sscottl 131196212Sscottl /* mpt(4) only handles devices on bus 0. */ 132196212Sscottl if (VolumeBus != 0) 133196212Sscottl return (ENXIO); 134196212Sscottl 135196212Sscottl if (xpt_open() < 0) 136196212Sscottl return (ENXIO); 137196212Sscottl 138204090Sjhb /* Find the path ID of bus 0. */ 139204090Sjhb error = fetch_path_id(&path_id); 140204090Sjhb if (error) 141204090Sjhb return (error); 142204090Sjhb 143196212Sscottl bzero(&ccb, sizeof(ccb)); 144196212Sscottl 145196212Sscottl ccb.ccb_h.func_code = XPT_DEV_MATCH; 146196212Sscottl ccb.ccb_h.path_id = CAM_XPT_PATH_ID; 147196212Sscottl ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; 148196212Sscottl ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; 149196212Sscottl 150196212Sscottl bufsize = sizeof(struct dev_match_result) * 5; 151196212Sscottl ccb.cdm.num_matches = 0; 152196212Sscottl ccb.cdm.match_buf_len = bufsize; 153196212Sscottl ccb.cdm.matches = calloc(1, bufsize); 154196212Sscottl 155204090Sjhb bufsize = sizeof(struct dev_match_pattern) * 1; 156204090Sjhb ccb.cdm.num_patterns = 1; 157196212Sscottl ccb.cdm.pattern_buf_len = bufsize; 158196212Sscottl ccb.cdm.patterns = calloc(1, bufsize); 159196212Sscottl 160196212Sscottl /* Look for a "da" device at the specified target and lun. */ 161204090Sjhb ccb.cdm.patterns[0].type = DEV_MATCH_PERIPH; 162204090Sjhb p = &ccb.cdm.patterns[0].pattern.periph_pattern; 163204090Sjhb p->path_id = path_id; 164196212Sscottl snprintf(p->periph_name, sizeof(p->periph_name), "da"); 165196212Sscottl p->target_id = VolumeID; 166204090Sjhb p->flags = PERIPH_MATCH_PATH | PERIPH_MATCH_NAME | PERIPH_MATCH_TARGET; 167196212Sscottl 168196212Sscottl if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) { 169215046Sjhb error = errno; 170196212Sscottl free(ccb.cdm.matches); 171196212Sscottl free(ccb.cdm.patterns); 172215046Sjhb return (error); 173196212Sscottl } 174196212Sscottl free(ccb.cdm.patterns); 175196212Sscottl 176196212Sscottl if (((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) || 177196212Sscottl (ccb.cdm.status != CAM_DEV_MATCH_LAST)) { 178196212Sscottl warnx("mpt_query_disk got CAM error %#x, CDM error %d\n", 179196212Sscottl ccb.ccb_h.status, ccb.cdm.status); 180196212Sscottl free(ccb.cdm.matches); 181196212Sscottl return (EIO); 182196212Sscottl } 183196212Sscottl 184196212Sscottl /* 185204090Sjhb * We should have exactly 1 match for the peripheral. 186204090Sjhb * However, if we don't get a match, don't print an error 187204090Sjhb * message and return ENOENT. 188196212Sscottl */ 189204090Sjhb if (ccb.cdm.num_matches == 0) { 190196212Sscottl free(ccb.cdm.matches); 191196212Sscottl return (ENOENT); 192196212Sscottl } 193204090Sjhb if (ccb.cdm.num_matches != 1) { 194204090Sjhb warnx("mpt_query_disk got %d matches, expected 1", 195196212Sscottl ccb.cdm.num_matches); 196196212Sscottl free(ccb.cdm.matches); 197196212Sscottl return (EIO); 198196212Sscottl } 199204090Sjhb if (ccb.cdm.matches[0].type != DEV_MATCH_PERIPH) { 200204090Sjhb warnx("mpt_query_disk got wrong CAM match"); 201196212Sscottl free(ccb.cdm.matches); 202196212Sscottl return (EIO); 203196212Sscottl } 204196212Sscottl 205196212Sscottl /* Copy out the data. */ 206196212Sscottl r = &ccb.cdm.matches[1].result.periph_result; 207196212Sscottl snprintf(qd->devname, sizeof(qd->devname), "%s%d", r->periph_name, 208196212Sscottl r->unit_number); 209196212Sscottl free(ccb.cdm.matches); 210196212Sscottl 211196212Sscottl return (0); 212196212Sscottl} 213196212Sscottl 214196212Sscottlstatic int 215196212Sscottlperiph_is_volume(CONFIG_PAGE_IOC_2 *ioc2, struct periph_match_result *r) 216196212Sscottl{ 217196212Sscottl CONFIG_PAGE_IOC_2_RAID_VOL *vol; 218196212Sscottl int i; 219196212Sscottl 220196212Sscottl if (ioc2 == NULL) 221196212Sscottl return (0); 222196212Sscottl vol = ioc2->RaidVolume; 223196212Sscottl for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 224196212Sscottl if (vol->VolumeBus == 0 && vol->VolumeID == r->target_id) 225196212Sscottl return (1); 226196212Sscottl } 227196212Sscottl return (0); 228196212Sscottl} 229196212Sscottl 230196212Sscottl/* Much borrowed from scsireadcapacity() in src/sbin/camcontrol/camcontrol.c. */ 231196212Sscottlstatic int 232196212Sscottlfetch_scsi_capacity(struct cam_device *dev, struct mpt_standalone_disk *disk) 233196212Sscottl{ 234196212Sscottl struct scsi_read_capacity_data rcap; 235196212Sscottl struct scsi_read_capacity_data_long rcaplong; 236196212Sscottl union ccb *ccb; 237196212Sscottl int error; 238196212Sscottl 239196212Sscottl ccb = cam_getccb(dev); 240196212Sscottl if (ccb == NULL) 241196212Sscottl return (ENOMEM); 242196212Sscottl 243196212Sscottl /* Zero the rest of the ccb. */ 244196212Sscottl bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - 245196212Sscottl sizeof(struct ccb_hdr)); 246196212Sscottl 247196212Sscottl scsi_read_capacity(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, &rcap, 248196212Sscottl SSD_FULL_SIZE, 5000); 249196212Sscottl 250196212Sscottl /* Disable freezing the device queue */ 251196212Sscottl ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 252196212Sscottl 253196212Sscottl if (cam_send_ccb(dev, ccb) < 0) { 254196212Sscottl error = errno; 255196212Sscottl cam_freeccb(ccb); 256196212Sscottl return (error); 257196212Sscottl } 258196212Sscottl 259196212Sscottl if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 260196212Sscottl cam_freeccb(ccb); 261196212Sscottl return (EIO); 262196212Sscottl } 263196212Sscottl cam_freeccb(ccb); 264196212Sscottl 265196212Sscottl /* 266196212Sscottl * A last block of 2^32-1 means that the true capacity is over 2TB, 267196212Sscottl * and we need to issue the long READ CAPACITY to get the real 268196212Sscottl * capacity. Otherwise, we're all set. 269196212Sscottl */ 270196212Sscottl if (scsi_4btoul(rcap.addr) != 0xffffffff) { 271196212Sscottl disk->maxlba = scsi_4btoul(rcap.addr); 272196212Sscottl return (0); 273196212Sscottl } 274196212Sscottl 275196212Sscottl /* Zero the rest of the ccb. */ 276196212Sscottl bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - 277196212Sscottl sizeof(struct ccb_hdr)); 278196212Sscottl 279196212Sscottl scsi_read_capacity_16(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, 0, 0, 0, 280230590Sken (uint8_t *)&rcaplong, sizeof(rcaplong), SSD_FULL_SIZE, 5000); 281196212Sscottl 282196212Sscottl /* Disable freezing the device queue */ 283196212Sscottl ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 284196212Sscottl 285196212Sscottl if (cam_send_ccb(dev, ccb) < 0) { 286196212Sscottl error = errno; 287196212Sscottl cam_freeccb(ccb); 288196212Sscottl return (error); 289196212Sscottl } 290196212Sscottl 291196212Sscottl if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 292196212Sscottl cam_freeccb(ccb); 293196212Sscottl return (EIO); 294196212Sscottl } 295196212Sscottl cam_freeccb(ccb); 296196212Sscottl 297196212Sscottl disk->maxlba = scsi_8btou64(rcaplong.addr); 298196212Sscottl return (0); 299196212Sscottl} 300196212Sscottl 301196212Sscottl/* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */ 302196212Sscottlstatic void 303196212Sscottlformat_scsi_inquiry(struct mpt_standalone_disk *disk, 304196212Sscottl struct scsi_inquiry_data *inq_data) 305196212Sscottl{ 306196212Sscottl char vendor[16], product[48], revision[16], rstr[12]; 307196212Sscottl 308196212Sscottl if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data)) 309196212Sscottl return; 310196212Sscottl if (SID_TYPE(inq_data) != T_DIRECT) 311196212Sscottl return; 312196212Sscottl if (SID_QUAL(inq_data) != SID_QUAL_LU_CONNECTED) 313196212Sscottl return; 314196212Sscottl 315196212Sscottl cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor), 316196212Sscottl sizeof(vendor)); 317196212Sscottl cam_strvis(product, inq_data->product, sizeof(inq_data->product), 318196212Sscottl sizeof(product)); 319196212Sscottl cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision), 320196212Sscottl sizeof(revision)); 321196212Sscottl 322196212Sscottl /* Hack for SATA disks, no idea how to tell speed. */ 323196212Sscottl if (strcmp(vendor, "ATA") == 0) { 324196212Sscottl snprintf(disk->inqstring, sizeof(disk->inqstring), 325196212Sscottl "<%s %s> SATA", product, revision); 326196212Sscottl return; 327196212Sscottl } 328196212Sscottl 329196212Sscottl switch (SID_ANSI_REV(inq_data)) { 330196212Sscottl case SCSI_REV_CCS: 331196212Sscottl strcpy(rstr, "SCSI-CCS"); 332196212Sscottl break; 333196212Sscottl case 5: 334196212Sscottl strcpy(rstr, "SAS"); 335196212Sscottl break; 336196212Sscottl default: 337196212Sscottl snprintf(rstr, sizeof (rstr), "SCSI-%d", 338196212Sscottl SID_ANSI_REV(inq_data)); 339196212Sscottl break; 340196212Sscottl } 341196212Sscottl snprintf(disk->inqstring, sizeof(disk->inqstring), "<%s %s %s> %s", 342196212Sscottl vendor, product, revision, rstr); 343196212Sscottl} 344196212Sscottl 345196212Sscottl/* Much borrowed from scsiinquiry() in src/sbin/camcontrol/camcontrol.c. */ 346196212Sscottlstatic int 347196212Sscottlfetch_scsi_inquiry(struct cam_device *dev, struct mpt_standalone_disk *disk) 348196212Sscottl{ 349196212Sscottl struct scsi_inquiry_data *inq_buf; 350196212Sscottl union ccb *ccb; 351196212Sscottl int error; 352196212Sscottl 353196212Sscottl ccb = cam_getccb(dev); 354196212Sscottl if (ccb == NULL) 355196212Sscottl return (ENOMEM); 356196212Sscottl 357196212Sscottl /* Zero the rest of the ccb. */ 358196212Sscottl bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - 359196212Sscottl sizeof(struct ccb_hdr)); 360196212Sscottl 361196212Sscottl inq_buf = calloc(1, sizeof(*inq_buf)); 362196212Sscottl if (inq_buf == NULL) { 363196212Sscottl cam_freeccb(ccb); 364196212Sscottl return (ENOMEM); 365196212Sscottl } 366196212Sscottl scsi_inquiry(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, (void *)inq_buf, 367196212Sscottl SHORT_INQUIRY_LENGTH, 0, 0, SSD_FULL_SIZE, 5000); 368196212Sscottl 369196212Sscottl /* Disable freezing the device queue */ 370196212Sscottl ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 371196212Sscottl 372196212Sscottl if (cam_send_ccb(dev, ccb) < 0) { 373196212Sscottl error = errno; 374196212Sscottl free(inq_buf); 375196212Sscottl cam_freeccb(ccb); 376196212Sscottl return (error); 377196212Sscottl } 378196212Sscottl 379196212Sscottl if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 380196212Sscottl free(inq_buf); 381196212Sscottl cam_freeccb(ccb); 382196212Sscottl return (EIO); 383196212Sscottl } 384196212Sscottl 385196212Sscottl cam_freeccb(ccb); 386196212Sscottl format_scsi_inquiry(disk, inq_buf); 387196212Sscottl free(inq_buf); 388196212Sscottl return (0); 389196212Sscottl} 390196212Sscottl 391196212Sscottlint 392196212Sscottlmpt_fetch_disks(int fd, int *ndisks, struct mpt_standalone_disk **disksp) 393196212Sscottl{ 394196212Sscottl CONFIG_PAGE_IOC_2 *ioc2; 395196212Sscottl struct mpt_standalone_disk *disks; 396196212Sscottl struct periph_match_pattern *p; 397196212Sscottl struct periph_match_result *r; 398196212Sscottl struct cam_device *dev; 399196212Sscottl union ccb ccb; 400204090Sjhb path_id_t path_id; 401196212Sscottl size_t bufsize; 402204090Sjhb int count, error; 403215046Sjhb uint32_t i; 404196212Sscottl 405196212Sscottl if (xpt_open() < 0) 406196212Sscottl return (ENXIO); 407196212Sscottl 408204090Sjhb error = fetch_path_id(&path_id); 409204090Sjhb if (error) 410204090Sjhb return (error); 411204090Sjhb 412196212Sscottl for (count = 100;; count+= 100) { 413196212Sscottl /* Try to fetch 'count' disks in one go. */ 414196212Sscottl bzero(&ccb, sizeof(ccb)); 415196212Sscottl 416196212Sscottl ccb.ccb_h.func_code = XPT_DEV_MATCH; 417196212Sscottl 418204090Sjhb bufsize = sizeof(struct dev_match_result) * (count + 1); 419196212Sscottl ccb.cdm.num_matches = 0; 420196212Sscottl ccb.cdm.match_buf_len = bufsize; 421196212Sscottl ccb.cdm.matches = calloc(1, bufsize); 422196212Sscottl 423204090Sjhb bufsize = sizeof(struct dev_match_pattern) * 1; 424204090Sjhb ccb.cdm.num_patterns = 1; 425196212Sscottl ccb.cdm.pattern_buf_len = bufsize; 426196212Sscottl ccb.cdm.patterns = calloc(1, bufsize); 427196212Sscottl 428196212Sscottl /* Match any "da" peripherals. */ 429204090Sjhb ccb.cdm.patterns[0].type = DEV_MATCH_PERIPH; 430204090Sjhb p = &ccb.cdm.patterns[0].pattern.periph_pattern; 431204090Sjhb p->path_id = path_id; 432196212Sscottl snprintf(p->periph_name, sizeof(p->periph_name), "da"); 433204090Sjhb p->flags = PERIPH_MATCH_PATH | PERIPH_MATCH_NAME; 434196212Sscottl 435196212Sscottl if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) { 436215046Sjhb error = errno; 437196212Sscottl free(ccb.cdm.matches); 438196212Sscottl free(ccb.cdm.patterns); 439215046Sjhb return (error); 440196212Sscottl } 441196212Sscottl free(ccb.cdm.patterns); 442196212Sscottl 443196212Sscottl /* Check for CCB errors. */ 444196212Sscottl if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 445196212Sscottl free(ccb.cdm.matches); 446196212Sscottl return (EIO); 447196212Sscottl } 448196212Sscottl 449196212Sscottl /* If we need a longer list, try again. */ 450196212Sscottl if (ccb.cdm.status == CAM_DEV_MATCH_MORE) { 451196212Sscottl free(ccb.cdm.matches); 452196212Sscottl continue; 453196212Sscottl } 454196212Sscottl 455196212Sscottl /* If we got an error, abort. */ 456196212Sscottl if (ccb.cdm.status != CAM_DEV_MATCH_LAST) { 457196212Sscottl free(ccb.cdm.matches); 458196212Sscottl return (EIO); 459196212Sscottl } 460196212Sscottl break; 461196212Sscottl } 462196212Sscottl 463204090Sjhb /* Shortcut if we don't have any "da" devices. */ 464204090Sjhb if (ccb.cdm.num_matches == 0) { 465196212Sscottl free(ccb.cdm.matches); 466204090Sjhb *ndisks = 0; 467204090Sjhb *disksp = NULL; 468204090Sjhb return (0); 469196212Sscottl } 470204090Sjhb 471204090Sjhb /* We should have N matches, 1 for each "da" device. */ 472204090Sjhb for (i = 0; i < ccb.cdm.num_matches; i++) { 473196212Sscottl if (ccb.cdm.matches[i].type != DEV_MATCH_PERIPH) { 474196212Sscottl warnx("mpt_fetch_disks got wrong CAM matches"); 475196212Sscottl free(ccb.cdm.matches); 476196212Sscottl return (EIO); 477196212Sscottl } 478196212Sscottl } 479196212Sscottl 480196212Sscottl /* 481196212Sscottl * Some of the "da" peripherals may be for RAID volumes, so 482196212Sscottl * fetch the IOC 2 page (list of RAID volumes) so we can 483196212Sscottl * exclude them from the list. 484196212Sscottl */ 485196212Sscottl ioc2 = mpt_read_ioc_page(fd, 2, NULL); 486215046Sjhb if (ioc2 == NULL) 487215046Sjhb return (errno); 488196212Sscottl disks = calloc(ccb.cdm.num_matches, sizeof(*disks)); 489196212Sscottl count = 0; 490204090Sjhb for (i = 0; i < ccb.cdm.num_matches; i++) { 491196212Sscottl r = &ccb.cdm.matches[i].result.periph_result; 492196212Sscottl if (periph_is_volume(ioc2, r)) 493196212Sscottl continue; 494196212Sscottl disks[count].bus = 0; 495196212Sscottl disks[count].target = r->target_id; 496196212Sscottl snprintf(disks[count].devname, sizeof(disks[count].devname), 497196212Sscottl "%s%d", r->periph_name, r->unit_number); 498196212Sscottl 499196212Sscottl dev = cam_open_device(disks[count].devname, O_RDWR); 500196212Sscottl if (dev != NULL) { 501196212Sscottl fetch_scsi_capacity(dev, &disks[count]); 502196212Sscottl fetch_scsi_inquiry(dev, &disks[count]); 503196212Sscottl cam_close_device(dev); 504196212Sscottl } 505196212Sscottl count++; 506196212Sscottl } 507196212Sscottl free(ccb.cdm.matches); 508196212Sscottl free(ioc2); 509196212Sscottl 510196212Sscottl *ndisks = count; 511196212Sscottl *disksp = disks; 512196212Sscottl return (0); 513196212Sscottl} 514196212Sscottl 515196212Sscottl/* 516196212Sscottl * Instruct the mpt(4) device to rescan its busses to find new devices 517196212Sscottl * such as disks whose RAID physdisk page was removed or volumes that 518196212Sscottl * were created. If id is -1, the entire bus is rescanned. 519196212Sscottl * Otherwise, only devices at the specified ID are rescanned. If bus 520196212Sscottl * is -1, then all busses are scanned instead of the specified bus. 521196212Sscottl * Note that currently, only bus 0 is supported. 522196212Sscottl */ 523196212Sscottlint 524196212Sscottlmpt_rescan_bus(int bus, int id) 525196212Sscottl{ 526196212Sscottl union ccb ccb; 527196212Sscottl path_id_t path_id; 528204090Sjhb int error; 529196212Sscottl 530196212Sscottl /* mpt(4) only handles devices on bus 0. */ 531196212Sscottl if (bus != -1 && bus != 0) 532196212Sscottl return (EINVAL); 533196212Sscottl 534196212Sscottl if (xpt_open() < 0) 535196212Sscottl return (ENXIO); 536196212Sscottl 537204090Sjhb error = fetch_path_id(&path_id); 538204090Sjhb if (error) 539204090Sjhb return (error); 540204090Sjhb 541204090Sjhb /* Perform the actual rescan. */ 542196212Sscottl bzero(&ccb, sizeof(ccb)); 543196212Sscottl ccb.ccb_h.path_id = path_id; 544196212Sscottl if (id == -1) { 545196212Sscottl ccb.ccb_h.func_code = XPT_SCAN_BUS; 546196212Sscottl ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; 547196212Sscottl ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; 548196212Sscottl ccb.ccb_h.timeout = 5000; 549196212Sscottl } else { 550196212Sscottl ccb.ccb_h.func_code = XPT_SCAN_LUN; 551196212Sscottl ccb.ccb_h.target_id = id; 552196212Sscottl ccb.ccb_h.target_lun = 0; 553196212Sscottl } 554196212Sscottl ccb.crcn.flags = CAM_FLAG_NONE; 555196212Sscottl 556196212Sscottl /* Run this at a low priority. */ 557196212Sscottl ccb.ccb_h.pinfo.priority = 5; 558196212Sscottl 559196212Sscottl if (ioctl(xptfd, CAMIOCOMMAND, &ccb) == -1) 560196212Sscottl return (errno); 561196212Sscottl 562196212Sscottl if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 563196212Sscottl warnx("mpt_rescan_bus rescan got CAM error %#x\n", 564196212Sscottl ccb.ccb_h.status & CAM_STATUS_MASK); 565196212Sscottl return (EIO); 566196212Sscottl } 567196212Sscottl 568196212Sscottl return (0); 569196212Sscottl} 570