blockdev: make image streaming safe across hotplug
authorStefan Hajnoczi <stefanha@linux.vnet.ibm.com>
Wed, 18 Jan 2012 14:40:50 +0000 (14:40 +0000)
committerKevin Wolf <kwolf@redhat.com>
Thu, 26 Jan 2012 13:49:18 +0000 (14:49 +0100)
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>
blockdev.c

index d026cd4fda7f1673075edf15866a579c9e238eac..2fa01516f9e7ceef3815ce6e2d7638d2988397fb 100644 (file)
@@ -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);
 }