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>
master
Peter Maydell 2020-10-30 14:36:52 +00:00
commit c99fa56b95
8 changed files with 128 additions and 29 deletions

View File

@ -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)

View File

@ -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)
{ {

View File

@ -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;
} }

View File

@ -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

View File

@ -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().
*/ */

View File

@ -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,

View File

@ -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 */

View File

@ -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);