diff options
author | mjacob <mjacob@FreeBSD.org> | 2012-07-28 20:08:14 +0000 |
---|---|---|
committer | mjacob <mjacob@FreeBSD.org> | 2012-07-28 20:08:14 +0000 |
commit | 4253fe261fe70065de3c9a7c4bbd5b93a959bd8f (patch) | |
tree | 761d30576763a20a70fea2e5fb4df96d6e1221d9 | |
parent | a41cb450d69215d0da52701d70146c12971be38d (diff) | |
download | FreeBSD-src-4253fe261fe70065de3c9a7c4bbd5b93a959bd8f.zip FreeBSD-src-4253fe261fe70065de3c9a7c4bbd5b93a959bd8f.tar.gz |
Handle a case where we had an SRR that pushed back the
data pointer. This is a temp fix that resubmits the
command, adjusted, so that the backend can fetch the
data again.
Sponsored by: Spectralogic
MFC after: 1 month
-rw-r--r-- | sys/cam/ctl/scsi_ctl.c | 128 |
1 files changed, 124 insertions, 4 deletions
diff --git a/sys/cam/ctl/scsi_ctl.c b/sys/cam/ctl/scsi_ctl.c index ab51f01..a6f58dd 100644 --- a/sys/cam/ctl/scsi_ctl.c +++ b/sys/cam/ctl/scsi_ctl.c @@ -1081,11 +1081,81 @@ ctlfe_free_ccb(struct cam_periph *periph, union ccb *ccb) } } +static int +ctlfe_adjust_cdb(struct ccb_accept_tio *atio, uint32_t offset) +{ + uint64_t lba; + uint32_t num_blocks, nbc; + uint8_t *cmdbyt = (atio->ccb_h.flags & CAM_CDB_POINTER)? + atio->cdb_io.cdb_ptr : atio->cdb_io.cdb_bytes; + + nbc = offset >> 9; /* ASSUMING 512 BYTE BLOCKS */ + + switch (cmdbyt[0]) { + case READ_6: + case WRITE_6: + { + struct scsi_rw_6 *cdb = (struct scsi_rw_6 *)cmdbyt; + lba = scsi_3btoul(cdb->addr); + lba &= 0x1fffff; + num_blocks = cdb->length; + if (num_blocks == 0) + num_blocks = 256; + lba += nbc; + num_blocks -= nbc; + scsi_ulto3b(lba, cdb->addr); + cdb->length = num_blocks; + break; + } + case READ_10: + case WRITE_10: + { + struct scsi_rw_10 *cdb = (struct scsi_rw_10 *)cmdbyt; + lba = scsi_4btoul(cdb->addr); + num_blocks = scsi_2btoul(cdb->length); + lba += nbc; + num_blocks -= nbc; + scsi_ulto4b(lba, cdb->addr); + scsi_ulto2b(num_blocks, cdb->length); + break; + } + case READ_12: + case WRITE_12: + { + struct scsi_rw_12 *cdb = (struct scsi_rw_12 *)cmdbyt; + lba = scsi_4btoul(cdb->addr); + num_blocks = scsi_4btoul(cdb->length); + lba += nbc; + num_blocks -= nbc; + scsi_ulto4b(lba, cdb->addr); + scsi_ulto4b(num_blocks, cdb->length); + break; + } + case READ_16: + case WRITE_16: + { + struct scsi_rw_16 *cdb = (struct scsi_rw_16 *)cmdbyt; + lba = scsi_8btou64(cdb->addr); + num_blocks = scsi_4btoul(cdb->length); + lba += nbc; + num_blocks -= nbc; + scsi_u64to8b(lba, cdb->addr); + scsi_ulto4b(num_blocks, cdb->length); + break; + } + default: + return -1; + } + return (0); +} + static void ctlfedone(struct cam_periph *periph, union ccb *done_ccb) { struct ctlfe_lun_softc *softc; struct ctlfe_softc *bus_softc; + struct ccb_accept_tio *atio = NULL; + union ctl_io *io = NULL; #ifdef CTLFE_DEBUG printf("%s: entered, func_code = %#x, type = %#lx\n", __func__, @@ -1123,13 +1193,12 @@ ctlfedone(struct cam_periph *periph, union ccb *done_ccb) } switch (done_ccb->ccb_h.func_code) { case XPT_ACCEPT_TARGET_IO: { - union ctl_io *io; - struct ccb_accept_tio *atio; atio = &done_ccb->atio; softc->atios_returned++; + resubmit: /* * Allocate a ctl_io, pass it to CTL, and wait for the * datamove or done. @@ -1213,8 +1282,8 @@ ctlfedone(struct cam_periph *periph, union ccb *done_ccb) break; } case XPT_CONT_TARGET_IO: { - struct ccb_accept_tio *atio; - union ctl_io *io; + int srr = 0; + uint32_t srr_off = 0; atio = (struct ccb_accept_tio *)done_ccb->ccb_h.ccb_atio; io = (union ctl_io *)atio->ccb_h.io_ptr; @@ -1225,6 +1294,57 @@ ctlfedone(struct cam_periph *periph, union ccb *done_ccb) __func__, atio->tag_id, done_ccb->ccb_h.flags); #endif /* + * Handle SRR case were the data pointer is pushed back hack + */ + if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_MESSAGE_RECV + && done_ccb->csio.msg_ptr != NULL + && done_ccb->csio.msg_ptr[0] == MSG_EXTENDED + && done_ccb->csio.msg_ptr[1] == 5 + && done_ccb->csio.msg_ptr[2] == 0) { + srr = 1; + srr_off = + (done_ccb->csio.msg_ptr[3] << 24) + | (done_ccb->csio.msg_ptr[4] << 16) + | (done_ccb->csio.msg_ptr[5] << 8) + | (done_ccb->csio.msg_ptr[6]); + } + + if (srr && (done_ccb->ccb_h.flags & CAM_SEND_STATUS)) { + /* + * If status was being sent, the back end data is now + * history. Hack it up and resubmit a new command with + * the CDB adjusted. If the SIM does the right thing, + * all of the resid math should work. + */ + softc->ccbs_freed++; + xpt_release_ccb(done_ccb); + ctl_free_io(io); + if (ctlfe_adjust_cdb(atio, srr_off) == 0) { + done_ccb = (union ccb *)atio; + goto resubmit; + } + /* + * Fall through to doom.... + */ + } else if (srr) { + /* + * If we have an srr and we're still sending data, we + * should be able to adjust offsets and cycle again. + */ + io->scsiio.kern_rel_offset = + io->scsiio.ext_data_filled = srr_off; + io->scsiio.ext_data_len = io->scsiio.kern_total_len - + io->scsiio.kern_rel_offset; + softc->ccbs_freed++; + io->scsiio.io_hdr.status = CTL_STATUS_NONE; + xpt_release_ccb(done_ccb); + TAILQ_INSERT_HEAD(&softc->work_queue, &atio->ccb_h, + periph_links.tqe); + xpt_schedule(periph, /*priority*/ 1); + return; + } + + /* * If we were sending status back to the initiator, free up * resources. If we were doing a datamove, call the * datamove done routine. |