Commit b262fce1 authored by Anthony Liguori's avatar Anthony Liguori
Browse files

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

* kwolf/for-anthony:
  qemu-img: use QemuOpts instead of QEMUOptionParameter in resize function
  qemu-iotests: Be more flexible with image creation options
  qemu-iotests: add 039 qcow2 lazy refcounts test
  qemu-io: add "abort" command to simulate program crash
  qcow2: implement lazy refcounts
  qemu-iotests: ignore qemu-img create lazy_refcounts output
  docs: add lazy refcounts bit to qcow2 specification
  qcow2: introduce dirty bit
  docs: add dirty bit to qcow2 specification
  qemu-iotests: add qed.py image manipulation utility
  qapi: generalize documentation of streaming commands
  ide scsi: Mess with geometry only for hard disk devices
parents 0b8db8fe 20caf0f7
......@@ -662,7 +662,10 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
qcow2_cache_depends_on_flush(s->l2_table_cache);
}
qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache);
if (qcow2_need_accurate_refcounts(s)) {
qcow2_cache_set_dependency(bs, s->l2_table_cache,
s->refcount_block_cache);
}
ret = get_cluster_table(bs, m->offset, &l2_table, &l2_index);
if (ret < 0) {
goto err;
......
......@@ -214,6 +214,62 @@ static void report_unsupported_feature(BlockDriverState *bs,
}
}
/*
* Sets the dirty bit and flushes afterwards if necessary.
*
* The incompatible_features bit is only set if the image file header was
* updated successfully. Therefore it is not required to check the return
* value of this function.
*/
static int qcow2_mark_dirty(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
uint64_t val;
int ret;
assert(s->qcow_version >= 3);
if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
return 0; /* already dirty */
}
val = cpu_to_be64(s->incompatible_features | QCOW2_INCOMPAT_DIRTY);
ret = bdrv_pwrite(bs->file, offsetof(QCowHeader, incompatible_features),
&val, sizeof(val));
if (ret < 0) {
return ret;
}
ret = bdrv_flush(bs->file);
if (ret < 0) {
return ret;
}
/* Only treat image as dirty if the header was updated successfully */
s->incompatible_features |= QCOW2_INCOMPAT_DIRTY;
return 0;
}
/*
* Clears the dirty bit and flushes before if necessary. Only call this
* function when there are no pending requests, it does not guard against
* concurrent requests dirtying the image.
*/
static int qcow2_mark_clean(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
int ret = bdrv_flush(bs);
if (ret < 0) {
return ret;
}
s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY;
return qcow2_update_header(bs);
}
return 0;
}
static int qcow2_open(BlockDriverState *bs, int flags)
{
BDRVQcowState *s = bs->opaque;
......@@ -287,12 +343,13 @@ static int qcow2_open(BlockDriverState *bs, int flags)
s->compatible_features = header.compatible_features;
s->autoclear_features = header.autoclear_features;
if (s->incompatible_features != 0) {
if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) {
void *feature_table = NULL;
qcow2_read_extensions(bs, header.header_length, ext_end,
&feature_table);
report_unsupported_feature(bs, feature_table,
s->incompatible_features);
s->incompatible_features &
~QCOW2_INCOMPAT_MASK);
ret = -ENOTSUP;
goto fail;
}
......@@ -412,6 +469,22 @@ static int qcow2_open(BlockDriverState *bs, int flags)
/* Initialise locks */
qemu_co_mutex_init(&s->lock);
/* Repair image if dirty */
if ((s->incompatible_features & QCOW2_INCOMPAT_DIRTY) &&
!bs->read_only) {
BdrvCheckResult result = {0};
ret = qcow2_check_refcounts(bs, &result, BDRV_FIX_ERRORS);
if (ret < 0) {
goto fail;
}
ret = qcow2_mark_clean(bs);
if (ret < 0) {
goto fail;
}
}
#ifdef DEBUG_ALLOC
{
BdrvCheckResult result = {0};
......@@ -714,6 +787,11 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
goto fail;
}
if (l2meta.nb_clusters > 0 &&
(s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS)) {
qcow2_mark_dirty(bs);
}
cluster_offset = l2meta.cluster_offset;
assert((cluster_offset & 511) == 0);
......@@ -785,6 +863,8 @@ static void qcow2_close(BlockDriverState *bs)
qcow2_cache_flush(bs, s->l2_table_cache);
qcow2_cache_flush(bs, s->refcount_block_cache);
qcow2_mark_clean(bs);
qcow2_cache_destroy(bs, s->l2_table_cache);
qcow2_cache_destroy(bs, s->refcount_block_cache);
......@@ -949,7 +1029,16 @@ int qcow2_update_header(BlockDriverState *bs)
/* Feature table */
Qcow2Feature features[] = {
/* no feature defined yet */
{
.type = QCOW2_FEAT_TYPE_INCOMPATIBLE,
.bit = QCOW2_INCOMPAT_DIRTY_BITNR,
.name = "dirty bit",
},
{
.type = QCOW2_FEAT_TYPE_COMPATIBLE,
.bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
.name = "lazy refcounts",
},
};
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE,
......@@ -1132,6 +1221,11 @@ static int qcow2_create2(const char *filename, int64_t total_size,
header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
}
if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) {
header.compatible_features |=
cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS);
}
ret = bdrv_pwrite(bs, 0, &header, sizeof(header));
if (ret < 0) {
goto out;
......@@ -1245,6 +1339,8 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
options->value.s);
return -EINVAL;
}
} else if (!strcmp(options->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
flags |= options->value.n ? BLOCK_FLAG_LAZY_REFCOUNTS : 0;
}
options++;
}
......@@ -1255,6 +1351,12 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
return -EINVAL;
}
if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) {
fprintf(stderr, "Lazy refcounts only supported with compatibility "
"level 1.1 and above (use compat=1.1 or greater)\n");
return -EINVAL;
}
return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags,
cluster_size, prealloc, options, version);
}
......@@ -1441,10 +1543,12 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
return ret;
}
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
if (ret < 0) {
qemu_co_mutex_unlock(&s->lock);
return ret;
if (qcow2_need_accurate_refcounts(s)) {
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
if (ret < 0) {
qemu_co_mutex_unlock(&s->lock);
return ret;
}
}
qemu_co_mutex_unlock(&s->lock);
......@@ -1559,6 +1663,11 @@ static QEMUOptionParameter qcow2_create_options[] = {
.type = OPT_STRING,
.help = "Preallocation mode (allowed values: off, metadata)"
},
{
.name = BLOCK_OPT_LAZY_REFCOUNTS,
.type = OPT_FLAG,
.help = "Postpone refcount updates",
},
{ NULL }
};
......
......@@ -110,6 +110,22 @@ enum {
QCOW2_FEAT_TYPE_AUTOCLEAR = 2,
};
/* Incompatible feature bits */
enum {
QCOW2_INCOMPAT_DIRTY_BITNR = 0,
QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR,
QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY,
};
/* Compatible feature bits */
enum {
QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR = 0,
QCOW2_COMPAT_LAZY_REFCOUNTS = 1 << QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR,
QCOW2_COMPAT_FEAT_MASK = QCOW2_COMPAT_LAZY_REFCOUNTS,
};
typedef struct Qcow2Feature {
uint8_t type;
uint8_t bit;
......@@ -237,6 +253,11 @@ static inline int qcow2_get_cluster_type(uint64_t l2_entry)
}
}
/* Check whether refcounts are eager or lazy */
static inline bool qcow2_need_accurate_refcounts(BDRVQcowState *s)
{
return !(s->incompatible_features & QCOW2_INCOMPAT_DIRTY);
}
// FIXME Need qcow2_ prefix to global functions
......
......@@ -31,8 +31,9 @@
#include "qemu-timer.h"
#include "qapi-types.h"
#define BLOCK_FLAG_ENCRYPT 1
#define BLOCK_FLAG_COMPAT6 4
#define BLOCK_FLAG_ENCRYPT 1
#define BLOCK_FLAG_COMPAT6 4
#define BLOCK_FLAG_LAZY_REFCOUNTS 8
#define BLOCK_IO_LIMIT_READ 0
#define BLOCK_IO_LIMIT_WRITE 1
......@@ -41,16 +42,17 @@
#define BLOCK_IO_SLICE_TIME 100000000
#define NANOSECONDS_PER_SECOND 1000000000.0
#define BLOCK_OPT_SIZE "size"
#define BLOCK_OPT_ENCRYPT "encryption"
#define BLOCK_OPT_COMPAT6 "compat6"
#define BLOCK_OPT_BACKING_FILE "backing_file"
#define BLOCK_OPT_BACKING_FMT "backing_fmt"
#define BLOCK_OPT_CLUSTER_SIZE "cluster_size"
#define BLOCK_OPT_TABLE_SIZE "table_size"
#define BLOCK_OPT_PREALLOC "preallocation"
#define BLOCK_OPT_SUBFMT "subformat"
#define BLOCK_OPT_COMPAT_LEVEL "compat"
#define BLOCK_OPT_SIZE "size"
#define BLOCK_OPT_ENCRYPT "encryption"
#define BLOCK_OPT_COMPAT6 "compat6"
#define BLOCK_OPT_BACKING_FILE "backing_file"
#define BLOCK_OPT_BACKING_FMT "backing_fmt"
#define BLOCK_OPT_CLUSTER_SIZE "cluster_size"
#define BLOCK_OPT_TABLE_SIZE "table_size"
#define BLOCK_OPT_PREALLOC "preallocation"
#define BLOCK_OPT_SUBFMT "subformat"
#define BLOCK_OPT_COMPAT_LEVEL "compat"
#define BLOCK_OPT_LAZY_REFCOUNTS "lazy_refcounts"
typedef struct BdrvTrackedRequest BdrvTrackedRequest;
......
......@@ -75,13 +75,23 @@ in the description of a field.
Bitmask of incompatible features. An implementation must
fail to open an image if an unknown bit is set.
Bits 0-63: Reserved (set to 0)
Bit 0: Dirty bit. If this bit is set then refcounts
may be inconsistent, make sure to scan L1/L2
tables to repair refcounts before accessing the
image.
Bits 1-63: Reserved (set to 0)
80 - 87: compatible_features
Bitmask of compatible features. An implementation can
safely ignore any unknown bits that are set.
Bits 0-63: Reserved (set to 0)
Bit 0: Lazy refcounts bit. If this bit is set then
lazy refcount updates can be used. This means
marking the image file dirty and postponing
refcount metadata updates.
Bits 1-63: Reserved (set to 0)
88 - 95: autoclear_features
Bitmask of auto-clear features. An implementation may only
......
......@@ -101,7 +101,7 @@ ETEXI
.name = "block_job_cancel",
.args_type = "device:B",
.params = "device",
.help = "stop an active block streaming operation",
.help = "stop an active background block operation",
.mhandler.cmd = hmp_block_job_cancel,
},
......
......@@ -149,7 +149,8 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
}
blkconf_serial(&dev->conf, &dev->serial);
if (blkconf_geometry(&dev->conf, &dev->chs_trans, 65536, 16, 255) < 0) {
if (kind != IDE_CD
&& blkconf_geometry(&dev->conf, &dev->chs_trans, 65536, 16, 255) < 0) {
return -1;
}
......
......@@ -1958,7 +1958,8 @@ static int scsi_initfn(SCSIDevice *dev)
}
blkconf_serial(&s->qdev.conf, &s->serial);
if (blkconf_geometry(&dev->conf, NULL, 65535, 255, 255) < 0) {
if (dev->type == TYPE_DISK
&& blkconf_geometry(&dev->conf, NULL, 65535, 255, 255) < 0) {
return -1;
}
......
......@@ -1660,7 +1660,7 @@
# Returns: Nothing on success
# If the job type does not support throttling, NotSupported
# If the speed value is invalid, InvalidParameter
# If streaming is not active on this device, DeviceNotActive
# If no background operation is active on this device, DeviceNotActive
#
# Since: 1.1
##
......@@ -1670,9 +1670,9 @@
##
# @block-job-cancel:
#
# Stop an active block streaming operation.
# Stop an active background block operation.
#
# This command returns immediately after marking the active block streaming
# This command returns immediately after marking the active background block
# operation for cancellation. It is an error to call this command if no
# operation is in progress.
#
......@@ -1680,16 +1680,15 @@
# BLOCK_JOB_CANCELLED event. Before that happens the job is still visible when
# enumerated using query-block-jobs.
#
# The image file retains its backing file unless the streaming operation happens
# to complete just as it is being cancelled.
#
# A new block streaming operation can be started at a later time to finish
# copying all data from the backing file.
# For streaming, the image file retains its backing file unless the streaming
# operation happens to complete just as it is being cancelled. A new streaming
# operation can be started at a later time to finish copying all data from the
# backing file.
#
# @device: the device name
#
# Returns: Nothing on success
# If streaming is not active on this device, DeviceNotActive
# If no background operation is active on this device, DeviceNotActive
# If cancellation already in progress, DeviceInUse
#
# Since: 1.1
......
......@@ -1567,14 +1567,19 @@ static int img_resize(int argc, char **argv)
const char *filename, *fmt, *size;
int64_t n, total_size;
BlockDriverState *bs = NULL;
QEMUOptionParameter *param;
QEMUOptionParameter resize_options[] = {
{
.name = BLOCK_OPT_SIZE,
.type = OPT_SIZE,
.help = "Virtual disk size"
QemuOpts *param;
static QemuOptsList resize_options = {
.name = "resize_options",
.head = QTAILQ_HEAD_INITIALIZER(resize_options.head),
.desc = {
{
.name = BLOCK_OPT_SIZE,
.type = QEMU_OPT_SIZE,
.help = "Virtual disk size"
}, {
/* end of list */
}
},
{ NULL }
};
/* Remove size from argv manually so that negative numbers are not treated
......@@ -1624,14 +1629,15 @@ static int img_resize(int argc, char **argv)
}
/* Parse size */
param = parse_option_parameters("", resize_options, NULL);
if (set_option_parameter(param, BLOCK_OPT_SIZE, size)) {
param = qemu_opts_create(&resize_options, NULL, 0, NULL);
if (qemu_opt_set(param, BLOCK_OPT_SIZE, size)) {
/* Error message already printed when size parsing fails */
ret = -1;
qemu_opts_del(param);
goto out;
}
n = get_option_parameter(param, BLOCK_OPT_SIZE)->value.n;
free_option_parameters(param);
n = qemu_opt_get_size(param, BLOCK_OPT_SIZE, 0);
qemu_opts_del(param);
bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR);
if (!bs) {
......
......@@ -1652,6 +1652,17 @@ static const cmdinfo_t map_cmd = {
.oneline = "prints the allocated areas of a file",
};
static int abort_f(int argc, char **argv)
{
abort();
}
static const cmdinfo_t abort_cmd = {
.name = "abort",
.cfunc = abort_f,
.flags = CMD_NOFILE_OK,
.oneline = "simulate a program crash using abort(3)",
};
static int close_f(int argc, char **argv)
{
......@@ -1905,6 +1916,7 @@ int main(int argc, char **argv)
add_command(&discard_cmd);
add_command(&alloc_cmd);
add_command(&map_cmd);
add_command(&abort_cmd);
add_args_command(init_args_command);
add_check_command(init_check_command);
......
......@@ -54,8 +54,8 @@ header_length 72
Header extension:
magic 0x6803f857
length 0
data ''
length 96
data <binary>
Header extension:
magic 0x12345678
......@@ -68,7 +68,7 @@ No errors were found on the image.
magic 0x514649fb
version 2
backing_file_offset 0x98
backing_file_offset 0xf8
backing_file_size 0x17
cluster_bits 16
size 67108864
......@@ -92,8 +92,8 @@ data 'host_device'
Header extension:
magic 0x6803f857
length 0
data ''
length 96
data <binary>
Header extension:
magic 0x12345678
......@@ -155,8 +155,8 @@ header_length 104
Header extension:
magic 0x6803f857
length 0
data ''
length 96
data <binary>
Header extension:
magic 0x12345678
......@@ -169,7 +169,7 @@ No errors were found on the image.
magic 0x514649fb
version 3
backing_file_offset 0xb8
backing_file_offset 0x118
backing_file_size 0x17
cluster_bits 16
size 67108864
......@@ -193,8 +193,8 @@ data 'host_device'
Header extension:
magic 0x6803f857
length 0
data ''
length 96
data <binary>
Header extension:
magic 0x12345678
......
......@@ -46,7 +46,7 @@ header_length 104
Header extension:
magic 0x6803f857
length 0
data ''
length 96
data <binary>
*** done
#!/bin/bash
#
# Test qcow2 lazy refcounts
#
# Copyright (C) 2012 Red Hat, Inc.
# Copyright IBM, Corp. 2010
#
# Based on test 038.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# creator
owner=stefanha@linux.vnet.ibm.com
seq=`basename $0`
echo "QA output created by $seq"
here=`pwd`
tmp=/tmp/$$
status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
_supported_fmt qcow2
_supported_proto generic
_supported_os Linux
size=128M
echo
echo "== Checking that image is clean on shutdown =="
IMGOPTS="compat=1.1,lazy_refcounts=on"
_make_test_img $size
$QEMU_IO -c "write -P 0x5a 0 512" $TEST_IMG | _filter_qemu_io
# The dirty bit must not be set
./qcow2.py $TEST_IMG dump-header | grep incompatible_features
_check_test_img
echo
echo "== Creating a dirty image file =="
IMGOPTS="compat=1.1,lazy_refcounts=on"
_make_test_img $size
old_ulimit=$(ulimit -c)
ulimit -c 0 # do not produce a core dump on abort(3)
$QEMU_IO -c "write -P 0x5a 0 512" -c "abort" $TEST_IMG | _filter_qemu_io
ulimit -c "$old_ulimit"
# The dirty bit must be set
./qcow2.py $TEST_IMG dump-header | grep incompatible_features
_check_test_img
echo
echo "== Read-only access must still work =="
$QEMU_IO -r -c "read -P 0x5a 0 512" $TEST_IMG | _filter_qemu_io
# The dirty bit must be set
./qcow2.py $TEST_IMG dump-header | grep incompatible_features
echo
echo "== Repairing the image file must succeed =="
$QEMU_IMG check -r all $TEST_IMG
# The dirty bit must not be set
./qcow2.py $TEST_IMG dump-header | grep incompatible_features
echo
echo "== Data should still be accessible after repair =="
$QEMU_IO -c "read -P 0x5a 0 512" $TEST_IMG | _filter_qemu_io
echo
echo "== Opening a dirty image read/write should repair it =="
IMGOPTS="compat=1.1,lazy_refcounts=on"
_make_test_img $size