1fc58801cSScott Long /*- 2fc58801cSScott Long * Copyright (c) 2008 Yahoo!, Inc. 3fc58801cSScott Long * All rights reserved. 4fc58801cSScott Long * Written by: John Baldwin <jhb@FreeBSD.org> 5fc58801cSScott Long * 6fc58801cSScott Long * Redistribution and use in source and binary forms, with or without 7fc58801cSScott Long * modification, are permitted provided that the following conditions 8fc58801cSScott Long * are met: 9fc58801cSScott Long * 1. Redistributions of source code must retain the above copyright 10fc58801cSScott Long * notice, this list of conditions and the following disclaimer. 11fc58801cSScott Long * 2. Redistributions in binary form must reproduce the above copyright 12fc58801cSScott Long * notice, this list of conditions and the following disclaimer in the 13fc58801cSScott Long * documentation and/or other materials provided with the distribution. 14fc58801cSScott Long * 3. Neither the name of the author nor the names of any co-contributors 15fc58801cSScott Long * may be used to endorse or promote products derived from this software 16fc58801cSScott Long * without specific prior written permission. 17fc58801cSScott Long * 18fc58801cSScott Long * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19fc58801cSScott Long * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20fc58801cSScott Long * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21fc58801cSScott Long * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22fc58801cSScott Long * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23fc58801cSScott Long * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24fc58801cSScott Long * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25fc58801cSScott Long * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26fc58801cSScott Long * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27fc58801cSScott Long * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28fc58801cSScott Long * SUCH DAMAGE. 29fc58801cSScott Long */ 30fc58801cSScott Long 31fc58801cSScott Long #include <sys/cdefs.h> 32fc58801cSScott Long __RCSID("$FreeBSD$"); 33fc58801cSScott Long 34fc58801cSScott Long #include <sys/param.h> 35fc58801cSScott Long #include <err.h> 36fc58801cSScott Long #include <errno.h> 37fc58801cSScott Long #include <fcntl.h> 38fc58801cSScott Long #include <stdio.h> 39fc58801cSScott Long #include <stdlib.h> 40fc58801cSScott Long #include <string.h> 41fc58801cSScott Long 42fc58801cSScott Long #include <camlib.h> 43fc58801cSScott Long #include <cam/scsi/scsi_message.h> 44fc58801cSScott Long #include <cam/scsi/scsi_pass.h> 45fc58801cSScott Long 46fc58801cSScott Long #include "mptutil.h" 47fc58801cSScott Long 48fc58801cSScott Long static int xptfd; 49fc58801cSScott Long 50fc58801cSScott Long static int 51fc58801cSScott Long xpt_open(void) 52fc58801cSScott Long { 53fc58801cSScott Long 54fc58801cSScott Long if (xptfd == 0) 55fc58801cSScott Long xptfd = open(XPT_DEVICE, O_RDWR); 56fc58801cSScott Long return (xptfd); 57fc58801cSScott Long } 58fc58801cSScott Long 59fc58801cSScott Long int 60fc58801cSScott Long mpt_query_disk(U8 VolumeBus, U8 VolumeID, struct mpt_query_disk *qd) 61fc58801cSScott Long { 62fc58801cSScott Long struct bus_match_pattern *b; 63fc58801cSScott Long struct periph_match_pattern *p; 64fc58801cSScott Long struct periph_match_result *r; 65fc58801cSScott Long union ccb ccb; 66fc58801cSScott Long size_t bufsize; 67fc58801cSScott Long int i; 68fc58801cSScott Long 69fc58801cSScott Long /* mpt(4) only handles devices on bus 0. */ 70fc58801cSScott Long if (VolumeBus != 0) 71fc58801cSScott Long return (ENXIO); 72fc58801cSScott Long 73fc58801cSScott Long if (xpt_open() < 0) 74fc58801cSScott Long return (ENXIO); 75fc58801cSScott Long 76fc58801cSScott Long bzero(&ccb, sizeof(ccb)); 77fc58801cSScott Long 78fc58801cSScott Long ccb.ccb_h.func_code = XPT_DEV_MATCH; 79fc58801cSScott Long ccb.ccb_h.path_id = CAM_XPT_PATH_ID; 80fc58801cSScott Long ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; 81fc58801cSScott Long ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; 82fc58801cSScott Long 83fc58801cSScott Long bufsize = sizeof(struct dev_match_result) * 5; 84fc58801cSScott Long ccb.cdm.num_matches = 0; 85fc58801cSScott Long ccb.cdm.match_buf_len = bufsize; 86fc58801cSScott Long ccb.cdm.matches = calloc(1, bufsize); 87fc58801cSScott Long 88fc58801cSScott Long bufsize = sizeof(struct dev_match_pattern) * 2; 89fc58801cSScott Long ccb.cdm.num_patterns = 2; 90fc58801cSScott Long ccb.cdm.pattern_buf_len = bufsize; 91fc58801cSScott Long ccb.cdm.patterns = calloc(1, bufsize); 92fc58801cSScott Long 93fc58801cSScott Long /* Match mptX bus 0. */ 94fc58801cSScott Long ccb.cdm.patterns[0].type = DEV_MATCH_BUS; 95fc58801cSScott Long b = &ccb.cdm.patterns[0].pattern.bus_pattern; 96fc58801cSScott Long snprintf(b->dev_name, sizeof(b->dev_name), "mpt"); 97fc58801cSScott Long b->unit_number = mpt_unit; 98fc58801cSScott Long b->bus_id = 0; 99fc58801cSScott Long b->flags = BUS_MATCH_NAME | BUS_MATCH_UNIT | BUS_MATCH_BUS_ID; 100fc58801cSScott Long 101fc58801cSScott Long /* Look for a "da" device at the specified target and lun. */ 102fc58801cSScott Long ccb.cdm.patterns[1].type = DEV_MATCH_PERIPH; 103fc58801cSScott Long p = &ccb.cdm.patterns[1].pattern.periph_pattern; 104fc58801cSScott Long snprintf(p->periph_name, sizeof(p->periph_name), "da"); 105fc58801cSScott Long p->target_id = VolumeID; 106fc58801cSScott Long p->flags = PERIPH_MATCH_NAME | PERIPH_MATCH_TARGET; 107fc58801cSScott Long 108fc58801cSScott Long if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) { 109fc58801cSScott Long i = errno; 110fc58801cSScott Long free(ccb.cdm.matches); 111fc58801cSScott Long free(ccb.cdm.patterns); 112fc58801cSScott Long return (i); 113fc58801cSScott Long } 114fc58801cSScott Long free(ccb.cdm.patterns); 115fc58801cSScott Long 116fc58801cSScott Long if (((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) || 117fc58801cSScott Long (ccb.cdm.status != CAM_DEV_MATCH_LAST)) { 118fc58801cSScott Long warnx("mpt_query_disk got CAM error %#x, CDM error %d\n", 119fc58801cSScott Long ccb.ccb_h.status, ccb.cdm.status); 120fc58801cSScott Long free(ccb.cdm.matches); 121fc58801cSScott Long return (EIO); 122fc58801cSScott Long } 123fc58801cSScott Long 124fc58801cSScott Long /* 125fc58801cSScott Long * We should have exactly 2 matches, 1 for the bus and 1 for 126fc58801cSScott Long * the peripheral. However, if we only have 1 match and it is 127fc58801cSScott Long * for the bus, don't print an error message and return 128fc58801cSScott Long * ENOENT. 129fc58801cSScott Long */ 130fc58801cSScott Long if (ccb.cdm.num_matches == 1 && 131fc58801cSScott Long ccb.cdm.matches[0].type == DEV_MATCH_BUS) { 132fc58801cSScott Long free(ccb.cdm.matches); 133fc58801cSScott Long return (ENOENT); 134fc58801cSScott Long } 135fc58801cSScott Long if (ccb.cdm.num_matches != 2) { 136fc58801cSScott Long warnx("mpt_query_disk got %d matches, expected 2", 137fc58801cSScott Long ccb.cdm.num_matches); 138fc58801cSScott Long free(ccb.cdm.matches); 139fc58801cSScott Long return (EIO); 140fc58801cSScott Long } 141fc58801cSScott Long if (ccb.cdm.matches[0].type != DEV_MATCH_BUS || 142fc58801cSScott Long ccb.cdm.matches[1].type != DEV_MATCH_PERIPH) { 143fc58801cSScott Long warnx("mpt_query_disk got wrong CAM matches"); 144fc58801cSScott Long free(ccb.cdm.matches); 145fc58801cSScott Long return (EIO); 146fc58801cSScott Long } 147fc58801cSScott Long 148fc58801cSScott Long /* Copy out the data. */ 149fc58801cSScott Long r = &ccb.cdm.matches[1].result.periph_result; 150fc58801cSScott Long snprintf(qd->devname, sizeof(qd->devname), "%s%d", r->periph_name, 151fc58801cSScott Long r->unit_number); 152fc58801cSScott Long free(ccb.cdm.matches); 153fc58801cSScott Long 154fc58801cSScott Long return (0); 155fc58801cSScott Long } 156fc58801cSScott Long 157fc58801cSScott Long static int 158fc58801cSScott Long periph_is_volume(CONFIG_PAGE_IOC_2 *ioc2, struct periph_match_result *r) 159fc58801cSScott Long { 160fc58801cSScott Long CONFIG_PAGE_IOC_2_RAID_VOL *vol; 161fc58801cSScott Long int i; 162fc58801cSScott Long 163fc58801cSScott Long if (ioc2 == NULL) 164fc58801cSScott Long return (0); 165fc58801cSScott Long vol = ioc2->RaidVolume; 166fc58801cSScott Long for (i = 0; i < ioc2->NumActiveVolumes; vol++, i++) { 167fc58801cSScott Long if (vol->VolumeBus == 0 && vol->VolumeID == r->target_id) 168fc58801cSScott Long return (1); 169fc58801cSScott Long } 170fc58801cSScott Long return (0); 171fc58801cSScott Long } 172fc58801cSScott Long 173fc58801cSScott Long /* Much borrowed from scsireadcapacity() in src/sbin/camcontrol/camcontrol.c. */ 174fc58801cSScott Long static int 175fc58801cSScott Long fetch_scsi_capacity(struct cam_device *dev, struct mpt_standalone_disk *disk) 176fc58801cSScott Long { 177fc58801cSScott Long struct scsi_read_capacity_data rcap; 178fc58801cSScott Long struct scsi_read_capacity_data_long rcaplong; 179fc58801cSScott Long union ccb *ccb; 180fc58801cSScott Long int error; 181fc58801cSScott Long 182fc58801cSScott Long ccb = cam_getccb(dev); 183fc58801cSScott Long if (ccb == NULL) 184fc58801cSScott Long return (ENOMEM); 185fc58801cSScott Long 186fc58801cSScott Long /* Zero the rest of the ccb. */ 187fc58801cSScott Long bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - 188fc58801cSScott Long sizeof(struct ccb_hdr)); 189fc58801cSScott Long 190fc58801cSScott Long scsi_read_capacity(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, &rcap, 191fc58801cSScott Long SSD_FULL_SIZE, 5000); 192fc58801cSScott Long 193fc58801cSScott Long /* Disable freezing the device queue */ 194fc58801cSScott Long ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 195fc58801cSScott Long 196fc58801cSScott Long if (cam_send_ccb(dev, ccb) < 0) { 197fc58801cSScott Long error = errno; 198fc58801cSScott Long cam_freeccb(ccb); 199fc58801cSScott Long return (error); 200fc58801cSScott Long } 201fc58801cSScott Long 202fc58801cSScott Long if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 203fc58801cSScott Long cam_freeccb(ccb); 204fc58801cSScott Long return (EIO); 205fc58801cSScott Long } 206fc58801cSScott Long cam_freeccb(ccb); 207fc58801cSScott Long 208fc58801cSScott Long /* 209fc58801cSScott Long * A last block of 2^32-1 means that the true capacity is over 2TB, 210fc58801cSScott Long * and we need to issue the long READ CAPACITY to get the real 211fc58801cSScott Long * capacity. Otherwise, we're all set. 212fc58801cSScott Long */ 213fc58801cSScott Long if (scsi_4btoul(rcap.addr) != 0xffffffff) { 214fc58801cSScott Long disk->maxlba = scsi_4btoul(rcap.addr); 215fc58801cSScott Long return (0); 216fc58801cSScott Long } 217fc58801cSScott Long 218fc58801cSScott Long /* Zero the rest of the ccb. */ 219fc58801cSScott Long bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - 220fc58801cSScott Long sizeof(struct ccb_hdr)); 221fc58801cSScott Long 222fc58801cSScott Long scsi_read_capacity_16(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, 0, 0, 0, 223fc58801cSScott Long &rcaplong, SSD_FULL_SIZE, 5000); 224fc58801cSScott Long 225fc58801cSScott Long /* Disable freezing the device queue */ 226fc58801cSScott Long ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 227fc58801cSScott Long 228fc58801cSScott Long if (cam_send_ccb(dev, ccb) < 0) { 229fc58801cSScott Long error = errno; 230fc58801cSScott Long cam_freeccb(ccb); 231fc58801cSScott Long return (error); 232fc58801cSScott Long } 233fc58801cSScott Long 234fc58801cSScott Long if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 235fc58801cSScott Long cam_freeccb(ccb); 236fc58801cSScott Long return (EIO); 237fc58801cSScott Long } 238fc58801cSScott Long cam_freeccb(ccb); 239fc58801cSScott Long 240fc58801cSScott Long disk->maxlba = scsi_8btou64(rcaplong.addr); 241fc58801cSScott Long return (0); 242fc58801cSScott Long } 243fc58801cSScott Long 244fc58801cSScott Long /* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */ 245fc58801cSScott Long static void 246fc58801cSScott Long format_scsi_inquiry(struct mpt_standalone_disk *disk, 247fc58801cSScott Long struct scsi_inquiry_data *inq_data) 248fc58801cSScott Long { 249fc58801cSScott Long char vendor[16], product[48], revision[16], rstr[12]; 250fc58801cSScott Long 251fc58801cSScott Long if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data)) 252fc58801cSScott Long return; 253fc58801cSScott Long if (SID_TYPE(inq_data) != T_DIRECT) 254fc58801cSScott Long return; 255fc58801cSScott Long if (SID_QUAL(inq_data) != SID_QUAL_LU_CONNECTED) 256fc58801cSScott Long return; 257fc58801cSScott Long 258fc58801cSScott Long cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor), 259fc58801cSScott Long sizeof(vendor)); 260fc58801cSScott Long cam_strvis(product, inq_data->product, sizeof(inq_data->product), 261fc58801cSScott Long sizeof(product)); 262fc58801cSScott Long cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision), 263fc58801cSScott Long sizeof(revision)); 264fc58801cSScott Long 265fc58801cSScott Long /* Hack for SATA disks, no idea how to tell speed. */ 266fc58801cSScott Long if (strcmp(vendor, "ATA") == 0) { 267fc58801cSScott Long snprintf(disk->inqstring, sizeof(disk->inqstring), 268fc58801cSScott Long "<%s %s> SATA", product, revision); 269fc58801cSScott Long return; 270fc58801cSScott Long } 271fc58801cSScott Long 272fc58801cSScott Long switch (SID_ANSI_REV(inq_data)) { 273fc58801cSScott Long case SCSI_REV_CCS: 274fc58801cSScott Long strcpy(rstr, "SCSI-CCS"); 275fc58801cSScott Long break; 276fc58801cSScott Long case 5: 277fc58801cSScott Long strcpy(rstr, "SAS"); 278fc58801cSScott Long break; 279fc58801cSScott Long default: 280fc58801cSScott Long snprintf(rstr, sizeof (rstr), "SCSI-%d", 281fc58801cSScott Long SID_ANSI_REV(inq_data)); 282fc58801cSScott Long break; 283fc58801cSScott Long } 284fc58801cSScott Long snprintf(disk->inqstring, sizeof(disk->inqstring), "<%s %s %s> %s", 285fc58801cSScott Long vendor, product, revision, rstr); 286fc58801cSScott Long } 287fc58801cSScott Long 288fc58801cSScott Long /* Much borrowed from scsiinquiry() in src/sbin/camcontrol/camcontrol.c. */ 289fc58801cSScott Long static int 290fc58801cSScott Long fetch_scsi_inquiry(struct cam_device *dev, struct mpt_standalone_disk *disk) 291fc58801cSScott Long { 292fc58801cSScott Long struct scsi_inquiry_data *inq_buf; 293fc58801cSScott Long union ccb *ccb; 294fc58801cSScott Long int error; 295fc58801cSScott Long 296fc58801cSScott Long ccb = cam_getccb(dev); 297fc58801cSScott Long if (ccb == NULL) 298fc58801cSScott Long return (ENOMEM); 299fc58801cSScott Long 300fc58801cSScott Long /* Zero the rest of the ccb. */ 301fc58801cSScott Long bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - 302fc58801cSScott Long sizeof(struct ccb_hdr)); 303fc58801cSScott Long 304fc58801cSScott Long inq_buf = calloc(1, sizeof(*inq_buf)); 305fc58801cSScott Long if (inq_buf == NULL) { 306fc58801cSScott Long cam_freeccb(ccb); 307fc58801cSScott Long return (ENOMEM); 308fc58801cSScott Long } 309fc58801cSScott Long scsi_inquiry(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, (void *)inq_buf, 310fc58801cSScott Long SHORT_INQUIRY_LENGTH, 0, 0, SSD_FULL_SIZE, 5000); 311fc58801cSScott Long 312fc58801cSScott Long /* Disable freezing the device queue */ 313fc58801cSScott Long ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; 314fc58801cSScott Long 315fc58801cSScott Long if (cam_send_ccb(dev, ccb) < 0) { 316fc58801cSScott Long error = errno; 317fc58801cSScott Long free(inq_buf); 318fc58801cSScott Long cam_freeccb(ccb); 319fc58801cSScott Long return (error); 320fc58801cSScott Long } 321fc58801cSScott Long 322fc58801cSScott Long if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 323fc58801cSScott Long free(inq_buf); 324fc58801cSScott Long cam_freeccb(ccb); 325fc58801cSScott Long return (EIO); 326fc58801cSScott Long } 327fc58801cSScott Long 328fc58801cSScott Long cam_freeccb(ccb); 329fc58801cSScott Long format_scsi_inquiry(disk, inq_buf); 330fc58801cSScott Long free(inq_buf); 331fc58801cSScott Long return (0); 332fc58801cSScott Long } 333fc58801cSScott Long 334fc58801cSScott Long int 335fc58801cSScott Long mpt_fetch_disks(int fd, int *ndisks, struct mpt_standalone_disk **disksp) 336fc58801cSScott Long { 337fc58801cSScott Long CONFIG_PAGE_IOC_2 *ioc2; 338fc58801cSScott Long struct mpt_standalone_disk *disks; 339fc58801cSScott Long struct bus_match_pattern *b; 340fc58801cSScott Long struct periph_match_pattern *p; 341fc58801cSScott Long struct periph_match_result *r; 342fc58801cSScott Long struct cam_device *dev; 343fc58801cSScott Long union ccb ccb; 344fc58801cSScott Long size_t bufsize; 345fc58801cSScott Long u_int i; 346fc58801cSScott Long int count; 347fc58801cSScott Long 348fc58801cSScott Long if (xpt_open() < 0) 349fc58801cSScott Long return (ENXIO); 350fc58801cSScott Long 351fc58801cSScott Long for (count = 100;; count+= 100) { 352fc58801cSScott Long /* Try to fetch 'count' disks in one go. */ 353fc58801cSScott Long bzero(&ccb, sizeof(ccb)); 354fc58801cSScott Long 355fc58801cSScott Long ccb.ccb_h.func_code = XPT_DEV_MATCH; 356fc58801cSScott Long 357fc58801cSScott Long bufsize = sizeof(struct dev_match_result) * (count + 2); 358fc58801cSScott Long ccb.cdm.num_matches = 0; 359fc58801cSScott Long ccb.cdm.match_buf_len = bufsize; 360fc58801cSScott Long ccb.cdm.matches = calloc(1, bufsize); 361fc58801cSScott Long 362fc58801cSScott Long bufsize = sizeof(struct dev_match_pattern) * 2; 363fc58801cSScott Long ccb.cdm.num_patterns = 2; 364fc58801cSScott Long ccb.cdm.pattern_buf_len = bufsize; 365fc58801cSScott Long ccb.cdm.patterns = calloc(1, bufsize); 366fc58801cSScott Long 367fc58801cSScott Long /* Match mptX bus 0. */ 368fc58801cSScott Long ccb.cdm.patterns[0].type = DEV_MATCH_BUS; 369fc58801cSScott Long b = &ccb.cdm.patterns[0].pattern.bus_pattern; 370fc58801cSScott Long snprintf(b->dev_name, sizeof(b->dev_name), "mpt"); 371fc58801cSScott Long b->unit_number = mpt_unit; 372fc58801cSScott Long b->bus_id = 0; 373fc58801cSScott Long b->flags = BUS_MATCH_NAME | BUS_MATCH_UNIT | BUS_MATCH_BUS_ID; 374fc58801cSScott Long 375fc58801cSScott Long /* Match any "da" peripherals. */ 376fc58801cSScott Long ccb.cdm.patterns[1].type = DEV_MATCH_PERIPH; 377fc58801cSScott Long p = &ccb.cdm.patterns[1].pattern.periph_pattern; 378fc58801cSScott Long snprintf(p->periph_name, sizeof(p->periph_name), "da"); 379fc58801cSScott Long p->flags = PERIPH_MATCH_NAME; 380fc58801cSScott Long 381fc58801cSScott Long if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) { 382fc58801cSScott Long i = errno; 383fc58801cSScott Long free(ccb.cdm.matches); 384fc58801cSScott Long free(ccb.cdm.patterns); 385fc58801cSScott Long return (i); 386fc58801cSScott Long } 387fc58801cSScott Long free(ccb.cdm.patterns); 388fc58801cSScott Long 389fc58801cSScott Long /* Check for CCB errors. */ 390fc58801cSScott Long if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 391fc58801cSScott Long free(ccb.cdm.matches); 392fc58801cSScott Long return (EIO); 393fc58801cSScott Long } 394fc58801cSScott Long 395fc58801cSScott Long /* If we need a longer list, try again. */ 396fc58801cSScott Long if (ccb.cdm.status == CAM_DEV_MATCH_MORE) { 397fc58801cSScott Long free(ccb.cdm.matches); 398fc58801cSScott Long continue; 399fc58801cSScott Long } 400fc58801cSScott Long 401fc58801cSScott Long /* If we got an error, abort. */ 402fc58801cSScott Long if (ccb.cdm.status != CAM_DEV_MATCH_LAST) { 403fc58801cSScott Long free(ccb.cdm.matches); 404fc58801cSScott Long return (EIO); 405fc58801cSScott Long } 406fc58801cSScott Long break; 407fc58801cSScott Long } 408fc58801cSScott Long 409fc58801cSScott Long /* 410fc58801cSScott Long * We should have N + 1 matches, 1 for the bus and 1 for each 411fc58801cSScott Long * "da" device. 412fc58801cSScott Long */ 413fc58801cSScott Long if (ccb.cdm.num_matches < 1) { 414fc58801cSScott Long warnx("mpt_fetch_disks didn't get any matches"); 415fc58801cSScott Long free(ccb.cdm.matches); 416fc58801cSScott Long return (EIO); 417fc58801cSScott Long } 418fc58801cSScott Long if (ccb.cdm.matches[0].type != DEV_MATCH_BUS) { 419fc58801cSScott Long warnx("mpt_fetch_disks got wrong CAM matches"); 420fc58801cSScott Long free(ccb.cdm.matches); 421fc58801cSScott Long return (EIO); 422fc58801cSScott Long } 423fc58801cSScott Long for (i = 1; i < ccb.cdm.num_matches; i++) { 424fc58801cSScott Long if (ccb.cdm.matches[i].type != DEV_MATCH_PERIPH) { 425fc58801cSScott Long warnx("mpt_fetch_disks got wrong CAM matches"); 426fc58801cSScott Long free(ccb.cdm.matches); 427fc58801cSScott Long return (EIO); 428fc58801cSScott Long } 429fc58801cSScott Long } 430fc58801cSScott Long 431fc58801cSScott Long /* Shortcut if we don't have any "da" devices. */ 432fc58801cSScott Long if (ccb.cdm.num_matches == 1) { 433fc58801cSScott Long free(ccb.cdm.matches); 434fc58801cSScott Long *ndisks = 0; 435fc58801cSScott Long *disksp = NULL; 436fc58801cSScott Long return (0); 437fc58801cSScott Long } 438fc58801cSScott Long 439fc58801cSScott Long /* 440fc58801cSScott Long * Some of the "da" peripherals may be for RAID volumes, so 441fc58801cSScott Long * fetch the IOC 2 page (list of RAID volumes) so we can 442fc58801cSScott Long * exclude them from the list. 443fc58801cSScott Long */ 444fc58801cSScott Long ioc2 = mpt_read_ioc_page(fd, 2, NULL); 445fc58801cSScott Long disks = calloc(ccb.cdm.num_matches, sizeof(*disks)); 446fc58801cSScott Long count = 0; 447fc58801cSScott Long for (i = 1; i < ccb.cdm.num_matches; i++) { 448fc58801cSScott Long r = &ccb.cdm.matches[i].result.periph_result; 449fc58801cSScott Long if (periph_is_volume(ioc2, r)) 450fc58801cSScott Long continue; 451fc58801cSScott Long disks[count].bus = 0; 452fc58801cSScott Long disks[count].target = r->target_id; 453fc58801cSScott Long snprintf(disks[count].devname, sizeof(disks[count].devname), 454fc58801cSScott Long "%s%d", r->periph_name, r->unit_number); 455fc58801cSScott Long 456fc58801cSScott Long dev = cam_open_device(disks[count].devname, O_RDWR); 457fc58801cSScott Long if (dev != NULL) { 458fc58801cSScott Long fetch_scsi_capacity(dev, &disks[count]); 459fc58801cSScott Long fetch_scsi_inquiry(dev, &disks[count]); 460fc58801cSScott Long cam_close_device(dev); 461fc58801cSScott Long } 462fc58801cSScott Long count++; 463fc58801cSScott Long } 464fc58801cSScott Long free(ccb.cdm.matches); 465fc58801cSScott Long free(ioc2); 466fc58801cSScott Long 467fc58801cSScott Long *ndisks = count; 468fc58801cSScott Long *disksp = disks; 469fc58801cSScott Long return (0); 470fc58801cSScott Long } 471fc58801cSScott Long 472fc58801cSScott Long /* 473fc58801cSScott Long * Instruct the mpt(4) device to rescan its busses to find new devices 474fc58801cSScott Long * such as disks whose RAID physdisk page was removed or volumes that 475fc58801cSScott Long * were created. If id is -1, the entire bus is rescanned. 476fc58801cSScott Long * Otherwise, only devices at the specified ID are rescanned. If bus 477fc58801cSScott Long * is -1, then all busses are scanned instead of the specified bus. 478fc58801cSScott Long * Note that currently, only bus 0 is supported. 479fc58801cSScott Long */ 480fc58801cSScott Long int 481fc58801cSScott Long mpt_rescan_bus(int bus, int id) 482fc58801cSScott Long { 483fc58801cSScott Long struct bus_match_pattern *b; 484fc58801cSScott Long union ccb ccb; 485fc58801cSScott Long path_id_t path_id; 486fc58801cSScott Long size_t bufsize; 487fc58801cSScott Long 488fc58801cSScott Long /* mpt(4) only handles devices on bus 0. */ 489fc58801cSScott Long if (bus != -1 && bus != 0) 490fc58801cSScott Long return (EINVAL); 491fc58801cSScott Long 492fc58801cSScott Long if (xpt_open() < 0) 493fc58801cSScott Long return (ENXIO); 494fc58801cSScott Long 495fc58801cSScott Long /* First, find the path id of bus 0 for this mpt controller. */ 496fc58801cSScott Long bzero(&ccb, sizeof(ccb)); 497fc58801cSScott Long 498fc58801cSScott Long ccb.ccb_h.func_code = XPT_DEV_MATCH; 499fc58801cSScott Long 500fc58801cSScott Long bufsize = sizeof(struct dev_match_result) * 1; 501fc58801cSScott Long ccb.cdm.num_matches = 0; 502fc58801cSScott Long ccb.cdm.match_buf_len = bufsize; 503fc58801cSScott Long ccb.cdm.matches = calloc(1, bufsize); 504fc58801cSScott Long 505fc58801cSScott Long bufsize = sizeof(struct dev_match_pattern) * 1; 506fc58801cSScott Long ccb.cdm.num_patterns = 1; 507fc58801cSScott Long ccb.cdm.pattern_buf_len = bufsize; 508fc58801cSScott Long ccb.cdm.patterns = calloc(1, bufsize); 509fc58801cSScott Long 510fc58801cSScott Long /* Match mptX bus 0. */ 511fc58801cSScott Long ccb.cdm.patterns[0].type = DEV_MATCH_BUS; 512fc58801cSScott Long b = &ccb.cdm.patterns[0].pattern.bus_pattern; 513fc58801cSScott Long snprintf(b->dev_name, sizeof(b->dev_name), "mpt"); 514fc58801cSScott Long b->unit_number = mpt_unit; 515fc58801cSScott Long b->bus_id = 0; 516fc58801cSScott Long b->flags = BUS_MATCH_NAME | BUS_MATCH_UNIT | BUS_MATCH_BUS_ID; 517fc58801cSScott Long 518fc58801cSScott Long if (ioctl(xptfd, CAMIOCOMMAND, &ccb) < 0) { 519fc58801cSScott Long free(ccb.cdm.matches); 520fc58801cSScott Long free(ccb.cdm.patterns); 521fc58801cSScott Long return (errno); 522fc58801cSScott Long } 523fc58801cSScott Long free(ccb.cdm.patterns); 524fc58801cSScott Long 525fc58801cSScott Long if (((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) || 526fc58801cSScott Long (ccb.cdm.status != CAM_DEV_MATCH_LAST)) { 527fc58801cSScott Long warnx("mpt_rescan_bus got CAM error %#x, CDM error %d\n", 528fc58801cSScott Long ccb.ccb_h.status, ccb.cdm.status); 529fc58801cSScott Long free(ccb.cdm.matches); 530fc58801cSScott Long return (EIO); 531fc58801cSScott Long } 532fc58801cSScott Long 533fc58801cSScott Long /* We should have exactly 1 match for the bus. */ 534fc58801cSScott Long if (ccb.cdm.num_matches != 1 || 535fc58801cSScott Long ccb.cdm.matches[0].type != DEV_MATCH_BUS) { 536fc58801cSScott Long free(ccb.cdm.matches); 537fc58801cSScott Long return (ENOENT); 538fc58801cSScott Long } 539fc58801cSScott Long path_id = ccb.cdm.matches[0].result.bus_result.path_id; 540fc58801cSScott Long free(ccb.cdm.matches); 541fc58801cSScott Long 542fc58801cSScott Long /* Now perform the actual rescan. */ 543fc58801cSScott Long ccb.ccb_h.path_id = path_id; 544fc58801cSScott Long if (id == -1) { 545fc58801cSScott Long ccb.ccb_h.func_code = XPT_SCAN_BUS; 546fc58801cSScott Long ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; 547fc58801cSScott Long ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; 548fc58801cSScott Long ccb.ccb_h.timeout = 5000; 549fc58801cSScott Long } else { 550fc58801cSScott Long ccb.ccb_h.func_code = XPT_SCAN_LUN; 551fc58801cSScott Long ccb.ccb_h.target_id = id; 552fc58801cSScott Long ccb.ccb_h.target_lun = 0; 553fc58801cSScott Long } 554fc58801cSScott Long ccb.crcn.flags = CAM_FLAG_NONE; 555fc58801cSScott Long 556fc58801cSScott Long /* Run this at a low priority. */ 557fc58801cSScott Long ccb.ccb_h.pinfo.priority = 5; 558fc58801cSScott Long 559fc58801cSScott Long if (ioctl(xptfd, CAMIOCOMMAND, &ccb) == -1) 560fc58801cSScott Long return (errno); 561fc58801cSScott Long 562fc58801cSScott Long if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 563fc58801cSScott Long warnx("mpt_rescan_bus rescan got CAM error %#x\n", 564fc58801cSScott Long ccb.ccb_h.status & CAM_STATUS_MASK); 565fc58801cSScott Long return (EIO); 566fc58801cSScott Long } 567fc58801cSScott Long 568fc58801cSScott Long return (0); 569fc58801cSScott Long } 570