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:
Kenneth D. Merry 2012-01-26 18:09:28 +00:00
parent ab6fe159c8
commit e6bd5983ca
10 changed files with 164 additions and 46 deletions

View File

@ -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);

View File

@ -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 */

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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 *,

View File

@ -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

View File

@ -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;
}

View File

@ -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,

View File

@ -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;