Merge remote-tracking branch 'bonzini/scsi-next' into staging

* bonzini/scsi-next:
  scsi: add SANITIZE command
  SCSI emulation: should tell the guest that we actually support thin provisioning
  SCSI emulation: Support unmap via WRITE_SAME_10.
  scsi: advertise DPOFUA
  scsi: small refactoring of MMC mode-sense
  scsi: support FUA on reads
  scsi: add a started field to SCSIDiskReq
  scsi: force unit access on VERIFY
  scsi: add support for FUA on writes
  scsi: move scsi_flush_complete around
  scsi: make code more homogeneous in AIO callback functions
  scsi: add missing test for cancelled request
  virtio-scsi: add multiqueue capability
  virtio: add virtio_queue_get_id
  virtio-scsi: prepare migration format for multiqueue
  scsi: fix memory leak
This commit is contained in:
Anthony Liguori 2012-04-23 09:57:47 -05:00
commit 4a1873fc8c
6 changed files with 171 additions and 42 deletions

View File

@ -1200,6 +1200,7 @@ static const char *scsi_command_name(uint8_t cmd)
[ UNMAP ] = "UNMAP",
[ READ_TOC ] = "READ_TOC",
[ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT",
[ SANITIZE ] = "SANITIZE",
[ GET_CONFIGURATION ] = "GET_CONFIGURATION",
[ LOG_SELECT ] = "LOG_SELECT",
[ LOG_SENSE ] = "LOG_SENSE",
@ -1430,15 +1431,18 @@ static char *scsibus_get_dev_path(DeviceState *dev)
SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev);
DeviceState *hba = dev->parent_bus->parent;
char *id = NULL;
char *path;
if (hba && hba->parent_bus && hba->parent_bus->info->get_dev_path) {
id = hba->parent_bus->info->get_dev_path(hba);
}
if (id) {
return g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun);
path = g_strdup_printf("%s/%d:%d:%d", id, d->channel, d->id, d->lun);
} else {
return g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun);
path = g_strdup_printf("%d:%d:%d", d->channel, d->id, d->lun);
}
g_free(id);
return path;
}
static char *scsibus_get_fw_dev_path(DeviceState *dev)

View File

@ -78,6 +78,7 @@
#define READ_TOC 0x43
#define REPORT_DENSITY_SUPPORT 0x44
#define GET_CONFIGURATION 0x46
#define SANITIZE 0x48
#define GET_EVENT_STATUS_NOTIFICATION 0x4a
#define LOG_SELECT 0x4c
#define LOG_SENSE 0x4d

View File

@ -55,6 +55,7 @@ typedef struct SCSIDiskReq {
uint64_t sector;
uint32_t sector_count;
uint32_t buflen;
bool started;
struct iovec iov;
QEMUIOVector qiov;
BlockAcctCookie acct;
@ -153,14 +154,80 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req)
qemu_iovec_init_external(&r->qiov, &r->iov, 1);
}
static void scsi_dma_complete(void *opaque, int ret)
static void scsi_flush_complete(void * opaque, int ret)
{
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
if (ret) {
if (ret < 0) {
if (scsi_handle_rw_error(r, -ret)) {
goto done;
}
}
scsi_req_complete(&r->req, GOOD);
done:
if (!r->req.io_canceled) {
scsi_req_unref(&r->req);
}
}
static bool scsi_is_cmd_fua(SCSICommand *cmd)
{
switch (cmd->buf[0]) {
case READ_10:
case READ_12:
case READ_16:
case WRITE_10:
case WRITE_12:
case WRITE_16:
return (cmd->buf[1] & 8) != 0;
case VERIFY_10:
case VERIFY_12:
case VERIFY_16:
case WRITE_VERIFY_10:
case WRITE_VERIFY_12:
case WRITE_VERIFY_16:
return true;
case READ_6:
case WRITE_6:
default:
return false;
}
}
static void scsi_write_do_fua(SCSIDiskReq *r)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
if (scsi_is_cmd_fua(&r->req.cmd)) {
bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_flush_complete, r);
return;
}
scsi_req_complete(&r->req, GOOD);
if (!r->req.io_canceled) {
scsi_req_unref(&r->req);
}
}
static void scsi_dma_complete(void *opaque, int ret)
{
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
if (r->req.aiocb != NULL) {
r->req.aiocb = NULL;
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
}
if (ret < 0) {
if (scsi_handle_rw_error(r, -ret)) {
goto done;
}
@ -168,10 +235,17 @@ static void scsi_dma_complete(void *opaque, int ret)
r->sector += r->sector_count;
r->sector_count = 0;
scsi_req_complete(&r->req, GOOD);
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
scsi_write_do_fua(r);
return;
} else {
scsi_req_complete(&r->req, GOOD);
}
done:
scsi_req_unref(&r->req);
if (!r->req.io_canceled) {
scsi_req_unref(&r->req);
}
}
static void scsi_read_complete(void * opaque, int ret)
@ -185,7 +259,7 @@ static void scsi_read_complete(void * opaque, int ret)
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
}
if (ret) {
if (ret < 0) {
if (scsi_handle_rw_error(r, -ret)) {
goto done;
}
@ -204,10 +278,12 @@ done:
}
}
static void scsi_flush_complete(void * opaque, int ret)
/* Actually issue a read to the block device. */
static void scsi_do_read(void *opaque, int ret)
{
SCSIDiskReq *r = (SCSIDiskReq *)opaque;
SCSIDiskReq *r = opaque;
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint32_t n;
if (r->req.aiocb != NULL) {
r->req.aiocb = NULL;
@ -220,7 +296,17 @@ static void scsi_flush_complete(void * opaque, int ret)
}
}
scsi_req_complete(&r->req, GOOD);
if (r->req.sg) {
dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
r->req.resid -= r->req.sg->size;
r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector,
scsi_dma_complete, r);
} else {
n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
scsi_read_complete, r);
}
done:
if (!r->req.io_canceled) {
@ -233,11 +319,12 @@ static void scsi_read_data(SCSIRequest *req)
{
SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint32_t n;
bool first;
if (r->sector_count == (uint32_t)-1) {
DPRINTF("Read buf_len=%zd\n", r->iov.iov_len);
r->sector_count = 0;
r->started = true;
scsi_req_data(&r->req, r->iov.iov_len);
return;
}
@ -264,16 +351,13 @@ static void scsi_read_data(SCSIRequest *req)
return;
}
if (r->req.sg) {
dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_READ);
r->req.resid -= r->req.sg->size;
r->req.aiocb = dma_bdrv_read(s->qdev.conf.bs, r->req.sg, r->sector,
scsi_dma_complete, r);
first = !r->started;
r->started = true;
if (first && scsi_is_cmd_fua(&r->req.cmd)) {
bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_do_read, r);
} else {
n = scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
scsi_read_complete, r);
scsi_do_read(r, 0);
}
}
@ -333,7 +417,7 @@ static void scsi_write_complete(void * opaque, int ret)
bdrv_acct_done(s->qdev.conf.bs, &r->acct);
}
if (ret) {
if (ret < 0) {
if (scsi_handle_rw_error(r, -ret)) {
goto done;
}
@ -343,7 +427,8 @@ static void scsi_write_complete(void * opaque, int ret)
r->sector += n;
r->sector_count -= n;
if (r->sector_count == 0) {
scsi_req_complete(&r->req, GOOD);
scsi_write_do_fua(r);
return;
} else {
scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, r->qiov.size);
@ -375,6 +460,7 @@ static void scsi_write_data(SCSIRequest *req)
if (!r->req.sg && !r->qiov.size) {
/* Called for the first time. Ask the driver to send us more data. */
r->started = true;
scsi_write_complete(r, 0);
return;
}
@ -383,6 +469,16 @@ static void scsi_write_data(SCSIRequest *req)
return;
}
if (r->req.cmd.buf[0] == VERIFY_10 || r->req.cmd.buf[0] == VERIFY_12 ||
r->req.cmd.buf[0] == VERIFY_16) {
if (r->req.sg) {
scsi_dma_complete(r, 0);
} else {
scsi_write_complete(r, 0);
}
return;
}
if (r->req.sg) {
dma_acct_start(s->qdev.conf.bs, &r->acct, r->req.sg, BDRV_ACCT_WRITE);
r->req.resid -= r->req.sg->size;
@ -531,8 +627,8 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
{
outbuf[3] = buflen = 8;
outbuf[4] = 0;
outbuf[5] = 0x40; /* write same with unmap supported */
outbuf[6] = 0;
outbuf[5] = 0x60; /* write_same 10/16 supported */
outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1;
outbuf[7] = 0;
break;
}
@ -984,11 +1080,12 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
uint64_t nb_sectors;
int page, dbd, buflen, ret, page_control;
bool dbd;
int page, buflen, ret, page_control;
uint8_t *p;
uint8_t dev_specific_param;
dbd = r->req.cmd.buf[1] & 0x8;
dbd = (r->req.cmd.buf[1] & 0x8) != 0;
page = r->req.cmd.buf[2] & 0x3f;
page_control = (r->req.cmd.buf[2] & 0xc0) >> 6;
DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n",
@ -996,10 +1093,16 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
memset(outbuf, 0, r->req.cmd.xfer);
p = outbuf;
if (bdrv_is_read_only(s->qdev.conf.bs)) {
dev_specific_param = 0x80; /* Readonly. */
if (s->qdev.type == TYPE_DISK) {
dev_specific_param = 0x10; /* DPOFUA */
if (bdrv_is_read_only(s->qdev.conf.bs)) {
dev_specific_param |= 0x80; /* Readonly. */
}
} else {
/* MMC prescribes that CD/DVD drives have no block descriptors,
* and defines no device-specific parameter. */
dev_specific_param = 0x00;
dbd = true;
}
if (r->req.cmd.buf[0] == MODE_SENSE) {
@ -1014,9 +1117,8 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
p += 8;
}
/* MMC prescribes that CD/DVD drives have no block descriptors. */
bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
if (!dbd && s->qdev.type == TYPE_DISK && nb_sectors) {
if (!dbd && nb_sectors) {
if (r->req.cmd.buf[0] == MODE_SENSE) {
outbuf[3] = 8; /* Block descriptor length */
} else { /* MODE_SENSE_10 */
@ -1306,8 +1408,6 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r)
}
DPRINTF("Unsupported Service Action In\n");
goto illegal_request;
case VERIFY_10:
break;
default:
scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
return -1;
@ -1391,7 +1491,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
case MECHANISM_STATUS:
case SERVICE_ACTION_IN_16:
case REQUEST_SENSE:
case VERIFY_10:
rc = scsi_disk_emulate_command(r);
if (rc < 0) {
return 0;
@ -1417,6 +1516,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512);
r->sector_count = len * (s->qdev.blocksize / 512);
break;
case VERIFY_10:
case VERIFY_12:
case VERIFY_16:
case WRITE_6:
case WRITE_10:
case WRITE_12:
@ -1456,10 +1558,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
goto illegal_lba;
}
break;
case WRITE_SAME_10:
case WRITE_SAME_16:
len = r->req.cmd.xfer / s->qdev.blocksize;
DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n",
DPRINTF("WRITE SAME() (sector %" PRId64 ", count %d)\n",
r->req.cmd.lba, len);
if (r->req.cmd.lba > s->qdev.max_lba) {
@ -1767,6 +1870,9 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
case READ_10:
case READ_12:
case READ_16:
case VERIFY_10:
case VERIFY_12:
case VERIFY_16:
case WRITE_6:
case WRITE_10:
case WRITE_12:

View File

@ -129,12 +129,12 @@ typedef struct {
VirtIOSCSIConf *conf;
SCSIBus bus;
VirtQueue *ctrl_vq;
VirtQueue *event_vq;
VirtQueue *cmd_vq;
uint32_t sense_size;
uint32_t cdb_size;
int resetting;
VirtQueue *ctrl_vq;
VirtQueue *event_vq;
VirtQueue *cmd_vqs[0];
} VirtIOSCSI;
typedef struct VirtIOSCSIReq {
@ -240,7 +240,10 @@ static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq)
static void virtio_scsi_save_request(QEMUFile *f, SCSIRequest *sreq)
{
VirtIOSCSIReq *req = sreq->hba_private;
uint32_t n = virtio_queue_get_id(req->vq) - 2;
assert(n < req->dev->conf->num_queues);
qemu_put_be32s(f, &n);
qemu_put_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
}
@ -249,10 +252,13 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
SCSIBus *bus = sreq->bus;
VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
VirtIOSCSIReq *req;
uint32_t n;
req = g_malloc(sizeof(*req));
qemu_get_be32s(f, &n);
assert(n < s->conf->num_queues);
qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem));
virtio_scsi_parse_req(s, s->cmd_vq, req);
virtio_scsi_parse_req(s, s->cmd_vqs[n], req);
scsi_req_ref(sreq);
req->sreq = sreq;
@ -579,10 +585,12 @@ VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf)
{
VirtIOSCSI *s;
static int virtio_scsi_id;
size_t sz;
int i;
sz = sizeof(VirtIOSCSI) + proxyconf->num_queues * sizeof(VirtQueue *);
s = (VirtIOSCSI *)virtio_common_init("virtio-scsi", VIRTIO_ID_SCSI,
sizeof(VirtIOSCSIConfig),
sizeof(VirtIOSCSI));
sizeof(VirtIOSCSIConfig), sz);
s->qdev = dev;
s->conf = proxyconf;
@ -597,8 +605,10 @@ VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *proxyconf)
virtio_scsi_handle_ctrl);
s->event_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
NULL);
s->cmd_vq = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
virtio_scsi_handle_cmd);
for (i = 0; i < s->conf->num_queues; i++) {
s->cmd_vqs[i] = virtio_add_queue(&s->vdev, VIRTIO_SCSI_VQ_SIZE,
virtio_scsi_handle_cmd);
}
scsi_bus_new(&s->bus, dev, &virtio_scsi_scsi_info);
if (!dev->hotplugged) {

View File

@ -624,6 +624,13 @@ int virtio_queue_get_num(VirtIODevice *vdev, int n)
return vdev->vq[n].vring.num;
}
int virtio_queue_get_id(VirtQueue *vq)
{
VirtIODevice *vdev = vq->vdev;
assert(vq >= &vdev->vq[0] && vq < &vdev->vq[VIRTIO_PCI_QUEUE_MAX]);
return vq - &vdev->vq[0];
}
void virtio_queue_notify_vq(VirtQueue *vq)
{
if (vq->vring.desc) {

View File

@ -229,6 +229,7 @@ target_phys_addr_t virtio_queue_get_ring_size(VirtIODevice *vdev, int n);
uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n);
void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx);
VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n);
int virtio_queue_get_id(VirtQueue *vq);
EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq);
EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq);
void virtio_queue_notify_vq(VirtQueue *vq);