2 * Copyright (c) 2008 Yahoo!, Inc.
4 * Written by: John Baldwin <jhb@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the names of any co-contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * $FreeBSD: src/usr.sbin/mptutil/mpt_cam.c,v 1.3 2010/11/09 19:28:06 jhb Exp $
33 #include <sys/param.h>
42 #include <cam/scsi/scsi_message.h>
43 #include <cam/scsi/scsi_pass.h>
54 xptfd
= open(XPT_DEVICE
, O_RDWR
);
58 /* Fetch the path id of bus 0 for the opened mpt controller. */
60 fetch_path_id(path_id_t
*path_id
)
62 struct bus_match_pattern
*b
;
70 /* First, find the path id of bus 0 for this mpt controller. */
71 bzero(&ccb
, sizeof(ccb
));
73 ccb
.ccb_h
.func_code
= XPT_DEV_MATCH
;
75 bufsize
= sizeof(struct dev_match_result
) * 1;
76 ccb
.cdm
.num_matches
= 0;
77 ccb
.cdm
.match_buf_len
= bufsize
;
78 ccb
.cdm
.matches
= calloc(1, bufsize
);
80 bufsize
= sizeof(struct dev_match_pattern
) * 1;
81 ccb
.cdm
.num_patterns
= 1;
82 ccb
.cdm
.pattern_buf_len
= bufsize
;
83 ccb
.cdm
.patterns
= calloc(1, bufsize
);
85 /* Match mptX bus 0. */
86 ccb
.cdm
.patterns
[0].type
= DEV_MATCH_BUS
;
87 b
= &ccb
.cdm
.patterns
[0].pattern
.bus_pattern
;
88 snprintf(b
->dev_name
, sizeof(b
->dev_name
), "mpt");
89 b
->unit_number
= mpt_unit
;
91 b
->flags
= BUS_MATCH_NAME
| BUS_MATCH_UNIT
| BUS_MATCH_BUS_ID
;
93 if (ioctl(xptfd
, CAMIOCOMMAND
, &ccb
) < 0) {
95 free(ccb
.cdm
.matches
);
96 free(ccb
.cdm
.patterns
);
99 free(ccb
.cdm
.patterns
);
101 if (((ccb
.ccb_h
.status
& CAM_STATUS_MASK
) != CAM_REQ_CMP
) ||
102 (ccb
.cdm
.status
!= CAM_DEV_MATCH_LAST
)) {
103 warnx("fetch_path_id got CAM error %#x, CDM error %d\n",
104 ccb
.ccb_h
.status
, ccb
.cdm
.status
);
105 free(ccb
.cdm
.matches
);
109 /* We should have exactly 1 match for the bus. */
110 if (ccb
.cdm
.num_matches
!= 1 ||
111 ccb
.cdm
.matches
[0].type
!= DEV_MATCH_BUS
) {
112 free(ccb
.cdm
.matches
);
115 *path_id
= ccb
.cdm
.matches
[0].result
.bus_result
.path_id
;
116 free(ccb
.cdm
.matches
);
121 mpt_query_disk(U8 VolumeBus
, U8 VolumeID
, struct mpt_query_disk
*qd
)
123 struct periph_match_pattern
*p
;
124 struct periph_match_result
*r
;
130 /* mpt(4) only handles devices on bus 0. */
137 /* Find the path ID of bus 0. */
138 error
= fetch_path_id(&path_id
);
142 bzero(&ccb
, sizeof(ccb
));
144 ccb
.ccb_h
.func_code
= XPT_DEV_MATCH
;
145 ccb
.ccb_h
.path_id
= CAM_XPT_PATH_ID
;
146 ccb
.ccb_h
.target_id
= CAM_TARGET_WILDCARD
;
147 ccb
.ccb_h
.target_lun
= CAM_LUN_WILDCARD
;
149 bufsize
= sizeof(struct dev_match_result
) * 5;
150 ccb
.cdm
.num_matches
= 0;
151 ccb
.cdm
.match_buf_len
= bufsize
;
152 ccb
.cdm
.matches
= calloc(1, bufsize
);
154 bufsize
= sizeof(struct dev_match_pattern
) * 1;
155 ccb
.cdm
.num_patterns
= 1;
156 ccb
.cdm
.pattern_buf_len
= bufsize
;
157 ccb
.cdm
.patterns
= calloc(1, bufsize
);
159 /* Look for a "da" device at the specified target and lun. */
160 ccb
.cdm
.patterns
[0].type
= DEV_MATCH_PERIPH
;
161 p
= &ccb
.cdm
.patterns
[0].pattern
.periph_pattern
;
162 p
->path_id
= path_id
;
163 snprintf(p
->periph_name
, sizeof(p
->periph_name
), "da");
164 p
->target_id
= VolumeID
;
165 p
->flags
= PERIPH_MATCH_PATH
| PERIPH_MATCH_NAME
| PERIPH_MATCH_TARGET
;
167 if (ioctl(xptfd
, CAMIOCOMMAND
, &ccb
) < 0) {
169 free(ccb
.cdm
.matches
);
170 free(ccb
.cdm
.patterns
);
173 free(ccb
.cdm
.patterns
);
175 if (((ccb
.ccb_h
.status
& CAM_STATUS_MASK
) != CAM_REQ_CMP
) ||
176 (ccb
.cdm
.status
!= CAM_DEV_MATCH_LAST
)) {
177 warnx("mpt_query_disk got CAM error %#x, CDM error %d\n",
178 ccb
.ccb_h
.status
, ccb
.cdm
.status
);
179 free(ccb
.cdm
.matches
);
184 * We should have exactly 1 match for the peripheral.
185 * However, if we don't get a match, don't print an error
186 * message and return ENOENT.
188 if (ccb
.cdm
.num_matches
== 0) {
189 free(ccb
.cdm
.matches
);
192 if (ccb
.cdm
.num_matches
!= 1) {
193 warnx("mpt_query_disk got %d matches, expected 1",
194 ccb
.cdm
.num_matches
);
195 free(ccb
.cdm
.matches
);
198 if (ccb
.cdm
.matches
[0].type
!= DEV_MATCH_PERIPH
) {
199 warnx("mpt_query_disk got wrong CAM match");
200 free(ccb
.cdm
.matches
);
204 /* Copy out the data. */
205 r
= &ccb
.cdm
.matches
[1].result
.periph_result
;
206 snprintf(qd
->devname
, sizeof(qd
->devname
), "%s%d", r
->periph_name
,
208 free(ccb
.cdm
.matches
);
214 periph_is_volume(CONFIG_PAGE_IOC_2
*ioc2
, struct periph_match_result
*r
)
216 CONFIG_PAGE_IOC_2_RAID_VOL
*vol
;
221 vol
= ioc2
->RaidVolume
;
222 for (i
= 0; i
< ioc2
->NumActiveVolumes
; vol
++, i
++) {
223 if (vol
->VolumeBus
== 0 && vol
->VolumeID
== r
->target_id
)
229 /* Much borrowed from scsireadcapacity() in src/sbin/camcontrol/camcontrol.c. */
231 fetch_scsi_capacity(struct cam_device
*dev
, struct mpt_standalone_disk
*disk
)
233 struct scsi_read_capacity_data rcap
;
234 struct scsi_read_capacity_data_16 rcaplong
;
238 ccb
= cam_getccb(dev
);
242 /* Zero the rest of the ccb. */
243 bzero(&(&ccb
->ccb_h
)[1], sizeof(struct ccb_scsiio
) -
244 sizeof(struct ccb_hdr
));
246 scsi_read_capacity(&ccb
->csio
, 1, NULL
, MSG_SIMPLE_Q_TAG
, &rcap
,
247 SSD_FULL_SIZE
, 5000);
249 /* Disable freezing the device queue */
250 ccb
->ccb_h
.flags
|= CAM_DEV_QFRZDIS
;
252 if (cam_send_ccb(dev
, ccb
) < 0) {
258 if ((ccb
->ccb_h
.status
& CAM_STATUS_MASK
) != CAM_REQ_CMP
) {
265 * A last block of 2^32-1 means that the true capacity is over 2TB,
266 * and we need to issue the long READ CAPACITY to get the real
267 * capacity. Otherwise, we're all set.
269 if (scsi_4btoul(rcap
.addr
) != 0xffffffff) {
270 disk
->maxlba
= scsi_4btoul(rcap
.addr
);
274 /* Zero the rest of the ccb. */
275 bzero(&(&ccb
->ccb_h
)[1], sizeof(struct ccb_scsiio
) -
276 sizeof(struct ccb_hdr
));
278 scsi_read_capacity_16(&ccb
->csio
, 1, NULL
, MSG_SIMPLE_Q_TAG
, 0, 0, 0,
279 &rcaplong
, SSD_FULL_SIZE
, 5000);
281 /* Disable freezing the device queue */
282 ccb
->ccb_h
.flags
|= CAM_DEV_QFRZDIS
;
284 if (cam_send_ccb(dev
, ccb
) < 0) {
290 if ((ccb
->ccb_h
.status
& CAM_STATUS_MASK
) != CAM_REQ_CMP
) {
296 disk
->maxlba
= scsi_8btou64(rcaplong
.addr
);
300 /* Borrowed heavily from scsi_all.c:scsi_print_inquiry(). */
302 format_scsi_inquiry(struct mpt_standalone_disk
*disk
,
303 struct scsi_inquiry_data
*inq_data
)
305 char vendor
[16], product
[48], revision
[16], rstr
[12];
307 if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data
))
309 if (SID_TYPE(inq_data
) != T_DIRECT
)
311 if (SID_QUAL(inq_data
) != SID_QUAL_LU_CONNECTED
)
314 cam_strvis(vendor
, inq_data
->vendor
, sizeof(inq_data
->vendor
),
316 cam_strvis(product
, inq_data
->product
, sizeof(inq_data
->product
),
318 cam_strvis(revision
, inq_data
->revision
, sizeof(inq_data
->revision
),
321 /* Hack for SATA disks, no idea how to tell speed. */
322 if (strcmp(vendor
, "ATA") == 0) {
323 snprintf(disk
->inqstring
, sizeof(disk
->inqstring
),
324 "<%s %s> SATA", product
, revision
);
328 switch (SID_ANSI_REV(inq_data
)) {
330 strcpy(rstr
, "SCSI-CCS");
336 snprintf(rstr
, sizeof (rstr
), "SCSI-%d",
337 SID_ANSI_REV(inq_data
));
340 snprintf(disk
->inqstring
, sizeof(disk
->inqstring
), "<%s %s %s> %s",
341 vendor
, product
, revision
, rstr
);
344 /* Much borrowed from scsiinquiry() in src/sbin/camcontrol/camcontrol.c. */
346 fetch_scsi_inquiry(struct cam_device
*dev
, struct mpt_standalone_disk
*disk
)
348 struct scsi_inquiry_data
*inq_buf
;
352 ccb
= cam_getccb(dev
);
356 /* Zero the rest of the ccb. */
357 bzero(&(&ccb
->ccb_h
)[1], sizeof(struct ccb_scsiio
) -
358 sizeof(struct ccb_hdr
));
360 inq_buf
= calloc(1, sizeof(*inq_buf
));
361 if (inq_buf
== NULL
) {
365 scsi_inquiry(&ccb
->csio
, 1, NULL
, MSG_SIMPLE_Q_TAG
, (void *)inq_buf
,
366 SHORT_INQUIRY_LENGTH
, 0, 0, SSD_FULL_SIZE
, 5000);
368 /* Disable freezing the device queue */
369 ccb
->ccb_h
.flags
|= CAM_DEV_QFRZDIS
;
371 if (cam_send_ccb(dev
, ccb
) < 0) {
378 if ((ccb
->ccb_h
.status
& CAM_STATUS_MASK
) != CAM_REQ_CMP
) {
385 format_scsi_inquiry(disk
, inq_buf
);
391 mpt_fetch_disks(int fd
, int *ndisks
, struct mpt_standalone_disk
**disksp
)
393 CONFIG_PAGE_IOC_2
*ioc2
;
394 struct mpt_standalone_disk
*disks
;
395 struct periph_match_pattern
*p
;
396 struct periph_match_result
*r
;
397 struct cam_device
*dev
;
407 error
= fetch_path_id(&path_id
);
411 for (count
= 100;; count
+= 100) {
412 /* Try to fetch 'count' disks in one go. */
413 bzero(&ccb
, sizeof(ccb
));
415 ccb
.ccb_h
.func_code
= XPT_DEV_MATCH
;
417 bufsize
= sizeof(struct dev_match_result
) * (count
+ 1);
418 ccb
.cdm
.num_matches
= 0;
419 ccb
.cdm
.match_buf_len
= bufsize
;
420 ccb
.cdm
.matches
= calloc(1, bufsize
);
422 bufsize
= sizeof(struct dev_match_pattern
) * 1;
423 ccb
.cdm
.num_patterns
= 1;
424 ccb
.cdm
.pattern_buf_len
= bufsize
;
425 ccb
.cdm
.patterns
= calloc(1, bufsize
);
427 /* Match any "da" peripherals. */
428 ccb
.cdm
.patterns
[0].type
= DEV_MATCH_PERIPH
;
429 p
= &ccb
.cdm
.patterns
[0].pattern
.periph_pattern
;
430 p
->path_id
= path_id
;
431 snprintf(p
->periph_name
, sizeof(p
->periph_name
), "da");
432 p
->flags
= PERIPH_MATCH_PATH
| PERIPH_MATCH_NAME
;
434 if (ioctl(xptfd
, CAMIOCOMMAND
, &ccb
) < 0) {
436 free(ccb
.cdm
.matches
);
437 free(ccb
.cdm
.patterns
);
440 free(ccb
.cdm
.patterns
);
442 /* Check for CCB errors. */
443 if ((ccb
.ccb_h
.status
& CAM_STATUS_MASK
) != CAM_REQ_CMP
) {
444 free(ccb
.cdm
.matches
);
448 /* If we need a longer list, try again. */
449 if (ccb
.cdm
.status
== CAM_DEV_MATCH_MORE
) {
450 free(ccb
.cdm
.matches
);
454 /* If we got an error, abort. */
455 if (ccb
.cdm
.status
!= CAM_DEV_MATCH_LAST
) {
456 free(ccb
.cdm
.matches
);
462 /* Shortcut if we don't have any "da" devices. */
463 if (ccb
.cdm
.num_matches
== 0) {
464 free(ccb
.cdm
.matches
);
470 /* We should have N matches, 1 for each "da" device. */
471 for (i
= 0; i
< ccb
.cdm
.num_matches
; i
++) {
472 if (ccb
.cdm
.matches
[i
].type
!= DEV_MATCH_PERIPH
) {
473 warnx("mpt_fetch_disks got wrong CAM matches");
474 free(ccb
.cdm
.matches
);
480 * Some of the "da" peripherals may be for RAID volumes, so
481 * fetch the IOC 2 page (list of RAID volumes) so we can
482 * exclude them from the list.
484 ioc2
= mpt_read_ioc_page(fd
, 2, NULL
);
487 disks
= calloc(ccb
.cdm
.num_matches
, sizeof(*disks
));
489 for (i
= 0; i
< ccb
.cdm
.num_matches
; i
++) {
490 r
= &ccb
.cdm
.matches
[i
].result
.periph_result
;
491 if (periph_is_volume(ioc2
, r
))
493 disks
[count
].bus
= 0;
494 disks
[count
].target
= r
->target_id
;
495 snprintf(disks
[count
].devname
, sizeof(disks
[count
].devname
),
496 "%s%d", r
->periph_name
, r
->unit_number
);
498 dev
= cam_open_device(disks
[count
].devname
, O_RDWR
);
500 fetch_scsi_capacity(dev
, &disks
[count
]);
501 fetch_scsi_inquiry(dev
, &disks
[count
]);
502 cam_close_device(dev
);
506 free(ccb
.cdm
.matches
);
515 * Instruct the mpt(4) device to rescan its busses to find new devices
516 * such as disks whose RAID physdisk page was removed or volumes that
517 * were created. If id is -1, the entire bus is rescanned.
518 * Otherwise, only devices at the specified ID are rescanned. If bus
519 * is -1, then all busses are scanned instead of the specified bus.
520 * Note that currently, only bus 0 is supported.
523 mpt_rescan_bus(int bus
, int id
)
529 /* mpt(4) only handles devices on bus 0. */
530 if (bus
!= -1 && bus
!= 0)
536 error
= fetch_path_id(&path_id
);
540 /* Perform the actual rescan. */
541 bzero(&ccb
, sizeof(ccb
));
542 ccb
.ccb_h
.path_id
= path_id
;
544 ccb
.ccb_h
.func_code
= XPT_SCAN_BUS
;
545 ccb
.ccb_h
.target_id
= CAM_TARGET_WILDCARD
;
546 ccb
.ccb_h
.target_lun
= CAM_LUN_WILDCARD
;
547 ccb
.ccb_h
.timeout
= 5000;
549 ccb
.ccb_h
.func_code
= XPT_SCAN_LUN
;
550 ccb
.ccb_h
.target_id
= id
;
551 ccb
.ccb_h
.target_lun
= 0;
553 ccb
.crcn
.flags
= CAM_FLAG_NONE
;
555 /* Run this at a low priority. */
556 ccb
.ccb_h
.pinfo
.priority
= 5;
558 if (ioctl(xptfd
, CAMIOCOMMAND
, &ccb
) == -1)
561 if ((ccb
.ccb_h
.status
& CAM_STATUS_MASK
) != CAM_REQ_CMP
) {
562 warnx("mpt_rescan_bus rescan got CAM error %#x\n",
563 ccb
.ccb_h
.status
& CAM_STATUS_MASK
);