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.
This commit is contained in:
Alexander Motin 2011-12-23 19:12:02 +00:00
parent d0c7b0751a
commit f67daabb25
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=228846
2 changed files with 125 additions and 10 deletions

View File

@ -1349,6 +1349,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

View File

@ -82,7 +82,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 {
@ -1533,6 +1535,14 @@ daregister(struct cam_periph *periph, void *arg)
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.
*/
@ -1922,10 +1932,14 @@ dadone(struct cam_periph *periph, union ccb *done_ccb)
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
@ -1945,6 +1959,8 @@ dadone(struct cam_periph *periph, union ccb *done_ccb)
} else {
block_size = scsi_4btoul(rcaplong->length);
maxsector = scsi_8btou64(rcaplong->addr);
lbppbe = rcaplong->prot_lbppbe & SRC16_LBPPBE;
lalba = scsi_2btoul(rcaplong->lalba_lbp);
}
/*
@ -1962,7 +1978,12 @@ dadone(struct cam_periph *periph, union ccb *done_ccb)
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 "
@ -2028,6 +2049,24 @@ dadone(struct cam_periph *periph, union ccb *done_ccb)
&error_code, &sense_key, &asc,
&ascq, /*show_errors*/ 1);
}
/*
* 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,
@ -2198,13 +2237,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;
@ -2212,11 +2256,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,
@ -2242,13 +2338,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,
@ -2274,8 +2369,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:
@ -2286,8 +2384,14 @@ dagetcapacity(struct cam_periph *periph)
"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);