#include "config-host.h"
#include "qemu-common.h"
#include "trace.h"
-#include "monitor/monitor.h"
#include "block/block_int.h"
#include "block/blockjob.h"
#include "qemu/module.h"
#include "block/qapi.h"
#include "qmp-commands.h"
#include "qemu/timer.h"
+#include "qapi-event.h"
#ifdef CONFIG_BSD
#include <sys/types.h>
{
assert(!bs->io_limits_enabled);
throttle_init(&bs->throttle_state,
+ bdrv_get_aio_context(bs),
QEMU_CLOCK_VIRTUAL,
bdrv_throttle_read_timer_cb,
bdrv_throttle_write_timer_cb,
qemu_co_queue_init(&bs->throttled_reqs[0]);
qemu_co_queue_init(&bs->throttled_reqs[1]);
bs->refcnt = 1;
+ bs->aio_context = qemu_get_aio_context();
return bs;
}
typedef struct CreateCo {
BlockDriver *drv;
char *filename;
- QEMUOptionParameter *options;
+ QemuOpts *opts;
int ret;
Error *err;
} CreateCo;
CreateCo *cco = opaque;
assert(cco->drv);
- ret = cco->drv->bdrv_create(cco->filename, cco->options, &local_err);
+ ret = cco->drv->bdrv_create(cco->filename, cco->opts, &local_err);
if (local_err) {
error_propagate(&cco->err, local_err);
}
}
int bdrv_create(BlockDriver *drv, const char* filename,
- QEMUOptionParameter *options, Error **errp)
+ QemuOpts *opts, Error **errp)
{
int ret;
CreateCo cco = {
.drv = drv,
.filename = g_strdup(filename),
- .options = options,
+ .opts = opts,
.ret = NOT_DONE,
.err = NULL,
};
return ret;
}
-int bdrv_create_file(const char* filename, QEMUOptionParameter *options,
- Error **errp)
+int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp)
{
BlockDriver *drv;
Error *local_err = NULL;
return -ENOENT;
}
- ret = bdrv_create(drv, filename, options, &local_err);
+ ret = bdrv_create(drv, filename, opts, &local_err);
if (local_err) {
error_propagate(errp, local_err);
}
* Clear flags that are internal to the block layer before opening the
* image.
*/
- open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
+ open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_PROTOCOL);
/*
* Snapshots should be writable.
bs->zero_beyond_eof = true;
open_flags = bdrv_open_flags(bs, flags);
bs->read_only = !(open_flags & BDRV_O_RDWR);
+ bs->growable = !!(flags & BDRV_O_PROTOCOL);
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
error_setg(errp,
return ret;
}
+static QDict *parse_json_filename(const char *filename, Error **errp)
+{
+ QObject *options_obj;
+ QDict *options;
+ int ret;
+
+ ret = strstart(filename, "json:", &filename);
+ assert(ret);
+
+ options_obj = qobject_from_json(filename);
+ if (!options_obj) {
+ error_setg(errp, "Could not parse the JSON options");
+ return NULL;
+ }
+
+ if (qobject_type(options_obj) != QTYPE_QDICT) {
+ qobject_decref(options_obj);
+ error_setg(errp, "Invalid JSON object given");
+ return NULL;
+ }
+
+ options = qobject_to_qdict(options_obj);
+ qdict_flatten(options);
+
+ return options;
+}
+
/*
- * Opens a file using a protocol (file, host_device, nbd, ...)
- *
- * options is an indirect pointer to a QDict of options to pass to the block
- * drivers, or pointer to NULL for an empty set of options. If this function
- * takes ownership of the QDict reference, it will set *options to NULL;
- * otherwise, it will contain unused/unrecognized options after this function
- * returns. Then, the caller is responsible for freeing it. If it intends to
- * reuse the QDict, QINCREF() should be called beforehand.
+ * Fills in default options for opening images and converts the legacy
+ * filename/flags pair to option QDict entries.
*/
-static int bdrv_file_open(BlockDriverState *bs, const char *filename,
- QDict **options, int flags, Error **errp)
+static int bdrv_fill_options(QDict **options, const char **pfilename, int flags,
+ BlockDriver *drv, Error **errp)
{
- BlockDriver *drv;
+ const char *filename = *pfilename;
const char *drvname;
+ bool protocol = flags & BDRV_O_PROTOCOL;
bool parse_filename = false;
Error *local_err = NULL;
- int ret;
+
+ /* Parse json: pseudo-protocol */
+ if (filename && g_str_has_prefix(filename, "json:")) {
+ QDict *json_options = parse_json_filename(filename, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return -EINVAL;
+ }
+
+ /* Options given in the filename have lower priority than options
+ * specified directly */
+ qdict_join(*options, json_options, false);
+ QDECREF(json_options);
+ *pfilename = filename = NULL;
+ }
/* Fetch the file name from the options QDict if necessary */
- if (!filename) {
- filename = qdict_get_try_str(*options, "filename");
- } else if (filename && !qdict_haskey(*options, "filename")) {
- qdict_put(*options, "filename", qstring_from_str(filename));
- parse_filename = true;
- } else {
- error_setg(errp, "Can't specify 'file' and 'filename' options at the "
- "same time");
- ret = -EINVAL;
- goto fail;
+ if (protocol && filename) {
+ if (!qdict_haskey(*options, "filename")) {
+ qdict_put(*options, "filename", qstring_from_str(filename));
+ parse_filename = true;
+ } else {
+ error_setg(errp, "Can't specify 'file' and 'filename' options at "
+ "the same time");
+ return -EINVAL;
+ }
}
/* Find the right block driver */
+ filename = qdict_get_try_str(*options, "filename");
drvname = qdict_get_try_str(*options, "driver");
- if (drvname) {
- drv = bdrv_find_format(drvname);
- if (!drv) {
- error_setg(errp, "Unknown driver '%s'", drvname);
- }
- qdict_del(*options, "driver");
- } else if (filename) {
- drv = bdrv_find_protocol(filename, parse_filename);
- if (!drv) {
- error_setg(errp, "Unknown protocol");
+
+ if (drv) {
+ if (drvname) {
+ error_setg(errp, "Driver specified twice");
+ return -EINVAL;
}
+ drvname = drv->format_name;
+ qdict_put(*options, "driver", qstring_from_str(drvname));
} else {
- error_setg(errp, "Must specify either driver or file");
- drv = NULL;
- }
+ if (!drvname && protocol) {
+ if (filename) {
+ drv = bdrv_find_protocol(filename, parse_filename);
+ if (!drv) {
+ error_setg(errp, "Unknown protocol");
+ return -EINVAL;
+ }
- if (!drv) {
- /* errp has been set already */
- ret = -ENOENT;
- goto fail;
+ drvname = drv->format_name;
+ qdict_put(*options, "driver", qstring_from_str(drvname));
+ } else {
+ error_setg(errp, "Must specify either driver or file");
+ return -EINVAL;
+ }
+ } else if (drvname) {
+ drv = bdrv_find_format(drvname);
+ if (!drv) {
+ error_setg(errp, "Unknown driver '%s'", drvname);
+ return -ENOENT;
+ }
+ }
}
- /* Parse the filename and open it */
- if (drv->bdrv_parse_filename && parse_filename) {
+ assert(drv || !protocol);
+
+ /* Driver-specific filename parsing */
+ if (drv && drv->bdrv_parse_filename && parse_filename) {
drv->bdrv_parse_filename(filename, *options, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- ret = -EINVAL;
- goto fail;
+ return -EINVAL;
}
if (!drv->bdrv_needs_filename) {
qdict_del(*options, "filename");
- } else {
- filename = qdict_get_str(*options, "filename");
}
}
- if (!drv->bdrv_file_open) {
- ret = bdrv_open(&bs, filename, NULL, *options, flags, drv, &local_err);
- *options = NULL;
- } else {
- ret = bdrv_open_common(bs, NULL, *options, flags, drv, &local_err);
- }
- if (ret < 0) {
- error_propagate(errp, local_err);
- goto fail;
- }
-
- bs->growable = 1;
return 0;
-
-fail:
- return ret;
}
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd)
bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX);
}
+ if (!bs->drv || !bs->drv->supports_backing) {
+ ret = -EINVAL;
+ error_setg(errp, "Driver doesn't support backing files");
+ QDECREF(options);
+ goto free_exit;
+ }
+
backing_hd = bdrv_new("", errp);
if (bs->backing_format[0] != '\0') {
return ret;
}
-void bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
+int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
{
/* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
char *tmp_filename = g_malloc0(PATH_MAX + 1);
int64_t total_size;
BlockDriver *bdrv_qcow2;
- QEMUOptionParameter *create_options;
+ QemuOpts *opts = NULL;
QDict *snapshot_options;
BlockDriverState *bs_snapshot;
Error *local_err;
/* Get the required size from the image */
total_size = bdrv_getlength(bs);
if (total_size < 0) {
+ ret = total_size;
error_setg_errno(errp, -total_size, "Could not get image size");
goto out;
}
}
bdrv_qcow2 = bdrv_find_format("qcow2");
- create_options = parse_option_parameters("", bdrv_qcow2->create_options,
- NULL);
-
- set_option_parameter_int(create_options, BLOCK_OPT_SIZE, total_size);
-
- ret = bdrv_create(bdrv_qcow2, tmp_filename, create_options, &local_err);
- free_option_parameters(create_options);
+ opts = qemu_opts_create(bdrv_qcow2->create_opts, NULL, 0,
+ &error_abort);
+ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_size);
+ ret = bdrv_create(bdrv_qcow2, tmp_filename, opts, &local_err);
+ qemu_opts_del(opts);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not create temporary overlay "
"'%s': %s", tmp_filename,
out:
g_free(tmp_filename);
-}
-
-static QDict *parse_json_filename(const char *filename, Error **errp)
-{
- QObject *options_obj;
- QDict *options;
- int ret;
-
- ret = strstart(filename, "json:", &filename);
- assert(ret);
-
- options_obj = qobject_from_json(filename);
- if (!options_obj) {
- error_setg(errp, "Could not parse the JSON options");
- return NULL;
- }
-
- if (qobject_type(options_obj) != QTYPE_QDICT) {
- qobject_decref(options_obj);
- error_setg(errp, "Invalid JSON object given");
- return NULL;
- }
-
- options = qobject_to_qdict(options_obj);
- qdict_flatten(options);
-
- return options;
+ return ret;
}
/*
options = qdict_new();
}
- if (filename && g_str_has_prefix(filename, "json:")) {
- QDict *json_options = parse_json_filename(filename, &local_err);
- if (local_err) {
- ret = -EINVAL;
- goto fail;
- }
-
- /* Options given in the filename have lower priority than options
- * specified directly */
- qdict_join(options, json_options, false);
- QDECREF(json_options);
- filename = NULL;
- }
-
- bs->options = options;
- options = qdict_clone_shallow(options);
-
- if (flags & BDRV_O_PROTOCOL) {
- assert(!drv);
- ret = bdrv_file_open(bs, filename, &options, flags & ~BDRV_O_PROTOCOL,
- &local_err);
- if (!ret) {
- drv = bs->drv;
- goto done;
- } else if (bs->drv) {
- goto close_and_fail;
- } else {
- goto fail;
- }
- }
-
- /* Open image file without format layer */
- if (flags & BDRV_O_RDWR) {
- flags |= BDRV_O_ALLOW_RDWR;
- }
- if (flags & BDRV_O_SNAPSHOT) {
- snapshot_flags = bdrv_temp_snapshot_flags(flags);
- flags = bdrv_backing_flags(flags);
- }
-
- assert(file == NULL);
- ret = bdrv_open_image(&file, filename, options, "file",
- bdrv_inherited_flags(flags),
- true, &local_err);
- if (ret < 0) {
+ ret = bdrv_fill_options(&options, &filename, flags, drv, &local_err);
+ if (local_err) {
goto fail;
}
/* Find the right image format driver */
+ drv = NULL;
drvname = qdict_get_try_str(options, "driver");
if (drvname) {
drv = bdrv_find_format(drvname);
qdict_del(options, "driver");
if (!drv) {
- error_setg(errp, "Invalid driver: '%s'", drvname);
+ error_setg(errp, "Unknown driver: '%s'", drvname);
ret = -EINVAL;
goto fail;
}
}
- if (!drv) {
- if (file) {
- ret = find_image_format(file, filename, &drv, &local_err);
- } else {
- error_setg(errp, "Must specify either driver or file");
- ret = -EINVAL;
+ assert(drvname || !(flags & BDRV_O_PROTOCOL));
+ if (drv && !drv->bdrv_file_open) {
+ /* If the user explicitly wants a format driver here, we'll need to add
+ * another layer for the protocol in bs->file */
+ flags &= ~BDRV_O_PROTOCOL;
+ }
+
+ 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_flags = bdrv_temp_snapshot_flags(flags);
+ flags = bdrv_backing_flags(flags);
+ }
+
+ assert(file == NULL);
+ ret = bdrv_open_image(&file, filename, options, "file",
+ bdrv_inherited_flags(flags),
+ true, &local_err);
+ if (ret < 0) {
goto fail;
}
}
- if (!drv) {
+ /* Image format probing */
+ if (!drv && file) {
+ ret = find_image_format(file, filename, &drv, &local_err);
+ if (ret < 0) {
+ goto fail;
+ }
+ } else if (!drv) {
+ error_setg(errp, "Must specify either driver or file");
+ ret = -EINVAL;
goto fail;
}
/* For snapshot=on, create a temporary qcow2 overlay. bs points to the
* temporary snapshot afterwards. */
if (snapshot_flags) {
- bdrv_append_temp_snapshot(bs, snapshot_flags, &local_err);
+ ret = bdrv_append_temp_snapshot(bs, snapshot_flags, &local_err);
if (local_err) {
- error_propagate(errp, local_err);
goto close_and_fail;
}
}
-
-done:
/* Check if any unknown options were used */
if (options && (qdict_size(options) != 0)) {
const QDictEntry *entry = qdict_first(options);
BlockDriverState *bs;
QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
+ AioContext *aio_context = bdrv_get_aio_context(bs);
+
+ aio_context_acquire(aio_context);
bdrv_close(bs);
+ aio_context_release(aio_context);
}
}
return false;
}
-static bool bdrv_requests_pending_all(void)
-{
- BlockDriverState *bs;
- QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
- if (bdrv_requests_pending(bs)) {
- return true;
- }
- }
- return false;
-}
-
/*
* Wait for pending requests to complete across all BlockDriverStates
*
BlockDriverState *bs;
while (busy) {
+ busy = false;
+
QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
+ AioContext *aio_context = bdrv_get_aio_context(bs);
+ bool bs_busy;
+
+ aio_context_acquire(aio_context);
+ bdrv_flush_io_queue(bs);
bdrv_start_throttled_reqs(bs);
- }
+ bs_busy = bdrv_requests_pending(bs);
+ bs_busy |= aio_poll(aio_context, bs_busy);
+ aio_context_release(aio_context);
- busy = bdrv_requests_pending_all();
- busy |= aio_poll(qemu_get_aio_context(), busy);
+ busy |= bs_busy;
+ }
}
}
bs->dev_opaque = opaque;
}
-void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv,
- enum MonitorEvent ev,
- BlockErrorAction action, bool is_read)
-{
- QObject *data;
- const char *action_str;
-
- switch (action) {
- case BDRV_ACTION_REPORT:
- action_str = "report";
- break;
- case BDRV_ACTION_IGNORE:
- action_str = "ignore";
- break;
- case BDRV_ACTION_STOP:
- action_str = "stop";
- break;
- default:
- abort();
- }
-
- data = qobject_from_jsonf("{ 'device': %s, 'action': %s, 'operation': %s }",
- bdrv->device_name,
- action_str,
- is_read ? "read" : "write");
- monitor_protocol_event(ev, data);
-
- qobject_decref(data);
-}
-
-static void bdrv_emit_qmp_eject_event(BlockDriverState *bs, bool ejected)
-{
- QObject *data;
-
- data = qobject_from_jsonf("{ 'device': %s, 'tray-open': %i }",
- bdrv_get_device_name(bs), ejected);
- monitor_protocol_event(QEVENT_DEVICE_TRAY_MOVED, data);
-
- qobject_decref(data);
-}
-
static void bdrv_dev_change_media_cb(BlockDriverState *bs, bool load)
{
if (bs->dev_ops && bs->dev_ops->change_media_cb) {
bs->dev_ops->change_media_cb(bs->dev_opaque, load);
if (tray_was_closed) {
/* tray open */
- bdrv_emit_qmp_eject_event(bs, true);
+ qapi_event_send_device_tray_moved(bdrv_get_device_name(bs),
+ true, &error_abort);
}
if (load) {
/* tray close */
- bdrv_emit_qmp_eject_event(bs, false);
+ qapi_event_send_device_tray_moved(bdrv_get_device_name(bs),
+ false, &error_abort);
}
}
}
BlockDriverState *bs;
QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
+ AioContext *aio_context = bdrv_get_aio_context(bs);
+
+ aio_context_acquire(aio_context);
if (bs->drv && bs->backing_hd) {
int ret = bdrv_commit(bs);
if (ret < 0) {
+ aio_context_release(aio_context);
return ret;
}
}
+ aio_context_release(aio_context);
}
return 0;
}
*
* Returns NULL if bs is not found in active's image chain,
* or if active == bs.
+ *
+ * Returns the bottommost base image if bs == NULL.
*/
BlockDriverState *bdrv_find_overlay(BlockDriverState *active,
BlockDriverState *bs)
{
- BlockDriverState *overlay = NULL;
- BlockDriverState *intermediate;
-
- assert(active != NULL);
- assert(bs != NULL);
-
- /* if bs is the same as active, then by definition it has no overlay
- */
- if (active == bs) {
- return NULL;
+ while (active && bs != active->backing_hd) {
+ active = active->backing_hd;
}
- intermediate = active;
- while (intermediate->backing_hd) {
- if (intermediate->backing_hd == bs) {
- overlay = intermediate;
- break;
- }
- intermediate = intermediate->backing_hd;
- }
+ return active;
+}
- return overlay;
+/* Given a BDS, searches for the base layer. */
+BlockDriverState *bdrv_find_base(BlockDriverState *bs)
+{
+ return bdrv_find_overlay(bs, NULL);
}
typedef struct BlkIntermediateStates {
*
* base <- active
*
+ * If backing_file_str is non-NULL, it will be used when modifying top's
+ * overlay image metadata.
+ *
* Error conditions:
* if active == top, that is considered an error
*
*/
int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
- BlockDriverState *base)
+ BlockDriverState *base, const char *backing_file_str)
{
BlockDriverState *intermediate;
BlockDriverState *base_bs = NULL;
}
/* success - we can delete the intermediate states, and link top->base */
- ret = bdrv_change_backing_file(new_top_bs, base_bs->filename,
+ backing_file_str = backing_file_str ? backing_file_str : base_bs->filename;
+ ret = bdrv_change_backing_file(new_top_bs, backing_file_str,
base_bs->drv ? base_bs->drv->format_name : "");
if (ret) {
goto exit;
return -ENOTSUP;
if (bs->read_only)
return -EACCES;
- if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, NULL)) {
- return -EBUSY;
- }
+
ret = drv->bdrv_truncate(bs, offset);
if (ret == 0) {
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
switch (on_err) {
case BLOCKDEV_ON_ERROR_ENOSPC:
- return (error == ENOSPC) ? BDRV_ACTION_STOP : BDRV_ACTION_REPORT;
+ return (error == ENOSPC) ?
+ BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT;
case BLOCKDEV_ON_ERROR_STOP:
- return BDRV_ACTION_STOP;
+ return BLOCK_ERROR_ACTION_STOP;
case BLOCKDEV_ON_ERROR_REPORT:
- return BDRV_ACTION_REPORT;
+ return BLOCK_ERROR_ACTION_REPORT;
case BLOCKDEV_ON_ERROR_IGNORE:
- return BDRV_ACTION_IGNORE;
+ return BLOCK_ERROR_ACTION_IGNORE;
default:
abort();
}
bool is_read, int error)
{
assert(error >= 0);
- bdrv_emit_qmp_error_event(bs, QEVENT_BLOCK_IO_ERROR, action, is_read);
- if (action == BDRV_ACTION_STOP) {
- vm_stop(RUN_STATE_IO_ERROR);
+
+ if (action == BLOCK_ERROR_ACTION_STOP) {
+ /* First set the iostatus, so that "info block" returns an iostatus
+ * that matches the events raised so far (an additional error iostatus
+ * is fine, but not a lost one).
+ */
bdrv_iostatus_set_err(bs, error);
+
+ /* Then raise the request to stop the VM and the event.
+ * qemu_system_vmstop_request_prepare has two effects. First,
+ * it ensures that the STOP event always comes after the
+ * BLOCK_IO_ERROR event. Second, it ensures that even if management
+ * can observe the STOP event and do a "cont" before the STOP
+ * event is issued, the VM will not stop. In this case, vm_start()
+ * also ensures that the STOP/RESUME pair of events is emitted.
+ */
+ qemu_system_vmstop_request_prepare();
+ qapi_event_send_block_io_error(bdrv_get_device_name(bs),
+ is_read ? IO_OPERATION_TYPE_READ :
+ IO_OPERATION_TYPE_WRITE,
+ action, &error_abort);
+ qemu_system_vmstop_request(RUN_STATE_IO_ERROR);
+ } else {
+ qapi_event_send_block_io_error(bdrv_get_device_name(bs),
+ is_read ? IO_OPERATION_TYPE_READ :
+ IO_OPERATION_TYPE_WRITE,
+ action, &error_abort);
}
}
return NULL;
}
+/* If 'base' is in the same chain as 'top', return true. Otherwise,
+ * return false. If either argument is NULL, return false. */
+bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base)
+{
+ while (top && top != base) {
+ top = top->backing_hd;
+ }
+
+ return top != NULL;
+}
+
BlockDriverState *bdrv_next(BlockDriverState *bs)
{
if (!bs) {
int result = 0;
QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
- int ret = bdrv_flush(bs);
+ AioContext *aio_context = bdrv_get_aio_context(bs);
+ int ret;
+
+ aio_context_acquire(aio_context);
+ ret = bdrv_flush(bs);
if (ret < 0 && !result) {
result = ret;
}
+ aio_context_release(aio_context);
}
return result;
return 1 + bdrv_get_backing_file_depth(bs->backing_hd);
}
-BlockDriverState *bdrv_find_base(BlockDriverState *bs)
-{
- BlockDriverState *curr_bs = NULL;
-
- if (!bs) {
- return NULL;
- }
-
- curr_bs = bs;
-
- while (curr_bs->backing_hd) {
- curr_bs = curr_bs->backing_hd;
- }
- return curr_bs;
-}
-
/**************************************************************/
/* async I/Os */
Error *local_err = NULL;
QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
+ AioContext *aio_context = bdrv_get_aio_context(bs);
+
+ aio_context_acquire(aio_context);
bdrv_invalidate_cache(bs, &local_err);
+ aio_context_release(aio_context);
if (local_err) {
error_propagate(errp, local_err);
return;
BlockDriverState *bs;
QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
+ AioContext *aio_context = bdrv_get_aio_context(bs);
+
+ aio_context_acquire(aio_context);
bs->open_flags = bs->open_flags & ~(BDRV_O_INCOMING);
+ aio_context_release(aio_context);
}
}
}
if (bs->device_name[0] != '\0') {
- bdrv_emit_qmp_eject_event(bs, eject_flag);
+ qapi_event_send_device_tray_moved(bdrv_get_device_name(bs),
+ eject_flag, &error_abort);
}
}
char *options, uint64_t img_size, int flags,
Error **errp, bool quiet)
{
- QEMUOptionParameter *param = NULL, *create_options = NULL;
- QEMUOptionParameter *backing_fmt, *backing_file, *size;
+ QemuOptsList *create_opts = NULL;
+ QemuOpts *opts = NULL;
+ const char *backing_fmt, *backing_file;
+ int64_t size;
BlockDriver *drv, *proto_drv;
BlockDriver *backing_drv = NULL;
Error *local_err = NULL;
return;
}
- create_options = append_option_parameters(create_options,
- drv->create_options);
- create_options = append_option_parameters(create_options,
- proto_drv->create_options);
+ create_opts = qemu_opts_append(create_opts, drv->create_opts);
+ create_opts = qemu_opts_append(create_opts, proto_drv->create_opts);
/* Create parameter list with default values */
- param = parse_option_parameters("", create_options, param);
-
- set_option_parameter_int(param, BLOCK_OPT_SIZE, img_size);
+ opts = qemu_opts_create(create_opts, NULL, 0, &error_abort);
+ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size);
/* Parse -o options */
if (options) {
- param = parse_option_parameters(options, create_options, param);
- if (param == NULL) {
- error_setg(errp, "Invalid options for file format '%s'.", fmt);
+ if (qemu_opts_do_parse(opts, options, NULL) != 0) {
+ error_setg(errp, "Invalid options for file format '%s'", fmt);
goto out;
}
}
if (base_filename) {
- if (set_option_parameter(param, BLOCK_OPT_BACKING_FILE,
- base_filename)) {
+ if (qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename)) {
error_setg(errp, "Backing file not supported for file format '%s'",
fmt);
goto out;
}
if (base_fmt) {
- if (set_option_parameter(param, BLOCK_OPT_BACKING_FMT, base_fmt)) {
+ if (qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, base_fmt)) {
error_setg(errp, "Backing file format not supported for file "
"format '%s'", fmt);
goto out;
}
}
- backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
- if (backing_file && backing_file->value.s) {
- if (!strcmp(filename, backing_file->value.s)) {
+ backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE);
+ if (backing_file) {
+ if (!strcmp(filename, backing_file)) {
error_setg(errp, "Error: Trying to create an image with the "
"same filename as the backing file");
goto out;
}
}
- backing_fmt = get_option_parameter(param, BLOCK_OPT_BACKING_FMT);
- if (backing_fmt && backing_fmt->value.s) {
- backing_drv = bdrv_find_format(backing_fmt->value.s);
+ backing_fmt = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
+ if (backing_fmt) {
+ backing_drv = bdrv_find_format(backing_fmt);
if (!backing_drv) {
error_setg(errp, "Unknown backing file format '%s'",
- backing_fmt->value.s);
+ backing_fmt);
goto out;
}
}
// The size for the image must always be specified, with one exception:
// If we are using a backing file, we can obtain the size from there
- size = get_option_parameter(param, BLOCK_OPT_SIZE);
- if (size && size->value.n == -1) {
- if (backing_file && backing_file->value.s) {
+ size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0);
+ if (size == -1) {
+ if (backing_file) {
BlockDriverState *bs;
uint64_t size;
- char buf[32];
int back_flags;
/* backing files always opened read-only */
flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
bs = NULL;
- ret = bdrv_open(&bs, backing_file->value.s, NULL, NULL, back_flags,
+ ret = bdrv_open(&bs, backing_file, NULL, NULL, back_flags,
backing_drv, &local_err);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not open '%s': %s",
- backing_file->value.s,
+ backing_file,
error_get_pretty(local_err));
error_free(local_err);
local_err = NULL;
bdrv_get_geometry(bs, &size);
size *= 512;
- snprintf(buf, sizeof(buf), "%" PRId64, size);
- set_option_parameter(param, BLOCK_OPT_SIZE, buf);
+ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, size);
bdrv_unref(bs);
} else {
if (!quiet) {
printf("Formatting '%s', fmt=%s ", filename, fmt);
- print_option_parameters(param);
+ qemu_opts_print(opts);
puts("");
}
- ret = bdrv_create(drv, filename, param, &local_err);
+
+ ret = bdrv_create(drv, filename, opts, &local_err);
+
if (ret == -EFBIG) {
/* This is generally a better message than whatever the driver would
* deliver (especially because of the cluster_size_hint), since that
* is most probably not much different from "image too large". */
const char *cluster_size_hint = "";
- if (get_option_parameter(create_options, BLOCK_OPT_CLUSTER_SIZE)) {
+ if (qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, 0)) {
cluster_size_hint = " (try using a larger cluster size)";
}
error_setg(errp, "The image size is too large for file format '%s'"
}
out:
- free_option_parameters(create_options);
- free_option_parameters(param);
-
+ qemu_opts_del(opts);
+ qemu_opts_free(create_opts);
if (local_err) {
error_propagate(errp, local_err);
}
AioContext *bdrv_get_aio_context(BlockDriverState *bs)
{
- /* Currently BlockDriverState always uses the main loop AioContext */
- return qemu_get_aio_context();
+ return bs->aio_context;
+}
+
+void bdrv_detach_aio_context(BlockDriverState *bs)
+{
+ if (!bs->drv) {
+ return;
+ }
+
+ if (bs->io_limits_enabled) {
+ throttle_detach_aio_context(&bs->throttle_state);
+ }
+ if (bs->drv->bdrv_detach_aio_context) {
+ bs->drv->bdrv_detach_aio_context(bs);
+ }
+ if (bs->file) {
+ bdrv_detach_aio_context(bs->file);
+ }
+ if (bs->backing_hd) {
+ bdrv_detach_aio_context(bs->backing_hd);
+ }
+
+ bs->aio_context = NULL;
+}
+
+void bdrv_attach_aio_context(BlockDriverState *bs,
+ AioContext *new_context)
+{
+ if (!bs->drv) {
+ return;
+ }
+
+ bs->aio_context = new_context;
+
+ if (bs->backing_hd) {
+ bdrv_attach_aio_context(bs->backing_hd, new_context);
+ }
+ if (bs->file) {
+ bdrv_attach_aio_context(bs->file, new_context);
+ }
+ if (bs->drv->bdrv_attach_aio_context) {
+ bs->drv->bdrv_attach_aio_context(bs, new_context);
+ }
+ if (bs->io_limits_enabled) {
+ throttle_attach_aio_context(&bs->throttle_state, new_context);
+ }
+}
+
+void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
+{
+ bdrv_drain_all(); /* ensure there are no in-flight requests */
+
+ bdrv_detach_aio_context(bs);
+
+ /* This function executes in the old AioContext so acquire the new one in
+ * case it runs in a different thread.
+ */
+ aio_context_acquire(new_context);
+ bdrv_attach_aio_context(bs, new_context);
+ aio_context_release(new_context);
}
void bdrv_add_before_write_notifier(BlockDriverState *bs,
notifier_with_return_list_add(&bs->before_write_notifiers, notifier);
}
-int bdrv_amend_options(BlockDriverState *bs, QEMUOptionParameter *options)
+int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts)
{
- if (bs->drv->bdrv_amend_options == NULL) {
+ if (!bs->drv->bdrv_amend_options) {
return -ENOTSUP;
}
- return bs->drv->bdrv_amend_options(bs, options);
+ return bs->drv->bdrv_amend_options(bs, opts);
}
/* This function will be called by the bdrv_recurse_is_first_non_filter method
return false;
}
+
+BlockDriverState *check_to_replace_node(const char *node_name, Error **errp)
+{
+ BlockDriverState *to_replace_bs = bdrv_find_node(node_name);
+ if (!to_replace_bs) {
+ error_setg(errp, "Node name '%s' not found", node_name);
+ return NULL;
+ }
+
+ if (bdrv_op_is_blocked(to_replace_bs, BLOCK_OP_TYPE_REPLACE, errp)) {
+ return NULL;
+ }
+
+ /* We don't want arbitrary node of the BDS chain to be replaced only the top
+ * most non filter in order to prevent data corruption.
+ * Another benefit is that this tests exclude backing files which are
+ * blocked by the backing blockers.
+ */
+ if (!bdrv_is_first_non_filter(to_replace_bs)) {
+ error_setg(errp, "Only top most non filter can be replaced");
+ return NULL;
+ }
+
+ return to_replace_bs;
+}
+
+void bdrv_io_plug(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ if (drv && drv->bdrv_io_plug) {
+ drv->bdrv_io_plug(bs);
+ } else if (bs->file) {
+ bdrv_io_plug(bs->file);
+ }
+}
+
+void bdrv_io_unplug(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ if (drv && drv->bdrv_io_unplug) {
+ drv->bdrv_io_unplug(bs);
+ } else if (bs->file) {
+ bdrv_io_unplug(bs->file);
+ }
+}
+
+void bdrv_flush_io_queue(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ if (drv && drv->bdrv_flush_io_queue) {
+ drv->bdrv_flush_io_queue(bs);
+ } else if (bs->file) {
+ bdrv_flush_io_queue(bs->file);
+ }
+}