hw/scsi: cleanups before VPD BL emulation

To add support for the emulation of Block Limits VPD page
for passthrough devices, a few adjustments in the current code
base is required to avoid repetition and improve clarity.

In scsi-generic.c, detach the Inquiry handling from
scsi_read_complete and put it into a new function called
scsi_handle_inquiry_reply. This change aims to avoid
cluttering of scsi_read_complete when we more logic in the
Inquiry response handling is added in the next patches,
centralizing the changes in the new function.

In scsi-disk.c, take the build of all emulated VPD pages
from scsi_disk_emulate_inquiry and make it available to
other files into a non-static function called
scsi_disk_emulate_vpd_page. Making it public will allow
the future VPD BL emulation code for passthrough devices
to use it from scsi-generic.c, avoiding copy/pasting this
code solely for that purpose. It also has the advantage of
providing emulation of all VPD pages in case we need to
emulate other pages in other scenarios. As a bonus,
scsi_disk_emulate_inquiry got tidier.

Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Message-Id: <20180627172432.11120-2-danielhb413@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Daniel Henrique Barboza 2018-06-27 14:24:30 -03:00 committed by Paolo Bonzini
parent 2ad9b50f71
commit 0a96ca2437
3 changed files with 258 additions and 239 deletions

View file

@ -585,18 +585,14 @@ static uint8_t *scsi_get_buf(SCSIRequest *req)
return (uint8_t *)r->iov.iov_base; return (uint8_t *)r->iov.iov_base;
} }
static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf)
{ {
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
int buflen = 0;
int start;
if (req->cmd.buf[1] & 0x1) {
/* Vital product data */
uint8_t page_code = req->cmd.buf[2]; uint8_t page_code = req->cmd.buf[2];
int start, buflen = 0;
outbuf[buflen++] = s->qdev.type & 0x1f; outbuf[buflen++] = s->qdev.type & 0x1f;
outbuf[buflen++] = page_code ; // this page outbuf[buflen++] = page_code;
outbuf[buflen++] = 0x00; outbuf[buflen++] = 0x00;
outbuf[buflen++] = 0x00; outbuf[buflen++] = 0x00;
start = buflen; start = buflen;
@ -606,15 +602,15 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
{ {
DPRINTF("Inquiry EVPD[Supported pages] " DPRINTF("Inquiry EVPD[Supported pages] "
"buffer size %zd\n", req->cmd.xfer); "buffer size %zd\n", req->cmd.xfer);
outbuf[buflen++] = 0x00; // list of supported pages (this page) outbuf[buflen++] = 0x00; /* list of supported pages (this page) */
if (s->serial) { if (s->serial) {
outbuf[buflen++] = 0x80; // unit serial number outbuf[buflen++] = 0x80; /* unit serial number */
} }
outbuf[buflen++] = 0x83; // device identification outbuf[buflen++] = 0x83; /* device identification */
if (s->qdev.type == TYPE_DISK) { if (s->qdev.type == TYPE_DISK) {
outbuf[buflen++] = 0xb0; // block limits outbuf[buflen++] = 0xb0; /* block limits */
outbuf[buflen++] = 0xb1; /* block device characteristics */ outbuf[buflen++] = 0xb1; /* block device characteristics */
outbuf[buflen++] = 0xb2; // thin provisioning outbuf[buflen++] = 0xb2; /* thin provisioning */
} }
break; break;
} }
@ -651,35 +647,38 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
DPRINTF("Inquiry EVPD[Device identification] " DPRINTF("Inquiry EVPD[Device identification] "
"buffer size %zd\n", req->cmd.xfer); "buffer size %zd\n", req->cmd.xfer);
outbuf[buflen++] = 0x2; // ASCII outbuf[buflen++] = 0x2; /* ASCII */
outbuf[buflen++] = 0; // not officially assigned outbuf[buflen++] = 0; /* not officially assigned */
outbuf[buflen++] = 0; // reserved outbuf[buflen++] = 0; /* reserved */
outbuf[buflen++] = id_len; // length of data following outbuf[buflen++] = id_len; /* length of data following */
memcpy(outbuf + buflen, str, id_len); memcpy(outbuf + buflen, str, id_len);
buflen += id_len; buflen += id_len;
if (s->qdev.wwn) { if (s->qdev.wwn) {
outbuf[buflen++] = 0x1; // Binary outbuf[buflen++] = 0x1; /* Binary */
outbuf[buflen++] = 0x3; // NAA outbuf[buflen++] = 0x3; /* NAA */
outbuf[buflen++] = 0; // reserved outbuf[buflen++] = 0; /* reserved */
outbuf[buflen++] = 8; outbuf[buflen++] = 8;
stq_be_p(&outbuf[buflen], s->qdev.wwn); stq_be_p(&outbuf[buflen], s->qdev.wwn);
buflen += 8; buflen += 8;
} }
if (s->qdev.port_wwn) { if (s->qdev.port_wwn) {
outbuf[buflen++] = 0x61; // SAS / Binary outbuf[buflen++] = 0x61; /* SAS / Binary */
outbuf[buflen++] = 0x93; // PIV / Target port / NAA outbuf[buflen++] = 0x93; /* PIV / Target port / NAA */
outbuf[buflen++] = 0; // reserved outbuf[buflen++] = 0; /* reserved */
outbuf[buflen++] = 8; outbuf[buflen++] = 8;
stq_be_p(&outbuf[buflen], s->qdev.port_wwn); stq_be_p(&outbuf[buflen], s->qdev.port_wwn);
buflen += 8; buflen += 8;
} }
if (s->port_index) { if (s->port_index) {
outbuf[buflen++] = 0x61; // SAS / Binary outbuf[buflen++] = 0x61; /* SAS / Binary */
outbuf[buflen++] = 0x94; // PIV / Target port / relative target port
outbuf[buflen++] = 0; // reserved /* PIV/Target port/relative target port */
outbuf[buflen++] = 0x94;
outbuf[buflen++] = 0; /* reserved */
outbuf[buflen++] = 4; outbuf[buflen++] = 4;
stw_be_p(&outbuf[buflen + 2], s->port_index); stw_be_p(&outbuf[buflen + 2], s->port_index);
buflen += 4; buflen += 4;
@ -749,7 +748,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
outbuf[22] = (max_unmap_sectors >> 8) & 0xff; outbuf[22] = (max_unmap_sectors >> 8) & 0xff;
outbuf[23] = max_unmap_sectors & 0xff; outbuf[23] = max_unmap_sectors & 0xff;
/* max unmap descriptors, 255 fit in 4 kb with an 8-byte header. */ /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header */
outbuf[24] = 0; outbuf[24] = 0;
outbuf[25] = 0; outbuf[25] = 0;
outbuf[26] = 0; outbuf[26] = 0;
@ -800,6 +799,16 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
return buflen; return buflen;
} }
static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
int buflen = 0;
if (req->cmd.buf[1] & 0x1) {
/* Vital product data */
return scsi_disk_emulate_vpd_page(req, outbuf);
}
/* Standard INQUIRY data */ /* Standard INQUIRY data */
if (req->cmd.buf[2] != 0) { if (req->cmd.buf[2] != 0) {
return -1; return -1;
@ -3039,6 +3048,10 @@ static Property scsi_block_properties[] = {
DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.blk), DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.blk),
DEFINE_PROP_BOOL("share-rw", SCSIDiskState, qdev.conf.share_rw, false), DEFINE_PROP_BOOL("share-rw", SCSIDiskState, qdev.conf.share_rw, false),
DEFINE_PROP_UINT16("rotation_rate", SCSIDiskState, rotation_rate, 0), DEFINE_PROP_UINT16("rotation_rate", SCSIDiskState, rotation_rate, 0),
DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size,
DEFAULT_MAX_UNMAP_SIZE),
DEFINE_PROP_UINT64("max_io_size", SCSIDiskState, max_io_size,
DEFAULT_MAX_IO_SIZE),
DEFINE_PROP_INT32("scsi_version", SCSIDiskState, qdev.default_scsi_version, DEFINE_PROP_INT32("scsi_version", SCSIDiskState, qdev.default_scsi_version,
-1), -1),
DEFINE_PROP_END_OF_LIST(), DEFINE_PROP_END_OF_LIST(),

View file

@ -142,6 +142,43 @@ static int execute_command(BlockBackend *blk,
return 0; return 0;
} }
static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s)
{
/*
* EVPD set to zero returns the standard INQUIRY data.
*
* Check if scsi_version is unset (-1) to avoid re-defining it
* each time an INQUIRY with standard data is received.
* scsi_version is initialized with -1 in scsi_generic_reset
* and scsi_disk_reset, making sure that we'll set the
* scsi_version after a reset. If the version field of the
* INQUIRY response somehow changes after a guest reboot,
* we'll be able to keep track of it.
*
* On SCSI-2 and older, first 3 bits of byte 2 is the
* ANSI-approved version, while on later versions the
* whole byte 2 contains the version. Check if we're dealing
* with a newer version and, in that case, assign the
* whole byte.
*/
if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) {
s->scsi_version = r->buf[2] & 0x07;
if (s->scsi_version > 2) {
s->scsi_version = r->buf[2];
}
}
if (s->type == TYPE_DISK && r->req.cmd.buf[2] == 0xb0) {
uint32_t max_transfer =
blk_get_max_transfer(s->conf.blk) / s->blocksize;
assert(max_transfer);
stl_be_p(&r->buf[8], max_transfer);
/* Also take care of the opt xfer len. */
stl_be_p(&r->buf[12],
MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
}
}
static void scsi_read_complete(void * opaque, int ret) static void scsi_read_complete(void * opaque, int ret)
{ {
SCSIGenericReq *r = (SCSIGenericReq *)opaque; SCSIGenericReq *r = (SCSIGenericReq *)opaque;
@ -194,39 +231,7 @@ static void scsi_read_complete(void * opaque, int ret)
} }
} }
if (r->req.cmd.buf[0] == INQUIRY) { if (r->req.cmd.buf[0] == INQUIRY) {
/* scsi_handle_inquiry_reply(r, s);
* EVPD set to zero returns the standard INQUIRY data.
*
* Check if scsi_version is unset (-1) to avoid re-defining it
* each time an INQUIRY with standard data is received.
* scsi_version is initialized with -1 in scsi_generic_reset
* and scsi_disk_reset, making sure that we'll set the
* scsi_version after a reset. If the version field of the
* INQUIRY response somehow changes after a guest reboot,
* we'll be able to keep track of it.
*
* On SCSI-2 and older, first 3 bits of byte 2 is the
* ANSI-approved version, while on later versions the
* whole byte 2 contains the version. Check if we're dealing
* with a newer version and, in that case, assign the
* whole byte.
*/
if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) {
s->scsi_version = r->buf[2] & 0x07;
if (s->scsi_version > 2) {
s->scsi_version = r->buf[2];
}
}
if (s->type == TYPE_DISK && r->req.cmd.buf[2] == 0xb0) {
uint32_t max_transfer =
blk_get_max_transfer(s->conf.blk) / s->blocksize;
assert(max_transfer);
stl_be_p(&r->buf[8], max_transfer);
/* Also take care of the opt xfer len. */
stl_be_p(&r->buf[12],
MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
}
} }
scsi_req_data(&r->req, len); scsi_req_data(&r->req, len);
scsi_req_unref(&r->req); scsi_req_unref(&r->req);

View file

@ -186,6 +186,7 @@ void scsi_device_report_change(SCSIDevice *dev, SCSISense sense);
void scsi_device_unit_attention_reported(SCSIDevice *dev); void scsi_device_unit_attention_reported(SCSIDevice *dev);
void scsi_generic_read_device_identification(SCSIDevice *dev); void scsi_generic_read_device_identification(SCSIDevice *dev);
int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed);
int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf);
SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun); SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun);
/* scsi-generic.c. */ /* scsi-generic.c. */