Restructure camdd(8) slightly to make it easier to add support for
additional protocols. Submitted by: Chuck Tuffli <chuck@tuffli.net> MFC after: 1 week Differential Revision: D11230
This commit is contained in:
parent
e6a4cfc37b
commit
988bd0d72b
@ -260,6 +260,7 @@ struct camdd_buf {
|
||||
|
||||
struct camdd_dev_pass {
|
||||
int scsi_dev_type;
|
||||
int protocol;
|
||||
struct cam_device *dev;
|
||||
uint64_t max_sector;
|
||||
uint32_t block_len;
|
||||
@ -477,6 +478,9 @@ uint32_t camdd_buf_get_len(struct camdd_buf *buf);
|
||||
void camdd_buf_add_child(struct camdd_buf *buf, struct camdd_buf *child_buf);
|
||||
int camdd_probe_tape(int fd, char *filename, uint64_t *max_iosize,
|
||||
uint64_t *max_blk, uint64_t *min_blk, uint64_t *blk_gran);
|
||||
int camdd_probe_pass_scsi(struct cam_device *cam_dev, union ccb *ccb,
|
||||
camdd_argmask arglist, int probe_retry_count,
|
||||
int probe_timeout, uint64_t *maxsector, uint32_t *block_len);
|
||||
struct camdd_dev *camdd_probe_file(int fd, struct camdd_io_opts *io_opts,
|
||||
int retry_count, int timeout);
|
||||
struct camdd_dev *camdd_probe_pass(struct cam_device *cam_dev,
|
||||
@ -485,7 +489,8 @@ struct camdd_dev *camdd_probe_pass(struct cam_device *cam_dev,
|
||||
int probe_timeout, int io_retry_count,
|
||||
int io_timeout);
|
||||
void *camdd_file_worker(void *arg);
|
||||
camdd_buf_status camdd_ccb_status(union ccb *ccb);
|
||||
camdd_buf_status camdd_ccb_status(union ccb *ccb, int protocol);
|
||||
int camdd_get_cgd(struct cam_device *device, struct ccb_getdev *cgd);
|
||||
int camdd_queue_peer_buf(struct camdd_dev *dev, struct camdd_buf *buf);
|
||||
int camdd_complete_peer_buf(struct camdd_dev *dev, struct camdd_buf *peer_buf);
|
||||
void camdd_peer_done(struct camdd_buf *buf);
|
||||
@ -1248,56 +1253,59 @@ camdd_probe_file(int fd, struct camdd_io_opts *io_opts, int retry_count,
|
||||
}
|
||||
|
||||
/*
|
||||
* Need to implement this. Do a basic probe:
|
||||
* - Check the inquiry data, make sure we're talking to a device that we
|
||||
* can reasonably expect to talk to -- direct, RBC, CD, WORM.
|
||||
* - Send a test unit ready, make sure the device is available.
|
||||
* - Get the capacity and block size.
|
||||
* Get a get device CCB for the specified device.
|
||||
*/
|
||||
struct camdd_dev *
|
||||
camdd_probe_pass(struct cam_device *cam_dev, struct camdd_io_opts *io_opts,
|
||||
camdd_argmask arglist, int probe_retry_count,
|
||||
int probe_timeout, int io_retry_count, int io_timeout)
|
||||
int
|
||||
camdd_get_cgd(struct cam_device *device, struct ccb_getdev *cgd)
|
||||
{
|
||||
union ccb *ccb;
|
||||
uint64_t maxsector;
|
||||
uint32_t cpi_maxio, max_iosize, pass_numblocks;
|
||||
uint32_t block_len;
|
||||
struct scsi_read_capacity_data rcap;
|
||||
struct scsi_read_capacity_data_long rcaplong;
|
||||
struct camdd_dev *dev;
|
||||
struct camdd_dev_pass *pass_dev;
|
||||
struct kevent ke;
|
||||
int scsi_dev_type;
|
||||
union ccb *ccb;
|
||||
int retval = 0;
|
||||
|
||||
dev = NULL;
|
||||
|
||||
scsi_dev_type = SID_TYPE(&cam_dev->inq_data);
|
||||
maxsector = 0;
|
||||
block_len = 0;
|
||||
|
||||
/*
|
||||
* For devices that support READ CAPACITY, we'll attempt to get the
|
||||
* capacity. Otherwise, we really don't support tape or other
|
||||
* devices via SCSI passthrough, so just return an error in that case.
|
||||
*/
|
||||
switch (scsi_dev_type) {
|
||||
case T_DIRECT:
|
||||
case T_WORM:
|
||||
case T_CDROM:
|
||||
case T_OPTICAL:
|
||||
case T_RBC:
|
||||
case T_ZBC_HM:
|
||||
break;
|
||||
default:
|
||||
errx(1, "Unsupported SCSI device type %d", scsi_dev_type);
|
||||
break; /*NOTREACHED*/
|
||||
ccb = cam_getccb(device);
|
||||
|
||||
if (ccb == NULL) {
|
||||
warnx("%s: couldn't allocate CCB", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ccb = cam_getccb(cam_dev);
|
||||
CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->cgd);
|
||||
|
||||
ccb->ccb_h.func_code = XPT_GDEV_TYPE;
|
||||
|
||||
if (cam_send_ccb(device, ccb) < 0) {
|
||||
warn("%s: error sending Get Device Information CCB", __func__);
|
||||
cam_error_print(device, ccb, CAM_ESF_ALL,
|
||||
CAM_EPF_ALL, stderr);
|
||||
retval = -1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
|
||||
cam_error_print(device, ccb, CAM_ESF_ALL,
|
||||
CAM_EPF_ALL, stderr);
|
||||
retval = -1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
bcopy(&ccb->cgd, cgd, sizeof(struct ccb_getdev));
|
||||
|
||||
bailout:
|
||||
cam_freeccb(ccb);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
camdd_probe_pass_scsi(struct cam_device *cam_dev, union ccb *ccb,
|
||||
camdd_argmask arglist, int probe_retry_count,
|
||||
int probe_timeout, uint64_t *maxsector, uint32_t *block_len)
|
||||
{
|
||||
struct scsi_read_capacity_data rcap;
|
||||
struct scsi_read_capacity_data_long rcaplong;
|
||||
int retval = -1;
|
||||
|
||||
if (ccb == NULL) {
|
||||
warnx("%s: error allocating ccb", __func__);
|
||||
warnx("%s: error passed ccb is NULL", __func__);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
@ -1331,16 +1339,18 @@ camdd_probe_pass(struct cam_device *cam_dev, struct camdd_io_opts *io_opts,
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
maxsector = scsi_4btoul(rcap.addr);
|
||||
block_len = scsi_4btoul(rcap.length);
|
||||
*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 all set.
|
||||
*/
|
||||
if (maxsector != 0xffffffff)
|
||||
goto rcap_done;
|
||||
if (*maxsector != 0xffffffff) {
|
||||
retval = 0;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
scsi_read_capacity_16(&ccb->csio,
|
||||
/*retries*/ probe_retry_count,
|
||||
@ -1372,10 +1382,83 @@ camdd_probe_pass(struct cam_device *cam_dev, struct camdd_io_opts *io_opts,
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
maxsector = scsi_8btou64(rcaplong.addr);
|
||||
block_len = scsi_4btoul(rcaplong.length);
|
||||
*maxsector = scsi_8btou64(rcaplong.addr);
|
||||
*block_len = scsi_4btoul(rcaplong.length);
|
||||
|
||||
retval = 0;
|
||||
|
||||
bailout:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Need to implement this. Do a basic probe:
|
||||
* - Check the inquiry data, make sure we're talking to a device that we
|
||||
* can reasonably expect to talk to -- direct, RBC, CD, WORM.
|
||||
* - Send a test unit ready, make sure the device is available.
|
||||
* - Get the capacity and block size.
|
||||
*/
|
||||
struct camdd_dev *
|
||||
camdd_probe_pass(struct cam_device *cam_dev, struct camdd_io_opts *io_opts,
|
||||
camdd_argmask arglist, int probe_retry_count,
|
||||
int probe_timeout, int io_retry_count, int io_timeout)
|
||||
{
|
||||
union ccb *ccb;
|
||||
uint64_t maxsector = 0;
|
||||
uint32_t cpi_maxio, max_iosize, pass_numblocks;
|
||||
uint32_t block_len = 0;
|
||||
struct camdd_dev *dev = NULL;
|
||||
struct camdd_dev_pass *pass_dev;
|
||||
struct kevent ke;
|
||||
struct ccb_getdev cgd;
|
||||
int retval;
|
||||
int scsi_dev_type;
|
||||
|
||||
if ((retval = camdd_get_cgd(cam_dev, &cgd)) != 0) {
|
||||
warnx("%s: error retrieving CGD", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ccb = cam_getccb(cam_dev);
|
||||
|
||||
if (ccb == NULL) {
|
||||
warnx("%s: error allocating ccb", __func__);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
switch (cgd.protocol) {
|
||||
case PROTO_SCSI:
|
||||
scsi_dev_type = SID_TYPE(&cam_dev->inq_data);
|
||||
|
||||
/*
|
||||
* For devices that support READ CAPACITY, we'll attempt to get the
|
||||
* capacity. Otherwise, we really don't support tape or other
|
||||
* devices via SCSI passthrough, so just return an error in that case.
|
||||
*/
|
||||
switch (scsi_dev_type) {
|
||||
case T_DIRECT:
|
||||
case T_WORM:
|
||||
case T_CDROM:
|
||||
case T_OPTICAL:
|
||||
case T_RBC:
|
||||
case T_ZBC_HM:
|
||||
break;
|
||||
default:
|
||||
errx(1, "Unsupported SCSI device type %d", scsi_dev_type);
|
||||
break; /*NOTREACHED*/
|
||||
}
|
||||
|
||||
if ((retval = camdd_probe_pass_scsi(cam_dev, ccb, probe_retry_count,
|
||||
arglist, probe_timeout, &maxsector,
|
||||
&block_len))) {
|
||||
goto bailout;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
errx(1, "Unsupported PROTO type %d", cgd.protocol);
|
||||
break; /*NOTREACHED*/
|
||||
}
|
||||
|
||||
rcap_done:
|
||||
if (block_len == 0) {
|
||||
warnx("Sector size for %s%u is 0, cannot continue",
|
||||
cam_dev->device_name, cam_dev->dev_unit_num);
|
||||
@ -1405,6 +1488,7 @@ camdd_probe_pass(struct cam_device *cam_dev, struct camdd_io_opts *io_opts,
|
||||
|
||||
pass_dev = &dev->dev_spec.pass;
|
||||
pass_dev->scsi_dev_type = scsi_dev_type;
|
||||
pass_dev->protocol = cgd.protocol;
|
||||
pass_dev->dev = cam_dev;
|
||||
pass_dev->max_sector = maxsector;
|
||||
pass_dev->block_len = block_len;
|
||||
@ -1715,43 +1799,50 @@ camdd_worker(void *arg)
|
||||
* Simplistic translation of CCB status to our local status.
|
||||
*/
|
||||
camdd_buf_status
|
||||
camdd_ccb_status(union ccb *ccb)
|
||||
camdd_ccb_status(union ccb *ccb, int protocol)
|
||||
{
|
||||
camdd_buf_status status = CAMDD_STATUS_NONE;
|
||||
cam_status ccb_status;
|
||||
|
||||
ccb_status = ccb->ccb_h.status & CAM_STATUS_MASK;
|
||||
|
||||
switch (ccb_status) {
|
||||
case CAM_REQ_CMP: {
|
||||
if (ccb->csio.resid == 0) {
|
||||
status = CAMDD_STATUS_OK;
|
||||
} else if (ccb->csio.dxfer_len > ccb->csio.resid) {
|
||||
status = CAMDD_STATUS_SHORT_IO;
|
||||
} else {
|
||||
status = CAMDD_STATUS_EOF;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CAM_SCSI_STATUS_ERROR: {
|
||||
switch (ccb->csio.scsi_status) {
|
||||
case SCSI_STATUS_OK:
|
||||
case SCSI_STATUS_COND_MET:
|
||||
case SCSI_STATUS_INTERMED:
|
||||
case SCSI_STATUS_INTERMED_COND_MET:
|
||||
status = CAMDD_STATUS_OK;
|
||||
switch (protocol) {
|
||||
case PROTO_SCSI:
|
||||
switch (ccb_status) {
|
||||
case CAM_REQ_CMP: {
|
||||
if (ccb->csio.resid == 0) {
|
||||
status = CAMDD_STATUS_OK;
|
||||
} else if (ccb->csio.dxfer_len > ccb->csio.resid) {
|
||||
status = CAMDD_STATUS_SHORT_IO;
|
||||
} else {
|
||||
status = CAMDD_STATUS_EOF;
|
||||
}
|
||||
break;
|
||||
case SCSI_STATUS_CMD_TERMINATED:
|
||||
case SCSI_STATUS_CHECK_COND:
|
||||
case SCSI_STATUS_QUEUE_FULL:
|
||||
case SCSI_STATUS_BUSY:
|
||||
case SCSI_STATUS_RESERV_CONFLICT:
|
||||
}
|
||||
case CAM_SCSI_STATUS_ERROR: {
|
||||
switch (ccb->csio.scsi_status) {
|
||||
case SCSI_STATUS_OK:
|
||||
case SCSI_STATUS_COND_MET:
|
||||
case SCSI_STATUS_INTERMED:
|
||||
case SCSI_STATUS_INTERMED_COND_MET:
|
||||
status = CAMDD_STATUS_OK;
|
||||
break;
|
||||
case SCSI_STATUS_CMD_TERMINATED:
|
||||
case SCSI_STATUS_CHECK_COND:
|
||||
case SCSI_STATUS_QUEUE_FULL:
|
||||
case SCSI_STATUS_BUSY:
|
||||
case SCSI_STATUS_RESERV_CONFLICT:
|
||||
default:
|
||||
status = CAMDD_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
status = CAMDD_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
status = CAMDD_STATUS_ERROR;
|
||||
break;
|
||||
@ -2149,11 +2240,18 @@ camdd_pass_fetch(struct camdd_dev *dev)
|
||||
CAM_EPF_ALL, stderr);
|
||||
}
|
||||
|
||||
data->resid = ccb.csio.resid;
|
||||
dev->bytes_transferred += (ccb.csio.dxfer_len - ccb.csio.resid);
|
||||
switch (pass_dev->protocol) {
|
||||
case PROTO_SCSI:
|
||||
data->resid = ccb.csio.resid;
|
||||
dev->bytes_transferred += (ccb.csio.dxfer_len - ccb.csio.resid);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (buf->status == CAMDD_STATUS_NONE)
|
||||
buf->status = camdd_ccb_status(&ccb);
|
||||
buf->status = camdd_ccb_status(&ccb, pass_dev->protocol);
|
||||
if (buf->status == CAMDD_STATUS_ERROR)
|
||||
error_count++;
|
||||
else if (buf->status == CAMDD_STATUS_EOF) {
|
||||
@ -2433,9 +2531,6 @@ camdd_pass_run(struct camdd_dev *dev)
|
||||
|
||||
data = &buf->buf_type_spec.data;
|
||||
|
||||
ccb = &data->ccb;
|
||||
CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
|
||||
|
||||
/*
|
||||
* In almost every case the number of blocks should be the device
|
||||
* block size. The exception may be at the end of an I/O stream
|
||||
@ -2446,21 +2541,36 @@ camdd_pass_run(struct camdd_dev *dev)
|
||||
else
|
||||
num_blocks = data->fill_len / pass_dev->block_len;
|
||||
|
||||
scsi_read_write(&ccb->csio,
|
||||
/*retries*/ dev->retry_count,
|
||||
/*cbfcnp*/ NULL,
|
||||
/*tag_action*/ MSG_SIMPLE_Q_TAG,
|
||||
/*readop*/ (dev->write_dev == 0) ? SCSI_RW_READ :
|
||||
SCSI_RW_WRITE,
|
||||
/*byte2*/ 0,
|
||||
/*minimum_cmd_size*/ dev->min_cmd_size,
|
||||
/*lba*/ buf->lba,
|
||||
/*block_count*/ num_blocks,
|
||||
/*data_ptr*/ (data->sg_count != 0) ?
|
||||
(uint8_t *)data->segs : data->buf,
|
||||
/*dxfer_len*/ (num_blocks * pass_dev->block_len),
|
||||
/*sense_len*/ SSD_FULL_SIZE,
|
||||
/*timeout*/ dev->io_timeout);
|
||||
ccb = &data->ccb;
|
||||
|
||||
switch (pass_dev->protocol) {
|
||||
case PROTO_SCSI:
|
||||
CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
|
||||
|
||||
scsi_read_write(&ccb->csio,
|
||||
/*retries*/ dev->retry_count,
|
||||
/*cbfcnp*/ NULL,
|
||||
/*tag_action*/ MSG_SIMPLE_Q_TAG,
|
||||
/*readop*/ (dev->write_dev == 0) ? SCSI_RW_READ :
|
||||
SCSI_RW_WRITE,
|
||||
/*byte2*/ 0,
|
||||
/*minimum_cmd_size*/ dev->min_cmd_size,
|
||||
/*lba*/ buf->lba,
|
||||
/*block_count*/ num_blocks,
|
||||
/*data_ptr*/ (data->sg_count != 0) ?
|
||||
(uint8_t *)data->segs : data->buf,
|
||||
/*dxfer_len*/ (num_blocks * pass_dev->block_len),
|
||||
/*sense_len*/ SSD_FULL_SIZE,
|
||||
/*timeout*/ dev->io_timeout);
|
||||
|
||||
if (data->sg_count != 0) {
|
||||
ccb->csio.sglist_cnt = data->sg_count;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
retval = -1;
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
/* Disable freezing the device queue */
|
||||
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
|
||||
@ -2469,7 +2579,6 @@ camdd_pass_run(struct camdd_dev *dev)
|
||||
ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
|
||||
|
||||
if (data->sg_count != 0) {
|
||||
ccb->csio.sglist_cnt = data->sg_count;
|
||||
ccb->ccb_h.flags |= CAM_DATA_SG;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user