diff options
Diffstat (limited to 'sys/cam/scsi/scsi_da.c')
-rw-r--r-- | sys/cam/scsi/scsi_da.c | 125 |
1 files changed, 93 insertions, 32 deletions
diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c index 756093cbcf8d..b3bf5ffc35c4 100644 --- a/sys/cam/scsi/scsi_da.c +++ b/sys/cam/scsi/scsi_da.c @@ -160,6 +160,7 @@ struct da_softc { struct callout sendordered_c; uint64_t wwpn; uint8_t unmap_buf[UNMAP_MAX_RANGES * 16 + 8]; + struct scsi_read_capacity_data_long rcaplong; }; struct da_quirk_entry { @@ -830,7 +831,9 @@ static int daerror(union ccb *ccb, u_int32_t cam_flags, static void daprevent(struct cam_periph *periph, int action); static int dagetcapacity(struct cam_periph *periph); static void dasetgeom(struct cam_periph *periph, uint32_t block_len, - uint64_t maxsector, u_int lbppbe, u_int lalba); + uint64_t maxsector, + struct scsi_read_capacity_data_long *rcaplong, + size_t rcap_size); static timeout_t dasendorderedtag; static void dashutdown(void *arg, int howto); @@ -1948,7 +1951,8 @@ out: /*lba*/ 0, /*reladr*/ 0, /*pmi*/ 0, - rcaplong, + /*rcap_buf*/ (uint8_t *)rcaplong, + /*rcap_buf_len*/ sizeof(*rcaplong), /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ 60000); start_ccb->ccb_h.ccb_bp = NULL; @@ -2227,10 +2231,15 @@ dadone(struct cam_periph *periph, union ccb *done_ccb) announce_buf[0] = '\0'; cam_periph_invalidate(periph); } else { + /* + * We pass rcaplong into dasetgeom(), + * because it will only use it if it is + * non-NULL. + */ dasetgeom(periph, block_size, maxsector, - lbppbe, lalba & SRC16_LALBA); - if ((lalba & SRC16_LBPME) && - softc->delete_method == DA_DELETE_NONE) + rcaplong, sizeof(*rcaplong)); + if ((lalba & SRC16_LBPME_A) + && softc->delete_method == DA_DELETE_NONE) softc->delete_method = DA_DELETE_UNMAP; dp = &softc->params; snprintf(announce_buf, sizeof(announce_buf), @@ -2504,6 +2513,7 @@ dagetcapacity(struct cam_periph *periph) lalba = 0; error = 0; rc16failed = 0; + rcaplong = NULL; sense_flags = SF_RETRY_UA; if (softc->flags & DA_FLAG_PACK_REMOVABLE) sense_flags |= SF_NO_PRINT; @@ -2521,39 +2531,47 @@ dagetcapacity(struct cam_periph *periph) /* Try READ CAPACITY(16) first if we think it should work. */ if (softc->flags & DA_FLAG_CAN_RC16) { scsi_read_capacity_16(&ccb->csio, - /*retries*/ 4, - /*cbfcnp*/ dadone, - /*tag_action*/ MSG_SIMPLE_Q_TAG, - /*lba*/ 0, - /*reladr*/ 0, - /*pmi*/ 0, - rcaplong, - /*sense_len*/ SSD_FULL_SIZE, - /*timeout*/ 60000); + /*retries*/ 4, + /*cbfcnp*/ dadone, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*lba*/ 0, + /*reladr*/ 0, + /*pmi*/ 0, + /*rcap_buf*/ (uint8_t *)rcaplong, + /*rcap_buf_len*/ sizeof(*rcaplong), + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ 60000); ccb->ccb_h.ccb_bp = NULL; error = cam_periph_runccb(ccb, daerror, - /*cam_flags*/CAM_RETRY_SELTO, - sense_flags, - softc->disk->d_devstat); + /*cam_flags*/CAM_RETRY_SELTO, + sense_flags, softc->disk->d_devstat); if (error == 0) goto rc16ok; /* If we got ILLEGAL REQUEST, do not prefer RC16 any more. */ - if ((ccb->ccb_h.status & CAM_STATUS_MASK) == - CAM_REQ_INVALID) { + if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INVALID) { softc->flags &= ~DA_FLAG_CAN_RC16; } else if (((ccb->ccb_h.status & CAM_STATUS_MASK) == - CAM_SCSI_STATUS_ERROR) && - (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND) && - (ccb->ccb_h.status & CAM_AUTOSNS_VALID) && - ((ccb->ccb_h.flags & CAM_SENSE_PHYS) == 0) && - ((ccb->ccb_h.flags & CAM_SENSE_PTR) == 0)) { + CAM_SCSI_STATUS_ERROR) + && (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND) + && (ccb->ccb_h.status & CAM_AUTOSNS_VALID) + && ((ccb->ccb_h.flags & CAM_SENSE_PHYS) == 0) + && ((ccb->ccb_h.flags & CAM_SENSE_PTR) == 0)) { int sense_key, error_code, asc, ascq; - scsi_extract_sense(&ccb->csio.sense_data, - &error_code, &sense_key, &asc, &ascq); - if (sense_key == SSD_KEY_ILLEGAL_REQUEST) + scsi_extract_sense_len(&ccb->csio.sense_data, + ccb->csio.sense_len - + ccb->csio.sense_resid, + &error_code, &sense_key, + &asc, &ascq, /*show_errors*/1); + /* + * If we don't have enough sense to get the sense + * key, or if it's illegal request, turn off + * READ CAPACITY (16). + */ + if ((sense_key == -1) + || (sense_key == SSD_KEY_ILLEGAL_REQUEST)) softc->flags &= ~DA_FLAG_CAN_RC16; } rc16failed = 1; @@ -2590,7 +2608,8 @@ dagetcapacity(struct cam_periph *periph) /*lba*/ 0, /*reladr*/ 0, /*pmi*/ 0, - rcaplong, + /*rcap_buf*/ (uint8_t *)rcaplong, + /*rcap_buf_len*/ sizeof(*rcaplong), /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ 60000); ccb->ccb_h.ccb_bp = NULL; @@ -2617,9 +2636,9 @@ done: error = EINVAL; } else { dasetgeom(periph, block_len, maxsector, - lbppbe, lalba & SRC16_LALBA); - if ((lalba & SRC16_LBPME) && - softc->delete_method == DA_DELETE_NONE) + rcaplong, sizeof(*rcaplong)); + if ((lalba & SRC16_LBPME) + && softc->delete_method == DA_DELETE_NONE) softc->delete_method = DA_DELETE_UNMAP; } } @@ -2633,17 +2652,27 @@ done: static void dasetgeom(struct cam_periph *periph, uint32_t block_len, uint64_t maxsector, - u_int lbppbe, u_int lalba) + struct scsi_read_capacity_data_long *rcaplong, size_t rcap_len) { struct ccb_calc_geometry ccg; struct da_softc *softc; struct disk_params *dp; + u_int lbppbe, lalba; softc = (struct da_softc *)periph->softc; dp = &softc->params; dp->secsize = block_len; dp->sectors = maxsector + 1; + if (rcaplong != NULL) { + lbppbe = rcaplong->prot_lbppbe & SRC16_LBPPBE; + lalba = scsi_2btoul(rcaplong->lalba_lbp); + lalba &= SRC16_LALBA_A; + } else { + lbppbe = 0; + lalba = 0; + } + if (lbppbe > 0) { dp->stripesize = block_len << lbppbe; dp->stripeoffset = (dp->stripesize - block_len * lalba) % @@ -2688,6 +2717,38 @@ dasetgeom(struct cam_periph *periph, uint32_t block_len, uint64_t maxsector, dp->secs_per_track = ccg.secs_per_track; dp->cylinders = ccg.cylinders; } + + /* + * If the user supplied a read capacity buffer, and if it is + * different than the previous buffer, update the data in the EDT. + * If it's the same, we don't bother. This avoids sending an + * update every time someone opens this device. + */ + if ((rcaplong != NULL) + && (bcmp(rcaplong, &softc->rcaplong, + min(sizeof(softc->rcaplong), rcap_len)) != 0)) { + struct ccb_dev_advinfo cdai; + + xpt_setup_ccb(&cdai.ccb_h, periph->path, CAM_PRIORITY_NORMAL); + cdai.ccb_h.func_code = XPT_DEV_ADVINFO; + cdai.buftype = CDAI_TYPE_RCAPLONG; + cdai.flags |= CDAI_FLAG_STORE; + cdai.bufsiz = rcap_len; + cdai.buf = (uint8_t *)rcaplong; + xpt_action((union ccb *)&cdai); + if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) + cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); + if (cdai.ccb_h.status != CAM_REQ_CMP) { + xpt_print(periph->path, "%s: failed to set read " + "capacity advinfo\n", __func__); + /* Use cam_error_print() to decode the status */ + cam_error_print((union ccb *)&cdai, CAM_ESF_CAM_STATUS, + CAM_EPF_ALL); + } else { + bcopy(rcaplong, &softc->rcaplong, + min(sizeof(softc->rcaplong), rcap_len)); + } + } } static void |