#include "hw/block/block.h"
#include "block/blockjob.h"
#include "monitor/monitor.h"
-#include "qapi/qmp/qerror.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "qapi/qmp/types.h"
DriveInfo *dinfo = drive_get_by_blockdev(bs);
if (dinfo && dinfo->auto_del) {
- drive_put_ref(dinfo);
+ drive_del(dinfo);
}
}
error_printf(" %s", name);
}
-static void drive_uninit(DriveInfo *dinfo)
+void drive_del(DriveInfo *dinfo)
{
if (dinfo->opts) {
qemu_opts_del(dinfo->opts);
g_free(dinfo);
}
-void drive_put_ref(DriveInfo *dinfo)
-{
- assert(dinfo->refcount);
- if (--dinfo->refcount == 0) {
- drive_uninit(dinfo);
- }
-}
-
-void drive_get_ref(DriveInfo *dinfo)
-{
- dinfo->refcount++;
-}
-
typedef struct {
QEMUBH *bh;
BlockDriverState *bs;
}
}
+static inline int parse_enum_option(const char *lookup[], const char *buf,
+ int max, int def, Error **errp)
+{
+ int i;
+
+ if (!buf) {
+ return def;
+ }
+
+ for (i = 0; i < max; i++) {
+ if (!strcmp(buf, lookup[i])) {
+ return i;
+ }
+ }
+
+ error_setg(errp, "invalid parameter value: %s", buf);
+ return def;
+}
+
static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
{
if (throttle_conflicting(cfg)) {
Error **errp)
{
const char *buf;
- const char *serial;
int ro = 0;
int bdrv_flags = 0;
int on_read_error, on_write_error;
QemuOpts *opts;
const char *id;
bool has_driver_specific_opts;
+ BlockdevDetectZeroesOptions detect_zeroes;
BlockDriver *drv = NULL;
/* Check common options by copying from bs_opts to opts, all other options
opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error);
if (error) {
error_propagate(errp, error);
- return NULL;
+ goto err_no_opts;
}
qemu_opts_absorb_qdict(opts, bs_opts, &error);
ro = qemu_opt_get_bool(opts, "read-only", 0);
copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false);
- serial = qemu_opt_get(opts, "serial");
-
if ((buf = qemu_opt_get(opts, "discard")) != NULL) {
if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) {
error_setg(errp, "invalid discard option");
}
}
- if (bdrv_find_node(qemu_opts_id(opts))) {
- error_setg(errp, "device id=%s is conflicting with a node-name",
- qemu_opts_id(opts));
+ detect_zeroes =
+ parse_enum_option(BlockdevDetectZeroesOptions_lookup,
+ qemu_opt_get(opts, "detect-zeroes"),
+ BLOCKDEV_DETECT_ZEROES_OPTIONS_MAX,
+ BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF,
+ &error);
+ if (error) {
+ error_propagate(errp, error);
+ goto early_err;
+ }
+
+ if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
+ !(bdrv_flags & BDRV_O_UNMAP)) {
+ error_setg(errp, "setting detect-zeroes to unmap is not allowed "
+ "without setting discard operation to unmap");
goto early_err;
}
/* init */
dinfo = g_malloc0(sizeof(*dinfo));
dinfo->id = g_strdup(qemu_opts_id(opts));
- dinfo->bdrv = bdrv_new(dinfo->id);
+ dinfo->bdrv = bdrv_new(dinfo->id, &error);
+ if (error) {
+ error_propagate(errp, error);
+ goto bdrv_new_err;
+ }
dinfo->bdrv->open_flags = snapshot ? BDRV_O_SNAPSHOT : 0;
dinfo->bdrv->read_only = ro;
- dinfo->refcount = 1;
- if (serial != NULL) {
- dinfo->serial = g_strdup(serial);
- }
+ dinfo->bdrv->detect_zeroes = detect_zeroes;
QTAILQ_INSERT_TAIL(&drives, dinfo, next);
bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
QINCREF(bs_opts);
- ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv, &error);
+ ret = bdrv_open(&dinfo->bdrv, file, NULL, bs_opts, bdrv_flags, drv, &error);
if (ret < 0) {
error_setg(errp, "could not open disk image %s: %s",
err:
bdrv_unref(dinfo->bdrv);
- g_free(dinfo->id);
QTAILQ_REMOVE(&drives, dinfo, next);
+bdrv_new_err:
+ g_free(dinfo->id);
g_free(dinfo);
early_err:
- QDECREF(bs_opts);
qemu_opts_del(opts);
+err_no_opts:
+ QDECREF(bs_opts);
return NULL;
}
.name = "addr",
.type = QEMU_OPT_STRING,
.help = "pci address (virtio only)",
+ },{
+ .name = "serial",
+ .type = QEMU_OPT_STRING,
+ .help = "disk serial number",
},{
.name = "file",
.type = QEMU_OPT_STRING,
},
};
-DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
+DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
{
const char *value;
DriveInfo *dinfo = NULL;
const char *werror, *rerror;
bool read_only = false;
bool copy_on_read;
+ const char *serial;
const char *filename;
Error *local_err = NULL;
&error_abort);
qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err);
if (local_err) {
- qerror_report_err(local_err);
+ error_report("%s", error_get_pretty(local_err));
error_free(local_err);
goto fail;
}
goto fail;
}
+ /* Serial number */
+ serial = qemu_opt_get(legacy_opts, "serial");
+
/* no id supplied -> create one */
if (qemu_opts_id(all_opts) == NULL) {
char *new_id;
/* Actual block device init: Functionality shared with blockdev-add */
dinfo = blockdev_init(filename, bs_opts, &local_err);
+ bs_opts = NULL;
if (dinfo == NULL) {
if (local_err) {
- qerror_report_err(local_err);
+ error_report("%s", error_get_pretty(local_err));
error_free(local_err);
}
goto fail;
dinfo->unit = unit_id;
dinfo->devaddr = devaddr;
+ dinfo->serial = g_strdup(serial);
+
switch(type) {
case IF_IDE:
case IF_SCSI:
fail:
qemu_opts_del(legacy_opts);
+ QDECREF(bs_opts);
return dinfo;
}
static void internal_snapshot_prepare(BlkTransactionState *common,
Error **errp)
{
+ Error *local_err = NULL;
const char *device;
const char *name;
BlockDriverState *bs;
}
/* check whether a snapshot with name exist */
- ret = bdrv_snapshot_find_by_id_and_name(bs, NULL, name, &old_sn, errp);
- if (error_is_set(errp)) {
+ ret = bdrv_snapshot_find_by_id_and_name(bs, NULL, name, &old_sn,
+ &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
return;
} else if (ret) {
error_setg(errp,
return;
}
- if (bdrv_in_use(state->old_bs)) {
- error_set(errp, QERR_DEVICE_IN_USE, device);
+ if (bdrv_op_is_blocked(state->old_bs,
+ BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) {
return;
}
qstring_from_str(snapshot_node_name));
}
- /* We will manually add the backing_hd field to the bs later */
- state->new_bs = bdrv_new("");
/* TODO Inherit bs->options or only take explicit options with an
* extended QMP command? */
- ret = bdrv_open(state->new_bs, new_image_file, options,
+ assert(state->new_bs == NULL);
+ ret = bdrv_open(&state->new_bs, new_image_file, NULL, options,
flags | BDRV_O_NO_BACKING, drv, &local_err);
+ /* We will manually add the backing_hd field to the bs later */
if (ret != 0) {
error_propagate(errp, local_err);
}
static void eject_device(BlockDriverState *bs, int force, Error **errp)
{
- if (bdrv_in_use(bs)) {
- error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
+ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_EJECT, errp)) {
return;
}
if (!bdrv_dev_has_removable_media(bs)) {
- error_set(errp, QERR_DEVICE_NOT_REMOVABLE, bdrv_get_device_name(bs));
+ error_setg(errp, "Device '%s' is not removable",
+ bdrv_get_device_name(bs));
return;
}
if (bdrv_dev_is_medium_locked(bs) && !bdrv_dev_is_tray_open(bs)) {
bdrv_dev_eject_request(bs, force);
if (!force) {
- error_set(errp, QERR_DEVICE_LOCKED, bdrv_get_device_name(bs));
+ error_setg(errp, "Device '%s' is locked",
+ bdrv_get_device_name(bs));
return;
}
}
Error *local_err = NULL;
int ret;
- ret = bdrv_open(bs, filename, NULL, bdrv_flags, drv, &local_err);
+ ret = bdrv_open(&bs, filename, NULL, NULL, bdrv_flags, drv, &local_err);
if (ret < 0) {
error_propagate(errp, local_err);
return;
{
ThrottleConfig cfg;
BlockDriverState *bs;
+ AioContext *aio_context;
bs = bdrv_find(device);
if (!bs) {
return;
}
+ aio_context = bdrv_get_aio_context(bs);
+ aio_context_acquire(aio_context);
+
if (!bs->io_limits_enabled && throttle_enabled(&cfg)) {
bdrv_io_limits_enable(bs);
} else if (bs->io_limits_enabled && !throttle_enabled(&cfg)) {
if (bs->io_limits_enabled) {
bdrv_set_io_limits(bs, &cfg);
}
+
+ aio_context_release(aio_context);
}
int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
const char *id = qdict_get_str(qdict, "id");
BlockDriverState *bs;
+ Error *local_err = NULL;
bs = bdrv_find(id);
if (!bs) {
- qerror_report(QERR_DEVICE_NOT_FOUND, id);
+ error_report("Device '%s' not found", id);
return -1;
}
- if (bdrv_in_use(bs)) {
- qerror_report(QERR_DEVICE_IN_USE, id);
+ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) {
+ error_report("%s", error_get_pretty(local_err));
+ error_free(local_err);
return -1;
}
bdrv_set_on_error(bs, BLOCKDEV_ON_ERROR_REPORT,
BLOCKDEV_ON_ERROR_REPORT);
} else {
- drive_uninit(drive_get_by_blockdev(bs));
+ drive_del(drive_get_by_blockdev(bs));
}
return 0;
return;
}
+ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_STREAM, errp)) {
+ return;
+ }
+
if (base) {
base_bs = bdrv_find_backing_image(bs, base);
if (base_bs == NULL) {
*/
BlockdevOnError on_error = BLOCKDEV_ON_ERROR_REPORT;
+ if (!has_speed) {
+ speed = 0;
+ }
+
/* drain all i/o before commits */
bdrv_drain_all();
return;
}
+ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT, errp)) {
+ return;
+ }
+
/* default top_bs is the active layer */
top_bs = bs;
}
}
- if (bdrv_in_use(bs)) {
- error_set(errp, QERR_DEVICE_IN_USE, device);
+ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
return;
}
return;
}
- target_bs = bdrv_new("");
- ret = bdrv_open(target_bs, target, NULL, flags, drv, &local_err);
+ target_bs = NULL;
+ ret = bdrv_open(&target_bs, target, NULL, NULL, flags, drv, &local_err);
if (ret < 0) {
- bdrv_unref(target_bs);
error_propagate(errp, local_err);
return;
}
}
}
- if (bdrv_in_use(bs)) {
- error_set(errp, QERR_DEVICE_IN_USE, device);
+ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) {
return;
}
/* Mirroring takes care of copy-on-write using the source's backing
* file.
*/
- target_bs = bdrv_new("");
- ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv,
- &local_err);
+ target_bs = NULL;
+ ret = bdrv_open(&target_bs, target, NULL, NULL, flags | BDRV_O_NO_BACKING,
+ drv, &local_err);
if (ret < 0) {
- bdrv_unref(target_bs);
error_propagate(errp, local_err);
return;
}
return;
}
if (job->paused && !force) {
- error_set(errp, QERR_BLOCK_JOB_PAUSED, device);
+ error_setg(errp, "The block job for device '%s' is currently paused",
+ device);
return;
}
void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
{
QmpOutputVisitor *ov = qmp_output_visitor_new();
+ DriveInfo *dinfo;
QObject *obj;
QDict *qdict;
Error *local_err = NULL;
goto fail;
}
- /* TODO Sort it out in raw-posix and drive_init: Reject aio=native with
+ /* TODO Sort it out in raw-posix and drive_new(): Reject aio=native with
* cache.direct=false instead of silently switching to aio=threads, except
- * if called from drive_init.
+ * when called from drive_new().
*
* For now, simply forbidding the combination for all drivers will do. */
if (options->has_aio && options->aio == BLOCKDEV_AIO_OPTIONS_NATIVE) {
- bool direct = options->cache->has_direct && options->cache->direct;
- if (!options->has_cache && !direct) {
+ bool direct = options->has_cache &&
+ options->cache->has_direct &&
+ options->cache->direct;
+ if (!direct) {
error_setg(errp, "aio=native requires cache.direct=true");
goto fail;
}
qdict_flatten(qdict);
- blockdev_init(NULL, qdict, &local_err);
+ dinfo = blockdev_init(NULL, qdict, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto fail;
}
+ if (bdrv_key_required(dinfo->bdrv)) {
+ drive_del(dinfo);
+ error_setg(errp, "blockdev-add doesn't support encrypted devices");
+ goto fail;
+ }
+
fail:
qmp_output_visitor_cleanup(ov);
}
.name = "format",
.type = QEMU_OPT_STRING,
.help = "disk format (raw, qcow2, ...)",
- },{
- .name = "serial",
- .type = QEMU_OPT_STRING,
- .help = "disk serial number",
},{
.name = "rerror",
.type = QEMU_OPT_STRING,
.name = "copy-on-read",
.type = QEMU_OPT_BOOL,
.help = "copy read data from backing file into image file",
+ },{
+ .name = "detect-zeroes",
+ .type = QEMU_OPT_STRING,
+ .help = "try to optimize zero writes (off, on, unmap)",
},
{ /* end of list */ }
},