Merge remote-tracking branch 'kwolf/for-anthony' into staging

# By Paolo Bonzini (7) and others
# Via Kevin Wolf
* kwolf/for-anthony: (22 commits)
  pc: add compatibility machine types for 1.4
  blockdev: enable discard by default
  qemu-nbd: add --discard option
  blockdev: add discard suboption to -drive
  block: implement BDRV_O_UNMAP
  block: complete all IOs before .bdrv_truncate
  coroutine: trim down nesting level in perf_nesting test
  coroutine: move pooling to common code
  qemu-iotests: Test qcow2 image creation options
  qemu-iotests: Add qemu-img compare test
  qemu-img: Add compare subcommand
  qemu-img: Add "Quiet mode" option
  block: Add synchronous wrapper for bdrv_co_is_allocated_above
  block: refuse negative iops and bps values
  block: use Error in do_check_io_limits()
  qcow2: support compressed clusters in BlockFragInfo
  qemu-img: add compressed clusters to BlockFragInfo
  qemu-img: fix missing space in qemu-img check output
  qcow2: record fragmentation statistics during check
  qcow2: introduce check_refcounts_l1/l2() flags
  ...
This commit is contained in:
Anthony Liguori
2013-02-26 07:44:39 -06:00
32 changed files with 1445 additions and 234 deletions
+75 -5
View File
@@ -580,6 +580,26 @@ static int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
return 0;
}
/**
* Set open flags for a given discard mode
*
* Return 0 on success, -1 if the discard mode was invalid.
*/
int bdrv_parse_discard_flags(const char *mode, int *flags)
{
*flags &= ~BDRV_O_UNMAP;
if (!strcmp(mode, "off") || !strcmp(mode, "ignore")) {
/* do nothing */
} else if (!strcmp(mode, "on") || !strcmp(mode, "unmap")) {
*flags |= BDRV_O_UNMAP;
} else {
return -1;
}
return 0;
}
/**
* Set open flags for a given cache mode
*
@@ -2427,6 +2447,10 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset)
return -EACCES;
if (bdrv_in_use(bs))
return -EBUSY;
/* There better not be any in-flight IOs when we truncate the device. */
bdrv_drain_all();
ret = drv->bdrv_truncate(bs, offset);
if (ret == 0) {
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
@@ -2681,6 +2705,7 @@ int bdrv_has_zero_init(BlockDriverState *bs)
typedef struct BdrvCoIsAllocatedData {
BlockDriverState *bs;
BlockDriverState *base;
int64_t sector_num;
int nb_sectors;
int *pnum;
@@ -2813,6 +2838,44 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top,
return 0;
}
/* Coroutine wrapper for bdrv_is_allocated_above() */
static void coroutine_fn bdrv_is_allocated_above_co_entry(void *opaque)
{
BdrvCoIsAllocatedData *data = opaque;
BlockDriverState *top = data->bs;
BlockDriverState *base = data->base;
data->ret = bdrv_co_is_allocated_above(top, base, data->sector_num,
data->nb_sectors, data->pnum);
data->done = true;
}
/*
* Synchronous wrapper around bdrv_co_is_allocated_above().
*
* See bdrv_co_is_allocated_above() for details.
*/
int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
int64_t sector_num, int nb_sectors, int *pnum)
{
Coroutine *co;
BdrvCoIsAllocatedData data = {
.bs = top,
.base = base,
.sector_num = sector_num,
.nb_sectors = nb_sectors,
.pnum = pnum,
.done = false,
};
co = qemu_coroutine_create(bdrv_is_allocated_above_co_entry);
qemu_coroutine_enter(co, &data);
while (!data.done) {
qemu_aio_wait();
}
return data.ret;
}
BlockInfo *bdrv_query_info(BlockDriverState *bs)
{
BlockInfo *info = g_malloc0(sizeof(*info));
@@ -4148,6 +4211,11 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
bdrv_reset_dirty(bs, sector_num, nb_sectors);
}
/* Do nothing if disabled. */
if (!(bs->open_flags & BDRV_O_UNMAP)) {
return 0;
}
if (bs->drv->bdrv_co_discard) {
return bs->drv->bdrv_co_discard(bs, sector_num, nb_sectors);
} else if (bs->drv->bdrv_aio_discard) {
@@ -4431,7 +4499,8 @@ bdrv_acct_done(BlockDriverState *bs, BlockAcctCookie *cookie)
void bdrv_img_create(const char *filename, const char *fmt,
const char *base_filename, const char *base_fmt,
char *options, uint64_t img_size, int flags, Error **errp)
char *options, uint64_t img_size, int flags,
Error **errp, bool quiet)
{
QEMUOptionParameter *param = NULL, *create_options = NULL;
QEMUOptionParameter *backing_fmt, *backing_file, *size;
@@ -4540,10 +4609,11 @@ void bdrv_img_create(const char *filename, const char *fmt,
}
}
printf("Formatting '%s', fmt=%s ", filename, fmt);
print_option_parameters(param);
puts("");
if (!quiet) {
printf("Formatting '%s', fmt=%s ", filename, fmt);
print_option_parameters(param);
puts("");
}
ret = bdrv_create(drv, filename, param);
if (ret < 0) {
if (ret == -ENOTSUP) {
+44 -8
View File
@@ -914,6 +914,12 @@ static void inc_refcounts(BlockDriverState *bs,
}
}
/* Flags for check_refcounts_l1() and check_refcounts_l2() */
enum {
CHECK_OFLAG_COPIED = 0x1, /* check QCOW_OFLAG_COPIED matches refcount */
CHECK_FRAG_INFO = 0x2, /* update BlockFragInfo counters */
};
/*
* Increases the refcount in the given refcount table for the all clusters
* referenced in the L2 table. While doing so, performs some checks on L2
@@ -924,10 +930,11 @@ static void inc_refcounts(BlockDriverState *bs,
*/
static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
uint16_t *refcount_table, int refcount_table_size, int64_t l2_offset,
int check_copied)
int flags)
{
BDRVQcowState *s = bs->opaque;
uint64_t *l2_table, l2_entry;
uint64_t next_contiguous_offset = 0;
int i, l2_size, nb_csectors, refcount;
/* Read L2 table from disk */
@@ -958,6 +965,18 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
l2_entry &= s->cluster_offset_mask;
inc_refcounts(bs, res, refcount_table, refcount_table_size,
l2_entry & ~511, nb_csectors * 512);
if (flags & CHECK_FRAG_INFO) {
res->bfi.allocated_clusters++;
res->bfi.compressed_clusters++;
/* Compressed clusters are fragmented by nature. Since they
* take up sub-sector space but we only have sector granularity
* I/O we need to re-read the same sectors even for adjacent
* compressed clusters.
*/
res->bfi.fragmented_clusters++;
}
break;
case QCOW2_CLUSTER_ZERO:
@@ -971,7 +990,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
uint64_t offset = l2_entry & L2E_OFFSET_MASK;
if (check_copied) {
if (flags & CHECK_OFLAG_COPIED) {
refcount = get_refcount(bs, offset >> s->cluster_bits);
if (refcount < 0) {
fprintf(stderr, "Can't get refcount for offset %"
@@ -985,6 +1004,15 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
}
}
if (flags & CHECK_FRAG_INFO) {
res->bfi.allocated_clusters++;
if (next_contiguous_offset &&
offset != next_contiguous_offset) {
res->bfi.fragmented_clusters++;
}
next_contiguous_offset = offset + s->cluster_size;
}
/* Mark cluster as used */
inc_refcounts(bs, res, refcount_table,refcount_table_size,
offset, s->cluster_size);
@@ -1028,7 +1056,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
uint16_t *refcount_table,
int refcount_table_size,
int64_t l1_table_offset, int l1_size,
int check_copied)
int flags)
{
BDRVQcowState *s = bs->opaque;
uint64_t *l1_table, l2_offset, l1_size2;
@@ -1057,7 +1085,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
l2_offset = l1_table[i];
if (l2_offset) {
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
if (check_copied) {
if (flags & CHECK_OFLAG_COPIED) {
refcount = get_refcount(bs, (l2_offset & ~QCOW_OFLAG_COPIED)
>> s->cluster_bits);
if (refcount < 0) {
@@ -1086,7 +1114,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
/* Process and check L2 entries */
ret = check_refcounts_l2(bs, res, refcount_table,
refcount_table_size, l2_offset, check_copied);
refcount_table_size, l2_offset, flags);
if (ret < 0) {
goto fail;
}
@@ -1112,7 +1140,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
BdrvCheckMode fix)
{
BDRVQcowState *s = bs->opaque;
int64_t size, i;
int64_t size, i, highest_cluster;
int nb_clusters, refcount1, refcount2;
QCowSnapshot *sn;
uint16_t *refcount_table;
@@ -1120,6 +1148,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
size = bdrv_getlength(bs->file);
nb_clusters = size_to_clusters(s, size);
res->bfi.total_clusters = nb_clusters;
refcount_table = g_malloc0(nb_clusters * sizeof(uint16_t));
/* header */
@@ -1128,7 +1157,8 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
/* current L1 table */
ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
s->l1_table_offset, s->l1_size, 1);
s->l1_table_offset, s->l1_size,
CHECK_OFLAG_COPIED | CHECK_FRAG_INFO);
if (ret < 0) {
goto fail;
}
@@ -1183,7 +1213,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
}
/* compare ref counts */
for(i = 0; i < nb_clusters; i++) {
for (i = 0, highest_cluster = 0; i < nb_clusters; i++) {
refcount1 = get_refcount(bs, i);
if (refcount1 < 0) {
fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n",
@@ -1193,6 +1223,11 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
}
refcount2 = refcount_table[i];
if (refcount1 > 0 || refcount2 > 0) {
highest_cluster = i;
}
if (refcount1 != refcount2) {
/* Check if we're allowed to fix the mismatch */
@@ -1227,6 +1262,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
}
}
res->image_end_offset = (highest_cluster + 1) * s->cluster_size;
ret = 0;
fail:
+32 -9
View File
@@ -255,7 +255,7 @@ static int parse_block_error_action(const char *buf, bool is_read)
}
}
static bool do_check_io_limits(BlockIOLimit *io_limits)
static bool do_check_io_limits(BlockIOLimit *io_limits, Error **errp)
{
bool bps_flag;
bool iops_flag;
@@ -269,6 +269,18 @@ static bool do_check_io_limits(BlockIOLimit *io_limits)
&& ((io_limits->iops[BLOCK_IO_LIMIT_READ] != 0)
|| (io_limits->iops[BLOCK_IO_LIMIT_WRITE] != 0));
if (bps_flag || iops_flag) {
error_setg(errp, "bps(iops) and bps_rd/bps_wr(iops_rd/iops_wr) "
"cannot be used at the same time");
return false;
}
if (io_limits->bps[BLOCK_IO_LIMIT_TOTAL] < 0 ||
io_limits->bps[BLOCK_IO_LIMIT_WRITE] < 0 ||
io_limits->bps[BLOCK_IO_LIMIT_READ] < 0 ||
io_limits->iops[BLOCK_IO_LIMIT_TOTAL] < 0 ||
io_limits->iops[BLOCK_IO_LIMIT_WRITE] < 0 ||
io_limits->iops[BLOCK_IO_LIMIT_READ] < 0) {
error_setg(errp, "bps and iops values must be 0 or greater");
return false;
}
@@ -297,6 +309,7 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type)
int snapshot = 0;
bool copy_on_read;
int ret;
Error *error = NULL;
translation = BIOS_ATA_TRANSLATION_AUTO;
media = MEDIA_DISK;
@@ -378,6 +391,13 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type)
}
}
if ((buf = qemu_opt_get(opts, "discard")) != NULL) {
if (bdrv_parse_discard_flags(buf, &bdrv_flags) != 0) {
error_report("invalid discard option");
return NULL;
}
}
bdrv_flags |= BDRV_O_CACHE_WB;
if ((buf = qemu_opt_get(opts, "cache")) != NULL) {
if (bdrv_parse_cache_flags(buf, &bdrv_flags) != 0) {
@@ -427,9 +447,9 @@ DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type)
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");
if (!do_check_io_limits(&io_limits, &error)) {
error_report("%s", error_get_pretty(error));
error_free(error);
return NULL;
}
@@ -791,7 +811,7 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
bdrv_img_create(new_image_file, format,
states->old_bs->filename,
states->old_bs->drv->format_name,
NULL, -1, flags, &local_err);
NULL, -1, flags, &local_err, false);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
goto delete_and_fail;
@@ -975,8 +995,7 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
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)) {
error_set(errp, QERR_INVALID_PARAMETER_COMBINATION);
if (!do_check_io_limits(&io_limits, errp)) {
return;
}
@@ -1284,7 +1303,7 @@ void qmp_drive_mirror(const char *device, const char *target,
/* create new image w/o backing file */
assert(format && drv);
bdrv_img_create(target, format,
NULL, NULL, NULL, size, flags, &local_err);
NULL, NULL, NULL, size, flags, &local_err, false);
} else {
switch (mode) {
case NEW_IMAGE_MODE_EXISTING:
@@ -1295,7 +1314,7 @@ void qmp_drive_mirror(const char *device, const char *target,
bdrv_img_create(target, format,
source->filename,
source->drv->format_name,
NULL, size, flags, &local_err);
NULL, size, flags, &local_err, false);
break;
default:
abort();
@@ -1488,6 +1507,10 @@ QemuOptsList qemu_drive_opts = {
.name = "file",
.type = QEMU_OPT_STRING,
.help = "disk image",
},{
.name = "discard",
.type = QEMU_OPT_STRING,
.help = "discard operation (ignore/off, unmap/on)",
},{
.name = "cache",
.type = QEMU_OPT_STRING,
+1 -42
View File
@@ -33,15 +33,6 @@
#include "qemu-common.h"
#include "block/coroutine_int.h"
enum {
/* Maximum free pool size prevents holding too many freed coroutines */
POOL_MAX_SIZE = 64,
};
/** Free list to speed up creation */
static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool);
static unsigned int pool_size;
typedef struct {
Coroutine base;
void *stack;
@@ -85,17 +76,6 @@ static void qemu_coroutine_thread_cleanup(void *opaque)
g_free(s);
}
static void __attribute__((destructor)) coroutine_cleanup(void)
{
Coroutine *co;
Coroutine *tmp;
QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
g_free(DO_UPCAST(CoroutineUContext, base, co)->stack);
g_free(co);
}
}
static void __attribute__((constructor)) coroutine_init(void)
{
int ret;
@@ -164,7 +144,7 @@ static void coroutine_trampoline(int signal)
coroutine_bootstrap(self, co);
}
static Coroutine *coroutine_new(void)
Coroutine *qemu_coroutine_new(void)
{
const size_t stack_size = 1 << 20;
CoroutineUContext *co;
@@ -272,31 +252,10 @@ static Coroutine *coroutine_new(void)
return &co->base;
}
Coroutine *qemu_coroutine_new(void)
{
Coroutine *co;
co = QSLIST_FIRST(&pool);
if (co) {
QSLIST_REMOVE_HEAD(&pool, pool_next);
pool_size--;
} else {
co = coroutine_new();
}
return co;
}
void qemu_coroutine_delete(Coroutine *co_)
{
CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_);
if (pool_size < POOL_MAX_SIZE) {
QSLIST_INSERT_HEAD(&pool, &co->base, pool_next);
co->base.caller = NULL;
pool_size++;
return;
}
g_free(co->stack);
g_free(co);
}
+1 -42
View File
@@ -34,15 +34,6 @@
#include <valgrind/valgrind.h>
#endif
enum {
/* Maximum free pool size prevents holding too many freed coroutines */
POOL_MAX_SIZE = 64,
};
/** Free list to speed up creation */
static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool);
static unsigned int pool_size;
typedef struct {
Coroutine base;
void *stack;
@@ -96,17 +87,6 @@ static void qemu_coroutine_thread_cleanup(void *opaque)
g_free(s);
}
static void __attribute__((destructor)) coroutine_cleanup(void)
{
Coroutine *co;
Coroutine *tmp;
QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
g_free(DO_UPCAST(CoroutineUContext, base, co)->stack);
g_free(co);
}
}
static void __attribute__((constructor)) coroutine_init(void)
{
int ret;
@@ -140,7 +120,7 @@ static void coroutine_trampoline(int i0, int i1)
}
}
static Coroutine *coroutine_new(void)
Coroutine *qemu_coroutine_new(void)
{
const size_t stack_size = 1 << 20;
CoroutineUContext *co;
@@ -186,20 +166,6 @@ static Coroutine *coroutine_new(void)
return &co->base;
}
Coroutine *qemu_coroutine_new(void)
{
Coroutine *co;
co = QSLIST_FIRST(&pool);
if (co) {
QSLIST_REMOVE_HEAD(&pool, pool_next);
pool_size--;
} else {
co = coroutine_new();
}
return co;
}
#ifdef CONFIG_VALGRIND_H
#ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
/* Work around an unused variable in the valgrind.h macro... */
@@ -218,13 +184,6 @@ void qemu_coroutine_delete(Coroutine *co_)
{
CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_);
if (pool_size < POOL_MAX_SIZE) {
QSLIST_INSERT_HEAD(&pool, &co->base, pool_next);
co->base.caller = NULL;
pool_size++;
return;
}
#ifdef CONFIG_VALGRIND_H
valgrind_stack_deregister(co);
#endif
+1 -1
View File
@@ -50,7 +50,7 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \
DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1), \
DEFINE_PROP_UINT32("discard_granularity", _state, \
_conf.discard_granularity, 0)
_conf.discard_granularity, -1)
#define DEFINE_BLOCK_CHS_PROPERTIES(_state, _conf) \
DEFINE_PROP_UINT32("cyls", _state, _conf.cyls, 0), \
+4 -1
View File
@@ -143,7 +143,10 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus);
IDEState *s = bus->ifs + dev->unit;
if (dev->conf.discard_granularity && dev->conf.discard_granularity != 512) {
if (dev->conf.discard_granularity == -1) {
dev->conf.discard_granularity = 512;
} else if (dev->conf.discard_granularity &&
dev->conf.discard_granularity != 512) {
error_report("discard_granularity must be 512 for ide");
return -1;
}
+31
View File
@@ -187,4 +187,35 @@ void pc_system_firmware_init(MemoryRegion *rom_memory);
int e820_add_entry(uint64_t, uint64_t, uint32_t);
#define PC_COMPAT_1_4 \
{\
.driver = "scsi-hd",\
.property = "discard_granularity",\
.value = stringify(0),\
},{\
.driver = "scsi-cd",\
.property = "discard_granularity",\
.value = stringify(0),\
},{\
.driver = "scsi-disk",\
.property = "discard_granularity",\
.value = stringify(0),\
},{\
.driver = "ide-hd",\
.property = "discard_granularity",\
.value = stringify(0),\
},{\
.driver = "ide-cd",\
.property = "discard_granularity",\
.value = stringify(0),\
},{\
.driver = "ide-drive",\
.property = "discard_granularity",\
.value = stringify(0),\
},{\
.driver = "virtio-blk-pci",\
.property = "discard_granularity",\
.value = stringify(0),\
}
#endif
+16 -2
View File
@@ -294,8 +294,8 @@ static void pc_xen_hvm_init(QEMUMachineInitArgs *args)
}
#endif
static QEMUMachine pc_i440fx_machine_v1_4 = {
.name = "pc-i440fx-1.4",
static QEMUMachine pc_i440fx_machine_v1_5 = {
.name = "pc-i440fx-1.5",
.alias = "pc",
.desc = "Standard PC (i440FX + PIIX, 1996)",
.init = pc_init_pci,
@@ -304,7 +304,20 @@ static QEMUMachine pc_i440fx_machine_v1_4 = {
DEFAULT_MACHINE_OPTIONS,
};
static QEMUMachine pc_i440fx_machine_v1_4 = {
.name = "pc-i440fx-1.4",
.desc = "Standard PC (i440FX + PIIX, 1996)",
.init = pc_init_pci,
.max_cpus = 255,
.compat_props = (GlobalProperty[]) {
PC_COMPAT_1_4,
{ /* end of list */ }
},
DEFAULT_MACHINE_OPTIONS,
};
#define PC_COMPAT_1_3 \
PC_COMPAT_1_4, \
{\
.driver = "usb-tablet",\
.property = "usb_version",\
@@ -679,6 +692,7 @@ static QEMUMachine xenfv_machine = {
static void pc_machine_init(void)
{
qemu_register_machine(&pc_i440fx_machine_v1_5);
qemu_register_machine(&pc_i440fx_machine_v1_4);
qemu_register_machine(&pc_machine_v1_3);
qemu_register_machine(&pc_machine_v1_2);
+16 -3
View File
@@ -209,8 +209,8 @@ static void pc_q35_init(QEMUMachineInitArgs *args)
}
}
static QEMUMachine pc_q35_machine = {
.name = "pc-q35-1.4",
static QEMUMachine pc_q35_machine_v1_5 = {
.name = "pc-q35-1.5",
.alias = "q35",
.desc = "Standard PC (Q35 + ICH9, 2009)",
.init = pc_q35_init,
@@ -218,9 +218,22 @@ static QEMUMachine pc_q35_machine = {
DEFAULT_MACHINE_OPTIONS,
};
static QEMUMachine pc_q35_machine_v1_4 = {
.name = "pc-q35-1.4",
.desc = "Standard PC (Q35 + ICH9, 2009)",
.init = pc_q35_init,
.max_cpus = 255,
.compat_props = (GlobalProperty[]) {
PC_COMPAT_1_4,
{ /* end of list */ }
},
DEFAULT_MACHINE_OPTIONS,
};
static void pc_q35_machine_init(void)
{
qemu_register_machine(&pc_q35_machine);
qemu_register_machine(&pc_q35_machine_v1_5);
qemu_register_machine(&pc_q35_machine_v1_4);
}
machine_init(pc_q35_machine_init);
+10 -3
View File
@@ -41,9 +41,11 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#include <scsi/sg.h>
#endif
#define SCSI_DMA_BUF_SIZE 131072
#define SCSI_MAX_INQUIRY_LEN 256
#define SCSI_MAX_MODE_LEN 256
#define SCSI_DMA_BUF_SIZE 131072
#define SCSI_MAX_INQUIRY_LEN 256
#define SCSI_MAX_MODE_LEN 256
#define DEFAULT_DISCARD_GRANULARITY 4096
typedef struct SCSIDiskState SCSIDiskState;
@@ -2059,6 +2061,11 @@ static int scsi_initfn(SCSIDevice *dev)
return -1;
}
if (s->qdev.conf.discard_granularity == -1) {
s->qdev.conf.discard_granularity =
MAX(s->qdev.conf.logical_block_size, DEFAULT_DISCARD_GRANULARITY);
}
if (!s->version) {
s->version = g_strdup(qemu_get_version());
}
+8 -1
View File
@@ -24,6 +24,7 @@ typedef struct BlockFragInfo {
uint64_t allocated_clusters;
uint64_t total_clusters;
uint64_t fragmented_clusters;
uint64_t compressed_clusters;
} BlockFragInfo;
typedef struct QEMUSnapshotInfo {
@@ -83,6 +84,7 @@ typedef struct BlockDevOps {
#define BDRV_O_INCOMING 0x0800 /* consistency hint for incoming migration */
#define BDRV_O_CHECK 0x1000 /* open solely for consistency check */
#define BDRV_O_ALLOW_RDWR 0x2000 /* allow reopen to change from r/o to r/w */
#define BDRV_O_UNMAP 0x4000 /* execute guest UNMAP/TRIM operations */
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
@@ -132,6 +134,7 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old);
void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
void bdrv_delete(BlockDriverState *bs);
int bdrv_parse_cache_flags(const char *mode, int *flags);
int bdrv_parse_discard_flags(const char *mode, int *flags);
int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags);
int bdrv_open_backing_file(BlockDriverState *bs);
int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
@@ -213,6 +216,7 @@ typedef struct BdrvCheckResult {
int check_errors;
int corruptions_fixed;
int leaks_fixed;
int64_t image_end_offset;
BlockFragInfo bfi;
} BdrvCheckResult;
@@ -278,6 +282,8 @@ int bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors);
int bdrv_has_zero_init(BlockDriverState *bs);
int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
int *pnum);
int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
int64_t sector_num, int nb_sectors, int *pnum);
void bdrv_set_on_error(BlockDriverState *bs, BlockdevOnError on_read_error,
BlockdevOnError on_write_error);
@@ -349,7 +355,8 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
void bdrv_img_create(const char *filename, const char *fmt,
const char *base_filename, const char *base_fmt,
char *options, uint64_t img_size, int flags, Error **errp);
char *options, uint64_t img_size, int flags,
Error **errp, bool quiet);
void bdrv_set_buffer_alignment(BlockDriverState *bs, int align);
void *qemu_blockalign(BlockDriverState *bs, size_t size);
+50
View File
@@ -244,6 +244,56 @@
'*backing-filename': 'str', '*full-backing-filename': 'str',
'*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'] } }
##
# @ImageCheck:
#
# Information about a QEMU image file check
#
# @filename: name of the image file checked
#
# @format: format of the image file checked
#
# @check-errors: number of unexpected errors occurred during check
#
# @image-end-offset: #optional offset (in bytes) where the image ends, this
# field is present if the driver for the image format
# supports it
#
# @corruptions: #optional number of corruptions found during the check if any
#
# @leaks: #optional number of leaks found during the check if any
#
# @corruptions-fixed: #optional number of corruptions fixed during the check
# if any
#
# @leaks-fixed: #optional number of leaks fixed during the check if any
#
# @total-clusters: #optional total number of clusters, this field is present
# if the driver for the image format supports it
#
# @allocated-clusters: #optional total number of allocated clusters, this
# field is present if the driver for the image format
# supports it
#
# @fragmented-clusters: #optional total number of fragmented clusters, this
# field is present if the driver for the image format
# supports it
#
# @compressed-clusters: #optional total number of compressed clusters, this
# field is present if the driver for the image format
# supports it
#
# Since: 1.4
#
##
{ 'type': 'ImageCheck',
'data': {'filename': 'str', 'format': 'str', 'check-errors': 'int',
'*image-end-offset': 'int', '*corruptions': 'int', '*leaks': 'int',
'*corruptions-fixed': 'int', '*leaks-fixed': 'int',
'*total-clusters': 'int', '*allocated-clusters': 'int',
'*fragmented-clusters': 'int', '*compressed-clusters': 'int' } }
##
# @StatusInfo:
#
+43 -2
View File
@@ -17,13 +17,54 @@
#include "block/coroutine.h"
#include "block/coroutine_int.h"
enum {
/* Maximum free pool size prevents holding too many freed coroutines */
POOL_MAX_SIZE = 64,
};
/** Free list to speed up creation */
static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool);
static unsigned int pool_size;
Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
{
Coroutine *co = qemu_coroutine_new();
Coroutine *co;
co = QSLIST_FIRST(&pool);
if (co) {
QSLIST_REMOVE_HEAD(&pool, pool_next);
pool_size--;
} else {
co = qemu_coroutine_new();
}
co->entry = entry;
return co;
}
static void coroutine_delete(Coroutine *co)
{
if (pool_size < POOL_MAX_SIZE) {
QSLIST_INSERT_HEAD(&pool, co, pool_next);
co->caller = NULL;
pool_size++;
return;
}
qemu_coroutine_delete(co);
}
static void __attribute__((destructor)) coroutine_cleanup(void)
{
Coroutine *co;
Coroutine *tmp;
QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
QSLIST_REMOVE_HEAD(&pool, pool_next);
qemu_coroutine_delete(co);
}
}
static void coroutine_swap(Coroutine *from, Coroutine *to)
{
CoroutineAction ret;
@@ -35,7 +76,7 @@ static void coroutine_swap(Coroutine *from, Coroutine *to)
return;
case COROUTINE_TERMINATE:
trace_qemu_coroutine_terminate(to);
qemu_coroutine_delete(to);
coroutine_delete(to);
return;
default:
abort();
+20 -14
View File
@@ -10,27 +10,33 @@ STEXI
ETEXI
DEF("check", img_check,
"check [-f fmt] [-r [leaks | all]] filename")
"check [-q] [-f fmt] [--output=ofmt] [-r [leaks | all]] filename")
STEXI
@item check [-f @var{fmt}] [-r [leaks | all]] @var{filename}
@item check [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] @var{filename}
ETEXI
DEF("create", img_create,
"create [-f fmt] [-o options] filename [size]")
"create [-q] [-f fmt] [-o options] filename [size]")
STEXI
@item create [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}]
@item create [-q] [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}]
ETEXI
DEF("commit", img_commit,
"commit [-f fmt] [-t cache] filename")
"commit [-q] [-f fmt] [-t cache] filename")
STEXI
@item commit [-f @var{fmt}] [-t @var{cache}] @var{filename}
@item commit [-q] [-f @var{fmt}] [-t @var{cache}] @var{filename}
ETEXI
DEF("compare", img_compare,
"compare [-f fmt] [-F fmt] [-p] [-q] [-s] filename1 filename2")
STEXI
@item compare [-f @var{fmt}] [-F @var{fmt}] [-p] [-q] [-s] @var{filename1} @var{filename2}
ETEXI
DEF("convert", img_convert,
"convert [-c] [-p] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] [-S sparse_size] filename [filename2 [...]] output_filename")
"convert [-c] [-p] [-q] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] [-S sparse_size] filename [filename2 [...]] output_filename")
STEXI
@item convert [-c] [-p] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
@item convert [-c] [-p] [-q] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
ETEXI
DEF("info", img_info,
@@ -40,20 +46,20 @@ STEXI
ETEXI
DEF("snapshot", img_snapshot,
"snapshot [-l | -a snapshot | -c snapshot | -d snapshot] filename")
"snapshot [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename")
STEXI
@item snapshot [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename}
@item snapshot [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename}
ETEXI
DEF("rebase", img_rebase,
"rebase [-f fmt] [-t cache] [-p] [-u] -b backing_file [-F backing_fmt] filename")
"rebase [-q] [-f fmt] [-t cache] [-p] [-u] -b backing_file [-F backing_fmt] filename")
STEXI
@item rebase [-f @var{fmt}] [-t @var{cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
@item rebase [-q] [-f @var{fmt}] [-t @var{cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename}
ETEXI
DEF("resize", img_resize,
"resize filename [+ | -]size")
"resize [-q] filename [+ | -]size")
STEXI
@item resize @var{filename} [+ | -]@var{size}
@item resize [-q] @var{filename} [+ | -]@var{size}
@end table
ETEXI
+545 -85
View File
File diff suppressed because it is too large Load Diff
+59 -2
View File
@@ -54,6 +54,9 @@ indicates that target image must be compressed (qcow format only)
with or without a command shows help and lists the supported formats
@item -p
display progress bar (convert and rebase commands only)
@item -q
Quiet mode - do not print any output (except errors). There's no progress bar
in case both @var{-q} and @var{-p} options are used.
@item -S @var{size}
indicates the consecutive number of bytes that must contain only zeros
for qemu-img to create a sparse image during conversion. This value is rounded
@@ -81,12 +84,25 @@ deletes a snapshot
lists all snapshots in the given image
@end table
Parameters to compare subcommand:
@table @option
@item -f
First image format
@item -F
Second image format
@item -s
Strict mode - fail on on different image size or sector allocation
@end table
Command description:
@table @option
@item check [-f @var{fmt}] [-r [leaks | all]] @var{filename}
@item check [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] @var{filename}
Perform a consistency check on the disk image @var{filename}.
Perform a consistency check on the disk image @var{filename}. The command can
output in the format @var{ofmt} which is either @code{human} or @code{json}.
If @code{-r} is specified, qemu-img tries to repair any inconsistencies found
during the check. @code{-r leaks} repairs only cluster leaks, whereas
@@ -114,6 +130,47 @@ it doesn't need to be specified separately in this case.
Commit the changes recorded in @var{filename} in its base image.
@item compare [-f @var{fmt}] [-F @var{fmt}] [-p] [-s] [-q] @var{filename1} @var{filename2}
Check if two images have the same content. You can compare images with
different format or settings.
The format is probed unless you specify it by @var{-f} (used for
@var{filename1}) and/or @var{-F} (used for @var{filename2}) option.
By default, images with different size are considered identical if the larger
image contains only unallocated and/or zeroed sectors in the area after the end
of the other image. In addition, if any sector is not allocated in one image
and contains only zero bytes in the second one, it is evaluated as equal. You
can use Strict mode by specifying the @var{-s} option. When compare runs in
Strict mode, it fails in case image size differs or a sector is allocated in
one image and is not allocated in the second one.
By default, compare prints out a result message. This message displays
information that both images are same or the position of the first different
byte. In addition, result message can report different image size in case
Strict mode is used.
Compare exits with @code{0} in case the images are equal and with @code{1}
in case the images differ. Other exit codes mean an error occurred during
execution and standard error output should contain an error message.
The following table sumarizes all exit codes of the compare subcommand:
@table @option
@item 0
Images are identical
@item 1
Images differ
@item 2
Error on opening an image
@item 3
Error on checking a sector allocation
@item 4
Error on reading data
@end table
@item convert [-c] [-p] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename}
+9 -2
View File
@@ -1899,7 +1899,7 @@ int main(int argc, char **argv)
{
int readonly = 0;
int growable = 0;
const char *sopt = "hVc:rsnmgkt:T:";
const char *sopt = "hVc:d:rsnmgkt:T:";
const struct option lopt[] = {
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' },
@@ -1911,13 +1911,14 @@ int main(int argc, char **argv)
{ "misalign", 0, NULL, 'm' },
{ "growable", 0, NULL, 'g' },
{ "native-aio", 0, NULL, 'k' },
{ "discard", 1, NULL, 'd' },
{ "cache", 1, NULL, 't' },
{ "trace", 1, NULL, 'T' },
{ NULL, 0, NULL, 0 }
};
int c;
int opt_index = 0;
int flags = 0;
int flags = BDRV_O_UNMAP;
progname = basename(argv[0]);
@@ -1929,6 +1930,12 @@ int main(int argc, char **argv)
case 'n':
flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB;
break;
case 'd':
if (bdrv_parse_discard_flags(optarg, &flags) < 0) {
error_report("Invalid discard option: %s", optarg);
exit(1);
}
break;
case 'c':
add_user_command(optarg);
break;
+15 -3
View File
@@ -33,9 +33,10 @@
#include <libgen.h>
#include <pthread.h>
#define SOCKET_PATH "/var/lock/qemu-nbd-%s"
#define QEMU_NBD_OPT_CACHE 1
#define QEMU_NBD_OPT_AIO 2
#define SOCKET_PATH "/var/lock/qemu-nbd-%s"
#define QEMU_NBD_OPT_CACHE 1
#define QEMU_NBD_OPT_AIO 2
#define QEMU_NBD_OPT_DISCARD 3
static NBDExport *exp;
static int verbose;
@@ -330,6 +331,7 @@ int main(int argc, char **argv)
#ifdef CONFIG_LINUX_AIO
{ "aio", 1, NULL, QEMU_NBD_OPT_AIO },
#endif
{ "discard", 1, NULL, QEMU_NBD_OPT_DISCARD },
{ "shared", 1, NULL, 'e' },
{ "persistent", 0, NULL, 't' },
{ "verbose", 0, NULL, 'v' },
@@ -344,6 +346,7 @@ int main(int argc, char **argv)
int ret;
int fd;
bool seen_cache = false;
bool seen_discard = false;
#ifdef CONFIG_LINUX_AIO
bool seen_aio = false;
#endif
@@ -389,6 +392,15 @@ int main(int argc, char **argv)
}
break;
#endif
case QEMU_NBD_OPT_DISCARD:
if (seen_discard) {
errx(EXIT_FAILURE, "--discard can only be specified once");
}
seen_discard = true;
if (bdrv_parse_discard_flags(optarg, &flags) == -1) {
errx(EXIT_FAILURE, "Invalid discard mode `%s'", optarg);
}
break;
case 'b':
bindto = optarg;
break;
+4
View File
@@ -35,6 +35,10 @@ Export QEMU disk image using NBD protocol.
@item --aio=@var{aio}
choose asynchronous I/O mode between @samp{threads} (the default)
and @samp{native} (Linux only).
@item --discard=@var{discard}
toggles whether @dfn{discard} (also known as @dfn{trim} or @dfn{unmap})
requests are ignored or passed to the filesystem. The default is no
(@samp{--discard=ignore}).
@item -c, --connect=@var{dev}
connect @var{filename} to NBD device @var{dev}
@item -d, --disconnect

Some files were not shown because too many files have changed in this diff Show More