qdev/scsi: add scsi bus support to qdev, convert drivers.
* Add SCSIBus.
* Add SCSIDeviceInfo, move device callbacks here.
* add qdev/scsi helper functions.
* convert drivers.
Adding scsi disks via -device works now, i.e. you can do:
-drive id=sda,if=none,...
-device lsi
-device scsi-disk,drive=sda
legacy command lines (-drive if=scsi,...) continue to work.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 5b825c9..ffc2654 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -44,8 +44,11 @@
#define SCSI_REQ_STATUS_RETRY 0x01
+typedef struct SCSIDiskState SCSIDiskState;
+
typedef struct SCSIRequest {
- SCSIDeviceState *dev;
+ SCSIBus *bus;
+ SCSIDiskState *dev;
uint32_t tag;
/* ??? We should probably keep track of whether the data transfer is
a read or a write. Currently we rely on the host getting it right. */
@@ -59,20 +62,16 @@
uint32_t status;
} SCSIRequest;
-struct SCSIDeviceState
+struct SCSIDiskState
{
- BlockDriverState *bdrv;
+ SCSIDevice qdev;
+ DriveInfo *dinfo;
SCSIRequest *requests;
/* The qemu block layer uses a fixed 512 byte sector size.
This is the number of 512 byte blocks in a single scsi sector. */
int cluster_size;
uint64_t max_lba;
int sense;
- int tcq;
- /* Completion functions may be called from either scsi_{read,write}_data
- or from the AIO completion routines. */
- scsi_completionfn completion;
- void *opaque;
char drive_serial_str[21];
QEMUBH *bh;
};
@@ -80,8 +79,9 @@
/* Global pool of SCSIRequest structures. */
static SCSIRequest *free_requests = NULL;
-static SCSIRequest *scsi_new_request(SCSIDeviceState *s, uint32_t tag)
+static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag)
{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *r;
if (free_requests) {
@@ -91,6 +91,7 @@
r = qemu_malloc(sizeof(SCSIRequest));
r->iov.iov_base = qemu_memalign(512, SCSI_DMA_BUF_SIZE);
}
+ r->bus = scsi_bus_from_device(d);
r->dev = s;
r->tag = tag;
r->sector_count = 0;
@@ -106,7 +107,7 @@
static void scsi_remove_request(SCSIRequest *r)
{
SCSIRequest *last;
- SCSIDeviceState *s = r->dev;
+ SCSIDiskState *s = r->dev;
if (s->requests == r) {
s->requests = r->next;
@@ -124,7 +125,7 @@
free_requests = r;
}
-static SCSIRequest *scsi_find_request(SCSIDeviceState *s, uint32_t tag)
+static SCSIRequest *scsi_find_request(SCSIDiskState *s, uint32_t tag)
{
SCSIRequest *r;
@@ -138,19 +139,19 @@
/* Helper function for command completion. */
static void scsi_command_complete(SCSIRequest *r, int status, int sense)
{
- SCSIDeviceState *s = r->dev;
+ SCSIDiskState *s = r->dev;
uint32_t tag;
DPRINTF("Command complete tag=0x%x status=%d sense=%d\n", r->tag, status, sense);
s->sense = sense;
tag = r->tag;
scsi_remove_request(r);
- s->completion(s->opaque, SCSI_REASON_DONE, tag, status);
+ r->bus->complete(r->bus, SCSI_REASON_DONE, tag, status);
}
/* Cancel a pending data transfer. */
static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
{
- SCSIDeviceState *s = d->state;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *r;
DPRINTF("Cancel tag=0x%x\n", tag);
r = scsi_find_request(s, tag);
@@ -165,23 +166,22 @@
static void scsi_read_complete(void * opaque, int ret)
{
SCSIRequest *r = (SCSIRequest *)opaque;
- SCSIDeviceState *s = r->dev;
if (ret) {
DPRINTF("IO error\n");
- s->completion(s->opaque, SCSI_REASON_DATA, r->tag, 0);
+ r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, 0);
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NO_SENSE);
return;
}
DPRINTF("Data ready tag=0x%x len=%" PRId64 "\n", r->tag, r->iov.iov_len);
- s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->iov.iov_len);
+ r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, r->iov.iov_len);
}
/* Read more data from scsi device into buffer. */
static void scsi_read_data(SCSIDevice *d, uint32_t tag)
{
- SCSIDeviceState *s = d->state;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *r;
uint32_t n;
@@ -195,7 +195,7 @@
if (r->sector_count == (uint32_t)-1) {
DPRINTF("Read buf_len=%" PRId64 "\n", r->iov.iov_len);
r->sector_count = 0;
- s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->iov.iov_len);
+ r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, r->iov.iov_len);
return;
}
DPRINTF("Read sector_count=%d\n", r->sector_count);
@@ -210,7 +210,7 @@
r->iov.iov_len = n * 512;
qemu_iovec_init_external(&r->qiov, &r->iov, 1);
- r->aiocb = bdrv_aio_readv(s->bdrv, r->sector, &r->qiov, n,
+ r->aiocb = bdrv_aio_readv(s->dinfo->bdrv, r->sector, &r->qiov, n,
scsi_read_complete, r);
if (r->aiocb == NULL)
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
@@ -220,7 +220,7 @@
static int scsi_handle_write_error(SCSIRequest *r, int error)
{
- BlockInterfaceErrorAction action = drive_get_onerror(r->dev->bdrv);
+ BlockInterfaceErrorAction action = drive_get_onerror(r->dev->dinfo->bdrv);
if (action == BLOCK_ERR_IGNORE)
return 0;
@@ -240,7 +240,6 @@
static void scsi_write_complete(void * opaque, int ret)
{
SCSIRequest *r = (SCSIRequest *)opaque;
- SCSIDeviceState *s = r->dev;
uint32_t len;
uint32_t n;
@@ -263,19 +262,19 @@
}
r->iov.iov_len = len;
DPRINTF("Write complete tag=0x%x more=%d\n", r->tag, len);
- s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len);
+ r->bus->complete(r->bus, SCSI_REASON_DATA, r->tag, len);
}
}
static void scsi_write_request(SCSIRequest *r)
{
- SCSIDeviceState *s = r->dev;
+ SCSIDiskState *s = r->dev;
uint32_t n;
n = r->iov.iov_len / 512;
if (n) {
qemu_iovec_init_external(&r->qiov, &r->iov, 1);
- r->aiocb = bdrv_aio_writev(s->bdrv, r->sector, &r->qiov, n,
+ r->aiocb = bdrv_aio_writev(s->dinfo->bdrv, r->sector, &r->qiov, n,
scsi_write_complete, r);
if (r->aiocb == NULL)
scsi_command_complete(r, STATUS_CHECK_CONDITION,
@@ -290,7 +289,7 @@
The transfer may complete asynchronously. */
static int scsi_write_data(SCSIDevice *d, uint32_t tag)
{
- SCSIDeviceState *s = d->state;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *r;
DPRINTF("Write data tag=0x%x\n", tag);
@@ -311,7 +310,7 @@
static void scsi_dma_restart_bh(void *opaque)
{
- SCSIDeviceState *s = opaque;
+ SCSIDiskState *s = opaque;
SCSIRequest *r = s->requests;
qemu_bh_delete(s->bh);
@@ -328,7 +327,7 @@
static void scsi_dma_restart_cb(void *opaque, int running, int reason)
{
- SCSIDeviceState *s = opaque;
+ SCSIDiskState *s = opaque;
if (!running)
return;
@@ -342,7 +341,7 @@
/* Return a pointer to the data buffer. */
static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
{
- SCSIDeviceState *s = d->state;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *r;
r = scsi_find_request(s, tag);
@@ -361,7 +360,7 @@
static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
uint8_t *buf, int lun)
{
- SCSIDeviceState *s = d->state;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
uint64_t nb_sectors;
uint64_t lba;
uint32_t len;
@@ -379,7 +378,7 @@
}
/* ??? Tags are not unique for different luns. We only implement a
single lun, so this should not matter. */
- r = scsi_new_request(s, tag);
+ r = scsi_new_request(d, tag);
outbuf = (uint8_t *)r->iov.iov_base;
is_write = 0;
DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
@@ -433,7 +432,7 @@
switch (command) {
case 0x0:
DPRINTF("Test Unit Ready\n");
- if (!bdrv_is_inserted(s->bdrv))
+ if (!bdrv_is_inserted(s->dinfo->bdrv))
goto notready;
break;
case 0x03:
@@ -479,7 +478,7 @@
r->iov.iov_len = 0;
- if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
outbuf[r->iov.iov_len++] = 5;
} else {
outbuf[r->iov.iov_len++] = 0;
@@ -510,7 +509,7 @@
r->iov.iov_len = 0;
/* Supported page codes */
- if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
outbuf[r->iov.iov_len++] = 5;
} else {
outbuf[r->iov.iov_len++] = 0;
@@ -528,14 +527,14 @@
{
/* Device identification page, mandatory */
int max_len = 255 - 8;
- int id_len = strlen(bdrv_get_device_name(s->bdrv));
+ int id_len = strlen(bdrv_get_device_name(s->dinfo->bdrv));
if (id_len > max_len)
id_len = max_len;
DPRINTF("Inquiry EVPD[Device identification] "
"buffer size %d\n", len);
r->iov.iov_len = 0;
- if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
outbuf[r->iov.iov_len++] = 5;
} else {
outbuf[r->iov.iov_len++] = 0;
@@ -551,7 +550,7 @@
outbuf[r->iov.iov_len++] = id_len; // length of data following
memcpy(&outbuf[r->iov.iov_len],
- bdrv_get_device_name(s->bdrv), id_len);
+ bdrv_get_device_name(s->dinfo->bdrv), id_len);
r->iov.iov_len += id_len;
}
break;
@@ -591,7 +590,7 @@
if (lun || buf[1] >> 5) {
outbuf[0] = 0x7f; /* LUN not supported */
- } else if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ } else if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
outbuf[0] = 5;
outbuf[1] = 0x80;
memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
@@ -607,7 +606,7 @@
outbuf[3] = 2; /* Format 2 */
outbuf[4] = len - 5; /* Additional Length = (Len - 1) - 4 */
/* Sync data transfer and TCQ. */
- outbuf[7] = 0x10 | (s->tcq ? 0x02 : 0);
+ outbuf[7] = 0x10 | (r->bus->tcq ? 0x02 : 0);
r->iov.iov_len = len;
break;
case 0x16:
@@ -632,7 +631,7 @@
memset(p, 0, 4);
outbuf[1] = 0; /* Default media type. */
outbuf[3] = 0; /* Block descriptor length. */
- if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
outbuf[2] = 0x80; /* Readonly. */
}
p += 4;
@@ -643,7 +642,7 @@
p[0] = 4;
p[1] = 0x16;
/* if a geometry hint is available, use it */
- bdrv_get_geometry_hint(s->bdrv, &cylinders, &heads, &secs);
+ bdrv_get_geometry_hint(s->dinfo->bdrv, &cylinders, &heads, &secs);
p[2] = (cylinders >> 16) & 0xff;
p[3] = (cylinders >> 8) & 0xff;
p[4] = cylinders & 0xff;
@@ -677,7 +676,7 @@
p[2] = 5000 >> 8;
p[3] = 5000 & 0xff;
/* if a geometry hint is available, use it */
- bdrv_get_geometry_hint(s->bdrv, &cylinders, &heads, &secs);
+ bdrv_get_geometry_hint(s->dinfo->bdrv, &cylinders, &heads, &secs);
p[4] = heads & 0xff;
p[5] = secs & 0xff;
p[6] = s->cluster_size * 2;
@@ -714,7 +713,7 @@
p += 20;
}
if ((page == 0x3f || page == 0x2a)
- && (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM)) {
+ && (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM)) {
/* CD Capabilities and Mechanical Status page. */
p[0] = 0x2a;
p[1] = 0x14;
@@ -725,7 +724,7 @@
p[5] = 0xff; /* CD DA, DA accurate, RW supported,
RW corrected, C2 errors, ISRC,
UPC, Bar code */
- p[6] = 0x2d | (bdrv_is_locked(s->bdrv)? 2 : 0);
+ p[6] = 0x2d | (bdrv_is_locked(s->dinfo->bdrv)? 2 : 0);
/* Locking supported, jumper present, eject, tray */
p[7] = 0; /* no volume & mute control, no
changer */
@@ -751,20 +750,20 @@
break;
case 0x1b:
DPRINTF("Start Stop Unit\n");
- if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM &&
+ if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM &&
(buf[4] & 2))
/* load/eject medium */
- bdrv_eject(s->bdrv, !(buf[4] & 1));
+ bdrv_eject(s->dinfo->bdrv, !(buf[4] & 1));
break;
case 0x1e:
DPRINTF("Prevent Allow Medium Removal (prevent = %d)\n", buf[4] & 3);
- bdrv_set_locked(s->bdrv, buf[4] & 1);
+ bdrv_set_locked(s->dinfo->bdrv, buf[4] & 1);
break;
case 0x25:
DPRINTF("Read Capacity\n");
/* The normal LEN field for this command is zero. */
memset(outbuf, 0, 8);
- bdrv_get_geometry(s->bdrv, &nb_sectors);
+ bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
nb_sectors /= s->cluster_size;
/* Returned value is the address of the last sector. */
if (nb_sectors) {
@@ -810,7 +809,7 @@
break;
case 0x35:
DPRINTF("Synchronise cache (sector %" PRId64 ", count %d)\n", lba, len);
- bdrv_flush(s->bdrv);
+ bdrv_flush(s->dinfo->bdrv);
break;
case 0x43:
{
@@ -819,7 +818,7 @@
msf = buf[1] & 2;
format = buf[2] & 0xf;
start_track = buf[6];
- bdrv_get_geometry(s->bdrv, &nb_sectors);
+ bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
nb_sectors /= s->cluster_size;
switch(format) {
@@ -873,7 +872,7 @@
if ((buf[1] & 31) == 0x10) {
DPRINTF("SAI READ CAPACITY(16)\n");
memset(outbuf, 0, len);
- bdrv_get_geometry(s->bdrv, &nb_sectors);
+ bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
nb_sectors /= s->cluster_size;
/* Returned value is the address of the last sector. */
if (nb_sectors) {
@@ -937,45 +936,56 @@
static void scsi_destroy(SCSIDevice *d)
{
- qemu_free(d->state);
qemu_free(d);
}
-SCSIDevice *scsi_disk_init(BlockDriverState *bdrv, int tcq,
- scsi_completionfn completion, void *opaque)
+static int scsi_disk_initfn(SCSIDevice *dev)
{
- SCSIDevice *d;
- SCSIDeviceState *s;
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
uint64_t nb_sectors;
- s = (SCSIDeviceState *)qemu_mallocz(sizeof(SCSIDeviceState));
- s->bdrv = bdrv;
- s->tcq = tcq;
- s->completion = completion;
- s->opaque = opaque;
- if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+ if (!s->dinfo || !s->dinfo->bdrv) {
+ qemu_error("scsi-disk: drive property not set\n");
+ return -1;
+ }
+
+ if (bdrv_get_type_hint(s->dinfo->bdrv) == BDRV_TYPE_CDROM) {
s->cluster_size = 4;
} else {
s->cluster_size = 1;
}
- bdrv_get_geometry(s->bdrv, &nb_sectors);
+ bdrv_get_geometry(s->dinfo->bdrv, &nb_sectors);
nb_sectors /= s->cluster_size;
if (nb_sectors)
nb_sectors--;
s->max_lba = nb_sectors;
- strncpy(s->drive_serial_str, drive_get_serial(s->bdrv),
+ strncpy(s->drive_serial_str, drive_get_serial(s->dinfo->bdrv),
sizeof(s->drive_serial_str));
if (strlen(s->drive_serial_str) == 0)
pstrcpy(s->drive_serial_str, sizeof(s->drive_serial_str), "0");
qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s);
- d = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
- d->state = s;
- d->destroy = scsi_destroy;
- d->send_command = scsi_send_command;
- d->read_data = scsi_read_data;
- d->write_data = scsi_write_data;
- d->cancel_io = scsi_cancel_io;
- d->get_buf = scsi_get_buf;
-
- return d;
+ return 0;
}
+
+static SCSIDeviceInfo scsi_disk_info = {
+ .qdev.name = "scsi-disk",
+ .qdev.desc = "virtual scsi disk or cdrom",
+ .qdev.size = sizeof(SCSIDiskState),
+ .init = scsi_disk_initfn,
+ .destroy = scsi_destroy,
+ .send_command = scsi_send_command,
+ .read_data = scsi_read_data,
+ .write_data = scsi_write_data,
+ .cancel_io = scsi_cancel_io,
+ .get_buf = scsi_get_buf,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_DRIVE("drive", SCSIDiskState, dinfo),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void scsi_disk_register_devices(void)
+{
+ scsi_qdev_register(&scsi_disk_info);
+}
+device_init(scsi_disk_register_devices)