summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--blockdev.c23
-rw-r--r--blockdev.h4
-rw-r--r--hw/qdev-properties.c10
-rw-r--r--hw/scsi-disk.c2
-rw-r--r--hw/scsi-generic.c2
-rw-r--r--hw/usb-msd.c20
-rw-r--r--hw/virtio-pci.c2
7 files changed, 56 insertions, 7 deletions
diff --git a/blockdev.c b/blockdev.c
index ba4f66f..4848112 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -17,6 +17,29 @@
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
+/*
+ * We automatically delete the drive when a device using it gets
+ * unplugged. Questionable feature, but we can't just drop it.
+ * Device models call blockdev_mark_auto_del() to schedule the
+ * automatic deletion, and generic qdev code calls blockdev_auto_del()
+ * when deletion is actually safe.
+ */
+void blockdev_mark_auto_del(BlockDriverState *bs)
+{
+ DriveInfo *dinfo = drive_get_by_blockdev(bs);
+
+ dinfo->auto_del = 1;
+}
+
+void blockdev_auto_del(BlockDriverState *bs)
+{
+ DriveInfo *dinfo = drive_get_by_blockdev(bs);
+
+ if (dinfo->auto_del) {
+ drive_uninit(dinfo);
+ }
+}
+
QemuOpts *drive_add(const char *file, const char *fmt, ...)
{
va_list ap;
diff --git a/blockdev.h b/blockdev.h
index 6ab592f..32e6979 100644
--- a/blockdev.h
+++ b/blockdev.h
@@ -13,6 +13,9 @@
#include "block.h"
#include "qemu-queue.h"
+void blockdev_mark_auto_del(BlockDriverState *bs);
+void blockdev_auto_del(BlockDriverState *bs);
+
typedef enum {
IF_NONE,
IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN,
@@ -28,6 +31,7 @@ typedef struct DriveInfo {
BlockInterfaceType type;
int bus;
int unit;
+ int auto_del; /* see blockdev_mark_auto_del() */
QemuOpts *opts;
char serial[BLOCK_SERIAL_STRLEN + 1];
QTAILQ_ENTRY(DriveInfo) next;
diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c
index 5b7fd77..d7dc234 100644
--- a/hw/qdev-properties.c
+++ b/hw/qdev-properties.c
@@ -313,6 +313,15 @@ static int parse_drive(DeviceState *dev, Property *prop, const char *str)
return 0;
}
+static void free_drive(DeviceState *dev, Property *prop)
+{
+ DriveInfo **ptr = qdev_get_prop_ptr(dev, prop);
+
+ if (*ptr) {
+ blockdev_auto_del((*ptr)->bdrv);
+ }
+}
+
static int print_drive(DeviceState *dev, Property *prop, char *dest, size_t len)
{
DriveInfo **ptr = qdev_get_prop_ptr(dev, prop);
@@ -325,6 +334,7 @@ PropertyInfo qdev_prop_drive = {
.size = sizeof(DriveInfo*),
.parse = parse_drive,
.print = print_drive,
+ .free = free_drive,
};
/* --- character device --- */
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 2b38984..d76e640 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -1043,7 +1043,7 @@ static void scsi_destroy(SCSIDevice *dev)
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
scsi_disk_purge_requests(s);
- drive_uninit(s->qdev.conf.dinfo);
+ blockdev_mark_auto_del(s->qdev.conf.dinfo->bdrv);
}
static int scsi_disk_initfn(SCSIDevice *dev)
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index e31060e..1859c94 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -453,7 +453,7 @@ static void scsi_destroy(SCSIDevice *d)
r = DO_UPCAST(SCSIGenericReq, req, QTAILQ_FIRST(&s->qdev.requests));
scsi_remove_request(r);
}
- drive_uninit(s->qdev.conf.dinfo);
+ blockdev_mark_auto_del(s->qdev.conf.dinfo->bdrv);
}
static int scsi_generic_initfn(SCSIDevice *dev)
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 8e9718c..3dbfcab 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -522,24 +522,36 @@ static void usb_msd_password_cb(void *opaque, int err)
static int usb_msd_initfn(USBDevice *dev)
{
MSDState *s = DO_UPCAST(MSDState, dev, dev);
+ DriveInfo *dinfo = s->conf.dinfo;
- if (!s->conf.dinfo || !s->conf.dinfo->bdrv) {
+ if (!dinfo || !dinfo->bdrv) {
error_report("usb-msd: drive property not set");
return -1;
}
+ /*
+ * Hack alert: this pretends to be a block device, but it's really
+ * a SCSI bus that can serve only a single device, which it
+ * creates automatically. Two drive properties pointing to the
+ * same drive is not good: free_drive() dies for the second one.
+ * Zap the one we're not going to use.
+ *
+ * The hack is probably a bad idea.
+ */
+ s->conf.dinfo = NULL;
+
s->dev.speed = USB_SPEED_FULL;
scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete);
- s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, s->conf.dinfo, 0);
+ s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, dinfo, 0);
if (!s->scsi_dev) {
return -1;
}
s->bus.qbus.allow_hotplug = 0;
usb_msd_handle_reset(dev);
- if (bdrv_key_required(s->conf.dinfo->bdrv)) {
+ if (bdrv_key_required(dinfo->bdrv)) {
if (cur_mon) {
- monitor_read_bdrv_key_start(cur_mon, s->conf.dinfo->bdrv,
+ monitor_read_bdrv_key_start(cur_mon, dinfo->bdrv,
usb_msd_password_cb, s);
s->dev.auto_attach = 0;
} else {
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index d1303b1..31a68fe 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -571,7 +571,7 @@ static int virtio_blk_exit_pci(PCIDevice *pci_dev)
{
VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
- drive_uninit(proxy->block.dinfo);
+ blockdev_mark_auto_del(proxy->block.dinfo->bdrv);
return virtio_exit_pci(pci_dev);
}
OpenPOWER on IntegriCloud