Block layer patches:
- qcow2: Skip copy-on-write when allocating a zero cluster - qemu-img: add support for rate limit in qemu-img convert/commit - Fix deadlock when deleting a block node during drain_all -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAl+YOT8RHGt3b2xmQHJl ZGhhdC5jb20ACgkQfwmycsiPL9YBPw//eAhI/entLtuhhGNgOq4ULmRgL5qrA/jU Cuel9/xoMsNSmh7hCa5ZSR39Wy4ZbqrTwnJZBSVGetg+v/6R1bvsDKpCKvAgFi14 bsZikgCHILB8oMWue80pTD+FF3SOoyv2C7uPjGM84EZNHRK5herW5qI2XXQWlV+G PMW9ZgWyYLNoD+D23DZfTusmLVC1PkuVlDahhYj1mNHpLbEqYd9RQBkt+ONhbm1X Ra6xcbRdL5jzLsb6DNRWUglO+mteNET58VVGdsB2Z4Qu0n+QQtnRl4SaatN0WsSY 5IEbHSOWC40NJQIhluxfcYHnSdPoNfTEyOC6wIb+BaFkZXV99sCYzZh1Gt+jRVM3 WEO47C1HCQ65c3aJcoVw0XC3Bm8MpfNU0DYa7O7hVEcmIA8de3N3yHDusHy552Pm FMFThGjkuKaRnA59BLCkNBXBgsqXbAzP6TxroBFVcKCEmM89On8ejAJOP0uig6Fq REqVnCny+0ESRPgw936IGwFHiL2MeRsbqam1TaHcvtgmUX3eMAOIW3xo85F1vziw d8XxC7vVcDwL2xn6sCD/E2UVhFHQQnZAEs4OTsW0R9FkM2u0PAR4jiiPsKpm+VQb /wJatDmAAQfVD8SJbsNDJEercWsUdtSEYrPq/rD0e2KaiEUQg6teyJxeVr2r1rq2 d5n4cwXpkYQ= =ON5g -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging Block layer patches: - qcow2: Skip copy-on-write when allocating a zero cluster - qemu-img: add support for rate limit in qemu-img convert/commit - Fix deadlock when deleting a block node during drain_all # gpg: Signature made Tue 27 Oct 2020 15:14:07 GMT # gpg: using RSA key DC3DEB159A9AF95D3D7456FE7F09B272C88F2FD6 # gpg: issuer "kwolf@redhat.com" # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full] # Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6 * remotes/kevin/tags/for-upstream: block: End quiescent sections when a BDS is deleted qcow2: Skip copy-on-write when allocating a zero cluster qcow2: Report BDRV_BLOCK_ZERO more accurately in bdrv_co_block_status() qemu-img: add support for rate limit in qemu-img convert qemu-img: add support for rate limit in qemu-img commit Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
c99fa56b95
9
block.c
9
block.c
@ -4458,6 +4458,15 @@ static void bdrv_close(BlockDriverState *bs)
|
|||||||
}
|
}
|
||||||
QLIST_INIT(&bs->aio_notifiers);
|
QLIST_INIT(&bs->aio_notifiers);
|
||||||
bdrv_drained_end(bs);
|
bdrv_drained_end(bs);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we're still inside some bdrv_drain_all_begin()/end() sections, end
|
||||||
|
* them now since this BDS won't exist anymore when bdrv_drain_all_end()
|
||||||
|
* gets called.
|
||||||
|
*/
|
||||||
|
if (bs->quiesce_counter) {
|
||||||
|
bdrv_drain_all_end_quiesce(bs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_close_all(void)
|
void bdrv_close_all(void)
|
||||||
|
48
block/io.c
48
block/io.c
@ -633,6 +633,19 @@ void bdrv_drain_all_begin(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bdrv_drain_all_end_quiesce(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
int drained_end_counter = 0;
|
||||||
|
|
||||||
|
g_assert(bs->quiesce_counter > 0);
|
||||||
|
g_assert(!bs->refcnt);
|
||||||
|
|
||||||
|
while (bs->quiesce_counter) {
|
||||||
|
bdrv_do_drained_end(bs, false, NULL, true, &drained_end_counter);
|
||||||
|
}
|
||||||
|
BDRV_POLL_WHILE(bs, qatomic_read(&drained_end_counter) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
void bdrv_drain_all_end(void)
|
void bdrv_drain_all_end(void)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = NULL;
|
BlockDriverState *bs = NULL;
|
||||||
@ -2282,17 +2295,17 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs,
|
|||||||
|
|
||||||
if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) {
|
if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) {
|
||||||
ret |= BDRV_BLOCK_ALLOCATED;
|
ret |= BDRV_BLOCK_ALLOCATED;
|
||||||
} else if (want_zero && bs->drv->supports_backing) {
|
} else if (bs->drv->supports_backing) {
|
||||||
BlockDriverState *cow_bs = bdrv_cow_bs(bs);
|
BlockDriverState *cow_bs = bdrv_cow_bs(bs);
|
||||||
|
|
||||||
if (cow_bs) {
|
if (!cow_bs) {
|
||||||
|
ret |= BDRV_BLOCK_ZERO;
|
||||||
|
} else if (want_zero) {
|
||||||
int64_t size2 = bdrv_getlength(cow_bs);
|
int64_t size2 = bdrv_getlength(cow_bs);
|
||||||
|
|
||||||
if (size2 >= 0 && offset >= size2) {
|
if (size2 >= 0 && offset >= size2) {
|
||||||
ret |= BDRV_BLOCK_ZERO;
|
ret |= BDRV_BLOCK_ZERO;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
ret |= BDRV_BLOCK_ZERO;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2447,6 +2460,33 @@ int bdrv_block_status(BlockDriverState *bs, int64_t offset, int64_t bytes,
|
|||||||
offset, bytes, pnum, map, file);
|
offset, bytes, pnum, map, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check @bs (and its backing chain) to see if the range defined
|
||||||
|
* by @offset and @bytes is known to read as zeroes.
|
||||||
|
* Return 1 if that is the case, 0 otherwise and -errno on error.
|
||||||
|
* This test is meant to be fast rather than accurate so returning 0
|
||||||
|
* does not guarantee non-zero data.
|
||||||
|
*/
|
||||||
|
int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset,
|
||||||
|
int64_t bytes)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int64_t pnum = bytes;
|
||||||
|
|
||||||
|
if (!bytes) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bdrv_common_block_status_above(bs, NULL, false, false, offset,
|
||||||
|
bytes, &pnum, NULL, NULL);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (pnum == bytes) && (ret & BDRV_BLOCK_ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset,
|
int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset,
|
||||||
int64_t bytes, int64_t *pnum)
|
int64_t bytes, int64_t *pnum)
|
||||||
{
|
{
|
||||||
|
@ -2387,26 +2387,26 @@ static bool merge_cow(uint64_t offset, unsigned bytes,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_unallocated(BlockDriverState *bs, int64_t offset, int64_t bytes)
|
/*
|
||||||
{
|
* Return 1 if the COW regions read as zeroes, 0 if not, < 0 on error.
|
||||||
int64_t nr;
|
* Note that returning 0 does not guarantee non-zero data.
|
||||||
return !bytes ||
|
*/
|
||||||
(!bdrv_is_allocated_above(bs, NULL, false, offset, bytes, &nr) &&
|
static int is_zero_cow(BlockDriverState *bs, QCowL2Meta *m)
|
||||||
nr == bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_zero_cow(BlockDriverState *bs, QCowL2Meta *m)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* This check is designed for optimization shortcut so it must be
|
* This check is designed for optimization shortcut so it must be
|
||||||
* efficient.
|
* efficient.
|
||||||
* Instead of is_zero(), use is_unallocated() as it is faster (but not
|
* Instead of is_zero(), use bdrv_co_is_zero_fast() as it is
|
||||||
* as accurate and can result in false negatives).
|
* faster (but not as accurate and can result in false negatives).
|
||||||
*/
|
*/
|
||||||
return is_unallocated(bs, m->offset + m->cow_start.offset,
|
int ret = bdrv_co_is_zero_fast(bs, m->offset + m->cow_start.offset,
|
||||||
m->cow_start.nb_bytes) &&
|
m->cow_start.nb_bytes);
|
||||||
is_unallocated(bs, m->offset + m->cow_end.offset,
|
if (ret <= 0) {
|
||||||
m->cow_end.nb_bytes);
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bdrv_co_is_zero_fast(bs, m->offset + m->cow_end.offset,
|
||||||
|
m->cow_end.nb_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta)
|
static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta)
|
||||||
@ -2432,7 +2432,10 @@ static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_zero_cow(bs, m)) {
|
ret = is_zero_cow(bs, m);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
} else if (ret == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,6 +188,10 @@ Parameters to convert subcommand:
|
|||||||
allocated target image depending on the host support for getting allocation
|
allocated target image depending on the host support for getting allocation
|
||||||
information.
|
information.
|
||||||
|
|
||||||
|
.. option:: -r
|
||||||
|
|
||||||
|
Rate limit for the convert process
|
||||||
|
|
||||||
.. option:: --salvage
|
.. option:: --salvage
|
||||||
|
|
||||||
Try to ignore I/O errors when reading. Unless in quiet mode (``-q``), errors
|
Try to ignore I/O errors when reading. Unless in quiet mode (``-q``), errors
|
||||||
@ -349,7 +353,7 @@ Command description:
|
|||||||
state after (the attempt at) repairing it. That is, a successful ``-r all``
|
state after (the attempt at) repairing it. That is, a successful ``-r all``
|
||||||
will yield the exit code 0, independently of the image state before.
|
will yield the exit code 0, independently of the image state before.
|
||||||
|
|
||||||
.. option:: commit [--object OBJECTDEF] [--image-opts] [-q] [-f FMT] [-t CACHE] [-b BASE] [-d] [-p] FILENAME
|
.. option:: commit [--object OBJECTDEF] [--image-opts] [-q] [-f FMT] [-t CACHE] [-b BASE] [-r RATE_LIMIT] [-d] [-p] FILENAME
|
||||||
|
|
||||||
Commit the changes recorded in *FILENAME* in its base image or backing file.
|
Commit the changes recorded in *FILENAME* in its base image or backing file.
|
||||||
If the backing file is smaller than the snapshot, then the backing file will be
|
If the backing file is smaller than the snapshot, then the backing file will be
|
||||||
@ -371,6 +375,8 @@ Command description:
|
|||||||
garbage data when read. For this reason, ``-b`` implies ``-d`` (so that
|
garbage data when read. For this reason, ``-b`` implies ``-d`` (so that
|
||||||
the top image stays valid).
|
the top image stays valid).
|
||||||
|
|
||||||
|
The rate limit for the commit process is specified by ``-r``.
|
||||||
|
|
||||||
.. option:: compare [--object OBJECTDEF] [--image-opts] [-f FMT] [-F FMT] [-T SRC_CACHE] [-p] [-q] [-s] [-U] FILENAME1 FILENAME2
|
.. option:: compare [--object OBJECTDEF] [--image-opts] [-f FMT] [-F FMT] [-T SRC_CACHE] [-p] [-q] [-s] [-U] FILENAME1 FILENAME2
|
||||||
|
|
||||||
Check if two images have the same content. You can compare images with
|
Check if two images have the same content. You can compare images with
|
||||||
@ -408,7 +414,7 @@ Command description:
|
|||||||
4
|
4
|
||||||
Error on reading data
|
Error on reading data
|
||||||
|
|
||||||
.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-m NUM_COROUTINES] [-W] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME
|
.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-r RATE_LIMIT] [-m NUM_COROUTINES] [-W] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME
|
||||||
|
|
||||||
Convert the disk image *FILENAME* or a snapshot *SNAPSHOT_PARAM*
|
Convert the disk image *FILENAME* or a snapshot *SNAPSHOT_PARAM*
|
||||||
to disk image *OUTPUT_FILENAME* using format *OUTPUT_FMT*. It can
|
to disk image *OUTPUT_FILENAME* using format *OUTPUT_FMT*. It can
|
||||||
|
@ -508,6 +508,8 @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes,
|
|||||||
int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
|
int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
|
||||||
bool include_base, int64_t offset, int64_t bytes,
|
bool include_base, int64_t offset, int64_t bytes,
|
||||||
int64_t *pnum);
|
int64_t *pnum);
|
||||||
|
int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset,
|
||||||
|
int64_t bytes);
|
||||||
|
|
||||||
bool bdrv_is_read_only(BlockDriverState *bs);
|
bool bdrv_is_read_only(BlockDriverState *bs);
|
||||||
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
|
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
|
||||||
@ -779,6 +781,12 @@ void bdrv_drained_end(BlockDriverState *bs);
|
|||||||
*/
|
*/
|
||||||
void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter);
|
void bdrv_drained_end_no_poll(BlockDriverState *bs, int *drained_end_counter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End all quiescent sections started by bdrv_drain_all_begin(). This is
|
||||||
|
* only needed when deleting a BDS before bdrv_drain_all_end() is called.
|
||||||
|
*/
|
||||||
|
void bdrv_drain_all_end_quiesce(BlockDriverState *bs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End a quiescent section started by bdrv_subtree_drained_begin().
|
* End a quiescent section started by bdrv_subtree_drained_begin().
|
||||||
*/
|
*/
|
||||||
|
@ -34,9 +34,9 @@ SRST
|
|||||||
ERST
|
ERST
|
||||||
|
|
||||||
DEF("commit", img_commit,
|
DEF("commit", img_commit,
|
||||||
"commit [--object objectdef] [--image-opts] [-q] [-f fmt] [-t cache] [-b base] [-d] [-p] filename")
|
"commit [--object objectdef] [--image-opts] [-q] [-f fmt] [-t cache] [-b base] [-r rate_limit] [-d] [-p] filename")
|
||||||
SRST
|
SRST
|
||||||
.. option:: commit [--object OBJECTDEF] [--image-opts] [-q] [-f FMT] [-t CACHE] [-b BASE] [-d] [-p] FILENAME
|
.. option:: commit [--object OBJECTDEF] [--image-opts] [-q] [-f FMT] [-t CACHE] [-b BASE] [-r RATE_LIMIT] [-d] [-p] FILENAME
|
||||||
ERST
|
ERST
|
||||||
|
|
||||||
DEF("compare", img_compare,
|
DEF("compare", img_compare,
|
||||||
@ -46,9 +46,9 @@ SRST
|
|||||||
ERST
|
ERST
|
||||||
|
|
||||||
DEF("convert", img_convert,
|
DEF("convert", img_convert,
|
||||||
"convert [--object objectdef] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] [--salvage] filename [filename2 [...]] output_filename")
|
"convert [--object objectdef] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-r rate_limit] [-m num_coroutines] [-W] [--salvage] filename [filename2 [...]] output_filename")
|
||||||
SRST
|
SRST
|
||||||
.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-m NUM_COROUTINES] [-W] [--salvage] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME
|
.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-r RATE_LIMIT] [-m NUM_COROUTINES] [-W] [--salvage] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME
|
||||||
ERST
|
ERST
|
||||||
|
|
||||||
DEF("create", img_create,
|
DEF("create", img_create,
|
||||||
|
38
qemu-img.c
38
qemu-img.c
@ -50,6 +50,8 @@
|
|||||||
#include "block/qapi.h"
|
#include "block/qapi.h"
|
||||||
#include "crypto/init.h"
|
#include "crypto/init.h"
|
||||||
#include "trace/control.h"
|
#include "trace/control.h"
|
||||||
|
#include "qemu/throttle.h"
|
||||||
|
#include "block/throttle-groups.h"
|
||||||
|
|
||||||
#define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \
|
#define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \
|
||||||
"\n" QEMU_COPYRIGHT "\n"
|
"\n" QEMU_COPYRIGHT "\n"
|
||||||
@ -980,6 +982,7 @@ static int img_commit(int argc, char **argv)
|
|||||||
CommonBlockJobCBInfo cbi;
|
CommonBlockJobCBInfo cbi;
|
||||||
bool image_opts = false;
|
bool image_opts = false;
|
||||||
AioContext *aio_context;
|
AioContext *aio_context;
|
||||||
|
int64_t rate_limit = 0;
|
||||||
|
|
||||||
fmt = NULL;
|
fmt = NULL;
|
||||||
cache = BDRV_DEFAULT_CACHE;
|
cache = BDRV_DEFAULT_CACHE;
|
||||||
@ -991,7 +994,7 @@ static int img_commit(int argc, char **argv)
|
|||||||
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
|
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
c = getopt_long(argc, argv, ":f:ht:b:dpq",
|
c = getopt_long(argc, argv, ":f:ht:b:dpqr:",
|
||||||
long_options, NULL);
|
long_options, NULL);
|
||||||
if (c == -1) {
|
if (c == -1) {
|
||||||
break;
|
break;
|
||||||
@ -1026,6 +1029,12 @@ static int img_commit(int argc, char **argv)
|
|||||||
case 'q':
|
case 'q':
|
||||||
quiet = true;
|
quiet = true;
|
||||||
break;
|
break;
|
||||||
|
case 'r':
|
||||||
|
rate_limit = cvtnum("rate limit", optarg);
|
||||||
|
if (rate_limit < 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case OPTION_OBJECT: {
|
case OPTION_OBJECT: {
|
||||||
QemuOpts *opts;
|
QemuOpts *opts;
|
||||||
opts = qemu_opts_parse_noisily(&qemu_object_opts,
|
opts = qemu_opts_parse_noisily(&qemu_object_opts,
|
||||||
@ -1099,7 +1108,7 @@ static int img_commit(int argc, char **argv)
|
|||||||
|
|
||||||
aio_context = bdrv_get_aio_context(bs);
|
aio_context = bdrv_get_aio_context(bs);
|
||||||
aio_context_acquire(aio_context);
|
aio_context_acquire(aio_context);
|
||||||
commit_active_start("commit", bs, base_bs, JOB_DEFAULT, 0,
|
commit_active_start("commit", bs, base_bs, JOB_DEFAULT, rate_limit,
|
||||||
BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb,
|
BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb,
|
||||||
&cbi, false, &local_err);
|
&cbi, false, &local_err);
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
@ -1662,6 +1671,7 @@ enum ImgConvertBlockStatus {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define MAX_COROUTINES 16
|
#define MAX_COROUTINES 16
|
||||||
|
#define CONVERT_THROTTLE_GROUP "img_convert"
|
||||||
|
|
||||||
typedef struct ImgConvertState {
|
typedef struct ImgConvertState {
|
||||||
BlockBackend **src;
|
BlockBackend **src;
|
||||||
@ -2177,6 +2187,17 @@ static int convert_copy_bitmaps(BlockDriverState *src, BlockDriverState *dst)
|
|||||||
|
|
||||||
#define MAX_BUF_SECTORS 32768
|
#define MAX_BUF_SECTORS 32768
|
||||||
|
|
||||||
|
static void set_rate_limit(BlockBackend *blk, int64_t rate_limit)
|
||||||
|
{
|
||||||
|
ThrottleConfig cfg;
|
||||||
|
|
||||||
|
throttle_config_init(&cfg);
|
||||||
|
cfg.buckets[THROTTLE_BPS_WRITE].avg = rate_limit;
|
||||||
|
|
||||||
|
blk_io_limits_enable(blk, CONVERT_THROTTLE_GROUP);
|
||||||
|
blk_set_io_limits(blk, &cfg);
|
||||||
|
}
|
||||||
|
|
||||||
static int img_convert(int argc, char **argv)
|
static int img_convert(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int c, bs_i, flags, src_flags = 0;
|
int c, bs_i, flags, src_flags = 0;
|
||||||
@ -2197,6 +2218,7 @@ static int img_convert(int argc, char **argv)
|
|||||||
bool force_share = false;
|
bool force_share = false;
|
||||||
bool explict_min_sparse = false;
|
bool explict_min_sparse = false;
|
||||||
bool bitmaps = false;
|
bool bitmaps = false;
|
||||||
|
int64_t rate_limit = 0;
|
||||||
|
|
||||||
ImgConvertState s = (ImgConvertState) {
|
ImgConvertState s = (ImgConvertState) {
|
||||||
/* Need at least 4k of zeros for sparse detection */
|
/* Need at least 4k of zeros for sparse detection */
|
||||||
@ -2219,7 +2241,7 @@ static int img_convert(int argc, char **argv)
|
|||||||
{"bitmaps", no_argument, 0, OPTION_BITMAPS},
|
{"bitmaps", no_argument, 0, OPTION_BITMAPS},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WU",
|
c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WUr:",
|
||||||
long_options, NULL);
|
long_options, NULL);
|
||||||
if (c == -1) {
|
if (c == -1) {
|
||||||
break;
|
break;
|
||||||
@ -2316,6 +2338,12 @@ static int img_convert(int argc, char **argv)
|
|||||||
case 'U':
|
case 'U':
|
||||||
force_share = true;
|
force_share = true;
|
||||||
break;
|
break;
|
||||||
|
case 'r':
|
||||||
|
rate_limit = cvtnum("rate limit", optarg);
|
||||||
|
if (rate_limit < 0) {
|
||||||
|
goto fail_getopt;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case OPTION_OBJECT: {
|
case OPTION_OBJECT: {
|
||||||
QemuOpts *object_opts;
|
QemuOpts *object_opts;
|
||||||
object_opts = qemu_opts_parse_noisily(&qemu_object_opts,
|
object_opts = qemu_opts_parse_noisily(&qemu_object_opts,
|
||||||
@ -2705,6 +2733,10 @@ static int img_convert(int argc, char **argv)
|
|||||||
s.cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE;
|
s.cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rate_limit) {
|
||||||
|
set_rate_limit(s.target, rate_limit);
|
||||||
|
}
|
||||||
|
|
||||||
ret = convert_do_copy(&s);
|
ret = convert_do_copy(&s);
|
||||||
|
|
||||||
/* Now copy the bitmaps */
|
/* Now copy the bitmaps */
|
||||||
|
@ -594,6 +594,7 @@ static void test_graph_change_drain_all(void)
|
|||||||
|
|
||||||
g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
|
g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
|
||||||
g_assert_cmpint(b_s->drain_count, ==, 0);
|
g_assert_cmpint(b_s->drain_count, ==, 0);
|
||||||
|
g_assert_cmpint(qemu_get_aio_context()->external_disable_cnt, ==, 0);
|
||||||
|
|
||||||
bdrv_unref(bs_b);
|
bdrv_unref(bs_b);
|
||||||
blk_unref(blk_b);
|
blk_unref(blk_b);
|
||||||
|
Loading…
Reference in New Issue
Block a user