* later. See the COPYING file in the top-level directory.
*/
-#include "block.h"
#include "blockdev.h"
+#include "hw/block-common.h"
#include "monitor.h"
#include "qerror.h"
#include "qemu-option.h"
#include "qemu-config.h"
+#include "qemu-objects.h"
#include "sysemu.h"
#include "block_int.h"
+#include "qmp-commands.h"
+#include "trace.h"
+#include "arch_init.h"
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
{
DriveInfo *dinfo = drive_get_by_blockdev(bs);
+ if (bs->job) {
+ block_job_cancel(bs->job);
+ }
if (dinfo) {
dinfo->auto_del = 1;
}
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")) {
{
const char *buf;
const char *file = NULL;
- char devname[128];
const char *serial;
const char *mediastr = "";
BlockInterfaceType type;
serial = qemu_opt_get(opts, "serial");
if ((buf = qemu_opt_get(opts, "if")) != NULL) {
- pstrcpy(devname, sizeof(devname), buf);
for (type = 0; type < IF_COUNT && strcmp(buf, if_name[type]); type++)
;
if (type == IF_COUNT) {
}
} else {
type = default_to_scsi ? IF_SCSI : IF_IDE;
- pstrcpy(devname, sizeof(devname), if_name[type]);
}
max_devs = if_max_devs[type];
if (cyls || heads || secs) {
- if (cyls < 1 || (type == IF_IDE && cyls > 16383)) {
+ if (cyls < 1) {
error_report("invalid physical cyls number");
return NULL;
}
- if (heads < 1 || (type == IF_IDE && heads > 16)) {
+ if (heads < 1) {
error_report("invalid physical heads number");
return NULL;
}
- if (secs < 1 || (type == IF_IDE && secs > 63)) {
+ if (secs < 1) {
error_report("invalid physical secs number");
return NULL;
}
mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
if (max_devs)
snprintf(dinfo->id, 32, "%s%i%s%i",
- devname, bus_id, mediastr, unit_id);
+ if_name[type], bus_id, mediastr, unit_id);
else
snprintf(dinfo->id, 32, "%s%s%i",
- devname, mediastr, unit_id);
+ if_name[type], mediastr, unit_id);
}
dinfo->bdrv = bdrv_new(dinfo->id);
dinfo->devaddr = devaddr;
dinfo->type = type;
dinfo->bus = bus_id;
dinfo->unit = unit_id;
+ dinfo->cyls = cyls;
+ dinfo->heads = heads;
+ dinfo->secs = secs;
+ dinfo->trans = translation;
dinfo->opts = opts;
dinfo->refcount = 1;
- if (serial)
- strncpy(dinfo->serial, serial, sizeof(dinfo->serial) - 1);
+ dinfo->serial = serial;
QTAILQ_INSERT_TAIL(&drives, dinfo, next);
bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
case IF_SCSI:
case IF_XEN:
case IF_NONE:
- switch(media) {
- case MEDIA_DISK:
- if (cyls != 0) {
- bdrv_set_geometry_hint(dinfo->bdrv, cyls, heads, secs);
- bdrv_set_translation_hint(dinfo->bdrv, translation);
- }
- break;
- case MEDIA_CDROM:
- dinfo->media_cd = 1;
- break;
- }
+ dinfo->media_cd = media == MEDIA_CDROM;
break;
case IF_SD:
case IF_FLOPPY:
break;
case IF_VIRTIO:
/* add virtio block device */
- opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
- qemu_opt_set(opts, "driver", "virtio-blk");
+ opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL);
+ if (arch_type == QEMU_ARCH_S390X) {
+ qemu_opt_set(opts, "driver", "virtio-blk-s390");
+ } else {
+ qemu_opt_set(opts, "driver", "virtio-blk-pci");
+ }
qemu_opt_set(opts, "drive", dinfo->id);
if (devaddr)
qemu_opt_set(opts, "addr", devaddr);
bdrv_flags |= BDRV_O_COPY_ON_READ;
}
+ if (runstate_check(RUN_STATE_INMIGRATE)) {
+ bdrv_flags |= BDRV_O_INCOMING;
+ }
+
if (media == MEDIA_CDROM) {
/* CDROM is fine for any interface, don't check. */
ro = 1;
} else if (ro == 1) {
- if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY && type != IF_NONE) {
+ if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY &&
+ type != IF_NONE && type != IF_PFLASH) {
error_report("readonly not supported by this bus type");
goto err;
}
bdrv_flags |= ro ? 0 : BDRV_O_RDWR;
+ if (ro && copy_on_read) {
+ error_report("warning: disabling copy_on_read on readonly drive");
+ }
+
ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv);
if (ret < 0) {
error_report("could not open disk image %s: %s",
{
const char *device = qdict_get_str(qdict, "device");
BlockDriverState *bs;
+ int ret;
if (!strcmp(device, "all")) {
- bdrv_commit_all();
+ ret = bdrv_commit_all();
+ if (ret == -EBUSY) {
+ qerror_report(QERR_DEVICE_IN_USE, device);
+ return;
+ }
} else {
bs = bdrv_find(device);
if (!bs) {
qerror_report(QERR_DEVICE_NOT_FOUND, device);
return;
}
- bdrv_commit(bs);
+ ret = bdrv_commit(bs);
+ if (ret == -EBUSY) {
+ qerror_report(QERR_DEVICE_IN_USE, device);
+ return;
+ }
}
}
-int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
+static void blockdev_do_action(int kind, void *data, Error **errp)
+{
+ BlockdevAction action;
+ BlockdevActionList list;
+
+ action.kind = kind;
+ action.data = data;
+ list.value = &action;
+ list.next = NULL;
+ qmp_transaction(&list, errp);
+}
+
+void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file,
+ bool has_format, const char *format,
+ bool has_mode, enum NewImageMode mode,
+ Error **errp)
+{
+ BlockdevSnapshot snapshot = {
+ .device = (char *) device,
+ .snapshot_file = (char *) snapshot_file,
+ .has_format = has_format,
+ .format = (char *) format,
+ .has_mode = has_mode,
+ .mode = mode,
+ };
+ blockdev_do_action(BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC, &snapshot,
+ errp);
+}
+
+
+/* New and old BlockDriverState structs for group snapshots */
+typedef struct BlkTransactionStates {
+ BlockDriverState *old_bs;
+ BlockDriverState *new_bs;
+ QSIMPLEQ_ENTRY(BlkTransactionStates) entry;
+} BlkTransactionStates;
+
+/*
+ * 'Atomic' group snapshots. The snapshots are taken as a set, and if any fail
+ * then we do not pivot any of the devices in the group, and abandon the
+ * snapshots
+ */
+void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
{
- const char *device = qdict_get_str(qdict, "device");
- const char *filename = qdict_get_try_str(qdict, "snapshot-file");
- const char *format = qdict_get_try_str(qdict, "format");
- BlockDriverState *bs;
- BlockDriver *drv, *old_drv, *proto_drv;
int ret = 0;
- int flags;
- char old_filename[1024];
+ BlockdevActionList *dev_entry = dev_list;
+ BlkTransactionStates *states, *next;
- if (!filename) {
- qerror_report(QERR_MISSING_PARAMETER, "snapshot-file");
- ret = -1;
- goto out;
- }
+ QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionStates) snap_bdrv_states;
+ QSIMPLEQ_INIT(&snap_bdrv_states);
- bs = bdrv_find(device);
- if (!bs) {
- qerror_report(QERR_DEVICE_NOT_FOUND, device);
- ret = -1;
- goto out;
- }
+ /* drain all i/o before any snapshots */
+ bdrv_drain_all();
+
+ /* We don't do anything in this loop that commits us to the snapshot */
+ while (NULL != dev_entry) {
+ BlockdevAction *dev_info = NULL;
+ BlockDriver *proto_drv;
+ BlockDriver *drv;
+ int flags;
+ enum NewImageMode mode;
+ const char *new_image_file;
+ const char *device;
+ const char *format = "qcow2";
+
+ dev_info = dev_entry->value;
+ dev_entry = dev_entry->next;
+
+ states = g_malloc0(sizeof(BlkTransactionStates));
+ QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, states, entry);
+
+ switch (dev_info->kind) {
+ case BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC:
+ device = dev_info->blockdev_snapshot_sync->device;
+ if (!dev_info->blockdev_snapshot_sync->has_mode) {
+ dev_info->blockdev_snapshot_sync->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
+ }
+ new_image_file = dev_info->blockdev_snapshot_sync->snapshot_file;
+ if (dev_info->blockdev_snapshot_sync->has_format) {
+ format = dev_info->blockdev_snapshot_sync->format;
+ }
+ mode = dev_info->blockdev_snapshot_sync->mode;
+ break;
+ default:
+ abort();
+ }
- pstrcpy(old_filename, sizeof(old_filename), bs->filename);
+ drv = bdrv_find_format(format);
+ if (!drv) {
+ error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
+ goto delete_and_fail;
+ }
- old_drv = bs->drv;
- flags = bs->open_flags;
+ states->old_bs = bdrv_find(device);
+ if (!states->old_bs) {
+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ goto delete_and_fail;
+ }
- if (!format) {
- format = "qcow2";
- }
+ if (!bdrv_is_inserted(states->old_bs)) {
+ error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
+ goto delete_and_fail;
+ }
- drv = bdrv_find_format(format);
- if (!drv) {
- qerror_report(QERR_INVALID_BLOCK_FORMAT, format);
- ret = -1;
- goto out;
- }
+ if (bdrv_in_use(states->old_bs)) {
+ error_set(errp, QERR_DEVICE_IN_USE, device);
+ goto delete_and_fail;
+ }
+
+ if (!bdrv_is_read_only(states->old_bs)) {
+ if (bdrv_flush(states->old_bs)) {
+ error_set(errp, QERR_IO_ERROR);
+ goto delete_and_fail;
+ }
+ }
+
+ flags = states->old_bs->open_flags;
- proto_drv = bdrv_find_protocol(filename);
- if (!proto_drv) {
- qerror_report(QERR_INVALID_BLOCK_FORMAT, format);
- ret = -1;
- goto out;
+ proto_drv = bdrv_find_protocol(new_image_file);
+ if (!proto_drv) {
+ error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
+ goto delete_and_fail;
+ }
+
+ /* create new image w/backing file */
+ if (mode != NEW_IMAGE_MODE_EXISTING) {
+ ret = bdrv_img_create(new_image_file, format,
+ states->old_bs->filename,
+ states->old_bs->drv->format_name,
+ NULL, -1, flags);
+ if (ret) {
+ error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file);
+ goto delete_and_fail;
+ }
+ }
+
+ /* We will manually add the backing_hd field to the bs later */
+ states->new_bs = bdrv_new("");
+ ret = bdrv_open(states->new_bs, new_image_file,
+ flags | BDRV_O_NO_BACKING, drv);
+ if (ret != 0) {
+ error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file);
+ goto delete_and_fail;
+ }
}
- ret = bdrv_img_create(filename, format, bs->filename,
- bs->drv->format_name, NULL, -1, flags);
- if (ret) {
- goto out;
+
+ /* Now we are going to do the actual pivot. Everything up to this point
+ * is reversible, but we are committed at this point */
+ QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
+ /* This removes our old bs from the bdrv_states, and adds the new bs */
+ bdrv_append(states->new_bs, states->old_bs);
}
- bdrv_drain_all();
- bdrv_flush(bs);
+ /* success */
+ goto exit;
- bdrv_close(bs);
- ret = bdrv_open(bs, filename, flags, drv);
+delete_and_fail:
/*
- * If reopening the image file we just created fails, fall back
- * and try to re-open the original image. If that fails too, we
- * are in serious trouble.
- */
- if (ret != 0) {
- ret = bdrv_open(bs, old_filename, flags, old_drv);
- if (ret != 0) {
- qerror_report(QERR_OPEN_FILE_FAILED, old_filename);
- } else {
- qerror_report(QERR_OPEN_FILE_FAILED, filename);
+ * failure, and it is all-or-none; abandon each new bs, and keep using
+ * the original bs for all images
+ */
+ QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
+ if (states->new_bs) {
+ bdrv_delete(states->new_bs);
}
}
-out:
- if (ret) {
- ret = -1;
+exit:
+ QSIMPLEQ_FOREACH_SAFE(states, &snap_bdrv_states, entry, next) {
+ g_free(states);
}
-
- return ret;
+ return;
}
-static int eject_device(Monitor *mon, BlockDriverState *bs, int force)
+
+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));
+ return;
+ }
if (!bdrv_dev_has_removable_media(bs)) {
- qerror_report(QERR_DEVICE_NOT_REMOVABLE, bdrv_get_device_name(bs));
- return -1;
+ error_set(errp, QERR_DEVICE_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) {
- qerror_report(QERR_DEVICE_LOCKED, bdrv_get_device_name(bs));
- return -1;
+ error_set(errp, QERR_DEVICE_LOCKED, bdrv_get_device_name(bs));
+ return;
}
}
+
bdrv_close(bs);
- return 0;
}
-int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data)
+void qmp_eject(const char *device, bool has_force, bool force, Error **errp)
{
BlockDriverState *bs;
- int force = qdict_get_try_bool(qdict, "force", 0);
- const char *filename = qdict_get_str(qdict, "device");
- bs = bdrv_find(filename);
+ bs = bdrv_find(device);
if (!bs) {
- qerror_report(QERR_DEVICE_NOT_FOUND, filename);
- return -1;
+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ return;
}
- return eject_device(mon, bs, force);
+
+ eject_device(bs, force, errp);
}
-int do_block_set_passwd(Monitor *mon, const QDict *qdict,
- QObject **ret_data)
+void qmp_block_passwd(const char *device, const char *password, Error **errp)
{
BlockDriverState *bs;
int err;
- bs = bdrv_find(qdict_get_str(qdict, "device"));
+ bs = bdrv_find(device);
if (!bs) {
- qerror_report(QERR_DEVICE_NOT_FOUND, qdict_get_str(qdict, "device"));
- return -1;
+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ return;
}
- err = bdrv_set_key(bs, qdict_get_str(qdict, "password"));
+ err = bdrv_set_key(bs, password);
if (err == -EINVAL) {
- qerror_report(QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
- return -1;
+ error_set(errp, QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
+ return;
} else if (err < 0) {
- qerror_report(QERR_INVALID_PASSWORD);
- return -1;
+ error_set(errp, QERR_INVALID_PASSWORD);
+ return;
}
+}
- return 0;
+static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
+ int bdrv_flags, BlockDriver *drv,
+ const char *password, Error **errp)
+{
+ if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) {
+ error_set(errp, QERR_OPEN_FILE_FAILED, filename);
+ return;
+ }
+
+ if (bdrv_key_required(bs)) {
+ if (password) {
+ if (bdrv_set_key(bs, password) < 0) {
+ error_set(errp, QERR_INVALID_PASSWORD);
+ }
+ } else {
+ error_set(errp, QERR_DEVICE_ENCRYPTED, bdrv_get_device_name(bs),
+ bdrv_get_encrypted_filename(bs));
+ }
+ } else if (password) {
+ error_set(errp, QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
+ }
}
-int do_change_block(Monitor *mon, const char *device,
- const char *filename, const char *fmt)
+void qmp_change_blockdev(const char *device, const char *filename,
+ bool has_format, const char *format, Error **errp)
{
BlockDriverState *bs;
BlockDriver *drv = NULL;
int bdrv_flags;
+ Error *err = NULL;
bs = bdrv_find(device);
if (!bs) {
- qerror_report(QERR_DEVICE_NOT_FOUND, device);
- return -1;
+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ return;
}
- if (fmt) {
- drv = bdrv_find_whitelisted_format(fmt);
+
+ if (format) {
+ drv = bdrv_find_whitelisted_format(format);
if (!drv) {
- qerror_report(QERR_INVALID_BLOCK_FORMAT, fmt);
- return -1;
+ error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
+ return;
}
}
- if (eject_device(mon, bs, 0) < 0) {
- return -1;
+
+ eject_device(bs, 0, &err);
+ if (error_is_set(&err)) {
+ error_propagate(errp, err);
+ return;
}
+
bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR;
bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0;
- if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) {
- qerror_report(QERR_OPEN_FILE_FAILED, filename);
- return -1;
- }
- return monitor_read_bdrv_key_start(mon, bs, NULL, NULL);
+
+ qmp_bdrv_open_encrypted(bs, filename, bdrv_flags, drv, NULL, errp);
}
/* throttling disk I/O limits */
-int do_block_set_io_throttle(Monitor *mon,
- const QDict *qdict, QObject **ret_data)
+void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
+ int64_t bps_wr, int64_t iops, int64_t iops_rd,
+ int64_t iops_wr, Error **errp)
{
BlockIOLimit io_limits;
- const char *devname = qdict_get_str(qdict, "device");
BlockDriverState *bs;
- io_limits.bps[BLOCK_IO_LIMIT_TOTAL]
- = qdict_get_try_int(qdict, "bps", -1);
- io_limits.bps[BLOCK_IO_LIMIT_READ]
- = qdict_get_try_int(qdict, "bps_rd", -1);
- io_limits.bps[BLOCK_IO_LIMIT_WRITE]
- = qdict_get_try_int(qdict, "bps_wr", -1);
- io_limits.iops[BLOCK_IO_LIMIT_TOTAL]
- = qdict_get_try_int(qdict, "iops", -1);
- io_limits.iops[BLOCK_IO_LIMIT_READ]
- = qdict_get_try_int(qdict, "iops_rd", -1);
- io_limits.iops[BLOCK_IO_LIMIT_WRITE]
- = qdict_get_try_int(qdict, "iops_wr", -1);
-
- bs = bdrv_find(devname);
+ bs = bdrv_find(device);
if (!bs) {
- qerror_report(QERR_DEVICE_NOT_FOUND, devname);
- return -1;
+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ return;
}
- if ((io_limits.bps[BLOCK_IO_LIMIT_TOTAL] == -1)
- || (io_limits.bps[BLOCK_IO_LIMIT_READ] == -1)
- || (io_limits.bps[BLOCK_IO_LIMIT_WRITE] == -1)
- || (io_limits.iops[BLOCK_IO_LIMIT_TOTAL] == -1)
- || (io_limits.iops[BLOCK_IO_LIMIT_READ] == -1)
- || (io_limits.iops[BLOCK_IO_LIMIT_WRITE] == -1)) {
- qerror_report(QERR_MISSING_PARAMETER,
- "bps/bps_rd/bps_wr/iops/iops_rd/iops_wr");
- return -1;
- }
+ io_limits.bps[BLOCK_IO_LIMIT_TOTAL] = bps;
+ io_limits.bps[BLOCK_IO_LIMIT_READ] = bps_rd;
+ io_limits.bps[BLOCK_IO_LIMIT_WRITE] = bps_wr;
+ io_limits.iops[BLOCK_IO_LIMIT_TOTAL]= iops;
+ io_limits.iops[BLOCK_IO_LIMIT_READ] = iops_rd;
+ io_limits.iops[BLOCK_IO_LIMIT_WRITE]= iops_wr;
if (!do_check_io_limits(&io_limits)) {
- qerror_report(QERR_INVALID_PARAMETER_COMBINATION);
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER_COMBINATION);
+ return;
}
bs->io_limits = io_limits;
qemu_mod_timer(bs->block_timer, qemu_get_clock_ns(vm_clock));
}
}
-
- return 0;
}
int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
return 0;
}
-/*
- * XXX: replace the QERR_UNDEFINED_ERROR errors with real values once the
- * existing QERR_ macro mess is cleaned up. A good example for better
- * error reports can be found in the qemu-img resize code.
- */
-int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data)
+void qmp_block_resize(const char *device, int64_t size, Error **errp)
{
- const char *device = qdict_get_str(qdict, "device");
- int64_t size = qdict_get_int(qdict, "size");
BlockDriverState *bs;
bs = bdrv_find(device);
if (!bs) {
- qerror_report(QERR_DEVICE_NOT_FOUND, device);
- return -1;
+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ return;
}
if (size < 0) {
- qerror_report(QERR_UNDEFINED_ERROR);
- return -1;
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size");
+ return;
}
- if (bdrv_truncate(bs, size)) {
- qerror_report(QERR_UNDEFINED_ERROR);
- return -1;
+ switch (bdrv_truncate(bs, size)) {
+ case 0:
+ break;
+ case -ENOMEDIUM:
+ error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
+ break;
+ case -ENOTSUP:
+ error_set(errp, QERR_UNSUPPORTED);
+ break;
+ case -EACCES:
+ error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
+ break;
+ case -EBUSY:
+ error_set(errp, QERR_DEVICE_IN_USE, device);
+ break;
+ default:
+ error_set(errp, QERR_UNDEFINED_ERROR);
+ break;
}
+}
- return 0;
+static QObject *qobject_from_block_job(BlockJob *job)
+{
+ return qobject_from_jsonf("{ 'type': %s,"
+ "'device': %s,"
+ "'len': %" PRId64 ","
+ "'offset': %" PRId64 ","
+ "'speed': %" PRId64 " }",
+ job->job_type->job_type,
+ bdrv_get_device_name(job->bs),
+ job->len,
+ job->offset,
+ job->speed);
+}
+
+static void block_stream_cb(void *opaque, int ret)
+{
+ BlockDriverState *bs = opaque;
+ QObject *obj;
+
+ trace_block_stream_cb(bs, bs->job, ret);
+
+ assert(bs->job);
+ obj = qobject_from_block_job(bs->job);
+ if (ret < 0) {
+ QDict *dict = qobject_to_qdict(obj);
+ qdict_put(dict, "error", qstring_from_str(strerror(-ret)));
+ }
+
+ if (block_job_is_cancelled(bs->job)) {
+ monitor_protocol_event(QEVENT_BLOCK_JOB_CANCELLED, obj);
+ } else {
+ 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,
+ const char *base, bool has_speed,
+ int64_t speed, Error **errp)
+{
+ BlockDriverState *bs;
+ BlockDriverState *base_bs = NULL;
+ Error *local_err = NULL;
+
+ bs = bdrv_find(device);
+ if (!bs) {
+ error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+ return;
+ }
+
+ if (base) {
+ base_bs = bdrv_find_backing_image(bs, base);
+ if (base_bs == NULL) {
+ error_set(errp, QERR_BASE_NOT_FOUND, base);
+ return;
+ }
+ }
+
+ stream_start(bs, base_bs, base, has_speed ? speed : 0,
+ block_stream_cb, bs, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ /* 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);
+}
+
+static BlockJob *find_block_job(const char *device)
+{
+ BlockDriverState *bs;
+
+ bs = bdrv_find(device);
+ if (!bs || !bs->job) {
+ return NULL;
+ }
+ return bs->job;
+}
+
+void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp)
+{
+ BlockJob *job = find_block_job(device);
+
+ if (!job) {
+ error_set(errp, QERR_DEVICE_NOT_ACTIVE, device);
+ return;
+ }
+
+ block_job_set_speed(job, speed, errp);
+}
+
+void qmp_block_job_cancel(const char *device, Error **errp)
+{
+ BlockJob *job = find_block_job(device);
+
+ if (!job) {
+ error_set(errp, QERR_DEVICE_NOT_ACTIVE, device);
+ return;
+ }
+
+ trace_qmp_block_job_cancel(job);
+ block_job_cancel(job);
+}
+
+static void do_qmp_query_block_jobs_one(void *opaque, BlockDriverState *bs)
+{
+ BlockJobInfoList **prev = opaque;
+ BlockJob *job = bs->job;
+
+ if (job) {
+ BlockJobInfoList *elem;
+ BlockJobInfo *info = g_new(BlockJobInfo, 1);
+ *info = (BlockJobInfo){
+ .type = g_strdup(job->job_type->job_type),
+ .device = g_strdup(bdrv_get_device_name(bs)),
+ .len = job->len,
+ .offset = job->offset,
+ .speed = job->speed,
+ };
+
+ elem = g_new0(BlockJobInfoList, 1);
+ elem->value = info;
+
+ (*prev)->next = elem;
+ *prev = elem;
+ }
+}
+
+BlockJobInfoList *qmp_query_block_jobs(Error **errp)
+{
+ /* Dummy is a fake list element for holding the head pointer */
+ BlockJobInfoList dummy = {};
+ BlockJobInfoList *prev = &dummy;
+ bdrv_iterate(do_qmp_query_block_jobs_one, &prev);
+ return dummy.next;
}