#include "trace.h"
#include "block/block_int.h"
#include "block/blockjob.h"
+#include "block/nbd.h"
#include "qemu/error-report.h"
+#include "module_block.h"
#include "qemu/module.h"
#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qbool.h"
#include "qapi-event.h"
#include "qemu/cutils.h"
#include "qemu/id.h"
+#include "qapi/util.h"
#ifdef CONFIG_BSD
#include <sys/ioctl.h>
bs->refcnt = 1;
bs->aio_context = qemu_get_aio_context();
+ qemu_co_queue_init(&bs->flush_queue);
+
QTAILQ_INSERT_TAIL(&all_bdrv_states, bs, bs_list);
return bs;
}
-BlockDriver *bdrv_find_format(const char *format_name)
+static BlockDriver *bdrv_do_find_format(const char *format_name)
{
BlockDriver *drv1;
+
QLIST_FOREACH(drv1, &bdrv_drivers, list) {
if (!strcmp(drv1->format_name, format_name)) {
return drv1;
}
}
+
return NULL;
}
+BlockDriver *bdrv_find_format(const char *format_name)
+{
+ BlockDriver *drv1;
+ int i;
+
+ drv1 = bdrv_do_find_format(format_name);
+ if (drv1) {
+ return drv1;
+ }
+
+ /* The driver isn't registered, maybe we need to load a module */
+ for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) {
+ if (!strcmp(block_driver_modules[i].format_name, format_name)) {
+ block_module_load_one(block_driver_modules[i].library_name);
+ break;
+ }
+ }
+
+ return bdrv_do_find_format(format_name);
+}
+
static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
{
static const char *whitelist_rw[] = {
assert(cco->drv);
ret = cco->drv->bdrv_create(cco->filename, cco->opts, &local_err);
- if (local_err) {
- error_propagate(&cco->err, local_err);
- }
+ error_propagate(&cco->err, local_err);
cco->ret = ret;
}
/* Fast-path if already in coroutine context */
bdrv_create_co_entry(&cco);
} else {
- co = qemu_coroutine_create(bdrv_create_co_entry);
- qemu_coroutine_enter(co, &cco);
+ co = qemu_coroutine_create(bdrv_create_co_entry, &cco);
+ qemu_coroutine_enter(co);
while (cco.ret == NOT_DONE) {
aio_poll(qemu_get_aio_context(), true);
}
}
ret = bdrv_create(drv, filename, opts, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- }
+ error_propagate(errp, local_err);
return ret;
}
return drv;
}
+static BlockDriver *bdrv_do_find_protocol(const char *protocol)
+{
+ BlockDriver *drv1;
+
+ QLIST_FOREACH(drv1, &bdrv_drivers, list) {
+ if (drv1->protocol_name && !strcmp(drv1->protocol_name, protocol)) {
+ return drv1;
+ }
+ }
+
+ return NULL;
+}
+
BlockDriver *bdrv_find_protocol(const char *filename,
bool allow_protocol_prefix,
Error **errp)
char protocol[128];
int len;
const char *p;
+ int i;
/* TODO Drivers without bdrv_file_open must be specified explicitly */
len = sizeof(protocol) - 1;
memcpy(protocol, filename, len);
protocol[len] = '\0';
- QLIST_FOREACH(drv1, &bdrv_drivers, list) {
- if (drv1->protocol_name &&
- !strcmp(drv1->protocol_name, protocol)) {
- return drv1;
+
+ drv1 = bdrv_do_find_protocol(protocol);
+ if (drv1) {
+ return drv1;
+ }
+
+ for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) {
+ if (block_driver_modules[i].protocol_name &&
+ !strcmp(block_driver_modules[i].protocol_name, protocol)) {
+ block_module_load_one(block_driver_modules[i].library_name);
+ break;
}
}
- error_setg(errp, "Unknown protocol '%s'", protocol);
- return NULL;
+ drv1 = bdrv_do_find_protocol(protocol);
+ if (!drv1) {
+ error_setg(errp, "Unknown protocol '%s'", protocol);
+ }
+ return drv1;
}
/*
return drv;
}
-static int find_image_format(BlockDriverState *bs, const char *filename,
+static int find_image_format(BdrvChild *file, const char *filename,
BlockDriver **pdrv, Error **errp)
{
+ BlockDriverState *bs = file->bs;
BlockDriver *drv;
uint8_t buf[BLOCK_PROBE_BUF_SIZE];
int ret = 0;
return ret;
}
- ret = bdrv_pread(bs, 0, buf, sizeof(buf));
+ ret = bdrv_pread(file, 0, buf, sizeof(buf));
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read image for determining its "
"format");
qdict_set_default_str(child_options, BDRV_OPT_CACHE_DIRECT, "off");
qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
+ /* Copy the read-only option from the parent */
+ qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY);
+
/* aio=native doesn't work for cache.direct=off, so disable it for the
* temporary snapshot */
*child_flags &= ~BDRV_O_NATIVE_AIO;
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT);
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
+ /* Inherit the read-only option from the parent if it's not set */
+ qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY);
+
/* Our block drivers take care to send flushes and respect unmap policy,
* so we can default to enable both on lower layers regardless of the
* corresponding parent options. */
- flags |= BDRV_O_UNMAP;
+ qdict_set_default_str(child_options, BDRV_OPT_DISCARD, "unmap");
/* Clear flags that only apply to the top layer */
flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ |
qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH);
/* backing files always opened read-only */
- flags &= ~(BDRV_O_RDWR | BDRV_O_COPY_ON_READ);
+ qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on");
+ flags &= ~BDRV_O_COPY_ON_READ;
/* snapshot=on is handled on the top layer */
flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_TEMPORARY);
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) {
*flags |= BDRV_O_NOCACHE;
}
+
+ *flags &= ~BDRV_O_RDWR;
+
+ assert(qemu_opt_find(opts, BDRV_OPT_READ_ONLY));
+ if (!qemu_opt_get_bool(opts, BDRV_OPT_READ_ONLY, false)) {
+ *flags |= BDRV_O_RDWR;
+ }
+
}
static void update_options_from_flags(QDict *options, int flags)
qdict_put(options, BDRV_OPT_CACHE_NO_FLUSH,
qbool_from_bool(flags & BDRV_O_NO_FLUSH));
}
+ if (!qdict_haskey(options, BDRV_OPT_READ_ONLY)) {
+ qdict_put(options, BDRV_OPT_READ_ONLY,
+ qbool_from_bool(!(flags & BDRV_O_RDWR)));
+ }
}
static void bdrv_assign_node_name(BlockDriverState *bs,
g_free(gen_node_name);
}
-static QemuOptsList bdrv_runtime_opts = {
+QemuOptsList bdrv_runtime_opts = {
.name = "bdrv_common",
.head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head),
.desc = {
.type = QEMU_OPT_BOOL,
.help = "Ignore flush requests",
},
+ {
+ .name = BDRV_OPT_READ_ONLY,
+ .type = QEMU_OPT_BOOL,
+ .help = "Node is opened in read-only mode",
+ },
+ {
+ .name = "detect-zeroes",
+ .type = QEMU_OPT_STRING,
+ .help = "try to optimize zero writes (off, on, unmap)",
+ },
+ {
+ .name = "discard",
+ .type = QEMU_OPT_STRING,
+ .help = "discard operation (ignore/off, unmap/on)",
+ },
{ /* end of list */ }
},
};
const char *filename;
const char *driver_name = NULL;
const char *node_name = NULL;
+ const char *discard;
+ const char *detect_zeroes;
QemuOpts *opts;
BlockDriver *drv;
Error *local_err = NULL;
goto fail_opts;
}
+ update_flags_from_options(&bs->open_flags, opts);
+
driver_name = qemu_opt_get(opts, "driver");
drv = bdrv_find_format(driver_name);
assert(drv != NULL);
goto fail_opts;
}
- bs->request_alignment = drv->bdrv_co_preadv ? 1 : 512;
bs->read_only = !(bs->open_flags & BDRV_O_RDWR);
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
}
}
+ discard = qemu_opt_get(opts, "discard");
+ if (discard != NULL) {
+ if (bdrv_parse_discard_flags(discard, &bs->open_flags) != 0) {
+ error_setg(errp, "Invalid discard option");
+ ret = -EINVAL;
+ goto fail_opts;
+ }
+ }
+
+ detect_zeroes = qemu_opt_get(opts, "detect-zeroes");
+ if (detect_zeroes) {
+ BlockdevDetectZeroesOptions value =
+ qapi_enum_parse(BlockdevDetectZeroesOptions_lookup,
+ detect_zeroes,
+ BLOCKDEV_DETECT_ZEROES_OPTIONS__MAX,
+ BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ ret = -EINVAL;
+ goto fail_opts;
+ }
+
+ if (value == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
+ !(bs->open_flags & BDRV_O_UNMAP))
+ {
+ error_setg(errp, "setting detect-zeroes to unmap is not allowed "
+ "without setting discard operation to unmap");
+ ret = -EINVAL;
+ goto fail_opts;
+ }
+
+ bs->detect_zeroes = value;
+ }
+
if (filename != NULL) {
pstrcpy(bs->filename, sizeof(bs->filename), filename);
} else {
bs->drv = drv;
bs->opaque = g_malloc0(drv->instance_size);
- /* Apply cache mode options */
- update_flags_from_options(&bs->open_flags, opts);
-
/* Open the image, either directly or using a protocol */
open_flags = bdrv_open_flags(bs, bs->open_flags);
if (drv->bdrv_file_open) {
assert(bdrv_opt_mem_align(bs) != 0);
assert(bdrv_min_mem_align(bs) != 0);
- assert(is_power_of_2(bs->request_alignment) || bdrv_is_sg(bs));
+ assert(is_power_of_2(bs->bl.request_alignment));
qemu_opts_del(opts);
return 0;
backing_hd->drv ? backing_hd->drv->format_name : "");
bdrv_op_block_all(backing_hd, bs->backing_blocker);
- /* Otherwise we won't be able to commit due to check in bdrv_commit */
+ /* Otherwise we won't be able to commit or stream */
bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET,
bs->backing_blocker);
+ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_STREAM,
+ bs->backing_blocker);
+ /*
+ * We do backup in 3 ways:
+ * 1. drive backup
+ * The target bs is new opened, and the source is top BDS
+ * 2. blockdev backup
+ * Both the source and the target are top BDSes.
+ * 3. internal backup(used for block replication)
+ * Both the source and the target are backing file
+ *
+ * In case 1 and 2, neither the source nor the target is the backing file.
+ * In case 3, we will block the top BDS, so there is only one block job
+ * for the top BDS and its backing chain.
+ */
+ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_SOURCE,
+ bs->backing_blocker);
+ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
+ bs->backing_blocker);
out:
bdrv_refresh_limits(bs, NULL);
}
goto fail;
}
+ /* Set the BDRV_O_RDWR and BDRV_O_ALLOW_RDWR flags.
+ * FIXME: we're parsing the QDict to avoid having to create a
+ * QemuOpts just for this, but neither option is optimal. */
+ if (g_strcmp0(qdict_get_try_str(options, BDRV_OPT_READ_ONLY), "on") &&
+ !qdict_get_try_bool(options, BDRV_OPT_READ_ONLY, false)) {
+ flags |= (BDRV_O_RDWR | BDRV_O_ALLOW_RDWR);
+ } else {
+ flags &= ~BDRV_O_RDWR;
+ }
+
+ if (flags & BDRV_O_SNAPSHOT) {
+ snapshot_options = qdict_new();
+ bdrv_temp_snapshot_options(&snapshot_flags, snapshot_options,
+ flags, options);
+ /* Let bdrv_backing_options() override "read-only" */
+ qdict_del(options, BDRV_OPT_READ_ONLY);
+ bdrv_backing_options(&flags, options, flags, options);
+ }
+
bs->open_flags = flags;
bs->options = options;
options = qdict_clone_shallow(options);
/* Open image file without format layer */
if ((flags & BDRV_O_PROTOCOL) == 0) {
- if (flags & BDRV_O_RDWR) {
- flags |= BDRV_O_ALLOW_RDWR;
- }
- if (flags & BDRV_O_SNAPSHOT) {
- snapshot_options = qdict_new();
- bdrv_temp_snapshot_options(&snapshot_flags, snapshot_options,
- flags, options);
- bdrv_backing_options(&flags, options, flags, options);
- }
-
- bs->open_flags = flags;
-
file = bdrv_open_child(filename, options, "file", bs,
&child_file, true, &local_err);
if (local_err) {
/* Image format probing */
bs->probed = !drv;
if (!drv && file) {
- ret = find_image_format(file->bs, filename, &drv, &local_err);
+ ret = find_image_format(file, filename, &drv, &local_err);
if (ret < 0) {
goto fail;
}
bdrv_refresh_filename(bs);
/* Check if any unknown options were used */
- if (options && (qdict_size(options) != 0)) {
+ if (qdict_size(options) != 0) {
const QDictEntry *entry = qdict_first(options);
if (flags & BDRV_O_PROTOCOL) {
error_setg(errp, "Block protocol '%s' doesn't support the option "
QDECREF(options);
bs->options = NULL;
bdrv_unref(bs);
- if (local_err) {
- error_propagate(errp, local_err);
- }
+ error_propagate(errp, local_err);
return NULL;
close_and_fail:
bdrv_unref(bs);
QDECREF(snapshot_options);
QDECREF(options);
- if (local_err) {
- error_propagate(errp, local_err);
- }
+ error_propagate(errp, local_err);
return NULL;
}
options = qdict_new();
}
+ /* Check if this BlockDriverState is already in the queue */
+ QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
+ if (bs == bs_entry->state.bs) {
+ break;
+ }
+ }
+
/*
* Precedence of options:
* 1. Explicitly passed in options (highest)
}
/* Old explicitly set values (don't overwrite by inherited value) */
- old_options = qdict_clone_shallow(bs->explicit_options);
+ if (bs_entry) {
+ old_options = qdict_clone_shallow(bs_entry->state.explicit_options);
+ } else {
+ old_options = qdict_clone_shallow(bs->explicit_options);
+ }
bdrv_join_options(bs, options, old_options);
QDECREF(old_options);
child->role, options, flags);
}
- bs_entry = g_new0(BlockReopenQueueEntry, 1);
- QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry);
+ if (!bs_entry) {
+ bs_entry = g_new0(BlockReopenQueueEntry, 1);
+ QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry);
+ } else {
+ QDECREF(bs_entry->state.options);
+ QDECREF(bs_entry->state.explicit_options);
+ }
bs_entry->state.bs = bs;
bs_entry->state.options = options;
* to all devices.
*
*/
-int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
+int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
{
int ret = -1;
BlockReopenQueueEntry *bs_entry, *next;
assert(bs_queue != NULL);
- bdrv_drain_all();
+ aio_context_release(ctx);
+ bdrv_drain_all_begin();
+ aio_context_acquire(ctx);
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
g_free(bs_entry);
}
g_free(bs_queue);
+
+ bdrv_drain_all_end();
+
return ret;
}
Error *local_err = NULL;
BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
- ret = bdrv_reopen_multiple(queue, &local_err);
+ ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
}
bs->backing_file[0] = '\0';
bs->backing_format[0] = '\0';
bs->total_sectors = 0;
- bs->encrypted = 0;
- bs->valid_key = 0;
- bs->sg = 0;
+ bs->encrypted = false;
+ bs->valid_key = false;
+ bs->sg = false;
QDECREF(bs->options);
QDECREF(bs->explicit_options);
bs->options = NULL;
void bdrv_close_all(void)
{
block_job_cancel_sync_all();
+ nbd_export_close_all();
/* Drop references from requests still in flight, such as canceled block
* jobs whose AIO context has not been polled yet */
return bs->drv->bdrv_check(bs, res, fix);
}
-#define COMMIT_BUF_SECTORS 2048
-
-/* commit COW file into the raw image */
-int bdrv_commit(BlockDriverState *bs)
-{
- BlockDriver *drv = bs->drv;
- int64_t sector, total_sectors, length, backing_length;
- int n, ro, open_flags;
- int ret = 0;
- uint8_t *buf = NULL;
-
- if (!drv)
- return -ENOMEDIUM;
-
- if (!bs->backing) {
- return -ENOTSUP;
- }
-
- if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, NULL) ||
- bdrv_op_is_blocked(bs->backing->bs, BLOCK_OP_TYPE_COMMIT_TARGET, NULL)) {
- return -EBUSY;
- }
-
- ro = bs->backing->bs->read_only;
- open_flags = bs->backing->bs->open_flags;
-
- if (ro) {
- if (bdrv_reopen(bs->backing->bs, open_flags | BDRV_O_RDWR, NULL)) {
- return -EACCES;
- }
- }
-
- length = bdrv_getlength(bs);
- if (length < 0) {
- ret = length;
- goto ro_cleanup;
- }
-
- backing_length = bdrv_getlength(bs->backing->bs);
- if (backing_length < 0) {
- ret = backing_length;
- goto ro_cleanup;
- }
-
- /* If our top snapshot is larger than the backing file image,
- * grow the backing file image if possible. If not possible,
- * we must return an error */
- if (length > backing_length) {
- ret = bdrv_truncate(bs->backing->bs, length);
- if (ret < 0) {
- goto ro_cleanup;
- }
- }
-
- total_sectors = length >> BDRV_SECTOR_BITS;
-
- /* qemu_try_blockalign() for bs will choose an alignment that works for
- * bs->backing->bs as well, so no need to compare the alignment manually. */
- buf = qemu_try_blockalign(bs, COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE);
- if (buf == NULL) {
- ret = -ENOMEM;
- goto ro_cleanup;
- }
-
- for (sector = 0; sector < total_sectors; sector += n) {
- ret = bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n);
- if (ret < 0) {
- goto ro_cleanup;
- }
- if (ret) {
- ret = bdrv_read(bs, sector, buf, n);
- if (ret < 0) {
- goto ro_cleanup;
- }
-
- ret = bdrv_write(bs->backing->bs, sector, buf, n);
- if (ret < 0) {
- goto ro_cleanup;
- }
- }
- }
-
- if (drv->bdrv_make_empty) {
- ret = drv->bdrv_make_empty(bs);
- if (ret < 0) {
- goto ro_cleanup;
- }
- bdrv_flush(bs);
- }
-
- /*
- * Make sure all data we wrote to the backing device is actually
- * stable on disk.
- */
- if (bs->backing) {
- bdrv_flush(bs->backing->bs);
- }
-
- ret = 0;
-ro_cleanup:
- qemu_vfree(buf);
-
- if (ro) {
- /* ignoring error return here */
- bdrv_reopen(bs->backing->bs, open_flags & ~BDRV_O_RDWR, NULL);
- }
-
- return ret;
-}
-
/*
* Return values:
* 0 - success
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
bdrv_dirty_bitmap_truncate(bs);
bdrv_parent_cb_resize(bs);
+ ++bs->write_gen;
}
return ret;
}
*nb_sectors_ptr = nb_sectors < 0 ? 0 : nb_sectors;
}
-int bdrv_is_read_only(BlockDriverState *bs)
+bool bdrv_is_read_only(BlockDriverState *bs)
{
return bs->read_only;
}
-int bdrv_is_sg(BlockDriverState *bs)
+bool bdrv_is_sg(BlockDriverState *bs)
{
return bs->sg;
}
-int bdrv_is_encrypted(BlockDriverState *bs)
+bool bdrv_is_encrypted(BlockDriverState *bs)
{
if (bs->backing && bs->backing->bs->encrypted) {
- return 1;
+ return true;
}
return bs->encrypted;
}
-int bdrv_key_required(BlockDriverState *bs)
+bool bdrv_key_required(BlockDriverState *bs)
{
BdrvChild *backing = bs->backing;
if (backing && backing->bs->encrypted && !backing->bs->valid_key) {
- return 1;
+ return true;
}
return (bs->encrypted && !bs->valid_key);
}
}
ret = bs->drv->bdrv_set_key(bs, key);
if (ret < 0) {
- bs->valid_key = 0;
+ bs->valid_key = false;
} else if (!bs->valid_key) {
/* call the change callback now, we skipped it on open */
- bs->valid_key = 1;
+ bs->valid_key = true;
bdrv_parent_cb_change_media(bs, true);
}
return ret;
static int qsort_strcmp(const void *a, const void *b)
{
- return strcmp(a, b);
+ return strcmp(*(char *const *)a, *(char *const *)b);
}
void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
}
}
+ for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); i++) {
+ const char *format_name = block_driver_modules[i].format_name;
+
+ if (format_name) {
+ bool found = false;
+ int j = count;
+
+ while (formats && j && !found) {
+ found = !strcmp(formats[--j], format_name);
+ }
+
+ if (!found) {
+ formats = g_renew(const char *, formats, count + 1);
+ formats[count++] = format_name;
+ }
+ }
+ }
+
qsort(formats, count, sizeof(formats[0]), qsort_strcmp);
for (i = 0; i < count; i++) {
{
BlockDriverInfo bdi;
- if (bs->backing || !(bs->open_flags & BDRV_O_UNMAP)) {
+ if (!(bs->open_flags & BDRV_O_UNMAP)) {
return false;
}
return false;
}
-int bdrv_is_snapshot(BlockDriverState *bs)
-{
- return !!(bs->open_flags & BDRV_O_SNAPSHOT);
-}
-
/* backing_file can either be relative, or absolute, or a protocol. If it is
* relative, it must be relative to the chain. So, passing in bs->filename
* from a BDS as backing_file should not be done, as that may be relative to
void bdrv_eject(BlockDriverState *bs, bool eject_flag)
{
BlockDriver *drv = bs->drv;
- const char *device_name;
if (drv && drv->bdrv_eject) {
drv->bdrv_eject(bs, eject_flag);
}
-
- device_name = bdrv_get_device_name(bs);
- if (device_name[0] != '\0') {
- qapi_event_send_device_tray_moved(device_name,
- eject_flag, &error_abort);
- }
}
/**
out:
qemu_opts_del(opts);
qemu_opts_free(create_opts);
- if (local_err) {
- error_propagate(errp, local_err);
- }
+ error_propagate(errp, local_err);
}
AioContext *bdrv_get_aio_context(BlockDriverState *bs)