#include "qemu-option.h"
#include "qemu-config.h"
#include "sysemu.h"
-#include "hw/qdev.h"
#include "block_int.h"
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
{
qemu_opts_del(dinfo->opts);
bdrv_delete(dinfo->bdrv);
- qemu_free(dinfo->id);
+ g_free(dinfo->id);
QTAILQ_REMOVE(&drives, dinfo, next);
- qemu_free(dinfo);
+ g_free(dinfo);
}
void drive_put_ref(DriveInfo *dinfo)
}
}
+static bool do_check_io_limits(BlockIOLimit *io_limits)
+{
+ bool bps_flag;
+ bool iops_flag;
+
+ assert(io_limits);
+
+ bps_flag = (io_limits->bps[BLOCK_IO_LIMIT_TOTAL] != 0)
+ && ((io_limits->bps[BLOCK_IO_LIMIT_READ] != 0)
+ || (io_limits->bps[BLOCK_IO_LIMIT_WRITE] != 0));
+ iops_flag = (io_limits->iops[BLOCK_IO_LIMIT_TOTAL] != 0)
+ && ((io_limits->iops[BLOCK_IO_LIMIT_READ] != 0)
+ || (io_limits->iops[BLOCK_IO_LIMIT_WRITE] != 0));
+ if (bps_flag || iops_flag) {
+ return false;
+ }
+
+ return true;
+}
+
DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
{
const char *buf;
int on_read_error, on_write_error;
const char *devaddr;
DriveInfo *dinfo;
+ BlockIOLimit io_limits;
int snapshot = 0;
+ bool copy_on_read;
int ret;
translation = BIOS_ATA_TRANSLATION_AUTO;
-
- if (default_to_scsi) {
- type = IF_SCSI;
- pstrcpy(devname, sizeof(devname), "scsi");
- } else {
- type = IF_IDE;
- pstrcpy(devname, sizeof(devname), "ide");
- }
media = MEDIA_DISK;
/* extract parameters */
snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
ro = qemu_opt_get_bool(opts, "readonly", 0);
+ copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false);
file = qemu_opt_get(opts, "file");
serial = qemu_opt_get(opts, "serial");
error_report("unsupported bus type '%s'", buf);
return NULL;
}
+ } 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 ((buf = qemu_opt_get(opts, "trans")) != NULL) {
if (!cyls) {
- error_report("'%s' trans must be used with cyls,heads and secs",
+ error_report("'%s' trans must be used with cyls, heads and secs",
buf);
return NULL;
}
media = MEDIA_DISK;
} else if (!strcmp(buf, "cdrom")) {
if (cyls || secs || heads) {
- error_report("'%s' invalid physical CHS format", buf);
+ error_report("CHS can't be set with media=%s", buf);
return NULL;
}
media = MEDIA_CDROM;
}
if ((buf = qemu_opt_get(opts, "cache")) != NULL) {
- if (!strcmp(buf, "off") || !strcmp(buf, "none")) {
- bdrv_flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB;
- } else if (!strcmp(buf, "writeback")) {
- bdrv_flags |= BDRV_O_CACHE_WB;
- } else if (!strcmp(buf, "unsafe")) {
- bdrv_flags |= BDRV_O_CACHE_WB;
- bdrv_flags |= BDRV_O_NO_FLUSH;
- } else if (!strcmp(buf, "writethrough")) {
- /* this is the default */
- } else {
- error_report("invalid cache option");
- return NULL;
+ if (bdrv_parse_cache_flags(buf, &bdrv_flags) != 0) {
+ error_report("invalid cache option");
+ return NULL;
}
}
}
}
+ /* disk I/O throttling */
+ io_limits.bps[BLOCK_IO_LIMIT_TOTAL] =
+ qemu_opt_get_number(opts, "bps", 0);
+ io_limits.bps[BLOCK_IO_LIMIT_READ] =
+ qemu_opt_get_number(opts, "bps_rd", 0);
+ io_limits.bps[BLOCK_IO_LIMIT_WRITE] =
+ qemu_opt_get_number(opts, "bps_wr", 0);
+ io_limits.iops[BLOCK_IO_LIMIT_TOTAL] =
+ qemu_opt_get_number(opts, "iops", 0);
+ io_limits.iops[BLOCK_IO_LIMIT_READ] =
+ qemu_opt_get_number(opts, "iops_rd", 0);
+ io_limits.iops[BLOCK_IO_LIMIT_WRITE] =
+ qemu_opt_get_number(opts, "iops_wr", 0);
+
+ if (!do_check_io_limits(&io_limits)) {
+ error_report("bps(iops) and bps_rd/bps_wr(iops_rd/iops_wr) "
+ "cannot be used at the same time");
+ return NULL;
+ }
+
on_write_error = BLOCK_ERR_STOP_ENOSPC;
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
/* init */
- dinfo = qemu_mallocz(sizeof(*dinfo));
+ dinfo = g_malloc0(sizeof(*dinfo));
if ((buf = qemu_opts_id(opts)) != NULL) {
- dinfo->id = qemu_strdup(buf);
+ dinfo->id = g_strdup(buf);
} else {
/* no id supplied -> create one */
- dinfo->id = qemu_mallocz(32);
+ dinfo->id = g_malloc0(32);
if (type == IF_IDE || type == IF_SCSI)
mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
if (max_devs)
bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
+ /* disk I/O throttling */
+ bdrv_set_io_limits(dinfo->bdrv, &io_limits);
+
switch(type) {
case IF_IDE:
case IF_SCSI:
}
break;
case MEDIA_CDROM:
- bdrv_set_removable(dinfo->bdrv, 1);
dinfo->media_cd = 1;
break;
}
break;
case IF_SD:
- /* FIXME: This isn't really a floppy, but it's a reasonable
- approximation. */
case IF_FLOPPY:
- bdrv_set_removable(dinfo->bdrv, 1);
- break;
case IF_PFLASH:
case IF_MTD:
break;
bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH);
}
+ if (copy_on_read) {
+ bdrv_flags |= BDRV_O_COPY_ON_READ;
+ }
+
if (media == MEDIA_CDROM) {
/* CDROM is fine for any interface, don't check. */
ro = 1;
err:
bdrv_delete(dinfo->bdrv);
- qemu_free(dinfo->id);
+ g_free(dinfo->id);
QTAILQ_REMOVE(&drives, dinfo, next);
- qemu_free(dinfo);
+ g_free(dinfo);
return NULL;
}
int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
const char *device = qdict_get_str(qdict, "device");
- const char *filename = qdict_get_try_str(qdict, "snapshot_file");
+ 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;
char old_filename[1024];
if (!filename) {
- qerror_report(QERR_MISSING_PARAMETER, "snapshot_file");
+ qerror_report(QERR_MISSING_PARAMETER, "snapshot-file");
ret = -1;
goto out;
}
static int eject_device(Monitor *mon, BlockDriverState *bs, int force)
{
- if (!force) {
- if (!bdrv_is_removable(bs)) {
- qerror_report(QERR_DEVICE_NOT_REMOVABLE,
- bdrv_get_device_name(bs));
- return -1;
- }
- if (bdrv_is_locked(bs)) {
+ if (!bdrv_dev_has_removable_media(bs)) {
+ qerror_report(QERR_DEVICE_NOT_REMOVABLE, bdrv_get_device_name(bs));
+ return -1;
+ }
+ 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;
}
return monitor_read_bdrv_key_start(mon, bs, NULL, NULL);
}
+/* throttling disk I/O limits */
+int do_block_set_io_throttle(Monitor *mon,
+ const QDict *qdict, QObject **ret_data)
+{
+ 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);
+ if (!bs) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, devname);
+ return -1;
+ }
+
+ 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;
+ }
+
+ if (!do_check_io_limits(&io_limits)) {
+ qerror_report(QERR_INVALID_PARAMETER_COMBINATION);
+ return -1;
+ }
+
+ bs->io_limits = io_limits;
+ bs->slice_time = BLOCK_IO_SLICE_TIME;
+
+ if (!bs->io_limits_enabled && bdrv_io_limits_enabled(bs)) {
+ bdrv_io_limits_enable(bs);
+ } else if (bs->io_limits_enabled && !bdrv_io_limits_enabled(bs)) {
+ bdrv_io_limits_disable(bs);
+ } else {
+ if (bs->block_timer) {
+ 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)
{
const char *id = qdict_get_str(qdict, "id");
bdrv_flush(bs);
bdrv_close(bs);
- /* if we have a device associated with this BlockDriverState (bs->peer)
+ /* if we have a device attached to this BlockDriverState
* then we need to make the drive anonymous until the device
* can be removed. If this is a drive with no device backing
* then we can just get rid of the block driver state right here.
*/
- if (bs->peer) {
+ if (bdrv_get_attached_dev(bs)) {
bdrv_make_anon(bs);
} else {
drive_uninit(drive_get_by_blockdev(bs));