Add CAM infrastructure to allow reporting when a drive's long read capacity
data changes. cam_ccb.h: Add a new advanced information type, CDAI_TYPE_RCAPLONG, for long read capacity data. cam_xpt_internal.h: Add a read capacity data pointer and length to struct cam_ed. cam_xpt.c: Free the read capacity buffer when a device goes away. While we're here, make sure we don't leak memory for other malloced fields in struct cam_ed. scsi_all.c: Update the scsi_read_capacity_16() to take a uint8_t * and a length instead of just a pointer to the parameter data structure. This will hopefully make this function somewhat immune to future changes in the parameter data. scsi_all.h: Add some extra bit definitions to struct scsi_read_capacity_data_long, and bump up the structure size to the full size specified by SBC-3. Change the prototype for scsi_read_capacity_16(). scsi_da.c: Register changes in read capacity data with the transport layer. This allows the transport layer to send out an async notification to interested parties. Update the dasetgeom() API. Use scsi_extract_sense_len() instead of scsi_extract_sense(). scsi_xpt.c: Add support for the new CDAI_TYPE_RCAPLONG advanced information type. Make sure we set the physpath pointer to NULL after freeing it. This allows blindly freeing it in the struct cam_ed destructor. sys/param.h: Bump __FreeBSD_version from 1000005 to 1000006 to make it easier for third party drivers to determine that the read capacity data async notification is available. camcontrol.c, mptutil/mpt_cam.c: Update these for the new scsi_read_capacity_16() argument structure. Sponsored by: Spectra Logic
This commit is contained in:
parent
ab6fe159c8
commit
e6bd5983ca
@ -4232,7 +4232,8 @@ scsireadcapacity(struct cam_device *device, int argc, char **argv,
|
||||
/*lba*/ 0,
|
||||
/*reladdr*/ 0,
|
||||
/*pmi*/ 0,
|
||||
&rcaplong,
|
||||
/*rcap_buf*/ (uint8_t *)&rcaplong,
|
||||
/*rcap_buf_len*/ sizeof(rcaplong),
|
||||
/*sense_len*/ SSD_FULL_SIZE,
|
||||
/*timeout*/ timeout ? timeout : 5000);
|
||||
|
||||
|
@ -1118,6 +1118,7 @@ struct ccb_dev_advinfo {
|
||||
#define CDAI_TYPE_SCSI_DEVID 1
|
||||
#define CDAI_TYPE_SERIAL_NUM 2
|
||||
#define CDAI_TYPE_PHYS_PATH 3
|
||||
#define CDAI_TYPE_RCAPLONG 4
|
||||
off_t bufsiz; /* IN: Size of external buffer */
|
||||
#define CAM_SCSI_DEVID_MAXLEN 65536 /* length in buffer is an uint16_t */
|
||||
off_t provsiz; /* OUT: Size required/used */
|
||||
|
@ -4588,6 +4588,17 @@ xpt_release_device(struct cam_ed *device)
|
||||
cam_devq_resize(devq, devq->alloc_queue.array_size - 1);
|
||||
camq_fini(&device->drvq);
|
||||
cam_ccbq_fini(&device->ccbq);
|
||||
/*
|
||||
* Free allocated memory. free(9) does nothing if the
|
||||
* supplied pointer is NULL, so it is safe to call without
|
||||
* checking.
|
||||
*/
|
||||
free(device->supported_vpds, M_CAMXPT);
|
||||
free(device->device_id, M_CAMXPT);
|
||||
free(device->physpath, M_CAMXPT);
|
||||
free(device->rcap_buf, M_CAMXPT);
|
||||
free(device->serial_num, M_CAMXPT);
|
||||
|
||||
xpt_release_target(device->target);
|
||||
free(device, M_CAMXPT);
|
||||
} else
|
||||
|
@ -99,6 +99,8 @@ struct cam_ed {
|
||||
uint8_t *device_id;
|
||||
uint8_t physpath_len;
|
||||
uint8_t *physpath; /* physical path string form */
|
||||
uint32_t rcap_len;
|
||||
uint8_t *rcap_buf;
|
||||
struct ata_params ident_data;
|
||||
u_int8_t inq_flags; /*
|
||||
* Current settings for inquiry flags.
|
||||
|
@ -5325,8 +5325,8 @@ void
|
||||
scsi_read_capacity_16(struct ccb_scsiio *csio, uint32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *, union ccb *),
|
||||
uint8_t tag_action, uint64_t lba, int reladr, int pmi,
|
||||
struct scsi_read_capacity_data_long *rcap_buf,
|
||||
uint8_t sense_len, uint32_t timeout)
|
||||
uint8_t *rcap_buf, int rcap_buf_len, uint8_t sense_len,
|
||||
uint32_t timeout)
|
||||
{
|
||||
struct scsi_read_capacity_16 *scsi_cmd;
|
||||
|
||||
@ -5337,7 +5337,7 @@ scsi_read_capacity_16(struct ccb_scsiio *csio, uint32_t retries,
|
||||
/*flags*/CAM_DIR_IN,
|
||||
tag_action,
|
||||
/*data_ptr*/(u_int8_t *)rcap_buf,
|
||||
/*dxfer_len*/sizeof(*rcap_buf),
|
||||
/*dxfer_len*/rcap_buf_len,
|
||||
sense_len,
|
||||
sizeof(*scsi_cmd),
|
||||
timeout);
|
||||
@ -5346,7 +5346,7 @@ scsi_read_capacity_16(struct ccb_scsiio *csio, uint32_t retries,
|
||||
scsi_cmd->opcode = SERVICE_ACTION_IN;
|
||||
scsi_cmd->service_action = SRC16_SERVICE_ACTION;
|
||||
scsi_u64to8b(lba, scsi_cmd->addr);
|
||||
scsi_ulto4b(sizeof(*rcap_buf), scsi_cmd->alloc_len);
|
||||
scsi_ulto4b(rcap_buf_len, scsi_cmd->alloc_len);
|
||||
if (pmi)
|
||||
reladr |= SRC16_PMI;
|
||||
if (reladr)
|
||||
|
@ -1437,15 +1437,26 @@ struct scsi_read_capacity_data_long
|
||||
uint8_t length[4];
|
||||
#define SRC16_PROT_EN 0x01
|
||||
#define SRC16_P_TYPE 0x0e
|
||||
#define SRC16_PTYPE_1 0x00
|
||||
#define SRC16_PTYPE_2 0x02
|
||||
#define SRC16_PTYPE_3 0x04
|
||||
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
|
||||
#define SRC16_LALBA 0x3f
|
||||
#define SRC16_LBPRZ 0x40
|
||||
#define SRC16_LBPME 0x80
|
||||
/*
|
||||
* Alternate versions of these macros that are intended for use on a 16-bit
|
||||
* version of the lalba_lbp field instead of the array of 2 8 bit numbers.
|
||||
*/
|
||||
#define SRC16_LALBA_A 0x3fff
|
||||
#define SRC16_LBPRZ_A 0x4000
|
||||
#define SRC16_LBPME_A 0x8000
|
||||
uint8_t lalba_lbp[2];
|
||||
uint8_t reserved[16];
|
||||
};
|
||||
|
||||
struct scsi_report_luns
|
||||
@ -2293,9 +2304,8 @@ void scsi_read_capacity_16(struct ccb_scsiio *csio, uint32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *,
|
||||
union ccb *), uint8_t tag_action,
|
||||
uint64_t lba, int reladr, int pmi,
|
||||
struct scsi_read_capacity_data_long
|
||||
*rcap_buf, uint8_t sense_len,
|
||||
uint32_t timeout);
|
||||
uint8_t *rcap_buf, int rcap_buf_len,
|
||||
uint8_t sense_len, uint32_t timeout);
|
||||
|
||||
void scsi_report_luns(struct ccb_scsiio *csio, u_int32_t retries,
|
||||
void (*cbfcnp)(struct cam_periph *,
|
||||
|
@ -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 @@ dastart(struct cam_periph *periph, union ccb *start_ccb)
|
||||
/*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 @@ dagetcapacity(struct cam_periph *periph)
|
||||
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 @@ 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)
|
||||
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
|
||||
|
@ -2468,8 +2468,10 @@ scsi_dev_advinfo(union ccb *start_ccb)
|
||||
break;
|
||||
case CDAI_TYPE_PHYS_PATH:
|
||||
if (cdai->flags & CDAI_FLAG_STORE) {
|
||||
if (device->physpath != NULL)
|
||||
if (device->physpath != NULL) {
|
||||
free(device->physpath, M_CAMXPT);
|
||||
device->physpath = NULL;
|
||||
}
|
||||
device->physpath_len = cdai->bufsiz;
|
||||
/* Clear existing buffer if zero length */
|
||||
if (cdai->bufsiz == 0)
|
||||
@ -2490,6 +2492,36 @@ scsi_dev_advinfo(union ccb *start_ccb)
|
||||
memcpy(cdai->buf, device->physpath, amt);
|
||||
}
|
||||
break;
|
||||
case CDAI_TYPE_RCAPLONG:
|
||||
if (cdai->flags & CDAI_FLAG_STORE) {
|
||||
if (device->rcap_buf != NULL) {
|
||||
free(device->rcap_buf, M_CAMXPT);
|
||||
device->rcap_buf = NULL;
|
||||
}
|
||||
|
||||
device->rcap_len = cdai->bufsiz;
|
||||
/* Clear existing buffer if zero length */
|
||||
if (cdai->bufsiz == 0)
|
||||
break;
|
||||
|
||||
device->rcap_buf = malloc(cdai->bufsiz, M_CAMXPT,
|
||||
M_NOWAIT);
|
||||
if (device->rcap_buf == NULL) {
|
||||
start_ccb->ccb_h.status = CAM_REQ_ABORTED;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(device->rcap_buf, cdai->buf, cdai->bufsiz);
|
||||
} else {
|
||||
cdai->provsiz = device->rcap_len;
|
||||
if (device->rcap_len == 0)
|
||||
break;
|
||||
amt = device->rcap_len;
|
||||
if (cdai->provsiz > cdai->bufsiz)
|
||||
amt = cdai->bufsiz;
|
||||
memcpy(cdai->buf, device->rcap_buf, amt);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
@ -58,7 +58,7 @@
|
||||
* in the range 5 to 9.
|
||||
*/
|
||||
#undef __FreeBSD_version
|
||||
#define __FreeBSD_version 1000005 /* Master, propagated to newvers */
|
||||
#define __FreeBSD_version 1000006 /* Master, propagated to newvers */
|
||||
|
||||
/*
|
||||
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,
|
||||
|
@ -277,7 +277,7 @@ fetch_scsi_capacity(struct cam_device *dev, struct mpt_standalone_disk *disk)
|
||||
sizeof(struct ccb_hdr));
|
||||
|
||||
scsi_read_capacity_16(&ccb->csio, 1, NULL, MSG_SIMPLE_Q_TAG, 0, 0, 0,
|
||||
&rcaplong, SSD_FULL_SIZE, 5000);
|
||||
(uint8_t *)&rcaplong, sizeof(rcaplong), SSD_FULL_SIZE, 5000);
|
||||
|
||||
/* Disable freezing the device queue */
|
||||
ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
|
||||
|
Loading…
Reference in New Issue
Block a user