Changeset View
Changeset View
Standalone View
Standalone View
tests/sys/devad2/devad2_cam.cc
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/* | |||||
* Copyright (c) 1997-2007 Kenneth D. Merry | |||||
* All rights reserved. | |||||
* | |||||
* 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. The name of the author may not 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. | |||||
*/ | |||||
/*- | |||||
* Copyright (c) 2013, 2014 Spectra Logic Corporation | |||||
* All rights reserved. | |||||
* | |||||
* 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, | |||||
* without modification. | |||||
* 2. Redistributions in binary form must reproduce at minimum a disclaimer | |||||
* substantially similar to the "NO WARRANTY" disclaimer below | |||||
* ("Disclaimer") and any redistribution must be conditioned upon | |||||
* including a substantially similar Disclaimer requirement for further | |||||
* binary redistribution. | |||||
* | |||||
* NO WARRANTY | |||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR | |||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||||
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. | |||||
* | |||||
*/ | |||||
#include <pthread.h> | |||||
#include <stdint.h> | |||||
#include <stdio.h> | |||||
#include <stddef.h> | |||||
#include <stdlib.h> | |||||
#include <unistd.h> | |||||
#include <string.h> | |||||
#include <getopt.h> | |||||
#include <libgen.h> | |||||
#include <fcntl.h> | |||||
#include <signal.h> | |||||
#include <errno.h> | |||||
#include <err.h> | |||||
#include <sys/types.h> | |||||
#include <sys/stat.h> | |||||
#include <sys/queue.h> | |||||
#include <sys/sysctl.h> | |||||
#include <sys/event.h> | |||||
#include <netinet/in.h> | |||||
#include <netinet/tcp_seq.h> | |||||
#include <machine/atomic.h> | |||||
#include <cam/cam.h> | |||||
#include <cam/cam_debug.h> | |||||
#include <cam/cam_ccb.h> | |||||
#include <cam/scsi/scsi_all.h> | |||||
#include <cam/scsi/scsi_da.h> | |||||
#include <cam/scsi/scsi_pass.h> | |||||
#include <cam/scsi/scsi_message.h> | |||||
#include <cam/scsi/scsi_da_brand.h> | |||||
#include <camlib.h> | |||||
#include "devad2.h" | |||||
#define ERROR_BUF_LEN 512 | |||||
#define XPT_GEN_NAME "kern.cam.xpt_generation" | |||||
/* Generically usefull offsets into the peripheral private area */ | |||||
#define ppriv_ptr0 periph_priv.entries[0].ptr | |||||
#define ppriv_ptr1 periph_priv.entries[1].ptr | |||||
#define ppriv_field0 periph_priv.entries[0].field | |||||
#define ppriv_field1 periph_priv.entries[1].field | |||||
#define ccb_ptr ppriv_ptr0 | |||||
void devad_disk_free(struct devad_disk *disk); | |||||
int | |||||
devad_disk_read_start(struct devad_disk *disk, union ccb *ccb) | |||||
{ | |||||
int retval = 0; | |||||
if (((disk->cur_offset / disk->secsize) + | |||||
(sizeof(disk->read_buf) / disk->secsize)) > disk->max_sector + 1) { | |||||
/* | |||||
* We're at the end of the disk. | |||||
*/ | |||||
goto bailout; | |||||
} | |||||
if (ccb == NULL) { | |||||
ccb = cam_getccb(disk->dev); | |||||
if (ccb == NULL) | |||||
err(1, "Unable to allocate CCB"); | |||||
} | |||||
bzero(&(&ccb->ccb_h)[1], | |||||
sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); | |||||
scsi_read_write(&ccb->csio, | |||||
/*retries*/ 2, | |||||
/*cbfcnp*/ NULL, | |||||
/*tag_action*/ MSG_SIMPLE_Q_TAG, | |||||
/*read*/ SCSI_RW_READ, | |||||
/*byte2*/ 0, | |||||
/*minimum_cmd_size*/ 0, | |||||
/*lba*/ disk->cur_offset / disk->secsize, | |||||
/*block_count*/ sizeof(disk->read_buf) / disk->secsize, | |||||
/*data_ptr*/ disk->read_buf, | |||||
/*dxfer_len*/ sizeof(disk->read_buf), | |||||
/*sense_len*/ SSD_FULL_SIZE, | |||||
/*timeout*/ 60000); | |||||
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS | CAM_PASS_ERR_RECOVER; | |||||
/* Save a pointer to the original CCB so that we can free it */ | |||||
ccb->ccb_h.ccb_ptr = ccb; | |||||
disk->active_ccb = ccb; | |||||
if (ioctl(disk->dev->fd, CAMIOQUEUE, ccb) == -1) { | |||||
warn("error sending READ to %s%u", disk->periph_name, | |||||
disk->unit_number); | |||||
cam_freeccb(ccb); | |||||
} | |||||
bailout: | |||||
return (retval); | |||||
} | |||||
void | |||||
devad_disk_read_done(struct devad_disk *disk) | |||||
{ | |||||
union ccb *ccb; | |||||
int retval; | |||||
ccb = (union ccb *)calloc(sizeof(*ccb), 1); | |||||
if (ccb == NULL) | |||||
return; | |||||
/* | |||||
* Currently there should only be one outstanding I/O at a time, | |||||
* but we put this in a while loop just in case. | |||||
*/ | |||||
while ((retval = ioctl(disk->dev->fd, CAMIOGET, ccb)) != -1) { | |||||
union ccb *orig_ccb; | |||||
orig_ccb = (union ccb *)ccb->ccb_h.ccb_ptr; | |||||
bcopy(ccb, orig_ccb, sizeof(*ccb)); | |||||
if ((orig_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { | |||||
char error_buf[ERROR_BUF_LEN]; | |||||
cam_error_string(disk->dev, orig_ccb, error_buf, | |||||
sizeof(error_buf), CAM_ESF_ALL, CAM_EPF_ALL); | |||||
warnx("%s", error_buf); | |||||
cam_freeccb(orig_ccb); | |||||
disk->active_ccb = NULL; | |||||
} else { | |||||
disk->cur_offset += orig_ccb->csio.dxfer_len - | |||||
orig_ccb->csio.resid; | |||||
if (disk->gone == 0) | |||||
retval = devad_disk_read_start(disk, orig_ccb); | |||||
} | |||||
} | |||||
if (disk->gone != 0) | |||||
devad_disk_free(disk); | |||||
free(ccb); | |||||
} | |||||
int | |||||
devad_disk_read_cap(struct devad_disk *disk) | |||||
{ | |||||
struct scsi_read_capacity_data rcap; | |||||
struct scsi_read_capacity_data_long rcaplong; | |||||
union ccb *ccb = NULL; | |||||
uint64_t maxsector; | |||||
uint32_t block_len; | |||||
int retval = 0; | |||||
ccb = cam_getccb(disk->dev); | |||||
if (ccb == NULL) { | |||||
warnx("%s: error allocating ccb", __func__); | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
bzero(&(&ccb->ccb_h)[1], | |||||
sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); | |||||
scsi_read_capacity(&ccb->csio, | |||||
/*retries*/ 2, | |||||
/*cbfcnp*/ NULL, | |||||
/*tag_action*/ MSG_SIMPLE_Q_TAG, | |||||
&rcap, | |||||
/*sense_len*/ SSD_FULL_SIZE, | |||||
/*timeout*/ 5000); | |||||
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS | CAM_PASS_ERR_RECOVER; | |||||
if (cam_send_ccb(disk->dev, ccb) != 0) { | |||||
warn("error sending READ CAPACITY to %s%u", disk->periph_name, | |||||
disk->unit_number); | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { | |||||
cam_error_print(disk->dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, | |||||
stderr); | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
maxsector = scsi_4btoul(rcap.addr); | |||||
block_len = scsi_4btoul(rcap.length); | |||||
/* | |||||
* 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 done. | |||||
*/ | |||||
if (maxsector != 0xffffffff) | |||||
goto bailout; | |||||
scsi_read_capacity_16(&ccb->csio, | |||||
/*retries*/ 2, | |||||
/*cbfcnp*/ NULL, | |||||
/*tag_action*/ MSG_SIMPLE_Q_TAG, | |||||
/*lba*/ 0, | |||||
/*reladdr*/ 0, | |||||
/*pmi*/ 0, | |||||
(uint8_t*)&rcaplong, | |||||
sizeof(rcaplong), | |||||
/*sense_len*/ SSD_FULL_SIZE, | |||||
/*timeout*/ 5000); | |||||
/* Disable freezing the device queue */ | |||||
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS | CAM_PASS_ERR_RECOVER; | |||||
if (cam_send_ccb(disk->dev, ccb) < 0) { | |||||
warn("error sending READ CAPACITY (16) command"); | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { | |||||
cam_error_print(disk->dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, | |||||
stderr); | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
maxsector = scsi_8btou64(rcaplong.addr); | |||||
block_len = scsi_4btoul(rcaplong.length); | |||||
bailout: | |||||
if (retval == 0) { | |||||
disk->secsize = block_len; | |||||
disk->max_sector = maxsector; | |||||
} | |||||
if (ccb != NULL) | |||||
cam_freeccb(ccb); | |||||
return (retval); | |||||
} | |||||
/* | |||||
* Free storage allocated to a disk and close any file descriptors. The | |||||
* disk should be removed from the list first. | |||||
*/ | |||||
void | |||||
devad_disk_free(struct devad_disk *disk) | |||||
{ | |||||
cam_close_device(disk->dev); | |||||
disk->dev = NULL; | |||||
cam_freeccb(disk->active_ccb); | |||||
disk->active_ccb = NULL; | |||||
free(disk); | |||||
} | |||||
/* | |||||
* Remove a disk from the list and free it. | |||||
*/ | |||||
void | |||||
devad_disk_remove(struct devad_softc *softc, struct devad_disk *disk) | |||||
{ | |||||
pthread_mutex_lock(&softc->mutex); | |||||
TAILQ_REMOVE(&softc->disk_list, disk, links); | |||||
softc->num_disks--; | |||||
disk->gone = 1; | |||||
pthread_mutex_unlock(&softc->mutex); | |||||
} | |||||
/* | |||||
* Given a peripheral name and unit number, find a disk if it is in the | |||||
* list. | |||||
* | |||||
* Returns a disk structure on success, NULL on failure. | |||||
*/ | |||||
struct devad_disk * | |||||
devad_disk_find_dev(struct devad_softc *softc, char *name, | |||||
unsigned int unit_number) | |||||
{ | |||||
struct devad_disk *disk; | |||||
pthread_mutex_lock(&softc->mutex); | |||||
TAILQ_FOREACH(disk, &softc->disk_list, links) { | |||||
if ((disk->unit_number == unit_number) | |||||
&& (strcmp(name, disk->periph_name) == 0)) { | |||||
pthread_mutex_unlock(&softc->mutex); | |||||
return (disk); | |||||
} | |||||
} | |||||
pthread_mutex_unlock(&softc->mutex); | |||||
return (NULL); | |||||
} | |||||
/* | |||||
* Given a peripheral name and unit number, find a disk in the list if it | |||||
* exists, or allocate a new disk if it does not exist. | |||||
* | |||||
* Returns a disk structure on success, NULL on failure. | |||||
*/ | |||||
struct devad_disk * | |||||
devad_disk_find_or_alloc(struct devad_softc *softc, char *name, | |||||
unsigned int unit_number) | |||||
{ | |||||
struct devad_disk *disk = NULL, *cur_disk = NULL; | |||||
struct cam_device *dev = NULL; | |||||
int retval = 0; | |||||
dev = cam_open_spec_device(name, unit_number, O_RDWR, NULL); | |||||
if (dev == NULL) { | |||||
printf("%s\n", cam_errbuf); | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
cur_disk = devad_disk_find_dev(softc, name, unit_number); | |||||
if (cur_disk != NULL) { | |||||
/* | |||||
* We already have another disk by the same name. Is this | |||||
* the same device? | |||||
*/ | |||||
if ((dev->serial_num_len == 0) | |||||
&& (cur_disk->dev->serial_num_len == 0)) { | |||||
/* | |||||
* No serial numbers, so no way to easily | |||||
* distinguish between these two. So we'll keep | |||||
* the current disk. | |||||
*/ | |||||
disk = cur_disk; | |||||
cam_close_device(dev); | |||||
dev = disk->dev; | |||||
} else if (dev->serial_num_len != | |||||
cur_disk->dev->serial_num_len) { | |||||
/* | |||||
* These disks are different. The new one wins. | |||||
*/ | |||||
devad_disk_remove(softc, cur_disk); | |||||
} else if ((dev->serial_num_len != 0) | |||||
&& (cur_disk->dev->serial_num_len != 0) | |||||
&& (cur_disk->dev->serial_num_len == dev->serial_num_len) | |||||
&& (bcmp(dev->serial_num, cur_disk->dev->serial_num, | |||||
dev->serial_num_len) == 0)) { | |||||
/* | |||||
* These disks are the same. The current one wins. | |||||
*/ | |||||
disk = cur_disk; | |||||
cam_close_device(dev); | |||||
dev = disk->dev; | |||||
} else if ((dev->serial_num_len == 0) | |||||
|| (cur_disk->dev->serial_num_len == 0)) { | |||||
/* | |||||
* The disk without a serial number loses. | |||||
*/ | |||||
if (dev->serial_num_len == 0) { | |||||
disk = cur_disk; | |||||
cam_close_device(dev); | |||||
dev = disk->dev; | |||||
} else { | |||||
devad_disk_remove(softc, cur_disk); | |||||
} | |||||
} else { | |||||
/* | |||||
* These disks are different. This means that | |||||
* another device has appeared at the same name. | |||||
* We need to remove the existing device. | |||||
*/ | |||||
devad_disk_remove(softc, cur_disk); | |||||
} | |||||
} | |||||
if (disk == NULL) { | |||||
struct kevent ke; | |||||
disk = (struct devad_disk *)malloc(sizeof(*disk)); | |||||
if (disk == NULL) { | |||||
warn("unable to allocate %zu bytes", | |||||
sizeof(*disk)); | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
bzero(disk, sizeof(*disk)); | |||||
strlcpy(disk->periph_name, name, sizeof(disk->periph_name)); | |||||
disk->unit_number = unit_number; | |||||
disk->dev = dev; | |||||
disk->softc = softc; | |||||
/* | |||||
* This is just a placeholder to avoid divide by zero | |||||
* problems. We'll issue a READ CAPACITY shortly. | |||||
*/ | |||||
disk->secsize = 512; | |||||
EV_SET(&ke, dev->fd, EVFILT_READ, EV_ADD| EV_ENABLE, 0, 0, | |||||
disk); | |||||
/* | |||||
* Add a read event for this device. It will be deleted | |||||
* automatically when the file descriptor is closed. | |||||
*/ | |||||
if (kevent(softc->kq, &ke, 1, NULL, 0, NULL) == -1) { | |||||
/* | |||||
* XXX KDM should we just bail out here? We'll be | |||||
* able to queue commands but won't receive | |||||
* notification of | |||||
*/ | |||||
warn("unable to register kevent!"); | |||||
} | |||||
pthread_mutex_lock(&softc->mutex); | |||||
TAILQ_INSERT_TAIL(&softc->disk_list, disk, links); | |||||
softc->num_disks++; | |||||
pthread_mutex_unlock(&softc->mutex); | |||||
} | |||||
bailout: | |||||
return (disk); | |||||
} | |||||
int | |||||
devad_disk_remove_device(struct devad_softc *softc, char *name, | |||||
unsigned int unit_number) | |||||
{ | |||||
struct devad_disk *disk = NULL; | |||||
disk = devad_disk_find_dev(softc, name, unit_number); | |||||
if (disk == NULL) | |||||
return (0); | |||||
devad_disk_remove(softc, disk); | |||||
return (0); | |||||
} | |||||
/* | |||||
* Probe the given device, and add it to the list of disks. | |||||
* | |||||
* Returns 0 for success, non-zero for failure. | |||||
*/ | |||||
int | |||||
devad_disk_probe_device(struct devad_softc *softc, char *name, | |||||
unsigned int unit_number) | |||||
{ | |||||
int retval = 0; | |||||
struct devad_disk *disk = NULL; | |||||
/* | |||||
* See if we already have a device with the same name, and if so, | |||||
* whether it is unchanged. In any case, pass back a pointer to | |||||
* the new or existing disk structure. | |||||
*/ | |||||
disk = devad_disk_find_or_alloc(softc, name, unit_number); | |||||
if (disk == NULL) { | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
retval = devad_disk_read_cap(disk); | |||||
/* | |||||
* Mark the current XPT generation, so we know that this disk is | |||||
* currently active. | |||||
*/ | |||||
disk->generation = softc->xpt_generation; | |||||
/* | |||||
* Starting offset for reads. | |||||
*/ | |||||
disk->cur_offset = 0; | |||||
retval = devad_disk_read_start(disk, NULL); | |||||
bailout: | |||||
return (retval); | |||||
} | |||||
/* | |||||
* Given a completed XPT_DEV_MATCH CCB, probe the returned devices and | |||||
* optionally print them. | |||||
* | |||||
* Returns 0 for success, non-zero for failure. | |||||
*/ | |||||
static int | |||||
devad_disk_process_matches(struct devad_softc *softc, union ccb *ccb, | |||||
int *need_close, int print_devs) | |||||
{ | |||||
unsigned int i; | |||||
int retval = 0; | |||||
for (i = 0; i < ccb->cdm.num_matches; i++) { | |||||
switch (ccb->cdm.matches[i].type) { | |||||
case DEV_MATCH_DEVICE: { | |||||
struct device_match_result *dev_result; | |||||
uint8_t vendor[16], product[48], revision[16]; | |||||
char tmpstr[256]; | |||||
dev_result = &ccb->cdm.matches[i].result.device_result; | |||||
if (dev_result->protocol != PROTO_SCSI) | |||||
break; | |||||
cam_strvis(vendor, | |||||
(const uint8_t *)dev_result->inq_data.vendor, | |||||
sizeof(dev_result->inq_data.vendor),sizeof(vendor)); | |||||
cam_strvis(product, | |||||
(const uint8_t *) dev_result->inq_data.product, | |||||
sizeof(dev_result->inq_data.product), | |||||
sizeof(product)); | |||||
cam_strvis(revision, | |||||
(const uint8_t *)dev_result->inq_data.revision, | |||||
sizeof(dev_result->inq_data.revision), | |||||
sizeof(revision)); | |||||
if (print_devs == 0) | |||||
break; | |||||
if (*need_close != 0) { | |||||
fprintf(stdout, ")\n"); | |||||
*need_close = 0; | |||||
} | |||||
sprintf(tmpstr, "<%s %s %s>", vendor, product,revision); | |||||
fprintf(stdout, "%-33s at scbus%d target %d lun %jd (", | |||||
tmpstr, dev_result->path_id, | |||||
dev_result->target_id, | |||||
(intmax_t)dev_result->target_lun); | |||||
*need_close = 1; | |||||
break; | |||||
} | |||||
case DEV_MATCH_PERIPH: { | |||||
struct periph_match_result *periph_result; | |||||
periph_result = | |||||
&ccb->cdm.matches[i].result.periph_result; | |||||
retval = devad_disk_probe_device(softc, | |||||
periph_result->periph_name, | |||||
periph_result->unit_number); | |||||
if (retval != 0) | |||||
goto bailout; | |||||
if (print_devs == 0) | |||||
break; | |||||
if (*need_close > 1) | |||||
fprintf(stdout, ","); | |||||
fprintf(stdout, "%s%d", | |||||
periph_result->periph_name, | |||||
periph_result->unit_number); | |||||
(*need_close)++; | |||||
break; | |||||
} | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
bailout: | |||||
return (retval); | |||||
} | |||||
/* | |||||
* Get the current CAM transport layer generation. The generation number | |||||
* is incremented every time a device or peripheral is added or removed. | |||||
* So if it is unchanged since the last check, the topology is unchanged. | |||||
* | |||||
* Returns 0 for success, non-zero for failure. | |||||
*/ | |||||
int | |||||
devad_disk_get_generation(uint32_t *generation) | |||||
{ | |||||
int retval = 0; | |||||
uint32_t tmp_generation; | |||||
size_t gen_size; | |||||
gen_size = sizeof(tmp_generation); | |||||
retval = sysctlbyname(XPT_GEN_NAME, &tmp_generation, &gen_size,NULL, 0); | |||||
if (retval != 0) { | |||||
warn("sysctlbyname for %s failed", XPT_GEN_NAME); | |||||
return (retval); | |||||
} | |||||
/* | |||||
* We assume that the generation is a uint32_t. If it isn't, we've | |||||
* got a problem. | |||||
*/ | |||||
if (gen_size != sizeof(*generation)) { | |||||
warnx("XPT generation size %zu bytes != assumed size %zu bytes", | |||||
gen_size, sizeof(*generation)); | |||||
retval = 1; | |||||
} else { | |||||
*generation = tmp_generation; | |||||
} | |||||
return (retval); | |||||
} | |||||
/* | |||||
* Scan all disks in the system and add them to our list. Remove any disks | |||||
* that are no longer present. | |||||
* | |||||
* Returns 0 for success, non-zero for failure. | |||||
*/ | |||||
int | |||||
devad_disk_scan(struct devad_softc *softc, int print_devs) | |||||
{ | |||||
int fd = -1, retval = 0; | |||||
int need_close = 0; | |||||
ssize_t bufsize; | |||||
union ccb ccb; | |||||
struct dev_match_pattern patterns[2]; | |||||
bzero(&ccb, sizeof(ccb)); | |||||
retval = devad_disk_get_generation(&softc->xpt_generation); | |||||
if (retval != 0) | |||||
goto bailout; | |||||
/* | |||||
* Since we don't know yet which devices are in the system, open | |||||
* the XPT device to send the device match CCB. | |||||
*/ | |||||
fd = open(XPT_DEVICE, O_RDWR); | |||||
if (fd == -1) { | |||||
warn("cannot open %s", XPT_DEVICE); | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
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; | |||||
ccb.ccb_h.func_code = XPT_DEV_MATCH; | |||||
/* | |||||
* We size the buffer for 100 matches at a time. | |||||
*/ | |||||
bufsize = sizeof(struct dev_match_result) * 100; | |||||
ccb.cdm.match_buf_len = bufsize; | |||||
ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); | |||||
if (ccb.cdm.matches == NULL) { | |||||
warn("unable to malloc %zd bytes", bufsize); | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
ccb.cdm.num_matches = 0; | |||||
bzero(patterns, sizeof(patterns)); | |||||
ccb.cdm.num_patterns = 2; | |||||
ccb.cdm.pattern_buf_len = sizeof(patterns); | |||||
ccb.cdm.patterns = patterns; | |||||
/* | |||||
* Match all da(4) peripheral drivers. | |||||
*/ | |||||
patterns[0].type = DEV_MATCH_PERIPH; | |||||
snprintf(patterns[0].pattern.periph_pattern.periph_name, | |||||
sizeof(patterns[0].pattern.periph_pattern.periph_name), "da"); | |||||
patterns[0].pattern.periph_pattern.flags = PERIPH_MATCH_NAME; | |||||
/* | |||||
* And match all direct access devices. | |||||
*/ | |||||
patterns[1].type = DEV_MATCH_DEVICE; | |||||
patterns[1].pattern.device_pattern.flags = DEV_MATCH_INQUIRY; | |||||
patterns[1].pattern.device_pattern.data.inq_pat.type = T_DIRECT; | |||||
patterns[1].pattern.device_pattern.data.inq_pat.media_type = | |||||
SIP_MEDIA_FIXED; | |||||
snprintf(patterns[1].pattern.device_pattern.data.inq_pat.vendor, | |||||
sizeof(patterns[1].pattern.device_pattern.data.inq_pat.vendor), | |||||
"*"); | |||||
snprintf(patterns[1].pattern.device_pattern.data.inq_pat.product, | |||||
sizeof(patterns[1].pattern.device_pattern.data.inq_pat.product), | |||||
"*"); | |||||
snprintf(patterns[1].pattern.device_pattern.data.inq_pat.revision, | |||||
sizeof(patterns[1].pattern.device_pattern.data.inq_pat.revision), | |||||
"*"); | |||||
do { | |||||
if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { | |||||
warn("error sending CAMIOCOMMAND ioctl to %s", | |||||
XPT_DEVICE); | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
if ((ccb.ccb_h.status != CAM_REQ_CMP) | |||||
|| ((ccb.cdm.status != CAM_DEV_MATCH_LAST) | |||||
&& (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { | |||||
warnx("got CAM error %#x, CDM error %d", | |||||
ccb.ccb_h.status, ccb.cdm.status); | |||||
retval = 1; | |||||
goto bailout; | |||||
} | |||||
retval = devad_disk_process_matches(softc, &ccb, &need_close, | |||||
print_devs); | |||||
} while ((ccb.ccb_h.status == CAM_REQ_CMP) | |||||
&& (ccb.cdm.status == CAM_DEV_MATCH_MORE)); | |||||
bailout: | |||||
if ((print_devs != 0) | |||||
&& (need_close != 0)) | |||||
fprintf(stdout, ")\n"); | |||||
if (fd != -1) | |||||
close(fd); | |||||
if (ccb.cdm.matches != NULL) | |||||
free(ccb.cdm.matches); | |||||
return (retval); | |||||
} | |||||