Date: Tue, 13 Mar 2012 20:34:34 +0000 (UTC) From: Alexander Motin <mav@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org Subject: svn commit: r232942 - stable/8/sys/cam/scsi Message-ID: <201203132034.q2DKYYt3061738@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: mav Date: Tue Mar 13 20:34:33 2012 New Revision: 232942 URL: http://svn.freebsd.org/changeset/base/232942 Log: MFc r228846: Use READ CAPACITY(16) to get information about device physical sectors. As soon as not all devices support READ CAPACITY(16), automatically fall back to READ CAPACITY(10) if CAM_REQ_INVALID or SSD_KEY_ILLEGAL_REQUEST status returned. It also provides first bits of information about Logical Block Provisioning (aka UNMAP/TRIM) support by the device. Modified: stable/8/sys/cam/scsi/scsi_all.h stable/8/sys/cam/scsi/scsi_da.c Directory Properties: stable/8/sys/ (props changed) Modified: stable/8/sys/cam/scsi/scsi_all.h ============================================================================== --- stable/8/sys/cam/scsi/scsi_all.h Tue Mar 13 20:33:15 2012 (r232941) +++ stable/8/sys/cam/scsi/scsi_all.h Tue Mar 13 20:34:33 2012 (r232942) @@ -847,6 +847,17 @@ struct scsi_read_capacity_data_long { uint8_t addr[8]; uint8_t length[4]; +#define SRC16_PROT_EN 0x01 +#define SRC16_P_TYPE 0x0e + uint8_t prot; +#define SRC16_LBPPBE 0x0f +#define SRC16_PI_EXPONENT 0xf0 +#define SRC16_PI_EXPONENT_SHIFT 4 + uint8_t prot_lbppbe; +#define SRC16_LALBA 0x3fff +#define SRC16_LBPRZ 0x4000 +#define SRC16_LBPME 0x8000 + uint8_t lalba_lbp[2]; }; struct scsi_report_luns Modified: stable/8/sys/cam/scsi/scsi_da.c ============================================================================== --- stable/8/sys/cam/scsi/scsi_da.c Tue Mar 13 20:33:15 2012 (r232941) +++ stable/8/sys/cam/scsi/scsi_da.c Tue Mar 13 20:34:33 2012 (r232942) @@ -81,7 +81,9 @@ typedef enum { DA_FLAG_WENT_IDLE = 0x040, DA_FLAG_RETRY_UA = 0x080, DA_FLAG_OPEN = 0x100, - DA_FLAG_SCTX_INIT = 0x200 + DA_FLAG_SCTX_INIT = 0x200, + DA_FLAG_CAN_RC16 = 0x400, + DA_FLAG_CAN_LBPME = 0x800 } da_flags; typedef enum { @@ -1484,6 +1486,14 @@ daregister(struct cam_periph *periph, vo else if (softc->minimum_cmd_size > 12) softc->minimum_cmd_size = 16; + /* Predict whether device may support READ CAPACITY(16). */ + if (SID_ANSI_REV(&cgd->inq_data) >= SCSI_REV_SPC3 || + (SID_ANSI_REV(&cgd->inq_data) >= SCSI_REV_SPC && + (cgd->inq_data.spc3_flags & SPC3_SID_PROTECT))) { + softc->flags |= DA_FLAG_CAN_RC16; + softc->state = DA_STATE_PROBE2; + } + /* * Register this media as a disk. */ @@ -1842,10 +1852,14 @@ dadone(struct cam_periph *periph, union struct disk_params *dp; uint32_t block_size; uint64_t maxsector; + u_int lbppbe; /* LB per physical block exponent. */ + u_int lalba; /* Lowest aligned LBA. */ if (softc->state == DA_STATE_PROBE) { block_size = scsi_4btoul(rdcap->length); maxsector = scsi_4btoul(rdcap->addr); + lbppbe = 0; + lalba = 0; /* * According to SBC-2, if the standard 10 @@ -1865,6 +1879,8 @@ dadone(struct cam_periph *periph, union } else { block_size = scsi_4btoul(rcaplong->length); maxsector = scsi_8btou64(rcaplong->addr); + lbppbe = rcaplong->prot_lbppbe & SRC16_LBPPBE; + lalba = scsi_2btoul(rcaplong->lalba_lbp); } /* @@ -1882,7 +1898,12 @@ dadone(struct cam_periph *periph, union announce_buf[0] = '\0'; cam_periph_invalidate(periph); } else { - dasetgeom(periph, block_size, maxsector, 0, 0); + dasetgeom(periph, block_size, maxsector, + lbppbe, lalba & SRC16_LALBA); + if (lalba & SRC16_LBPME) + softc->flags |= DA_FLAG_CAN_LBPME; + else + softc->flags &= ~DA_FLAG_CAN_LBPME; dp = &softc->params; snprintf(announce_buf, sizeof(announce_buf), "%juMB (%ju %u byte sectors: %dH %dS/T " @@ -1948,6 +1969,24 @@ dadone(struct cam_periph *periph, union &asc, &ascq); } /* + * If we tried READ CAPACITY(16) and failed, + * fallback to READ CAPACITY(10). + */ + if ((softc->state == DA_STATE_PROBE2) && + (softc->flags & DA_FLAG_CAN_RC16) && + (((csio->ccb_h.status & CAM_STATUS_MASK) == + CAM_REQ_INVALID) || + ((have_sense) && + (error_code == SSD_CURRENT_ERROR) && + (sense_key == SSD_KEY_ILLEGAL_REQUEST)))) { + softc->flags &= ~DA_FLAG_CAN_RC16; + softc->state = DA_STATE_PROBE; + free(rdcap, M_SCSIDA); + xpt_release_ccb(done_ccb); + xpt_schedule(periph, priority); + return; + } else + /* * Attach to anything that claims to be a * direct access or optical disk device, * as long as it doesn't return a "Logical @@ -2124,13 +2163,18 @@ dagetcapacity(struct cam_periph *periph) struct scsi_read_capacity_data_long *rcaplong; uint32_t block_len; uint64_t maxsector; - int error; + int error, rc16failed; u_int32_t sense_flags; + u_int lbppbe; /* Logical blocks per physical block exponent. */ + u_int lalba; /* Lowest aligned LBA. */ softc = (struct da_softc *)periph->softc; block_len = 0; maxsector = 0; + lbppbe = 0; + lalba = 0; error = 0; + rc16failed = 0; sense_flags = SF_RETRY_UA; if (softc->flags & DA_FLAG_PACK_REMOVABLE) sense_flags |= SF_NO_PRINT; @@ -2138,11 +2182,63 @@ dagetcapacity(struct cam_periph *periph) /* Do a read capacity */ rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcaplong), M_SCSIDA, - M_NOWAIT); + M_NOWAIT | M_ZERO); if (rcap == NULL) return (ENOMEM); - + rcaplong = (struct scsi_read_capacity_data_long *)rcap; + ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); + + /* 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); + ccb->ccb_h.ccb_bp = NULL; + + error = cam_periph_runccb(ccb, daerror, + /*cam_flags*/CAM_RETRY_SELTO, + sense_flags, + softc->disk->d_devstat); + + if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) + cam_release_devq(ccb->ccb_h.path, + /*relsim_flags*/0, + /*reduction*/0, + /*timeout*/0, + /*getcount_only*/0); + + 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) { + 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)) { + 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) + softc->flags &= ~DA_FLAG_CAN_RC16; + } + rc16failed = 1; + } + + /* Do READ CAPACITY(10). */ scsi_read_capacity(&ccb->csio, /*retries*/4, /*cbfncp*/dadone, @@ -2168,13 +2264,12 @@ dagetcapacity(struct cam_periph *periph) block_len = scsi_4btoul(rcap->length); maxsector = scsi_4btoul(rcap->addr); - if (maxsector != 0xffffffff) + if (maxsector != 0xffffffff || rc16failed) goto done; } else goto done; - rcaplong = (struct scsi_read_capacity_data_long *)rcap; - + /* If READ CAPACITY(10) returned overflow, use READ CAPACITY(16) */ scsi_read_capacity_16(&ccb->csio, /*retries*/ 4, /*cbfcnp*/ dadone, @@ -2200,8 +2295,11 @@ dagetcapacity(struct cam_periph *periph) /*getcount_only*/0); if (error == 0) { +rc16ok: block_len = scsi_4btoul(rcaplong->length); maxsector = scsi_8btou64(rcaplong->addr); + lbppbe = rcaplong->prot_lbppbe & SRC16_LBPPBE; + lalba = scsi_2btoul(rcaplong->lalba_lbp); } done: @@ -2212,8 +2310,14 @@ done: "unsupportable block size %ju\n", (uintmax_t) block_len); error = EINVAL; - } else - dasetgeom(periph, block_len, maxsector, 0, 0); + } else { + dasetgeom(periph, block_len, maxsector, + lbppbe, lalba & SRC16_LALBA); + if (lalba & SRC16_LBPME) + softc->flags |= DA_FLAG_CAN_LBPME; + else + softc->flags &= ~DA_FLAG_CAN_LBPME; + } } xpt_release_ccb(ccb);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201203132034.q2DKYYt3061738>