block: Catch attempt to attach multiple devices to a blockdev
For instance, -device scsi-disk,drive=foo -device scsi-disk,drive=foo happily creates two SCSI disks connected to the same block device. It's all downhill from there. Device usb-storage deliberately attaches twice to the same blockdev, which fails with the fix in place. Detach before the second attach there. Also catch attempt to delete while a guest device model is attached. Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
dfb0acd887
commit
18846dee1a
22
block.c
22
block.c
@ -665,6 +665,8 @@ void bdrv_close_all(void)
|
||||
|
||||
void bdrv_delete(BlockDriverState *bs)
|
||||
{
|
||||
assert(!bs->peer);
|
||||
|
||||
/* remove from list, if necessary */
|
||||
if (bs->device_name[0] != '\0') {
|
||||
QTAILQ_REMOVE(&bdrv_states, bs, list);
|
||||
@ -678,6 +680,26 @@ void bdrv_delete(BlockDriverState *bs)
|
||||
qemu_free(bs);
|
||||
}
|
||||
|
||||
int bdrv_attach(BlockDriverState *bs, DeviceState *qdev)
|
||||
{
|
||||
if (bs->peer) {
|
||||
return -EBUSY;
|
||||
}
|
||||
bs->peer = qdev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bdrv_detach(BlockDriverState *bs, DeviceState *qdev)
|
||||
{
|
||||
assert(bs->peer == qdev);
|
||||
bs->peer = NULL;
|
||||
}
|
||||
|
||||
DeviceState *bdrv_get_attached(BlockDriverState *bs)
|
||||
{
|
||||
return bs->peer;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run consistency checks on an image
|
||||
*
|
||||
|
3
block.h
3
block.h
@ -71,6 +71,9 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags);
|
||||
int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
||||
BlockDriver *drv);
|
||||
void bdrv_close(BlockDriverState *bs);
|
||||
int bdrv_attach(BlockDriverState *bs, DeviceState *qdev);
|
||||
void bdrv_detach(BlockDriverState *bs, DeviceState *qdev);
|
||||
DeviceState *bdrv_get_attached(BlockDriverState *bs);
|
||||
int bdrv_check(BlockDriverState *bs);
|
||||
int bdrv_read(BlockDriverState *bs, int64_t sector_num,
|
||||
uint8_t *buf, int nb_sectors);
|
||||
|
@ -148,6 +148,8 @@ struct BlockDriverState {
|
||||
BlockDriver *drv; /* NULL means no media */
|
||||
void *opaque;
|
||||
|
||||
DeviceState *peer;
|
||||
|
||||
char filename[1024];
|
||||
char backing_file[1024]; /* if non zero, the image is a diff of
|
||||
this file image */
|
||||
|
10
hw/fdc.c
10
hw/fdc.c
@ -1860,10 +1860,10 @@ FDCtrl *fdctrl_init_isa(DriveInfo **fds)
|
||||
|
||||
dev = isa_create("isa-fdc");
|
||||
if (fds[0]) {
|
||||
qdev_prop_set_drive(&dev->qdev, "driveA", fds[0]->bdrv);
|
||||
qdev_prop_set_drive_nofail(&dev->qdev, "driveA", fds[0]->bdrv);
|
||||
}
|
||||
if (fds[1]) {
|
||||
qdev_prop_set_drive(&dev->qdev, "driveB", fds[1]->bdrv);
|
||||
qdev_prop_set_drive_nofail(&dev->qdev, "driveB", fds[1]->bdrv);
|
||||
}
|
||||
if (qdev_init(&dev->qdev) < 0)
|
||||
return NULL;
|
||||
@ -1882,10 +1882,10 @@ FDCtrl *fdctrl_init_sysbus(qemu_irq irq, int dma_chann,
|
||||
fdctrl = &sys->state;
|
||||
fdctrl->dma_chann = dma_chann; /* FIXME */
|
||||
if (fds[0]) {
|
||||
qdev_prop_set_drive(dev, "driveA", fds[0]->bdrv);
|
||||
qdev_prop_set_drive_nofail(dev, "driveA", fds[0]->bdrv);
|
||||
}
|
||||
if (fds[1]) {
|
||||
qdev_prop_set_drive(dev, "driveB", fds[1]->bdrv);
|
||||
qdev_prop_set_drive_nofail(dev, "driveB", fds[1]->bdrv);
|
||||
}
|
||||
qdev_init_nofail(dev);
|
||||
sysbus_connect_irq(&sys->busdev, 0, irq);
|
||||
@ -1903,7 +1903,7 @@ FDCtrl *sun4m_fdctrl_init(qemu_irq irq, target_phys_addr_t io_base,
|
||||
|
||||
dev = qdev_create(NULL, "SUNW,fdtwo");
|
||||
if (fds[0]) {
|
||||
qdev_prop_set_drive(dev, "drive", fds[0]->bdrv);
|
||||
qdev_prop_set_drive_nofail(dev, "drive", fds[0]->bdrv);
|
||||
}
|
||||
qdev_init_nofail(dev);
|
||||
sys = DO_UPCAST(FDCtrlSysBus, busdev.qdev, dev);
|
||||
|
@ -83,7 +83,7 @@ IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive)
|
||||
|
||||
dev = qdev_create(&bus->qbus, "ide-drive");
|
||||
qdev_prop_set_uint32(dev, "unit", unit);
|
||||
qdev_prop_set_drive(dev, "drive", drive->bdrv);
|
||||
qdev_prop_set_drive_nofail(dev, "drive", drive->bdrv);
|
||||
qdev_init_nofail(dev);
|
||||
return DO_UPCAST(IDEDevice, qdev, dev);
|
||||
}
|
||||
|
@ -214,7 +214,11 @@ static PCIDevice *qemu_pci_hot_add_storage(Monitor *mon,
|
||||
return NULL;
|
||||
}
|
||||
dev = pci_create(bus, devfn, "virtio-blk-pci");
|
||||
qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv);
|
||||
if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) {
|
||||
qdev_free(&dev->qdev);
|
||||
dev = NULL;
|
||||
break;
|
||||
}
|
||||
if (qdev_init(&dev->qdev) < 0)
|
||||
dev = NULL;
|
||||
break;
|
||||
|
@ -311,6 +311,8 @@ static int parse_drive(DeviceState *dev, Property *prop, const char *str)
|
||||
bs = bdrv_find(str);
|
||||
if (bs == NULL)
|
||||
return -ENOENT;
|
||||
if (bdrv_attach(bs, dev) < 0)
|
||||
return -EEXIST;
|
||||
*ptr = bs;
|
||||
return 0;
|
||||
}
|
||||
@ -320,6 +322,7 @@ static void free_drive(DeviceState *dev, Property *prop)
|
||||
BlockDriverState **ptr = qdev_get_prop_ptr(dev, prop);
|
||||
|
||||
if (*ptr) {
|
||||
bdrv_detach(*ptr, dev);
|
||||
blockdev_auto_del(*ptr);
|
||||
}
|
||||
}
|
||||
@ -660,11 +663,28 @@ void qdev_prop_set_string(DeviceState *dev, const char *name, char *value)
|
||||
qdev_prop_set(dev, name, &value, PROP_TYPE_STRING);
|
||||
}
|
||||
|
||||
void qdev_prop_set_drive(DeviceState *dev, const char *name, BlockDriverState *value)
|
||||
int qdev_prop_set_drive(DeviceState *dev, const char *name, BlockDriverState *value)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = bdrv_attach(value, dev);
|
||||
if (res < 0) {
|
||||
error_report("Can't attach drive %s to %s.%s: %s",
|
||||
bdrv_get_device_name(value),
|
||||
dev->id ? dev->id : dev->info->name,
|
||||
name, strerror(-res));
|
||||
return -1;
|
||||
}
|
||||
qdev_prop_set(dev, name, &value, PROP_TYPE_DRIVE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockDriverState *value)
|
||||
{
|
||||
if (qdev_prop_set_drive(dev, name, value) < 0) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value)
|
||||
{
|
||||
qdev_prop_set(dev, name, &value, PROP_TYPE_CHR);
|
||||
|
@ -275,7 +275,8 @@ void qdev_prop_set_string(DeviceState *dev, const char *name, char *value);
|
||||
void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value);
|
||||
void qdev_prop_set_netdev(DeviceState *dev, const char *name, VLANClientState *value);
|
||||
void qdev_prop_set_vlan(DeviceState *dev, const char *name, VLANState *value);
|
||||
void qdev_prop_set_drive(DeviceState *dev, const char *name, BlockDriverState *value);
|
||||
int qdev_prop_set_drive(DeviceState *dev, const char *name, BlockDriverState *value) QEMU_WARN_UNUSED_RESULT;
|
||||
void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockDriverState *value);
|
||||
void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value);
|
||||
/* FIXME: Remove opaque pointer properties. */
|
||||
void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value);
|
||||
|
@ -262,7 +262,7 @@ static void s390_init(ram_addr_t ram_size,
|
||||
}
|
||||
|
||||
dev = qdev_create((BusState *)s390_bus, "virtio-blk-s390");
|
||||
qdev_prop_set_drive(dev, "drive", dinfo->bdrv);
|
||||
qdev_prop_set_drive_nofail(dev, "drive", dinfo->bdrv);
|
||||
qdev_init_nofail(dev);
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +91,10 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, int
|
||||
driver = bdrv_is_sg(bdrv) ? "scsi-generic" : "scsi-disk";
|
||||
dev = qdev_create(&bus->qbus, driver);
|
||||
qdev_prop_set_uint32(dev, "scsi-id", unit);
|
||||
qdev_prop_set_drive(dev, "drive", bdrv);
|
||||
if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) {
|
||||
qdev_free(dev);
|
||||
return NULL;
|
||||
}
|
||||
if (qdev_init(dev) < 0)
|
||||
return NULL;
|
||||
return DO_UPCAST(SCSIDevice, qdev, dev);
|
||||
|
12
hw/usb-msd.c
12
hw/usb-msd.c
@ -532,12 +532,13 @@ static int usb_msd_initfn(USBDevice *dev)
|
||||
/*
|
||||
* 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.
|
||||
* creates automatically. But first it needs to detach from its
|
||||
* blockdev, or else scsi_bus_legacy_add_drive() dies when it
|
||||
* attaches again.
|
||||
*
|
||||
* The hack is probably a bad idea.
|
||||
*/
|
||||
bdrv_detach(bs, &s->dev.qdev);
|
||||
s->conf.bs = NULL;
|
||||
|
||||
s->dev.speed = USB_SPEED_FULL;
|
||||
@ -609,7 +610,10 @@ static USBDevice *usb_msd_init(const char *filename)
|
||||
if (!dev) {
|
||||
return NULL;
|
||||
}
|
||||
qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv);
|
||||
if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) {
|
||||
qdev_free(&dev->qdev);
|
||||
return NULL;
|
||||
}
|
||||
if (qdev_init(&dev->qdev) < 0)
|
||||
return NULL;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user