aboutsummaryrefslogtreecommitdiff
path: root/blockdev.c
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@linux.vnet.ibm.com>2012-01-18 14:40:50 +0000
committerKevin Wolf <kwolf@redhat.com>2012-01-26 14:49:18 +0100
commitaa398a5c3a4c0fc29baf02aee5283a7fa0f202a3 (patch)
tree2e17e804f7d2172063247e4a8b501238396e110a /blockdev.c
parentfb5458cd10a199e55e622a906b24f8085d922c0f (diff)
blockdev: make image streaming safe across hotplug
Unplugging a storage interface like virtio-blk causes the host block device to be deleted too. Long-running operations like block migration must take a DriveInfo reference to prevent the BlockDriverState from being freed. For image streaming we can do the same thing. Note that it is not possible to acquire/release the drive reference in block.c where the block job functions live because drive_get_ref()/drive_put_ref() are blockdev.c functions. Calling them from block.c would be a layering violation - tools like qemu-img don't even link against blockdev.c. Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Diffstat (limited to 'blockdev.c')
-rw-r--r--blockdev.c38
1 files changed, 38 insertions, 0 deletions
diff --git a/blockdev.c b/blockdev.c
index d026cd4fda..2fa01516f9 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -202,6 +202,37 @@ void drive_get_ref(DriveInfo *dinfo)
dinfo->refcount++;
}
+typedef struct {
+ QEMUBH *bh;
+ DriveInfo *dinfo;
+} DrivePutRefBH;
+
+static void drive_put_ref_bh(void *opaque)
+{
+ DrivePutRefBH *s = opaque;
+
+ drive_put_ref(s->dinfo);
+ qemu_bh_delete(s->bh);
+ g_free(s);
+}
+
+/*
+ * Release a drive reference in a BH
+ *
+ * It is not possible to use drive_put_ref() from a callback function when the
+ * callers still need the drive. In such cases we schedule a BH to release the
+ * reference.
+ */
+static void drive_put_ref_bh_schedule(DriveInfo *dinfo)
+{
+ DrivePutRefBH *s;
+
+ s = g_new(DrivePutRefBH, 1);
+ s->bh = qemu_bh_new(drive_put_ref_bh, s);
+ s->dinfo = dinfo;
+ qemu_bh_schedule(s->bh);
+}
+
static int parse_block_error_action(const char *buf, int is_read)
{
if (!strcmp(buf, "ignore")) {
@@ -934,6 +965,8 @@ static void block_stream_cb(void *opaque, int ret)
monitor_protocol_event(QEVENT_BLOCK_JOB_COMPLETED, obj);
}
qobject_decref(obj);
+
+ drive_put_ref_bh_schedule(drive_get_by_blockdev(bs));
}
void qmp_block_stream(const char *device, bool has_base,
@@ -966,6 +999,11 @@ void qmp_block_stream(const char *device, bool has_base,
}
}
+ /* Grab a reference so hotplug does not delete the BlockDriverState from
+ * underneath us.
+ */
+ drive_get_ref(drive_get_by_blockdev(bs));
+
trace_qmp_block_stream(bs, bs->job);
}