aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorAnthony Liguori <aliguori@us.ibm.com>2012-04-23 09:57:47 -0500
committerAnthony Liguori <aliguori@us.ibm.com>2012-04-23 09:57:47 -0500
commit4a1873fc8c3f692fcae25ed1ebf10003d5d5feb0 (patch)
treefbc85c54f41671fc2c963f4327918e502b0e5521 /hw
parente5e3895702514253120965cfda75c58f28c39a00 (diff)
parent3e46d87d665bd9114f18e8d4a1b6de3641147775 (diff)
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
Diffstat (limited to 'hw')
-rw-r--r--hw/scsi-bus.c8
-rw-r--r--hw/scsi-defs.h1
-rw-r--r--hw/scsi-disk.c170
-rw-r--r--hw/virtio-scsi.c26
-rw-r--r--hw/virtio.c7
-rw-r--r--hw/virtio.h1
6 files changed, 171 insertions, 42 deletions
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index 8e76c5d32c..dbdb99ce35 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -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)
diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h
index 354ed7b55b..ca24192d53 100644
--- a/hw/scsi-defs.h
+++ b/hw/scsi-defs.h
@@ -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
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 9949786e52..a029ab6e84 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -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:
diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c
index 45d54faeb5..e8328f4652 100644
--- a/hw/virtio-scsi.c
+++ b/hw/virtio-scsi.c
@@ -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) {
diff --git a/hw/virtio.c b/hw/virtio.c
index 064aecf553..314abf8a18 100644
--- a/hw/virtio.c
+++ b/hw/virtio.c
@@ -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) {
diff --git a/hw/virtio.h b/hw/virtio.h
index 400c092c95..0aef7d1bc0 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -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);