1 | The following changes since commit ba29883206d92a29ad5a466e679ccfc2ee6132ef: | 1 | The following changes since commit 9cf289af47bcfae5c75de37d8e5d6fd23705322c: |
---|---|---|---|
2 | 2 | ||
3 | Merge remote-tracking branch 'remotes/borntraeger/tags/s390x-20200310' into staging (2020-03-10 16:50:28 +0000) | 3 | Merge tag 'qga-pull-request' of gitlab.com:marcandre.lureau/qemu into staging (2022-05-04 03:42:49 -0700) |
4 | 4 | ||
5 | are available in the Git repository at: | 5 | are available in the Git repository at: |
6 | 6 | ||
7 | https://github.com/XanClic/qemu.git tags/pull-block-2020-03-11 | 7 | https://gitlab.com/stefanha/qemu.git tags/block-pull-request |
8 | 8 | ||
9 | for you to fetch changes up to 397f4e9d83e9c0000905f0a988ba1aeda162571c: | 9 | for you to fetch changes up to bef2e050d6a7feb865854c65570c496ac5a8cf53: |
10 | 10 | ||
11 | block/block-copy: hide structure definitions (2020-03-11 12:42:30 +0100) | 11 | util/event-loop-base: Introduce options to set the thread pool size (2022-05-04 17:02:19 +0100) |
12 | 12 | ||
13 | ---------------------------------------------------------------- | 13 | ---------------------------------------------------------------- |
14 | Block patches for the 5.0 softfreeze: | 14 | Pull request |
15 | - qemu-img measure for LUKS | 15 | |
16 | - Improve block-copy's performance by reducing inter-request | 16 | Add new thread-pool-min/thread-pool-max parameters to control the thread pool |
17 | dependencies | 17 | used for async I/O. |
18 | - Make curl's detection of accept-ranges more robust | ||
19 | - Memleak fixes | ||
20 | - iotest fix | ||
21 | 18 | ||
22 | ---------------------------------------------------------------- | 19 | ---------------------------------------------------------------- |
23 | David Edmondson (2): | ||
24 | block/curl: HTTP header fields allow whitespace around values | ||
25 | block/curl: HTTP header field names are case insensitive | ||
26 | 20 | ||
27 | Eric Blake (1): | 21 | Nicolas Saenz Julienne (3): |
28 | iotests: Fix nonportable use of od --endian | 22 | Introduce event-loop-base abstract class |
23 | util/main-loop: Introduce the main loop into QOM | ||
24 | util/event-loop-base: Introduce options to set the thread pool size | ||
29 | 25 | ||
30 | Pan Nengyuan (2): | 26 | qapi/qom.json | 43 ++++++++-- |
31 | block/qcow2: do free crypto_opts in qcow2_close() | 27 | meson.build | 26 +++--- |
32 | qemu-img: free memory before re-assign | 28 | include/block/aio.h | 10 +++ |
33 | 29 | include/block/thread-pool.h | 3 + | |
34 | Stefan Hajnoczi (4): | 30 | include/qemu/main-loop.h | 10 +++ |
35 | luks: extract qcrypto_block_calculate_payload_offset() | 31 | include/sysemu/event-loop-base.h | 41 +++++++++ |
36 | luks: implement .bdrv_measure() | 32 | include/sysemu/iothread.h | 6 +- |
37 | qemu-img: allow qemu-img measure --object without a filename | 33 | event-loop-base.c | 140 +++++++++++++++++++++++++++++++ |
38 | iotests: add 288 luks qemu-img measure test | 34 | iothread.c | 68 +++++---------- |
39 | 35 | util/aio-posix.c | 1 + | |
40 | Vladimir Sementsov-Ogievskiy (10): | 36 | util/async.c | 20 +++++ |
41 | block/qcow2-threads: fix qcow2_decompress | 37 | util/main-loop.c | 65 ++++++++++++++ |
42 | job: refactor progress to separate object | 38 | util/thread-pool.c | 55 +++++++++++- |
43 | block/block-copy: fix progress calculation | 39 | 13 files changed, 419 insertions(+), 69 deletions(-) |
44 | block/block-copy: specialcase first copy_range request | 40 | create mode 100644 include/sysemu/event-loop-base.h |
45 | block/block-copy: use block_status | 41 | create mode 100644 event-loop-base.c |
46 | block/block-copy: factor out find_conflicting_inflight_req | ||
47 | block/block-copy: refactor interfaces to use bytes instead of end | ||
48 | block/block-copy: rename start to offset in interfaces | ||
49 | block/block-copy: reduce intersecting request lock | ||
50 | block/block-copy: hide structure definitions | ||
51 | |||
52 | block/backup-top.c | 6 +- | ||
53 | block/backup.c | 38 ++- | ||
54 | block/block-copy.c | 405 ++++++++++++++++++++++++------- | ||
55 | block/crypto.c | 62 +++++ | ||
56 | block/curl.c | 32 ++- | ||
57 | block/qcow2-threads.c | 12 +- | ||
58 | block/qcow2.c | 75 ++---- | ||
59 | block/trace-events | 1 + | ||
60 | blockjob.c | 16 +- | ||
61 | crypto/block.c | 36 +++ | ||
62 | include/block/block-copy.h | 65 +---- | ||
63 | include/crypto/block.h | 22 ++ | ||
64 | include/qemu/job.h | 11 +- | ||
65 | include/qemu/progress_meter.h | 58 +++++ | ||
66 | job-qmp.c | 4 +- | ||
67 | job.c | 6 +- | ||
68 | qemu-img.c | 14 +- | ||
69 | tests/qemu-iotests/178 | 2 +- | ||
70 | tests/qemu-iotests/178.out.qcow2 | 8 +- | ||
71 | tests/qemu-iotests/178.out.raw | 8 +- | ||
72 | tests/qemu-iotests/288 | 93 +++++++ | ||
73 | tests/qemu-iotests/288.out | 30 +++ | ||
74 | tests/qemu-iotests/common.rc | 22 +- | ||
75 | tests/qemu-iotests/group | 1 + | ||
76 | 24 files changed, 749 insertions(+), 278 deletions(-) | ||
77 | create mode 100644 include/qemu/progress_meter.h | ||
78 | create mode 100755 tests/qemu-iotests/288 | ||
79 | create mode 100644 tests/qemu-iotests/288.out | ||
80 | 42 | ||
81 | -- | 43 | -- |
82 | 2.24.1 | 44 | 2.35.1 |
83 | |||
84 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
2 | 1 | ||
3 | The qcow2 .bdrv_measure() code calculates the crypto payload offset. | ||
4 | This logic really belongs in crypto/block.c where it can be reused by | ||
5 | other image formats. | ||
6 | |||
7 | The "luks" block driver will need this same logic in order to implement | ||
8 | .bdrv_measure(), so extract the qcrypto_block_calculate_payload_offset() | ||
9 | function now. | ||
10 | |||
11 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
12 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
13 | Message-Id: <20200221112522.1497712-2-stefanha@redhat.com> | ||
14 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
15 | --- | ||
16 | block/qcow2.c | 74 +++++++++++------------------------------- | ||
17 | crypto/block.c | 36 ++++++++++++++++++++ | ||
18 | include/crypto/block.h | 22 +++++++++++++ | ||
19 | 3 files changed, 77 insertions(+), 55 deletions(-) | ||
20 | |||
21 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
22 | index XXXXXXX..XXXXXXX 100644 | ||
23 | --- a/block/qcow2.c | ||
24 | +++ b/block/qcow2.c | ||
25 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) | ||
26 | return ret; | ||
27 | } | ||
28 | |||
29 | -static ssize_t qcow2_measure_crypto_hdr_init_func(QCryptoBlock *block, | ||
30 | - size_t headerlen, void *opaque, Error **errp) | ||
31 | -{ | ||
32 | - size_t *headerlenp = opaque; | ||
33 | - | ||
34 | - /* Stash away the payload size */ | ||
35 | - *headerlenp = headerlen; | ||
36 | - return 0; | ||
37 | -} | ||
38 | - | ||
39 | -static ssize_t qcow2_measure_crypto_hdr_write_func(QCryptoBlock *block, | ||
40 | - size_t offset, const uint8_t *buf, size_t buflen, | ||
41 | - void *opaque, Error **errp) | ||
42 | -{ | ||
43 | - /* Discard the bytes, we're not actually writing to an image */ | ||
44 | - return buflen; | ||
45 | -} | ||
46 | - | ||
47 | -/* Determine the number of bytes for the LUKS payload */ | ||
48 | -static bool qcow2_measure_luks_headerlen(QemuOpts *opts, size_t *len, | ||
49 | - Error **errp) | ||
50 | -{ | ||
51 | - QDict *opts_qdict; | ||
52 | - QDict *cryptoopts_qdict; | ||
53 | - QCryptoBlockCreateOptions *cryptoopts; | ||
54 | - QCryptoBlock *crypto; | ||
55 | - | ||
56 | - /* Extract "encrypt." options into a qdict */ | ||
57 | - opts_qdict = qemu_opts_to_qdict(opts, NULL); | ||
58 | - qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt."); | ||
59 | - qobject_unref(opts_qdict); | ||
60 | - | ||
61 | - /* Build QCryptoBlockCreateOptions object from qdict */ | ||
62 | - qdict_put_str(cryptoopts_qdict, "format", "luks"); | ||
63 | - cryptoopts = block_crypto_create_opts_init(cryptoopts_qdict, errp); | ||
64 | - qobject_unref(cryptoopts_qdict); | ||
65 | - if (!cryptoopts) { | ||
66 | - return false; | ||
67 | - } | ||
68 | - | ||
69 | - /* Fake LUKS creation in order to determine the payload size */ | ||
70 | - crypto = qcrypto_block_create(cryptoopts, "encrypt.", | ||
71 | - qcow2_measure_crypto_hdr_init_func, | ||
72 | - qcow2_measure_crypto_hdr_write_func, | ||
73 | - len, errp); | ||
74 | - qapi_free_QCryptoBlockCreateOptions(cryptoopts); | ||
75 | - if (!crypto) { | ||
76 | - return false; | ||
77 | - } | ||
78 | - | ||
79 | - qcrypto_block_free(crypto); | ||
80 | - return true; | ||
81 | -} | ||
82 | - | ||
83 | static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, | ||
84 | Error **errp) | ||
85 | { | ||
86 | @@ -XXX,XX +XXX,XX @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, | ||
87 | g_free(optstr); | ||
88 | |||
89 | if (has_luks) { | ||
90 | + g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL; | ||
91 | + QDict *opts_qdict; | ||
92 | + QDict *cryptoopts; | ||
93 | size_t headerlen; | ||
94 | |||
95 | - if (!qcow2_measure_luks_headerlen(opts, &headerlen, &local_err)) { | ||
96 | + opts_qdict = qemu_opts_to_qdict(opts, NULL); | ||
97 | + qdict_extract_subqdict(opts_qdict, &cryptoopts, "encrypt."); | ||
98 | + qobject_unref(opts_qdict); | ||
99 | + | ||
100 | + qdict_put_str(cryptoopts, "format", "luks"); | ||
101 | + | ||
102 | + create_opts = block_crypto_create_opts_init(cryptoopts, errp); | ||
103 | + qobject_unref(cryptoopts); | ||
104 | + if (!create_opts) { | ||
105 | + goto err; | ||
106 | + } | ||
107 | + | ||
108 | + if (!qcrypto_block_calculate_payload_offset(create_opts, | ||
109 | + "encrypt.", | ||
110 | + &headerlen, | ||
111 | + &local_err)) { | ||
112 | goto err; | ||
113 | } | ||
114 | |||
115 | diff --git a/crypto/block.c b/crypto/block.c | ||
116 | index XXXXXXX..XXXXXXX 100644 | ||
117 | --- a/crypto/block.c | ||
118 | +++ b/crypto/block.c | ||
119 | @@ -XXX,XX +XXX,XX @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, | ||
120 | } | ||
121 | |||
122 | |||
123 | +static ssize_t qcrypto_block_headerlen_hdr_init_func(QCryptoBlock *block, | ||
124 | + size_t headerlen, void *opaque, Error **errp) | ||
125 | +{ | ||
126 | + size_t *headerlenp = opaque; | ||
127 | + | ||
128 | + /* Stash away the payload size */ | ||
129 | + *headerlenp = headerlen; | ||
130 | + return 0; | ||
131 | +} | ||
132 | + | ||
133 | + | ||
134 | +static ssize_t qcrypto_block_headerlen_hdr_write_func(QCryptoBlock *block, | ||
135 | + size_t offset, const uint8_t *buf, size_t buflen, | ||
136 | + void *opaque, Error **errp) | ||
137 | +{ | ||
138 | + /* Discard the bytes, we're not actually writing to an image */ | ||
139 | + return buflen; | ||
140 | +} | ||
141 | + | ||
142 | + | ||
143 | +bool | ||
144 | +qcrypto_block_calculate_payload_offset(QCryptoBlockCreateOptions *create_opts, | ||
145 | + const char *optprefix, | ||
146 | + size_t *len, | ||
147 | + Error **errp) | ||
148 | +{ | ||
149 | + /* Fake LUKS creation in order to determine the payload size */ | ||
150 | + g_autoptr(QCryptoBlock) crypto = | ||
151 | + qcrypto_block_create(create_opts, optprefix, | ||
152 | + qcrypto_block_headerlen_hdr_init_func, | ||
153 | + qcrypto_block_headerlen_hdr_write_func, | ||
154 | + len, errp); | ||
155 | + return crypto != NULL; | ||
156 | +} | ||
157 | + | ||
158 | + | ||
159 | QCryptoBlockInfo *qcrypto_block_get_info(QCryptoBlock *block, | ||
160 | Error **errp) | ||
161 | { | ||
162 | diff --git a/include/crypto/block.h b/include/crypto/block.h | ||
163 | index XXXXXXX..XXXXXXX 100644 | ||
164 | --- a/include/crypto/block.h | ||
165 | +++ b/include/crypto/block.h | ||
166 | @@ -XXX,XX +XXX,XX @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, | ||
167 | Error **errp); | ||
168 | |||
169 | |||
170 | +/** | ||
171 | + * qcrypto_block_calculate_payload_offset: | ||
172 | + * @create_opts: the encryption options | ||
173 | + * @optprefix: name prefix for options | ||
174 | + * @len: output for number of header bytes before payload | ||
175 | + * @errp: pointer to a NULL-initialized error object | ||
176 | + * | ||
177 | + * Calculate the number of header bytes before the payload in an encrypted | ||
178 | + * storage volume. The header is an area before the payload that is reserved | ||
179 | + * for encryption metadata. | ||
180 | + * | ||
181 | + * Returns: true on success, false on error | ||
182 | + */ | ||
183 | +bool | ||
184 | +qcrypto_block_calculate_payload_offset(QCryptoBlockCreateOptions *create_opts, | ||
185 | + const char *optprefix, | ||
186 | + size_t *len, | ||
187 | + Error **errp); | ||
188 | + | ||
189 | + | ||
190 | /** | ||
191 | * qcrypto_block_get_info: | ||
192 | * @block: the block encryption object | ||
193 | @@ -XXX,XX +XXX,XX @@ uint64_t qcrypto_block_get_sector_size(QCryptoBlock *block); | ||
194 | void qcrypto_block_free(QCryptoBlock *block); | ||
195 | |||
196 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlock, qcrypto_block_free) | ||
197 | +G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockCreateOptions, | ||
198 | + qapi_free_QCryptoBlockCreateOptions) | ||
199 | |||
200 | #endif /* QCRYPTO_BLOCK_H */ | ||
201 | -- | ||
202 | 2.24.1 | ||
203 | |||
204 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
2 | 1 | ||
3 | Add qemu-img measure support in the "luks" block driver. | ||
4 | |||
5 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
6 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
7 | Message-Id: <20200221112522.1497712-3-stefanha@redhat.com> | ||
8 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
9 | --- | ||
10 | block/crypto.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ | ||
11 | 1 file changed, 62 insertions(+) | ||
12 | |||
13 | diff --git a/block/crypto.c b/block/crypto.c | ||
14 | index XXXXXXX..XXXXXXX 100644 | ||
15 | --- a/block/crypto.c | ||
16 | +++ b/block/crypto.c | ||
17 | @@ -XXX,XX +XXX,XX @@ static int64_t block_crypto_getlength(BlockDriverState *bs) | ||
18 | } | ||
19 | |||
20 | |||
21 | +static BlockMeasureInfo *block_crypto_measure(QemuOpts *opts, | ||
22 | + BlockDriverState *in_bs, | ||
23 | + Error **errp) | ||
24 | +{ | ||
25 | + g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL; | ||
26 | + Error *local_err = NULL; | ||
27 | + BlockMeasureInfo *info; | ||
28 | + uint64_t size; | ||
29 | + size_t luks_payload_size; | ||
30 | + QDict *cryptoopts; | ||
31 | + | ||
32 | + /* | ||
33 | + * Preallocation mode doesn't affect size requirements but we must consume | ||
34 | + * the option. | ||
35 | + */ | ||
36 | + g_free(qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC)); | ||
37 | + | ||
38 | + size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); | ||
39 | + | ||
40 | + if (in_bs) { | ||
41 | + int64_t ssize = bdrv_getlength(in_bs); | ||
42 | + | ||
43 | + if (ssize < 0) { | ||
44 | + error_setg_errno(&local_err, -ssize, | ||
45 | + "Unable to get image virtual_size"); | ||
46 | + goto err; | ||
47 | + } | ||
48 | + | ||
49 | + size = ssize; | ||
50 | + } | ||
51 | + | ||
52 | + cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL, | ||
53 | + &block_crypto_create_opts_luks, true); | ||
54 | + qdict_put_str(cryptoopts, "format", "luks"); | ||
55 | + create_opts = block_crypto_create_opts_init(cryptoopts, &local_err); | ||
56 | + qobject_unref(cryptoopts); | ||
57 | + if (!create_opts) { | ||
58 | + goto err; | ||
59 | + } | ||
60 | + | ||
61 | + if (!qcrypto_block_calculate_payload_offset(create_opts, NULL, | ||
62 | + &luks_payload_size, | ||
63 | + &local_err)) { | ||
64 | + goto err; | ||
65 | + } | ||
66 | + | ||
67 | + /* | ||
68 | + * Unallocated blocks are still encrypted so allocation status makes no | ||
69 | + * difference to the file size. | ||
70 | + */ | ||
71 | + info = g_new(BlockMeasureInfo, 1); | ||
72 | + info->fully_allocated = luks_payload_size + size; | ||
73 | + info->required = luks_payload_size + size; | ||
74 | + return info; | ||
75 | + | ||
76 | +err: | ||
77 | + error_propagate(errp, local_err); | ||
78 | + return NULL; | ||
79 | +} | ||
80 | + | ||
81 | + | ||
82 | static int block_crypto_probe_luks(const uint8_t *buf, | ||
83 | int buf_size, | ||
84 | const char *filename) { | ||
85 | @@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_crypto_luks = { | ||
86 | .bdrv_co_preadv = block_crypto_co_preadv, | ||
87 | .bdrv_co_pwritev = block_crypto_co_pwritev, | ||
88 | .bdrv_getlength = block_crypto_getlength, | ||
89 | + .bdrv_measure = block_crypto_measure, | ||
90 | .bdrv_get_info = block_crypto_get_info_luks, | ||
91 | .bdrv_get_specific_info = block_crypto_get_specific_info_luks, | ||
92 | |||
93 | -- | ||
94 | 2.24.1 | ||
95 | |||
96 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
2 | 1 | ||
3 | In most qemu-img sub-commands the --object option only makes sense when | ||
4 | there is a filename. qemu-img measure is an exception because objects | ||
5 | may be referenced from the image creation options instead of an existing | ||
6 | image file. Allow --object without a filename. | ||
7 | |||
8 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
10 | Message-Id: <20200221112522.1497712-4-stefanha@redhat.com> | ||
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
12 | --- | ||
13 | qemu-img.c | 6 ++---- | ||
14 | tests/qemu-iotests/178 | 2 +- | ||
15 | tests/qemu-iotests/178.out.qcow2 | 8 ++++---- | ||
16 | tests/qemu-iotests/178.out.raw | 8 ++++---- | ||
17 | 4 files changed, 11 insertions(+), 13 deletions(-) | ||
18 | |||
19 | diff --git a/qemu-img.c b/qemu-img.c | ||
20 | index XXXXXXX..XXXXXXX 100644 | ||
21 | --- a/qemu-img.c | ||
22 | +++ b/qemu-img.c | ||
23 | @@ -XXX,XX +XXX,XX @@ static int img_measure(int argc, char **argv) | ||
24 | filename = argv[optind]; | ||
25 | } | ||
26 | |||
27 | - if (!filename && | ||
28 | - (object_opts || image_opts || fmt || snapshot_name || sn_opts)) { | ||
29 | - error_report("--object, --image-opts, -f, and -l " | ||
30 | - "require a filename argument."); | ||
31 | + if (!filename && (image_opts || fmt || snapshot_name || sn_opts)) { | ||
32 | + error_report("--image-opts, -f, and -l require a filename argument."); | ||
33 | goto out; | ||
34 | } | ||
35 | if (filename && img_size != UINT64_MAX) { | ||
36 | diff --git a/tests/qemu-iotests/178 b/tests/qemu-iotests/178 | ||
37 | index XXXXXXX..XXXXXXX 100755 | ||
38 | --- a/tests/qemu-iotests/178 | ||
39 | +++ b/tests/qemu-iotests/178 | ||
40 | @@ -XXX,XX +XXX,XX @@ _make_test_img 1G | ||
41 | $QEMU_IMG measure # missing arguments | ||
42 | $QEMU_IMG measure --size 2G "$TEST_IMG" # only one allowed | ||
43 | $QEMU_IMG measure "$TEST_IMG" a # only one filename allowed | ||
44 | -$QEMU_IMG measure --object secret,id=sec0,data=MTIzNDU2,format=base64 # missing filename | ||
45 | +$QEMU_IMG measure --object secret,id=sec0,data=MTIzNDU2,format=base64 # size or filename needed | ||
46 | $QEMU_IMG measure --image-opts # missing filename | ||
47 | $QEMU_IMG measure -f qcow2 # missing filename | ||
48 | $QEMU_IMG measure -l snap1 # missing filename | ||
49 | diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2 | ||
50 | index XXXXXXX..XXXXXXX 100644 | ||
51 | --- a/tests/qemu-iotests/178.out.qcow2 | ||
52 | +++ b/tests/qemu-iotests/178.out.qcow2 | ||
53 | @@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 | ||
54 | qemu-img: Either --size N or one filename must be specified. | ||
55 | qemu-img: --size N cannot be used together with a filename. | ||
56 | qemu-img: At most one filename argument is allowed. | ||
57 | -qemu-img: --object, --image-opts, -f, and -l require a filename argument. | ||
58 | -qemu-img: --object, --image-opts, -f, and -l require a filename argument. | ||
59 | -qemu-img: --object, --image-opts, -f, and -l require a filename argument. | ||
60 | -qemu-img: --object, --image-opts, -f, and -l require a filename argument. | ||
61 | +qemu-img: Either --size N or one filename must be specified. | ||
62 | +qemu-img: --image-opts, -f, and -l require a filename argument. | ||
63 | +qemu-img: --image-opts, -f, and -l require a filename argument. | ||
64 | +qemu-img: --image-opts, -f, and -l require a filename argument. | ||
65 | qemu-img: Invalid option list: , | ||
66 | qemu-img: Invalid parameter 'snapshot.foo' | ||
67 | qemu-img: Failed in parsing snapshot param 'snapshot.foo' | ||
68 | diff --git a/tests/qemu-iotests/178.out.raw b/tests/qemu-iotests/178.out.raw | ||
69 | index XXXXXXX..XXXXXXX 100644 | ||
70 | --- a/tests/qemu-iotests/178.out.raw | ||
71 | +++ b/tests/qemu-iotests/178.out.raw | ||
72 | @@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 | ||
73 | qemu-img: Either --size N or one filename must be specified. | ||
74 | qemu-img: --size N cannot be used together with a filename. | ||
75 | qemu-img: At most one filename argument is allowed. | ||
76 | -qemu-img: --object, --image-opts, -f, and -l require a filename argument. | ||
77 | -qemu-img: --object, --image-opts, -f, and -l require a filename argument. | ||
78 | -qemu-img: --object, --image-opts, -f, and -l require a filename argument. | ||
79 | -qemu-img: --object, --image-opts, -f, and -l require a filename argument. | ||
80 | +qemu-img: Either --size N or one filename must be specified. | ||
81 | +qemu-img: --image-opts, -f, and -l require a filename argument. | ||
82 | +qemu-img: --image-opts, -f, and -l require a filename argument. | ||
83 | +qemu-img: --image-opts, -f, and -l require a filename argument. | ||
84 | qemu-img: Invalid option list: , | ||
85 | qemu-img: Invalid parameter 'snapshot.foo' | ||
86 | qemu-img: Failed in parsing snapshot param 'snapshot.foo' | ||
87 | -- | ||
88 | 2.24.1 | ||
89 | |||
90 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
2 | 1 | ||
3 | This test exercises the block/crypto.c "luks" block driver | ||
4 | .bdrv_measure() code. | ||
5 | |||
6 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
7 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
8 | Message-Id: <20200221112522.1497712-5-stefanha@redhat.com> | ||
9 | [mreitz: Renamed test from 282 to 288] | ||
10 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
11 | --- | ||
12 | tests/qemu-iotests/288 | 93 ++++++++++++++++++++++++++++++++++++++ | ||
13 | tests/qemu-iotests/288.out | 30 ++++++++++++ | ||
14 | tests/qemu-iotests/group | 1 + | ||
15 | 3 files changed, 124 insertions(+) | ||
16 | create mode 100755 tests/qemu-iotests/288 | ||
17 | create mode 100644 tests/qemu-iotests/288.out | ||
18 | |||
19 | diff --git a/tests/qemu-iotests/288 b/tests/qemu-iotests/288 | ||
20 | new file mode 100755 | ||
21 | index XXXXXXX..XXXXXXX | ||
22 | --- /dev/null | ||
23 | +++ b/tests/qemu-iotests/288 | ||
24 | @@ -XXX,XX +XXX,XX @@ | ||
25 | +#!/usr/bin/env bash | ||
26 | +# | ||
27 | +# qemu-img measure tests for LUKS images | ||
28 | +# | ||
29 | +# Copyright (C) 2020 Red Hat, Inc. | ||
30 | +# | ||
31 | +# This program is free software; you can redistribute it and/or modify | ||
32 | +# it under the terms of the GNU General Public License as published by | ||
33 | +# the Free Software Foundation; either version 2 of the License, or | ||
34 | +# (at your option) any later version. | ||
35 | +# | ||
36 | +# This program is distributed in the hope that it will be useful, | ||
37 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
38 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
39 | +# GNU General Public License for more details. | ||
40 | +# | ||
41 | +# You should have received a copy of the GNU General Public License | ||
42 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
43 | +# | ||
44 | + | ||
45 | +# creator | ||
46 | +owner=stefanha@redhat.com | ||
47 | + | ||
48 | +seq=`basename $0` | ||
49 | +echo "QA output created by $seq" | ||
50 | + | ||
51 | +status=1 # failure is the default! | ||
52 | + | ||
53 | +_cleanup() | ||
54 | +{ | ||
55 | + _cleanup_test_img | ||
56 | + rm -f "$TEST_IMG.converted" | ||
57 | +} | ||
58 | +trap "_cleanup; exit \$status" 0 1 2 3 15 | ||
59 | + | ||
60 | +# get standard environment, filters and checks | ||
61 | +. ./common.rc | ||
62 | +. ./common.filter | ||
63 | +. ./common.pattern | ||
64 | + | ||
65 | +_supported_fmt luks | ||
66 | +_supported_proto file | ||
67 | +_supported_os Linux | ||
68 | + | ||
69 | +SECRET=secret,id=sec0,data=passphrase | ||
70 | + | ||
71 | +echo "== measure 1G image file ==" | ||
72 | +echo | ||
73 | + | ||
74 | +$QEMU_IMG measure --object "$SECRET" \ | ||
75 | + -O "$IMGFMT" \ | ||
76 | + -o key-secret=sec0,iter-time=10 \ | ||
77 | + --size 1G | ||
78 | + | ||
79 | +echo | ||
80 | +echo "== create 1G image file (size should be no greater than measured) ==" | ||
81 | +echo | ||
82 | + | ||
83 | +_make_test_img 1G | ||
84 | +stat -c "image file size in bytes: %s" "$TEST_IMG_FILE" | ||
85 | + | ||
86 | +echo | ||
87 | +echo "== modified 1G image file (size should be no greater than measured) ==" | ||
88 | +echo | ||
89 | + | ||
90 | +$QEMU_IO --object "$SECRET" --image-opts "$TEST_IMG" -c "write -P 0x51 0x10000 0x400" | _filter_qemu_io | _filter_testdir | ||
91 | +stat -c "image file size in bytes: %s" "$TEST_IMG_FILE" | ||
92 | + | ||
93 | +echo | ||
94 | +echo "== measure preallocation=falloc 1G image file ==" | ||
95 | +echo | ||
96 | + | ||
97 | +$QEMU_IMG measure --object "$SECRET" \ | ||
98 | + -O "$IMGFMT" \ | ||
99 | + -o key-secret=sec0,iter-time=10,preallocation=falloc \ | ||
100 | + --size 1G | ||
101 | + | ||
102 | +echo | ||
103 | +echo "== measure with input image file ==" | ||
104 | +echo | ||
105 | + | ||
106 | +IMGFMT=raw IMGKEYSECRET= IMGOPTS= _make_test_img 1G | _filter_imgfmt | ||
107 | +QEMU_IO_OPTIONS= IMGOPTSSYNTAX= $QEMU_IO -f raw -c "write -P 0x51 0x10000 0x400" "$TEST_IMG_FILE" | _filter_qemu_io | _filter_testdir | ||
108 | +$QEMU_IMG measure --object "$SECRET" \ | ||
109 | + -O "$IMGFMT" \ | ||
110 | + -o key-secret=sec0,iter-time=10 \ | ||
111 | + -f raw \ | ||
112 | + "$TEST_IMG_FILE" | ||
113 | + | ||
114 | +# success, all done | ||
115 | +echo "*** done" | ||
116 | +rm -f $seq.full | ||
117 | +status=0 | ||
118 | diff --git a/tests/qemu-iotests/288.out b/tests/qemu-iotests/288.out | ||
119 | new file mode 100644 | ||
120 | index XXXXXXX..XXXXXXX | ||
121 | --- /dev/null | ||
122 | +++ b/tests/qemu-iotests/288.out | ||
123 | @@ -XXX,XX +XXX,XX @@ | ||
124 | +QA output created by 288 | ||
125 | +== measure 1G image file == | ||
126 | + | ||
127 | +required size: 1075810304 | ||
128 | +fully allocated size: 1075810304 | ||
129 | + | ||
130 | +== create 1G image file (size should be no greater than measured) == | ||
131 | + | ||
132 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 | ||
133 | +image file size in bytes: 1075810304 | ||
134 | + | ||
135 | +== modified 1G image file (size should be no greater than measured) == | ||
136 | + | ||
137 | +wrote 1024/1024 bytes at offset 65536 | ||
138 | +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
139 | +image file size in bytes: 1075810304 | ||
140 | + | ||
141 | +== measure preallocation=falloc 1G image file == | ||
142 | + | ||
143 | +required size: 1075810304 | ||
144 | +fully allocated size: 1075810304 | ||
145 | + | ||
146 | +== measure with input image file == | ||
147 | + | ||
148 | +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 | ||
149 | +wrote 1024/1024 bytes at offset 65536 | ||
150 | +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) | ||
151 | +required size: 1075810304 | ||
152 | +fully allocated size: 1075810304 | ||
153 | +*** done | ||
154 | diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group | ||
155 | index XXXXXXX..XXXXXXX 100644 | ||
156 | --- a/tests/qemu-iotests/group | ||
157 | +++ b/tests/qemu-iotests/group | ||
158 | @@ -XXX,XX +XXX,XX @@ | ||
159 | 283 auto quick | ||
160 | 284 rw | ||
161 | 286 rw quick | ||
162 | +288 quick | ||
163 | -- | ||
164 | 2.24.1 | ||
165 | |||
166 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: David Edmondson <david.edmondson@oracle.com> | ||
2 | 1 | ||
3 | RFC 7230 section 3.2 indicates that whitespace is permitted between | ||
4 | the field name and field value and after the field value. | ||
5 | |||
6 | Signed-off-by: David Edmondson <david.edmondson@oracle.com> | ||
7 | Message-Id: <20200224101310.101169-2-david.edmondson@oracle.com> | ||
8 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
9 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
10 | --- | ||
11 | block/curl.c | 31 +++++++++++++++++++++++++++---- | ||
12 | 1 file changed, 27 insertions(+), 4 deletions(-) | ||
13 | |||
14 | diff --git a/block/curl.c b/block/curl.c | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/block/curl.c | ||
17 | +++ b/block/curl.c | ||
18 | @@ -XXX,XX +XXX,XX @@ static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque) | ||
19 | { | ||
20 | BDRVCURLState *s = opaque; | ||
21 | size_t realsize = size * nmemb; | ||
22 | - const char *accept_line = "Accept-Ranges: bytes"; | ||
23 | + const char *header = (char *)ptr; | ||
24 | + const char *end = header + realsize; | ||
25 | + const char *accept_ranges = "Accept-Ranges:"; | ||
26 | + const char *bytes = "bytes"; | ||
27 | |||
28 | - if (realsize >= strlen(accept_line) | ||
29 | - && strncmp((char *)ptr, accept_line, strlen(accept_line)) == 0) { | ||
30 | - s->accept_range = true; | ||
31 | + if (realsize >= strlen(accept_ranges) | ||
32 | + && strncmp(header, accept_ranges, strlen(accept_ranges)) == 0) { | ||
33 | + | ||
34 | + char *p = strchr(header, ':') + 1; | ||
35 | + | ||
36 | + /* Skip whitespace between the header name and value. */ | ||
37 | + while (p < end && *p && g_ascii_isspace(*p)) { | ||
38 | + p++; | ||
39 | + } | ||
40 | + | ||
41 | + if (end - p >= strlen(bytes) | ||
42 | + && strncmp(p, bytes, strlen(bytes)) == 0) { | ||
43 | + | ||
44 | + /* Check that there is nothing but whitespace after the value. */ | ||
45 | + p += strlen(bytes); | ||
46 | + while (p < end && *p && g_ascii_isspace(*p)) { | ||
47 | + p++; | ||
48 | + } | ||
49 | + | ||
50 | + if (p == end || !*p) { | ||
51 | + s->accept_range = true; | ||
52 | + } | ||
53 | + } | ||
54 | } | ||
55 | |||
56 | return realsize; | ||
57 | -- | ||
58 | 2.24.1 | ||
59 | |||
60 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: David Edmondson <david.edmondson@oracle.com> | ||
2 | 1 | ||
3 | RFC 7230 section 3.2 indicates that HTTP header field names are case | ||
4 | insensitive. | ||
5 | |||
6 | Signed-off-by: David Edmondson <david.edmondson@oracle.com> | ||
7 | Message-Id: <20200224101310.101169-3-david.edmondson@oracle.com> | ||
8 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
9 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
10 | --- | ||
11 | block/curl.c | 5 +++-- | ||
12 | 1 file changed, 3 insertions(+), 2 deletions(-) | ||
13 | |||
14 | diff --git a/block/curl.c b/block/curl.c | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/block/curl.c | ||
17 | +++ b/block/curl.c | ||
18 | @@ -XXX,XX +XXX,XX @@ static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque) | ||
19 | size_t realsize = size * nmemb; | ||
20 | const char *header = (char *)ptr; | ||
21 | const char *end = header + realsize; | ||
22 | - const char *accept_ranges = "Accept-Ranges:"; | ||
23 | + const char *accept_ranges = "accept-ranges:"; | ||
24 | const char *bytes = "bytes"; | ||
25 | |||
26 | if (realsize >= strlen(accept_ranges) | ||
27 | - && strncmp(header, accept_ranges, strlen(accept_ranges)) == 0) { | ||
28 | + && g_ascii_strncasecmp(header, accept_ranges, | ||
29 | + strlen(accept_ranges)) == 0) { | ||
30 | |||
31 | char *p = strchr(header, ':') + 1; | ||
32 | |||
33 | -- | ||
34 | 2.24.1 | ||
35 | |||
36 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Eric Blake <eblake@redhat.com> | ||
2 | 1 | ||
3 | Tests 261 and 272 fail on RHEL 7 with coreutils 8.22, since od | ||
4 | --endian was not added until coreutils 8.23. Fix this by manually | ||
5 | constructing the final value one byte at a time. | ||
6 | |||
7 | Fixes: fc8ba423 | ||
8 | Reported-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com> | ||
9 | Signed-off-by: Eric Blake <eblake@redhat.com> | ||
10 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
11 | Message-Id: <20200226125424.481840-1-eblake@redhat.com> | ||
12 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
13 | --- | ||
14 | tests/qemu-iotests/common.rc | 22 +++++++++++++++++----- | ||
15 | 1 file changed, 17 insertions(+), 5 deletions(-) | ||
16 | |||
17 | diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc | ||
18 | index XXXXXXX..XXXXXXX 100644 | ||
19 | --- a/tests/qemu-iotests/common.rc | ||
20 | +++ b/tests/qemu-iotests/common.rc | ||
21 | @@ -XXX,XX +XXX,XX @@ poke_file() | ||
22 | # peek_file_le 'test.img' 512 2 => 65534 | ||
23 | peek_file_le() | ||
24 | { | ||
25 | - # Wrap in echo $() to strip spaces | ||
26 | - echo $(od -j"$2" -N"$3" --endian=little -An -vtu"$3" "$1") | ||
27 | + local val=0 shift=0 byte | ||
28 | + | ||
29 | + # coreutils' od --endian is not portable, so manually assemble bytes. | ||
30 | + for byte in $(od -j"$2" -N"$3" -An -v -tu1 "$1"); do | ||
31 | + val=$(( val | (byte << shift) )) | ||
32 | + shift=$((shift + 8)) | ||
33 | + done | ||
34 | + printf %llu $val | ||
35 | } | ||
36 | |||
37 | # peek_file_be 'test.img' 512 2 => 65279 | ||
38 | peek_file_be() | ||
39 | { | ||
40 | - # Wrap in echo $() to strip spaces | ||
41 | - echo $(od -j"$2" -N"$3" --endian=big -An -vtu"$3" "$1") | ||
42 | + local val=0 byte | ||
43 | + | ||
44 | + # coreutils' od --endian is not portable, so manually assemble bytes. | ||
45 | + for byte in $(od -j"$2" -N"$3" -An -v -tu1 "$1"); do | ||
46 | + val=$(( (val << 8) | byte )) | ||
47 | + done | ||
48 | + printf %llu $val | ||
49 | } | ||
50 | |||
51 | -# peek_file_raw 'test.img' 512 2 => '\xff\xfe' | ||
52 | +# peek_file_raw 'test.img' 512 2 => '\xff\xfe'. Do not use if the raw data | ||
53 | +# is likely to contain \0 or trailing \n. | ||
54 | peek_file_raw() | ||
55 | { | ||
56 | dd if="$1" bs=1 skip="$2" count="$3" status=none | ||
57 | -- | ||
58 | 2.24.1 | ||
59 | |||
60 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Pan Nengyuan <pannengyuan@huawei.com> | ||
2 | 1 | ||
3 | 'crypto_opts' forgot to free in qcow2_close(), this patch fix the bellow leak stack: | ||
4 | |||
5 | Direct leak of 24 byte(s) in 1 object(s) allocated from: | ||
6 | #0 0x7f0edd81f970 in __interceptor_calloc (/lib64/libasan.so.5+0xef970) | ||
7 | #1 0x7f0edc6d149d in g_malloc0 (/lib64/libglib-2.0.so.0+0x5249d) | ||
8 | #2 0x55d7eaede63d in qobject_input_start_struct /mnt/sdb/qemu-new/qemu_test/qemu/qapi/qobject-input-visitor.c:295 | ||
9 | #3 0x55d7eaed78b8 in visit_start_struct /mnt/sdb/qemu-new/qemu_test/qemu/qapi/qapi-visit-core.c:49 | ||
10 | #4 0x55d7eaf5140b in visit_type_QCryptoBlockOpenOptions qapi/qapi-visit-crypto.c:290 | ||
11 | #5 0x55d7eae43af3 in block_crypto_open_opts_init /mnt/sdb/qemu-new/qemu_test/qemu/block/crypto.c:163 | ||
12 | #6 0x55d7eacd2924 in qcow2_update_options_prepare /mnt/sdb/qemu-new/qemu_test/qemu/block/qcow2.c:1148 | ||
13 | #7 0x55d7eacd33f7 in qcow2_update_options /mnt/sdb/qemu-new/qemu_test/qemu/block/qcow2.c:1232 | ||
14 | #8 0x55d7eacd9680 in qcow2_do_open /mnt/sdb/qemu-new/qemu_test/qemu/block/qcow2.c:1512 | ||
15 | #9 0x55d7eacdc55e in qcow2_open_entry /mnt/sdb/qemu-new/qemu_test/qemu/block/qcow2.c:1792 | ||
16 | #10 0x55d7eacdc8fe in qcow2_open /mnt/sdb/qemu-new/qemu_test/qemu/block/qcow2.c:1819 | ||
17 | #11 0x55d7eac3742d in bdrv_open_driver /mnt/sdb/qemu-new/qemu_test/qemu/block.c:1317 | ||
18 | #12 0x55d7eac3e990 in bdrv_open_common /mnt/sdb/qemu-new/qemu_test/qemu/block.c:1575 | ||
19 | #13 0x55d7eac4442c in bdrv_open_inherit /mnt/sdb/qemu-new/qemu_test/qemu/block.c:3126 | ||
20 | #14 0x55d7eac45c3f in bdrv_open /mnt/sdb/qemu-new/qemu_test/qemu/block.c:3219 | ||
21 | #15 0x55d7ead8e8a4 in blk_new_open /mnt/sdb/qemu-new/qemu_test/qemu/block/block-backend.c:397 | ||
22 | #16 0x55d7eacde74c in qcow2_co_create /mnt/sdb/qemu-new/qemu_test/qemu/block/qcow2.c:3534 | ||
23 | #17 0x55d7eacdfa6d in qcow2_co_create_opts /mnt/sdb/qemu-new/qemu_test/qemu/block/qcow2.c:3668 | ||
24 | #18 0x55d7eac1c678 in bdrv_create_co_entry /mnt/sdb/qemu-new/qemu_test/qemu/block.c:485 | ||
25 | #19 0x55d7eb0024d2 in coroutine_trampoline /mnt/sdb/qemu-new/qemu_test/qemu/util/coroutine-ucontext.c:115 | ||
26 | |||
27 | Reported-by: Euler Robot <euler.robot@huawei.com> | ||
28 | Signed-off-by: Pan Nengyuan <pannengyuan@huawei.com> | ||
29 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
30 | Message-Id: <20200227012950.12256-2-pannengyuan@huawei.com> | ||
31 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
32 | --- | ||
33 | block/qcow2.c | 1 + | ||
34 | 1 file changed, 1 insertion(+) | ||
35 | |||
36 | diff --git a/block/qcow2.c b/block/qcow2.c | ||
37 | index XXXXXXX..XXXXXXX 100644 | ||
38 | --- a/block/qcow2.c | ||
39 | +++ b/block/qcow2.c | ||
40 | @@ -XXX,XX +XXX,XX @@ static void qcow2_close(BlockDriverState *bs) | ||
41 | |||
42 | qcrypto_block_free(s->crypto); | ||
43 | s->crypto = NULL; | ||
44 | + qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); | ||
45 | |||
46 | g_free(s->unknown_header_fields); | ||
47 | cleanup_unknown_header_ext(bs); | ||
48 | -- | ||
49 | 2.24.1 | ||
50 | |||
51 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Pan Nengyuan <pannengyuan@huawei.com> | ||
2 | 1 | ||
3 | collect_image_check() is called twice in img_check(), the filename/format will be alloced without free the original memory. | ||
4 | It is not a big deal since the process will exit anyway, but seems like a clean code and it will remove the warning spotted by asan. | ||
5 | |||
6 | Reported-by: Euler Robot <euler.robot@huawei.com> | ||
7 | Signed-off-by: Pan Nengyuan <pannengyuan@huawei.com> | ||
8 | Message-Id: <20200227012950.12256-3-pannengyuan@huawei.com> | ||
9 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
10 | --- | ||
11 | qemu-img.c | 2 ++ | ||
12 | 1 file changed, 2 insertions(+) | ||
13 | |||
14 | diff --git a/qemu-img.c b/qemu-img.c | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/qemu-img.c | ||
17 | +++ b/qemu-img.c | ||
18 | @@ -XXX,XX +XXX,XX @@ static int img_check(int argc, char **argv) | ||
19 | check->corruptions_fixed); | ||
20 | } | ||
21 | |||
22 | + qapi_free_ImageCheck(check); | ||
23 | + check = g_new0(ImageCheck, 1); | ||
24 | ret = collect_image_check(bs, check, filename, fmt, 0); | ||
25 | |||
26 | check->leaks_fixed = leaks_fixed; | ||
27 | -- | ||
28 | 2.24.1 | ||
29 | |||
30 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
2 | 1 | ||
3 | On success path we return what inflate() returns instead of 0. And it | ||
4 | most probably works for Z_STREAM_END as it is positive, but is | ||
5 | definitely broken for Z_BUF_ERROR. | ||
6 | |||
7 | While being here, switch to errno return code, to be closer to | ||
8 | qcow2_compress API (and usual expectations). | ||
9 | |||
10 | Revert condition in if to be more positive. Drop dead initialization of | ||
11 | ret. | ||
12 | |||
13 | Cc: qemu-stable@nongnu.org # v4.0 | ||
14 | Fixes: 341926ab83e2b | ||
15 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
16 | Message-Id: <20200302150930.16218-1-vsementsov@virtuozzo.com> | ||
17 | Reviewed-by: Alberto Garcia <berto@igalia.com> | ||
18 | Reviewed-by: Ján Tomko <jtomko@redhat.com> | ||
19 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
20 | --- | ||
21 | block/qcow2-threads.c | 12 +++++++----- | ||
22 | 1 file changed, 7 insertions(+), 5 deletions(-) | ||
23 | |||
24 | diff --git a/block/qcow2-threads.c b/block/qcow2-threads.c | ||
25 | index XXXXXXX..XXXXXXX 100644 | ||
26 | --- a/block/qcow2-threads.c | ||
27 | +++ b/block/qcow2-threads.c | ||
28 | @@ -XXX,XX +XXX,XX @@ static ssize_t qcow2_compress(void *dest, size_t dest_size, | ||
29 | * @src - source buffer, @src_size bytes | ||
30 | * | ||
31 | * Returns: 0 on success | ||
32 | - * -1 on fail | ||
33 | + * -EIO on fail | ||
34 | */ | ||
35 | static ssize_t qcow2_decompress(void *dest, size_t dest_size, | ||
36 | const void *src, size_t src_size) | ||
37 | { | ||
38 | - int ret = 0; | ||
39 | + int ret; | ||
40 | z_stream strm; | ||
41 | |||
42 | memset(&strm, 0, sizeof(strm)); | ||
43 | @@ -XXX,XX +XXX,XX @@ static ssize_t qcow2_decompress(void *dest, size_t dest_size, | ||
44 | |||
45 | ret = inflateInit2(&strm, -12); | ||
46 | if (ret != Z_OK) { | ||
47 | - return -1; | ||
48 | + return -EIO; | ||
49 | } | ||
50 | |||
51 | ret = inflate(&strm, Z_FINISH); | ||
52 | - if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) || strm.avail_out != 0) { | ||
53 | + if ((ret == Z_STREAM_END || ret == Z_BUF_ERROR) && strm.avail_out == 0) { | ||
54 | /* | ||
55 | * We approve Z_BUF_ERROR because we need @dest buffer to be filled, but | ||
56 | * @src buffer may be processed partly (because in qcow2 we know size of | ||
57 | * compressed data with precision of one sector) | ||
58 | */ | ||
59 | - ret = -1; | ||
60 | + ret = 0; | ||
61 | + } else { | ||
62 | + ret = -EIO; | ||
63 | } | ||
64 | |||
65 | inflateEnd(&strm); | ||
66 | -- | ||
67 | 2.24.1 | ||
68 | |||
69 | diff view generated by jsdifflib |
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 1 | From: Nicolas Saenz Julienne <nsaenzju@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | We need it in separate to pass to the block-copy object in the next | 3 | Introduce the 'event-loop-base' abstract class, it'll hold the |
4 | commit. | 4 | properties common to all event loops and provide the necessary hooks for |
5 | 5 | their creation and maintenance. Then have iothread inherit from it. | |
6 | Cc: qemu-stable@nongnu.org | 6 | |
7 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 7 | EventLoopBaseClass is defined as user creatable and provides a hook for |
8 | Reviewed-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com> | 8 | its children to attach themselves to the user creatable class 'complete' |
9 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 9 | function. It also provides an update_params() callback to propagate |
10 | Message-Id: <20200311103004.7649-2-vsementsov@virtuozzo.com> | 10 | property changes onto its children. |
11 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 11 | |
12 | The new 'event-loop-base' class will live in the root directory. It is | ||
13 | built on its own using the 'link_whole' option (there are no direct | ||
14 | function dependencies between the class and its children, it all happens | ||
15 | trough 'constructor' magic). And also imposes new compilation | ||
16 | dependencies: | ||
17 | |||
18 | qom <- event-loop-base <- blockdev (iothread.c) | ||
19 | |||
20 | And in subsequent patches: | ||
21 | |||
22 | qom <- event-loop-base <- qemuutil (util/main-loop.c) | ||
23 | |||
24 | All this forced some amount of reordering in meson.build: | ||
25 | |||
26 | - Moved qom build definition before qemuutil. Doing it the other way | ||
27 | around (i.e. moving qemuutil after qom) isn't possible as a lot of | ||
28 | core libraries that live in between the two depend on it. | ||
29 | |||
30 | - Process the 'hw' subdir earlier, as it introduces files into the | ||
31 | 'qom' source set. | ||
32 | |||
33 | No functional changes intended. | ||
34 | |||
35 | Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com> | ||
36 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
37 | Acked-by: Markus Armbruster <armbru@redhat.com> | ||
38 | Message-id: 20220425075723.20019-2-nsaenzju@redhat.com | ||
39 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
12 | --- | 40 | --- |
13 | blockjob.c | 16 +++++----- | 41 | qapi/qom.json | 22 +++++-- |
14 | include/qemu/job.h | 11 ++----- | 42 | meson.build | 23 ++++--- |
15 | include/qemu/progress_meter.h | 58 +++++++++++++++++++++++++++++++++++ | 43 | include/sysemu/event-loop-base.h | 36 +++++++++++ |
16 | job-qmp.c | 4 +-- | 44 | include/sysemu/iothread.h | 6 +- |
17 | job.c | 6 ++-- | 45 | event-loop-base.c | 104 +++++++++++++++++++++++++++++++ |
18 | qemu-img.c | 6 ++-- | 46 | iothread.c | 65 ++++++------------- |
19 | 6 files changed, 76 insertions(+), 25 deletions(-) | 47 | 6 files changed, 192 insertions(+), 64 deletions(-) |
20 | create mode 100644 include/qemu/progress_meter.h | 48 | create mode 100644 include/sysemu/event-loop-base.h |
21 | 49 | create mode 100644 event-loop-base.c | |
22 | diff --git a/blockjob.c b/blockjob.c | 50 | |
51 | diff --git a/qapi/qom.json b/qapi/qom.json | ||
23 | index XXXXXXX..XXXXXXX 100644 | 52 | index XXXXXXX..XXXXXXX 100644 |
24 | --- a/blockjob.c | 53 | --- a/qapi/qom.json |
25 | +++ b/blockjob.c | 54 | +++ b/qapi/qom.json |
26 | @@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) | 55 | @@ -XXX,XX +XXX,XX @@ |
27 | info->device = g_strdup(job->job.id); | 56 | '*repeat': 'bool', |
28 | info->busy = atomic_read(&job->job.busy); | 57 | '*grab-toggle': 'GrabToggleKeys' } } |
29 | info->paused = job->job.pause_count > 0; | 58 | |
30 | - info->offset = job->job.progress_current; | 59 | +## |
31 | - info->len = job->job.progress_total; | 60 | +# @EventLoopBaseProperties: |
32 | + info->offset = job->job.progress.current; | 61 | +# |
33 | + info->len = job->job.progress.total; | 62 | +# Common properties for event loops |
34 | info->speed = job->speed; | 63 | +# |
35 | info->io_status = job->iostatus; | 64 | +# @aio-max-batch: maximum number of requests in a batch for the AIO engine, |
36 | info->ready = job_is_ready(&job->job), | 65 | +# 0 means that the engine will use its default. |
37 | @@ -XXX,XX +XXX,XX @@ static void block_job_event_cancelled(Notifier *n, void *opaque) | 66 | +# (default: 0) |
38 | 67 | +# | |
39 | qapi_event_send_block_job_cancelled(job_type(&job->job), | 68 | +# Since: 7.1 |
40 | job->job.id, | 69 | +## |
41 | - job->job.progress_total, | 70 | +{ 'struct': 'EventLoopBaseProperties', |
42 | - job->job.progress_current, | 71 | + 'data': { '*aio-max-batch': 'int' } } |
43 | + job->job.progress.total, | 72 | + |
44 | + job->job.progress.current, | 73 | ## |
45 | job->speed); | 74 | # @IothreadProperties: |
46 | } | 75 | # |
47 | 76 | @@ -XXX,XX +XXX,XX @@ | |
48 | @@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(Notifier *n, void *opaque) | 77 | # algorithm detects it is spending too long polling without |
49 | 78 | # encountering events. 0 selects a default behaviour (default: 0) | |
50 | qapi_event_send_block_job_completed(job_type(&job->job), | 79 | # |
51 | job->job.id, | 80 | -# @aio-max-batch: maximum number of requests in a batch for the AIO engine, |
52 | - job->job.progress_total, | 81 | -# 0 means that the engine will use its default |
53 | - job->job.progress_current, | 82 | -# (default:0, since 6.1) |
54 | + job->job.progress.total, | 83 | +# The @aio-max-batch option is available since 6.1. |
55 | + job->job.progress.current, | 84 | # |
56 | job->speed, | 85 | # Since: 2.0 |
57 | !!msg, | 86 | ## |
58 | msg); | 87 | { 'struct': 'IothreadProperties', |
59 | @@ -XXX,XX +XXX,XX @@ static void block_job_event_ready(Notifier *n, void *opaque) | 88 | + 'base': 'EventLoopBaseProperties', |
60 | 89 | 'data': { '*poll-max-ns': 'int', | |
61 | qapi_event_send_block_job_ready(job_type(&job->job), | 90 | '*poll-grow': 'int', |
62 | job->job.id, | 91 | - '*poll-shrink': 'int', |
63 | - job->job.progress_total, | 92 | - '*aio-max-batch': 'int' } } |
64 | - job->job.progress_current, | 93 | + '*poll-shrink': 'int' } } |
65 | + job->job.progress.total, | 94 | |
66 | + job->job.progress.current, | 95 | ## |
67 | job->speed); | 96 | # @MemoryBackendProperties: |
68 | } | 97 | diff --git a/meson.build b/meson.build |
69 | |||
70 | diff --git a/include/qemu/job.h b/include/qemu/job.h | ||
71 | index XXXXXXX..XXXXXXX 100644 | 98 | index XXXXXXX..XXXXXXX 100644 |
72 | --- a/include/qemu/job.h | 99 | --- a/meson.build |
73 | +++ b/include/qemu/job.h | 100 | +++ b/meson.build |
74 | @@ -XXX,XX +XXX,XX @@ | 101 | @@ -XXX,XX +XXX,XX @@ subdir('qom') |
75 | 102 | subdir('authz') | |
76 | #include "qapi/qapi-types-job.h" | 103 | subdir('crypto') |
77 | #include "qemu/queue.h" | 104 | subdir('ui') |
78 | +#include "qemu/progress_meter.h" | 105 | +subdir('hw') |
79 | #include "qemu/coroutine.h" | 106 | |
80 | #include "block/aio.h" | 107 | |
81 | 108 | if enable_modules | |
82 | @@ -XXX,XX +XXX,XX @@ typedef struct Job { | 109 | @@ -XXX,XX +XXX,XX @@ if enable_modules |
83 | /** True if this job should automatically dismiss itself */ | 110 | modulecommon = declare_dependency(link_whole: libmodulecommon, compile_args: '-DBUILD_DSO') |
84 | bool auto_dismiss; | 111 | endif |
85 | 112 | ||
86 | - /** | 113 | +qom_ss = qom_ss.apply(config_host, strict: false) |
87 | - * Current progress. The unit is arbitrary as long as the ratio between | 114 | +libqom = static_library('qom', qom_ss.sources() + genh, |
88 | - * progress_current and progress_total represents the estimated percentage | 115 | + dependencies: [qom_ss.dependencies()], |
89 | - * of work already done. | 116 | + name_suffix: 'fa') |
90 | - */ | 117 | +qom = declare_dependency(link_whole: libqom) |
91 | - int64_t progress_current; | 118 | + |
92 | - | 119 | +event_loop_base = files('event-loop-base.c') |
93 | - /** Estimated progress_current value at the completion of the job */ | 120 | +event_loop_base = static_library('event-loop-base', sources: event_loop_base + genh, |
94 | - int64_t progress_total; | 121 | + build_by_default: true) |
95 | + ProgressMeter progress; | 122 | +event_loop_base = declare_dependency(link_whole: event_loop_base, |
96 | 123 | + dependencies: [qom]) | |
97 | /** | 124 | + |
98 | * Return code from @run and/or @prepare callback(s). | 125 | stub_ss = stub_ss.apply(config_all, strict: false) |
99 | diff --git a/include/qemu/progress_meter.h b/include/qemu/progress_meter.h | 126 | |
127 | util_ss.add_all(trace_ss) | ||
128 | @@ -XXX,XX +XXX,XX @@ subdir('monitor') | ||
129 | subdir('net') | ||
130 | subdir('replay') | ||
131 | subdir('semihosting') | ||
132 | -subdir('hw') | ||
133 | subdir('tcg') | ||
134 | subdir('fpu') | ||
135 | subdir('accel') | ||
136 | @@ -XXX,XX +XXX,XX @@ qemu_syms = custom_target('qemu.syms', output: 'qemu.syms', | ||
137 | capture: true, | ||
138 | command: [undefsym, nm, '@INPUT@']) | ||
139 | |||
140 | -qom_ss = qom_ss.apply(config_host, strict: false) | ||
141 | -libqom = static_library('qom', qom_ss.sources() + genh, | ||
142 | - dependencies: [qom_ss.dependencies()], | ||
143 | - name_suffix: 'fa') | ||
144 | - | ||
145 | -qom = declare_dependency(link_whole: libqom) | ||
146 | - | ||
147 | authz_ss = authz_ss.apply(config_host, strict: false) | ||
148 | libauthz = static_library('authz', authz_ss.sources() + genh, | ||
149 | dependencies: [authz_ss.dependencies()], | ||
150 | @@ -XXX,XX +XXX,XX @@ libblockdev = static_library('blockdev', blockdev_ss.sources() + genh, | ||
151 | build_by_default: false) | ||
152 | |||
153 | blockdev = declare_dependency(link_whole: [libblockdev], | ||
154 | - dependencies: [block]) | ||
155 | + dependencies: [block, event_loop_base]) | ||
156 | |||
157 | qmp_ss = qmp_ss.apply(config_host, strict: false) | ||
158 | libqmp = static_library('qmp', qmp_ss.sources() + genh, | ||
159 | diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h | ||
100 | new file mode 100644 | 160 | new file mode 100644 |
101 | index XXXXXXX..XXXXXXX | 161 | index XXXXXXX..XXXXXXX |
102 | --- /dev/null | 162 | --- /dev/null |
103 | +++ b/include/qemu/progress_meter.h | 163 | +++ b/include/sysemu/event-loop-base.h |
104 | @@ -XXX,XX +XXX,XX @@ | 164 | @@ -XXX,XX +XXX,XX @@ |
105 | +/* | 165 | +/* |
106 | + * Helper functionality for some process progress tracking. | 166 | + * QEMU event-loop backend |
107 | + * | 167 | + * |
108 | + * Copyright (c) 2011 IBM Corp. | 168 | + * Copyright (C) 2022 Red Hat Inc |
109 | + * Copyright (c) 2012, 2018 Red Hat, Inc. | 169 | + * |
110 | + * Copyright (c) 2020 Virtuozzo International GmbH | 170 | + * Authors: |
111 | + * | 171 | + * Nicolas Saenz Julienne <nsaenzju@redhat.com> |
112 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | 172 | + * |
113 | + * of this software and associated documentation files (the "Software"), to deal | 173 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. |
114 | + * in the Software without restriction, including without limitation the rights | 174 | + * See the COPYING file in the top-level directory. |
115 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
116 | + * copies of the Software, and to permit persons to whom the Software is | ||
117 | + * furnished to do so, subject to the following conditions: | ||
118 | + * | ||
119 | + * The above copyright notice and this permission notice shall be included in | ||
120 | + * all copies or substantial portions of the Software. | ||
121 | + * | ||
122 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
123 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
124 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
125 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
126 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
127 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
128 | + * THE SOFTWARE. | ||
129 | + */ | 175 | + */ |
130 | + | 176 | +#ifndef QEMU_EVENT_LOOP_BASE_H |
131 | +#ifndef QEMU_PROGRESS_METER_H | 177 | +#define QEMU_EVENT_LOOP_BASE_H |
132 | +#define QEMU_PROGRESS_METER_H | 178 | + |
133 | + | 179 | +#include "qom/object.h" |
134 | +typedef struct ProgressMeter { | 180 | +#include "block/aio.h" |
135 | + /** | 181 | +#include "qemu/typedefs.h" |
136 | + * Current progress. The unit is arbitrary as long as the ratio between | 182 | + |
137 | + * current and total represents the estimated percentage | 183 | +#define TYPE_EVENT_LOOP_BASE "event-loop-base" |
138 | + * of work already done. | 184 | +OBJECT_DECLARE_TYPE(EventLoopBase, EventLoopBaseClass, |
139 | + */ | 185 | + EVENT_LOOP_BASE) |
140 | + uint64_t current; | 186 | + |
141 | + | 187 | +struct EventLoopBaseClass { |
142 | + /** Estimated current value at the completion of the process */ | 188 | + ObjectClass parent_class; |
143 | + uint64_t total; | 189 | + |
144 | +} ProgressMeter; | 190 | + void (*init)(EventLoopBase *base, Error **errp); |
145 | + | 191 | + void (*update_params)(EventLoopBase *base, Error **errp); |
146 | +static inline void progress_work_done(ProgressMeter *pm, uint64_t done) | 192 | +}; |
193 | + | ||
194 | +struct EventLoopBase { | ||
195 | + Object parent; | ||
196 | + | ||
197 | + /* AioContext AIO engine parameters */ | ||
198 | + int64_t aio_max_batch; | ||
199 | +}; | ||
200 | +#endif | ||
201 | diff --git a/include/sysemu/iothread.h b/include/sysemu/iothread.h | ||
202 | index XXXXXXX..XXXXXXX 100644 | ||
203 | --- a/include/sysemu/iothread.h | ||
204 | +++ b/include/sysemu/iothread.h | ||
205 | @@ -XXX,XX +XXX,XX @@ | ||
206 | #include "block/aio.h" | ||
207 | #include "qemu/thread.h" | ||
208 | #include "qom/object.h" | ||
209 | +#include "sysemu/event-loop-base.h" | ||
210 | |||
211 | #define TYPE_IOTHREAD "iothread" | ||
212 | |||
213 | struct IOThread { | ||
214 | - Object parent_obj; | ||
215 | + EventLoopBase parent_obj; | ||
216 | |||
217 | QemuThread thread; | ||
218 | AioContext *ctx; | ||
219 | @@ -XXX,XX +XXX,XX @@ struct IOThread { | ||
220 | int64_t poll_max_ns; | ||
221 | int64_t poll_grow; | ||
222 | int64_t poll_shrink; | ||
223 | - | ||
224 | - /* AioContext AIO engine parameters */ | ||
225 | - int64_t aio_max_batch; | ||
226 | }; | ||
227 | typedef struct IOThread IOThread; | ||
228 | |||
229 | diff --git a/event-loop-base.c b/event-loop-base.c | ||
230 | new file mode 100644 | ||
231 | index XXXXXXX..XXXXXXX | ||
232 | --- /dev/null | ||
233 | +++ b/event-loop-base.c | ||
234 | @@ -XXX,XX +XXX,XX @@ | ||
235 | +/* | ||
236 | + * QEMU event-loop base | ||
237 | + * | ||
238 | + * Copyright (C) 2022 Red Hat Inc | ||
239 | + * | ||
240 | + * Authors: | ||
241 | + * Stefan Hajnoczi <stefanha@redhat.com> | ||
242 | + * Nicolas Saenz Julienne <nsaenzju@redhat.com> | ||
243 | + * | ||
244 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. | ||
245 | + * See the COPYING file in the top-level directory. | ||
246 | + */ | ||
247 | + | ||
248 | +#include "qemu/osdep.h" | ||
249 | +#include "qom/object_interfaces.h" | ||
250 | +#include "qapi/error.h" | ||
251 | +#include "sysemu/event-loop-base.h" | ||
252 | + | ||
253 | +typedef struct { | ||
254 | + const char *name; | ||
255 | + ptrdiff_t offset; /* field's byte offset in EventLoopBase struct */ | ||
256 | +} EventLoopBaseParamInfo; | ||
257 | + | ||
258 | +static EventLoopBaseParamInfo aio_max_batch_info = { | ||
259 | + "aio-max-batch", offsetof(EventLoopBase, aio_max_batch), | ||
260 | +}; | ||
261 | + | ||
262 | +static void event_loop_base_get_param(Object *obj, Visitor *v, | ||
263 | + const char *name, void *opaque, Error **errp) | ||
147 | +{ | 264 | +{ |
148 | + pm->current += done; | 265 | + EventLoopBase *event_loop_base = EVENT_LOOP_BASE(obj); |
266 | + EventLoopBaseParamInfo *info = opaque; | ||
267 | + int64_t *field = (void *)event_loop_base + info->offset; | ||
268 | + | ||
269 | + visit_type_int64(v, name, field, errp); | ||
149 | +} | 270 | +} |
150 | + | 271 | + |
151 | +static inline void progress_set_remaining(ProgressMeter *pm, uint64_t remaining) | 272 | +static void event_loop_base_set_param(Object *obj, Visitor *v, |
273 | + const char *name, void *opaque, Error **errp) | ||
152 | +{ | 274 | +{ |
153 | + pm->total = pm->current + remaining; | 275 | + EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(obj); |
276 | + EventLoopBase *base = EVENT_LOOP_BASE(obj); | ||
277 | + EventLoopBaseParamInfo *info = opaque; | ||
278 | + int64_t *field = (void *)base + info->offset; | ||
279 | + int64_t value; | ||
280 | + | ||
281 | + if (!visit_type_int64(v, name, &value, errp)) { | ||
282 | + return; | ||
283 | + } | ||
284 | + | ||
285 | + if (value < 0) { | ||
286 | + error_setg(errp, "%s value must be in range [0, %" PRId64 "]", | ||
287 | + info->name, INT64_MAX); | ||
288 | + return; | ||
289 | + } | ||
290 | + | ||
291 | + *field = value; | ||
292 | + | ||
293 | + if (bc->update_params) { | ||
294 | + bc->update_params(base, errp); | ||
295 | + } | ||
296 | + | ||
297 | + return; | ||
154 | +} | 298 | +} |
155 | + | 299 | + |
156 | +static inline void progress_increase_remaining(ProgressMeter *pm, | 300 | +static void event_loop_base_complete(UserCreatable *uc, Error **errp) |
157 | + uint64_t delta) | ||
158 | +{ | 301 | +{ |
159 | + pm->total += delta; | 302 | + EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc); |
303 | + EventLoopBase *base = EVENT_LOOP_BASE(uc); | ||
304 | + | ||
305 | + if (bc->init) { | ||
306 | + bc->init(base, errp); | ||
307 | + } | ||
160 | +} | 308 | +} |
161 | + | 309 | + |
162 | +#endif /* QEMU_PROGRESS_METER_H */ | 310 | +static void event_loop_base_class_init(ObjectClass *klass, void *class_data) |
163 | diff --git a/job-qmp.c b/job-qmp.c | 311 | +{ |
312 | + UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass); | ||
313 | + ucc->complete = event_loop_base_complete; | ||
314 | + | ||
315 | + object_class_property_add(klass, "aio-max-batch", "int", | ||
316 | + event_loop_base_get_param, | ||
317 | + event_loop_base_set_param, | ||
318 | + NULL, &aio_max_batch_info); | ||
319 | +} | ||
320 | + | ||
321 | +static const TypeInfo event_loop_base_info = { | ||
322 | + .name = TYPE_EVENT_LOOP_BASE, | ||
323 | + .parent = TYPE_OBJECT, | ||
324 | + .instance_size = sizeof(EventLoopBase), | ||
325 | + .class_size = sizeof(EventLoopBaseClass), | ||
326 | + .class_init = event_loop_base_class_init, | ||
327 | + .abstract = true, | ||
328 | + .interfaces = (InterfaceInfo[]) { | ||
329 | + { TYPE_USER_CREATABLE }, | ||
330 | + { } | ||
331 | + } | ||
332 | +}; | ||
333 | + | ||
334 | +static void register_types(void) | ||
335 | +{ | ||
336 | + type_register_static(&event_loop_base_info); | ||
337 | +} | ||
338 | +type_init(register_types); | ||
339 | diff --git a/iothread.c b/iothread.c | ||
164 | index XXXXXXX..XXXXXXX 100644 | 340 | index XXXXXXX..XXXXXXX 100644 |
165 | --- a/job-qmp.c | 341 | --- a/iothread.c |
166 | +++ b/job-qmp.c | 342 | +++ b/iothread.c |
167 | @@ -XXX,XX +XXX,XX @@ static JobInfo *job_query_single(Job *job, Error **errp) | 343 | @@ -XXX,XX +XXX,XX @@ |
168 | .id = g_strdup(job->id), | 344 | #include "qemu/module.h" |
169 | .type = job_type(job), | 345 | #include "block/aio.h" |
170 | .status = job->status, | 346 | #include "block/block.h" |
171 | - .current_progress = job->progress_current, | 347 | +#include "sysemu/event-loop-base.h" |
172 | - .total_progress = job->progress_total, | 348 | #include "sysemu/iothread.h" |
173 | + .current_progress = job->progress.current, | 349 | #include "qapi/error.h" |
174 | + .total_progress = job->progress.total, | 350 | #include "qapi/qapi-commands-misc.h" |
175 | .has_error = !!job->err, | 351 | @@ -XXX,XX +XXX,XX @@ static void iothread_init_gcontext(IOThread *iothread) |
176 | .error = job->err ? \ | 352 | iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE); |
177 | g_strdup(error_get_pretty(job->err)) : NULL, | 353 | } |
178 | diff --git a/job.c b/job.c | 354 | |
179 | index XXXXXXX..XXXXXXX 100644 | 355 | -static void iothread_set_aio_context_params(IOThread *iothread, Error **errp) |
180 | --- a/job.c | 356 | +static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp) |
181 | +++ b/job.c | ||
182 | @@ -XXX,XX +XXX,XX @@ void job_unref(Job *job) | ||
183 | |||
184 | void job_progress_update(Job *job, uint64_t done) | ||
185 | { | 357 | { |
186 | - job->progress_current += done; | 358 | + IOThread *iothread = IOTHREAD(base); |
187 | + progress_work_done(&job->progress, done); | 359 | ERRP_GUARD(); |
360 | |||
361 | + if (!iothread->ctx) { | ||
362 | + return; | ||
363 | + } | ||
364 | + | ||
365 | aio_context_set_poll_params(iothread->ctx, | ||
366 | iothread->poll_max_ns, | ||
367 | iothread->poll_grow, | ||
368 | @@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(IOThread *iothread, Error **errp) | ||
369 | } | ||
370 | |||
371 | aio_context_set_aio_params(iothread->ctx, | ||
372 | - iothread->aio_max_batch, | ||
373 | + iothread->parent_obj.aio_max_batch, | ||
374 | errp); | ||
188 | } | 375 | } |
189 | 376 | ||
190 | void job_progress_set_remaining(Job *job, uint64_t remaining) | 377 | -static void iothread_complete(UserCreatable *obj, Error **errp) |
378 | + | ||
379 | +static void iothread_init(EventLoopBase *base, Error **errp) | ||
191 | { | 380 | { |
192 | - job->progress_total = job->progress_current + remaining; | 381 | Error *local_error = NULL; |
193 | + progress_set_remaining(&job->progress, remaining); | 382 | - IOThread *iothread = IOTHREAD(obj); |
383 | + IOThread *iothread = IOTHREAD(base); | ||
384 | char *thread_name; | ||
385 | |||
386 | iothread->stopping = false; | ||
387 | @@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp) | ||
388 | */ | ||
389 | iothread_init_gcontext(iothread); | ||
390 | |||
391 | - iothread_set_aio_context_params(iothread, &local_error); | ||
392 | + iothread_set_aio_context_params(base, &local_error); | ||
393 | if (local_error) { | ||
394 | error_propagate(errp, local_error); | ||
395 | aio_context_unref(iothread->ctx); | ||
396 | @@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp) | ||
397 | * to inherit. | ||
398 | */ | ||
399 | thread_name = g_strdup_printf("IO %s", | ||
400 | - object_get_canonical_path_component(OBJECT(obj))); | ||
401 | + object_get_canonical_path_component(OBJECT(base))); | ||
402 | qemu_thread_create(&iothread->thread, thread_name, iothread_run, | ||
403 | iothread, QEMU_THREAD_JOINABLE); | ||
404 | g_free(thread_name); | ||
405 | @@ -XXX,XX +XXX,XX @@ static IOThreadParamInfo poll_grow_info = { | ||
406 | static IOThreadParamInfo poll_shrink_info = { | ||
407 | "poll-shrink", offsetof(IOThread, poll_shrink), | ||
408 | }; | ||
409 | -static IOThreadParamInfo aio_max_batch_info = { | ||
410 | - "aio-max-batch", offsetof(IOThread, aio_max_batch), | ||
411 | -}; | ||
412 | |||
413 | static void iothread_get_param(Object *obj, Visitor *v, | ||
414 | const char *name, IOThreadParamInfo *info, Error **errp) | ||
415 | @@ -XXX,XX +XXX,XX @@ static void iothread_set_poll_param(Object *obj, Visitor *v, | ||
416 | } | ||
194 | } | 417 | } |
195 | 418 | ||
196 | void job_progress_increase_remaining(Job *job, uint64_t delta) | 419 | -static void iothread_get_aio_param(Object *obj, Visitor *v, |
420 | - const char *name, void *opaque, Error **errp) | ||
421 | -{ | ||
422 | - IOThreadParamInfo *info = opaque; | ||
423 | - | ||
424 | - iothread_get_param(obj, v, name, info, errp); | ||
425 | -} | ||
426 | - | ||
427 | -static void iothread_set_aio_param(Object *obj, Visitor *v, | ||
428 | - const char *name, void *opaque, Error **errp) | ||
429 | -{ | ||
430 | - IOThread *iothread = IOTHREAD(obj); | ||
431 | - IOThreadParamInfo *info = opaque; | ||
432 | - | ||
433 | - if (!iothread_set_param(obj, v, name, info, errp)) { | ||
434 | - return; | ||
435 | - } | ||
436 | - | ||
437 | - if (iothread->ctx) { | ||
438 | - aio_context_set_aio_params(iothread->ctx, | ||
439 | - iothread->aio_max_batch, | ||
440 | - errp); | ||
441 | - } | ||
442 | -} | ||
443 | - | ||
444 | static void iothread_class_init(ObjectClass *klass, void *class_data) | ||
197 | { | 445 | { |
198 | - job->progress_total += delta; | 446 | - UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass); |
199 | + progress_increase_remaining(&job->progress, delta); | 447 | - ucc->complete = iothread_complete; |
448 | + EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(klass); | ||
449 | + | ||
450 | + bc->init = iothread_init; | ||
451 | + bc->update_params = iothread_set_aio_context_params; | ||
452 | |||
453 | object_class_property_add(klass, "poll-max-ns", "int", | ||
454 | iothread_get_poll_param, | ||
455 | @@ -XXX,XX +XXX,XX @@ static void iothread_class_init(ObjectClass *klass, void *class_data) | ||
456 | iothread_get_poll_param, | ||
457 | iothread_set_poll_param, | ||
458 | NULL, &poll_shrink_info); | ||
459 | - object_class_property_add(klass, "aio-max-batch", "int", | ||
460 | - iothread_get_aio_param, | ||
461 | - iothread_set_aio_param, | ||
462 | - NULL, &aio_max_batch_info); | ||
200 | } | 463 | } |
201 | 464 | ||
202 | void job_event_cancelled(Job *job) | 465 | static const TypeInfo iothread_info = { |
203 | diff --git a/qemu-img.c b/qemu-img.c | 466 | .name = TYPE_IOTHREAD, |
204 | index XXXXXXX..XXXXXXX 100644 | 467 | - .parent = TYPE_OBJECT, |
205 | --- a/qemu-img.c | 468 | + .parent = TYPE_EVENT_LOOP_BASE, |
206 | +++ b/qemu-img.c | 469 | .class_init = iothread_class_init, |
207 | @@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp) | 470 | .instance_size = sizeof(IOThread), |
208 | do { | 471 | .instance_init = iothread_instance_init, |
209 | float progress = 0.0f; | 472 | .instance_finalize = iothread_instance_finalize, |
210 | aio_poll(aio_context, true); | 473 | - .interfaces = (InterfaceInfo[]) { |
211 | - if (job->job.progress_total) { | 474 | - {TYPE_USER_CREATABLE}, |
212 | - progress = (float)job->job.progress_current / | 475 | - {} |
213 | - job->job.progress_total * 100.f; | 476 | - }, |
214 | + if (job->job.progress.total) { | 477 | }; |
215 | + progress = (float)job->job.progress.current / | 478 | |
216 | + job->job.progress.total * 100.f; | 479 | static void iothread_register_types(void) |
217 | } | 480 | @@ -XXX,XX +XXX,XX @@ static int query_one_iothread(Object *object, void *opaque) |
218 | qemu_progress_print(progress, 0); | 481 | info->poll_max_ns = iothread->poll_max_ns; |
219 | } while (!job_is_ready(&job->job) && !job_is_completed(&job->job)); | 482 | info->poll_grow = iothread->poll_grow; |
483 | info->poll_shrink = iothread->poll_shrink; | ||
484 | - info->aio_max_batch = iothread->aio_max_batch; | ||
485 | + info->aio_max_batch = iothread->parent_obj.aio_max_batch; | ||
486 | |||
487 | QAPI_LIST_APPEND(*tail, info); | ||
488 | return 0; | ||
220 | -- | 489 | -- |
221 | 2.24.1 | 490 | 2.35.1 |
222 | |||
223 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
2 | 1 | ||
3 | Assume we have two regions, A and B, and region B is in-flight now, | ||
4 | region A is not yet touched, but it is unallocated and should be | ||
5 | skipped. | ||
6 | |||
7 | Correspondingly, as progress we have | ||
8 | |||
9 | total = A + B | ||
10 | current = 0 | ||
11 | |||
12 | If we reset unallocated region A and call progress_reset_callback, | ||
13 | it will calculate 0 bytes dirty in the bitmap and call | ||
14 | job_progress_set_remaining, which will set | ||
15 | |||
16 | total = current + 0 = 0 + 0 = 0 | ||
17 | |||
18 | So, B bytes are actually removed from total accounting. When job | ||
19 | finishes we'll have | ||
20 | |||
21 | total = 0 | ||
22 | current = B | ||
23 | |||
24 | , which doesn't sound good. | ||
25 | |||
26 | This is because we didn't considered in-flight bytes, actually when | ||
27 | calculating remaining, we should have set (in_flight + dirty_bytes) | ||
28 | as remaining, not only dirty_bytes. | ||
29 | |||
30 | To fix it, let's refactor progress calculation, moving it to block-copy | ||
31 | itself instead of fixing callback. And, of course, track in_flight | ||
32 | bytes count. | ||
33 | |||
34 | We still have to keep one callback, to maintain backup job bytes_read | ||
35 | calculation, but it will go on soon, when we turn the whole backup | ||
36 | process into one block_copy call. | ||
37 | |||
38 | Cc: qemu-stable@nongnu.org | ||
39 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
40 | Reviewed-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com> | ||
41 | Message-Id: <20200311103004.7649-3-vsementsov@virtuozzo.com> | ||
42 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
43 | --- | ||
44 | block/backup.c | 13 ++----------- | ||
45 | block/block-copy.c | 16 ++++++++++++---- | ||
46 | include/block/block-copy.h | 15 +++++---------- | ||
47 | 3 files changed, 19 insertions(+), 25 deletions(-) | ||
48 | |||
49 | diff --git a/block/backup.c b/block/backup.c | ||
50 | index XXXXXXX..XXXXXXX 100644 | ||
51 | --- a/block/backup.c | ||
52 | +++ b/block/backup.c | ||
53 | @@ -XXX,XX +XXX,XX @@ static void backup_progress_bytes_callback(int64_t bytes, void *opaque) | ||
54 | BackupBlockJob *s = opaque; | ||
55 | |||
56 | s->bytes_read += bytes; | ||
57 | - job_progress_update(&s->common.job, bytes); | ||
58 | -} | ||
59 | - | ||
60 | -static void backup_progress_reset_callback(void *opaque) | ||
61 | -{ | ||
62 | - BackupBlockJob *s = opaque; | ||
63 | - uint64_t estimate = bdrv_get_dirty_count(s->bcs->copy_bitmap); | ||
64 | - | ||
65 | - job_progress_set_remaining(&s->common.job, estimate); | ||
66 | } | ||
67 | |||
68 | static int coroutine_fn backup_do_cow(BackupBlockJob *job, | ||
69 | @@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, | ||
70 | job->cluster_size = cluster_size; | ||
71 | job->len = len; | ||
72 | |||
73 | - block_copy_set_callbacks(bcs, backup_progress_bytes_callback, | ||
74 | - backup_progress_reset_callback, job); | ||
75 | + block_copy_set_progress_callback(bcs, backup_progress_bytes_callback, job); | ||
76 | + block_copy_set_progress_meter(bcs, &job->common.job.progress); | ||
77 | |||
78 | /* Required permissions are already taken by backup-top target */ | ||
79 | block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, | ||
80 | diff --git a/block/block-copy.c b/block/block-copy.c | ||
81 | index XXXXXXX..XXXXXXX 100644 | ||
82 | --- a/block/block-copy.c | ||
83 | +++ b/block/block-copy.c | ||
84 | @@ -XXX,XX +XXX,XX @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, | ||
85 | return s; | ||
86 | } | ||
87 | |||
88 | -void block_copy_set_callbacks( | ||
89 | +void block_copy_set_progress_callback( | ||
90 | BlockCopyState *s, | ||
91 | ProgressBytesCallbackFunc progress_bytes_callback, | ||
92 | - ProgressResetCallbackFunc progress_reset_callback, | ||
93 | void *progress_opaque) | ||
94 | { | ||
95 | s->progress_bytes_callback = progress_bytes_callback; | ||
96 | - s->progress_reset_callback = progress_reset_callback; | ||
97 | s->progress_opaque = progress_opaque; | ||
98 | } | ||
99 | |||
100 | +void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm) | ||
101 | +{ | ||
102 | + s->progress = pm; | ||
103 | +} | ||
104 | + | ||
105 | /* | ||
106 | * block_copy_do_copy | ||
107 | * | ||
108 | @@ -XXX,XX +XXX,XX @@ int64_t block_copy_reset_unallocated(BlockCopyState *s, | ||
109 | |||
110 | if (!ret) { | ||
111 | bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes); | ||
112 | - s->progress_reset_callback(s->progress_opaque); | ||
113 | + progress_set_remaining(s->progress, | ||
114 | + bdrv_get_dirty_count(s->copy_bitmap) + | ||
115 | + s->in_flight_bytes); | ||
116 | } | ||
117 | |||
118 | *count = bytes; | ||
119 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, | ||
120 | trace_block_copy_process(s, start); | ||
121 | |||
122 | bdrv_reset_dirty_bitmap(s->copy_bitmap, start, chunk_end - start); | ||
123 | + s->in_flight_bytes += chunk_end - start; | ||
124 | |||
125 | co_get_from_shres(s->mem, chunk_end - start); | ||
126 | ret = block_copy_do_copy(s, start, chunk_end, error_is_read); | ||
127 | co_put_to_shres(s->mem, chunk_end - start); | ||
128 | + s->in_flight_bytes -= chunk_end - start; | ||
129 | if (ret < 0) { | ||
130 | bdrv_set_dirty_bitmap(s->copy_bitmap, start, chunk_end - start); | ||
131 | break; | ||
132 | } | ||
133 | |||
134 | + progress_work_done(s->progress, chunk_end - start); | ||
135 | s->progress_bytes_callback(chunk_end - start, s->progress_opaque); | ||
136 | start = chunk_end; | ||
137 | ret = 0; | ||
138 | diff --git a/include/block/block-copy.h b/include/block/block-copy.h | ||
139 | index XXXXXXX..XXXXXXX 100644 | ||
140 | --- a/include/block/block-copy.h | ||
141 | +++ b/include/block/block-copy.h | ||
142 | @@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyInFlightReq { | ||
143 | } BlockCopyInFlightReq; | ||
144 | |||
145 | typedef void (*ProgressBytesCallbackFunc)(int64_t bytes, void *opaque); | ||
146 | -typedef void (*ProgressResetCallbackFunc)(void *opaque); | ||
147 | typedef struct BlockCopyState { | ||
148 | /* | ||
149 | * BdrvChild objects are not owned or managed by block-copy. They are | ||
150 | @@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyState { | ||
151 | BdrvChild *source; | ||
152 | BdrvChild *target; | ||
153 | BdrvDirtyBitmap *copy_bitmap; | ||
154 | + int64_t in_flight_bytes; | ||
155 | int64_t cluster_size; | ||
156 | bool use_copy_range; | ||
157 | int64_t copy_size; | ||
158 | @@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyState { | ||
159 | */ | ||
160 | bool skip_unallocated; | ||
161 | |||
162 | + ProgressMeter *progress; | ||
163 | /* progress_bytes_callback: called when some copying progress is done. */ | ||
164 | ProgressBytesCallbackFunc progress_bytes_callback; | ||
165 | - | ||
166 | - /* | ||
167 | - * progress_reset_callback: called when some bytes reset from copy_bitmap | ||
168 | - * (see @skip_unallocated above). The callee is assumed to recalculate how | ||
169 | - * many bytes remain based on the dirty bit count of copy_bitmap. | ||
170 | - */ | ||
171 | - ProgressResetCallbackFunc progress_reset_callback; | ||
172 | void *progress_opaque; | ||
173 | |||
174 | SharedResource *mem; | ||
175 | @@ -XXX,XX +XXX,XX @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, | ||
176 | BdrvRequestFlags write_flags, | ||
177 | Error **errp); | ||
178 | |||
179 | -void block_copy_set_callbacks( | ||
180 | +void block_copy_set_progress_callback( | ||
181 | BlockCopyState *s, | ||
182 | ProgressBytesCallbackFunc progress_bytes_callback, | ||
183 | - ProgressResetCallbackFunc progress_reset_callback, | ||
184 | void *progress_opaque); | ||
185 | |||
186 | +void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm); | ||
187 | + | ||
188 | void block_copy_state_free(BlockCopyState *s); | ||
189 | |||
190 | int64_t block_copy_reset_unallocated(BlockCopyState *s, | ||
191 | -- | ||
192 | 2.24.1 | ||
193 | |||
194 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
2 | 1 | ||
3 | In block_copy_do_copy we fallback to read+write if copy_range failed. | ||
4 | In this case copy_size is larger than defined for buffered IO, and | ||
5 | there is corresponding commit. Still, backup copies data cluster by | ||
6 | cluster, and most of requests are limited to one cluster anyway, so the | ||
7 | only source of this one bad-limited request is copy-before-write | ||
8 | operation. | ||
9 | |||
10 | Further patch will move backup to use block_copy directly, than for | ||
11 | cases where copy_range is not supported, first request will be | ||
12 | oversized in each backup. It's not good, let's change it now. | ||
13 | |||
14 | Fix is simple: just limit first copy_range request like buffer-based | ||
15 | request. If it succeed, set larger copy_range limit. | ||
16 | |||
17 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
18 | Reviewed-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com> | ||
19 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
20 | Message-Id: <20200311103004.7649-4-vsementsov@virtuozzo.com> | ||
21 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
22 | --- | ||
23 | block/block-copy.c | 41 +++++++++++++++++++++++++++++++---------- | ||
24 | 1 file changed, 31 insertions(+), 10 deletions(-) | ||
25 | |||
26 | diff --git a/block/block-copy.c b/block/block-copy.c | ||
27 | index XXXXXXX..XXXXXXX 100644 | ||
28 | --- a/block/block-copy.c | ||
29 | +++ b/block/block-copy.c | ||
30 | @@ -XXX,XX +XXX,XX @@ void block_copy_state_free(BlockCopyState *s) | ||
31 | g_free(s); | ||
32 | } | ||
33 | |||
34 | +static uint32_t block_copy_max_transfer(BdrvChild *source, BdrvChild *target) | ||
35 | +{ | ||
36 | + return MIN_NON_ZERO(INT_MAX, | ||
37 | + MIN_NON_ZERO(source->bs->bl.max_transfer, | ||
38 | + target->bs->bl.max_transfer)); | ||
39 | +} | ||
40 | + | ||
41 | BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, | ||
42 | int64_t cluster_size, | ||
43 | BdrvRequestFlags write_flags, Error **errp) | ||
44 | { | ||
45 | BlockCopyState *s; | ||
46 | BdrvDirtyBitmap *copy_bitmap; | ||
47 | - uint32_t max_transfer = | ||
48 | - MIN_NON_ZERO(INT_MAX, | ||
49 | - MIN_NON_ZERO(source->bs->bl.max_transfer, | ||
50 | - target->bs->bl.max_transfer)); | ||
51 | |||
52 | copy_bitmap = bdrv_create_dirty_bitmap(source->bs, cluster_size, NULL, | ||
53 | errp); | ||
54 | @@ -XXX,XX +XXX,XX @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, | ||
55 | .mem = shres_create(BLOCK_COPY_MAX_MEM), | ||
56 | }; | ||
57 | |||
58 | - if (max_transfer < cluster_size) { | ||
59 | + if (block_copy_max_transfer(source, target) < cluster_size) { | ||
60 | /* | ||
61 | * copy_range does not respect max_transfer. We don't want to bother | ||
62 | * with requests smaller than block-copy cluster size, so fallback to | ||
63 | @@ -XXX,XX +XXX,XX @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, | ||
64 | s->copy_size = cluster_size; | ||
65 | } else { | ||
66 | /* | ||
67 | - * copy_range does not respect max_transfer (it's a TODO), so we factor | ||
68 | - * that in here. | ||
69 | + * We enable copy-range, but keep small copy_size, until first | ||
70 | + * successful copy_range (look at block_copy_do_copy). | ||
71 | */ | ||
72 | s->use_copy_range = true; | ||
73 | - s->copy_size = MIN(MAX(cluster_size, BLOCK_COPY_MAX_COPY_RANGE), | ||
74 | - QEMU_ALIGN_DOWN(max_transfer, cluster_size)); | ||
75 | + s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER); | ||
76 | } | ||
77 | |||
78 | QLIST_INIT(&s->inflight_reqs); | ||
79 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_do_copy(BlockCopyState *s, | ||
80 | s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER); | ||
81 | /* Fallback to read+write with allocated buffer */ | ||
82 | } else { | ||
83 | + if (s->use_copy_range) { | ||
84 | + /* | ||
85 | + * Successful copy-range. Now increase copy_size. copy_range | ||
86 | + * does not respect max_transfer (it's a TODO), so we factor | ||
87 | + * that in here. | ||
88 | + * | ||
89 | + * Note: we double-check s->use_copy_range for the case when | ||
90 | + * parallel block-copy request unsets it during previous | ||
91 | + * bdrv_co_copy_range call. | ||
92 | + */ | ||
93 | + s->copy_size = | ||
94 | + MIN(MAX(s->cluster_size, BLOCK_COPY_MAX_COPY_RANGE), | ||
95 | + QEMU_ALIGN_DOWN(block_copy_max_transfer(s->source, | ||
96 | + s->target), | ||
97 | + s->cluster_size)); | ||
98 | + } | ||
99 | goto out; | ||
100 | } | ||
101 | } | ||
102 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_do_copy(BlockCopyState *s, | ||
103 | /* | ||
104 | * In case of failed copy_range request above, we may proceed with buffered | ||
105 | * request larger than BLOCK_COPY_MAX_BUFFER. Still, further requests will | ||
106 | - * be properly limited, so don't care too much. | ||
107 | + * be properly limited, so don't care too much. Moreover the most likely | ||
108 | + * case (copy_range is unsupported for the configuration, so the very first | ||
109 | + * copy_range request fails) is handled by setting large copy_size only | ||
110 | + * after first successful copy_range. | ||
111 | */ | ||
112 | |||
113 | bounce_buffer = qemu_blockalign(s->source->bs, nbytes); | ||
114 | -- | ||
115 | 2.24.1 | ||
116 | |||
117 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
2 | 1 | ||
3 | Use bdrv_block_status_above to chose effective chunk size and to handle | ||
4 | zeroes effectively. | ||
5 | |||
6 | This substitutes checking for just being allocated or not, and drops | ||
7 | old code path for it. Assistance by backup job is dropped too, as | ||
8 | caching block-status information is more difficult than just caching | ||
9 | is-allocated information in our dirty bitmap, and backup job is not | ||
10 | good place for this caching anyway. | ||
11 | |||
12 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
13 | Reviewed-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com> | ||
14 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
15 | Message-Id: <20200311103004.7649-5-vsementsov@virtuozzo.com> | ||
16 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
17 | --- | ||
18 | block/block-copy.c | 73 +++++++++++++++++++++++++++++++++++++--------- | ||
19 | block/trace-events | 1 + | ||
20 | 2 files changed, 61 insertions(+), 13 deletions(-) | ||
21 | |||
22 | diff --git a/block/block-copy.c b/block/block-copy.c | ||
23 | index XXXXXXX..XXXXXXX 100644 | ||
24 | --- a/block/block-copy.c | ||
25 | +++ b/block/block-copy.c | ||
26 | @@ -XXX,XX +XXX,XX @@ void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm) | ||
27 | */ | ||
28 | static int coroutine_fn block_copy_do_copy(BlockCopyState *s, | ||
29 | int64_t start, int64_t end, | ||
30 | - bool *error_is_read) | ||
31 | + bool zeroes, bool *error_is_read) | ||
32 | { | ||
33 | int ret; | ||
34 | int nbytes = MIN(end, s->len) - start; | ||
35 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_do_copy(BlockCopyState *s, | ||
36 | assert(QEMU_IS_ALIGNED(end, s->cluster_size)); | ||
37 | assert(end < s->len || end == QEMU_ALIGN_UP(s->len, s->cluster_size)); | ||
38 | |||
39 | + if (zeroes) { | ||
40 | + ret = bdrv_co_pwrite_zeroes(s->target, start, nbytes, s->write_flags & | ||
41 | + ~BDRV_REQ_WRITE_COMPRESSED); | ||
42 | + if (ret < 0) { | ||
43 | + trace_block_copy_write_zeroes_fail(s, start, ret); | ||
44 | + if (error_is_read) { | ||
45 | + *error_is_read = false; | ||
46 | + } | ||
47 | + } | ||
48 | + return ret; | ||
49 | + } | ||
50 | + | ||
51 | if (s->use_copy_range) { | ||
52 | ret = bdrv_co_copy_range(s->source, start, s->target, start, nbytes, | ||
53 | 0, s->write_flags); | ||
54 | @@ -XXX,XX +XXX,XX @@ out: | ||
55 | return ret; | ||
56 | } | ||
57 | |||
58 | +static int block_copy_block_status(BlockCopyState *s, int64_t offset, | ||
59 | + int64_t bytes, int64_t *pnum) | ||
60 | +{ | ||
61 | + int64_t num; | ||
62 | + BlockDriverState *base; | ||
63 | + int ret; | ||
64 | + | ||
65 | + if (s->skip_unallocated && s->source->bs->backing) { | ||
66 | + base = s->source->bs->backing->bs; | ||
67 | + } else { | ||
68 | + base = NULL; | ||
69 | + } | ||
70 | + | ||
71 | + ret = bdrv_block_status_above(s->source->bs, base, offset, bytes, &num, | ||
72 | + NULL, NULL); | ||
73 | + if (ret < 0 || num < s->cluster_size) { | ||
74 | + /* | ||
75 | + * On error or if failed to obtain large enough chunk just fallback to | ||
76 | + * copy one cluster. | ||
77 | + */ | ||
78 | + num = s->cluster_size; | ||
79 | + ret = BDRV_BLOCK_ALLOCATED | BDRV_BLOCK_DATA; | ||
80 | + } else if (offset + num == s->len) { | ||
81 | + num = QEMU_ALIGN_UP(num, s->cluster_size); | ||
82 | + } else { | ||
83 | + num = QEMU_ALIGN_DOWN(num, s->cluster_size); | ||
84 | + } | ||
85 | + | ||
86 | + *pnum = num; | ||
87 | + return ret; | ||
88 | +} | ||
89 | + | ||
90 | /* | ||
91 | * Check if the cluster starting at offset is allocated or not. | ||
92 | * return via pnum the number of contiguous clusters sharing this allocation. | ||
93 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, | ||
94 | { | ||
95 | int ret = 0; | ||
96 | int64_t end = bytes + start; /* bytes */ | ||
97 | - int64_t status_bytes; | ||
98 | BlockCopyInFlightReq req; | ||
99 | |||
100 | /* | ||
101 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, | ||
102 | block_copy_inflight_req_begin(s, &req, start, end); | ||
103 | |||
104 | while (start < end) { | ||
105 | - int64_t next_zero, chunk_end; | ||
106 | + int64_t next_zero, chunk_end, status_bytes; | ||
107 | |||
108 | if (!bdrv_dirty_bitmap_get(s->copy_bitmap, start)) { | ||
109 | trace_block_copy_skip(s, start); | ||
110 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, | ||
111 | chunk_end = next_zero; | ||
112 | } | ||
113 | |||
114 | - if (s->skip_unallocated) { | ||
115 | - ret = block_copy_reset_unallocated(s, start, &status_bytes); | ||
116 | - if (ret == 0) { | ||
117 | - trace_block_copy_skip_range(s, start, status_bytes); | ||
118 | - start += status_bytes; | ||
119 | - continue; | ||
120 | - } | ||
121 | - /* Clamp to known allocated region */ | ||
122 | - chunk_end = MIN(chunk_end, start + status_bytes); | ||
123 | + ret = block_copy_block_status(s, start, chunk_end - start, | ||
124 | + &status_bytes); | ||
125 | + if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) { | ||
126 | + bdrv_reset_dirty_bitmap(s->copy_bitmap, start, status_bytes); | ||
127 | + progress_set_remaining(s->progress, | ||
128 | + bdrv_get_dirty_count(s->copy_bitmap) + | ||
129 | + s->in_flight_bytes); | ||
130 | + trace_block_copy_skip_range(s, start, status_bytes); | ||
131 | + start += status_bytes; | ||
132 | + continue; | ||
133 | } | ||
134 | |||
135 | + chunk_end = MIN(chunk_end, start + status_bytes); | ||
136 | + | ||
137 | trace_block_copy_process(s, start); | ||
138 | |||
139 | bdrv_reset_dirty_bitmap(s->copy_bitmap, start, chunk_end - start); | ||
140 | s->in_flight_bytes += chunk_end - start; | ||
141 | |||
142 | co_get_from_shres(s->mem, chunk_end - start); | ||
143 | - ret = block_copy_do_copy(s, start, chunk_end, error_is_read); | ||
144 | + ret = block_copy_do_copy(s, start, chunk_end, ret & BDRV_BLOCK_ZERO, | ||
145 | + error_is_read); | ||
146 | co_put_to_shres(s->mem, chunk_end - start); | ||
147 | s->in_flight_bytes -= chunk_end - start; | ||
148 | if (ret < 0) { | ||
149 | diff --git a/block/trace-events b/block/trace-events | ||
150 | index XXXXXXX..XXXXXXX 100644 | ||
151 | --- a/block/trace-events | ||
152 | +++ b/block/trace-events | ||
153 | @@ -XXX,XX +XXX,XX @@ block_copy_process(void *bcs, int64_t start) "bcs %p start %"PRId64 | ||
154 | block_copy_copy_range_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" | ||
155 | block_copy_read_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" | ||
156 | block_copy_write_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" | ||
157 | +block_copy_write_zeroes_fail(void *bcs, int64_t start, int ret) "bcs %p start %"PRId64" ret %d" | ||
158 | |||
159 | # ../blockdev.c | ||
160 | qmp_block_job_cancel(void *job) "job %p" | ||
161 | -- | ||
162 | 2.24.1 | ||
163 | |||
164 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
2 | 1 | ||
3 | Split find_conflicting_inflight_req to be used separately. | ||
4 | |||
5 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
6 | Reviewed-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com> | ||
7 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
8 | Message-Id: <20200311103004.7649-6-vsementsov@virtuozzo.com> | ||
9 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
10 | --- | ||
11 | block/block-copy.c | 31 +++++++++++++++++++------------ | ||
12 | 1 file changed, 19 insertions(+), 12 deletions(-) | ||
13 | |||
14 | diff --git a/block/block-copy.c b/block/block-copy.c | ||
15 | index XXXXXXX..XXXXXXX 100644 | ||
16 | --- a/block/block-copy.c | ||
17 | +++ b/block/block-copy.c | ||
18 | @@ -XXX,XX +XXX,XX @@ | ||
19 | #define BLOCK_COPY_MAX_BUFFER (1 * MiB) | ||
20 | #define BLOCK_COPY_MAX_MEM (128 * MiB) | ||
21 | |||
22 | +static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s, | ||
23 | + int64_t start, | ||
24 | + int64_t end) | ||
25 | +{ | ||
26 | + BlockCopyInFlightReq *req; | ||
27 | + | ||
28 | + QLIST_FOREACH(req, &s->inflight_reqs, list) { | ||
29 | + if (end > req->start_byte && start < req->end_byte) { | ||
30 | + return req; | ||
31 | + } | ||
32 | + } | ||
33 | + | ||
34 | + return NULL; | ||
35 | +} | ||
36 | + | ||
37 | static void coroutine_fn block_copy_wait_inflight_reqs(BlockCopyState *s, | ||
38 | int64_t start, | ||
39 | int64_t end) | ||
40 | { | ||
41 | BlockCopyInFlightReq *req; | ||
42 | - bool waited; | ||
43 | - | ||
44 | - do { | ||
45 | - waited = false; | ||
46 | - QLIST_FOREACH(req, &s->inflight_reqs, list) { | ||
47 | - if (end > req->start_byte && start < req->end_byte) { | ||
48 | - qemu_co_queue_wait(&req->wait_queue, NULL); | ||
49 | - waited = true; | ||
50 | - break; | ||
51 | - } | ||
52 | - } | ||
53 | - } while (waited); | ||
54 | + | ||
55 | + while ((req = find_conflicting_inflight_req(s, start, end))) { | ||
56 | + qemu_co_queue_wait(&req->wait_queue, NULL); | ||
57 | + } | ||
58 | } | ||
59 | |||
60 | static void block_copy_inflight_req_begin(BlockCopyState *s, | ||
61 | -- | ||
62 | 2.24.1 | ||
63 | |||
64 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
2 | 1 | ||
3 | We have a lot of "chunk_end - start" invocations, let's switch to | ||
4 | bytes/cur_bytes scheme instead. | ||
5 | |||
6 | While being here, improve check on block_copy_do_copy parameters to not | ||
7 | overflow when calculating nbytes and use int64_t for bytes in | ||
8 | block_copy for consistency. | ||
9 | |||
10 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
11 | Reviewed-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com> | ||
12 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
13 | Message-Id: <20200311103004.7649-7-vsementsov@virtuozzo.com> | ||
14 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
15 | --- | ||
16 | block/block-copy.c | 78 ++++++++++++++++++++------------------ | ||
17 | include/block/block-copy.h | 6 +-- | ||
18 | 2 files changed, 44 insertions(+), 40 deletions(-) | ||
19 | |||
20 | diff --git a/block/block-copy.c b/block/block-copy.c | ||
21 | index XXXXXXX..XXXXXXX 100644 | ||
22 | --- a/block/block-copy.c | ||
23 | +++ b/block/block-copy.c | ||
24 | @@ -XXX,XX +XXX,XX @@ | ||
25 | |||
26 | static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s, | ||
27 | int64_t start, | ||
28 | - int64_t end) | ||
29 | + int64_t bytes) | ||
30 | { | ||
31 | BlockCopyInFlightReq *req; | ||
32 | |||
33 | QLIST_FOREACH(req, &s->inflight_reqs, list) { | ||
34 | - if (end > req->start_byte && start < req->end_byte) { | ||
35 | + if (start + bytes > req->start && start < req->start + req->bytes) { | ||
36 | return req; | ||
37 | } | ||
38 | } | ||
39 | @@ -XXX,XX +XXX,XX @@ static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s, | ||
40 | |||
41 | static void coroutine_fn block_copy_wait_inflight_reqs(BlockCopyState *s, | ||
42 | int64_t start, | ||
43 | - int64_t end) | ||
44 | + int64_t bytes) | ||
45 | { | ||
46 | BlockCopyInFlightReq *req; | ||
47 | |||
48 | - while ((req = find_conflicting_inflight_req(s, start, end))) { | ||
49 | + while ((req = find_conflicting_inflight_req(s, start, bytes))) { | ||
50 | qemu_co_queue_wait(&req->wait_queue, NULL); | ||
51 | } | ||
52 | } | ||
53 | |||
54 | static void block_copy_inflight_req_begin(BlockCopyState *s, | ||
55 | BlockCopyInFlightReq *req, | ||
56 | - int64_t start, int64_t end) | ||
57 | + int64_t start, int64_t bytes) | ||
58 | { | ||
59 | - req->start_byte = start; | ||
60 | - req->end_byte = end; | ||
61 | + req->start = start; | ||
62 | + req->bytes = bytes; | ||
63 | qemu_co_queue_init(&req->wait_queue); | ||
64 | QLIST_INSERT_HEAD(&s->inflight_reqs, req, list); | ||
65 | } | ||
66 | @@ -XXX,XX +XXX,XX @@ void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm) | ||
67 | /* | ||
68 | * block_copy_do_copy | ||
69 | * | ||
70 | - * Do copy of cluser-aligned chunk. @end is allowed to exceed s->len only to | ||
71 | - * cover last cluster when s->len is not aligned to clusters. | ||
72 | + * Do copy of cluster-aligned chunk. Requested region is allowed to exceed | ||
73 | + * s->len only to cover last cluster when s->len is not aligned to clusters. | ||
74 | * | ||
75 | * No sync here: nor bitmap neighter intersecting requests handling, only copy. | ||
76 | * | ||
77 | * Returns 0 on success. | ||
78 | */ | ||
79 | static int coroutine_fn block_copy_do_copy(BlockCopyState *s, | ||
80 | - int64_t start, int64_t end, | ||
81 | + int64_t start, int64_t bytes, | ||
82 | bool zeroes, bool *error_is_read) | ||
83 | { | ||
84 | int ret; | ||
85 | - int nbytes = MIN(end, s->len) - start; | ||
86 | + int64_t nbytes = MIN(start + bytes, s->len) - start; | ||
87 | void *bounce_buffer = NULL; | ||
88 | |||
89 | + assert(start >= 0 && bytes > 0 && INT64_MAX - start >= bytes); | ||
90 | assert(QEMU_IS_ALIGNED(start, s->cluster_size)); | ||
91 | - assert(QEMU_IS_ALIGNED(end, s->cluster_size)); | ||
92 | - assert(end < s->len || end == QEMU_ALIGN_UP(s->len, s->cluster_size)); | ||
93 | + assert(QEMU_IS_ALIGNED(bytes, s->cluster_size)); | ||
94 | + assert(start < s->len); | ||
95 | + assert(start + bytes <= s->len || | ||
96 | + start + bytes == QEMU_ALIGN_UP(s->len, s->cluster_size)); | ||
97 | + assert(nbytes < INT_MAX); | ||
98 | |||
99 | if (zeroes) { | ||
100 | ret = bdrv_co_pwrite_zeroes(s->target, start, nbytes, s->write_flags & | ||
101 | @@ -XXX,XX +XXX,XX @@ int64_t block_copy_reset_unallocated(BlockCopyState *s, | ||
102 | } | ||
103 | |||
104 | int coroutine_fn block_copy(BlockCopyState *s, | ||
105 | - int64_t start, uint64_t bytes, | ||
106 | + int64_t start, int64_t bytes, | ||
107 | bool *error_is_read) | ||
108 | { | ||
109 | int ret = 0; | ||
110 | - int64_t end = bytes + start; /* bytes */ | ||
111 | BlockCopyInFlightReq req; | ||
112 | |||
113 | /* | ||
114 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, | ||
115 | bdrv_get_aio_context(s->target->bs)); | ||
116 | |||
117 | assert(QEMU_IS_ALIGNED(start, s->cluster_size)); | ||
118 | - assert(QEMU_IS_ALIGNED(end, s->cluster_size)); | ||
119 | + assert(QEMU_IS_ALIGNED(bytes, s->cluster_size)); | ||
120 | |||
121 | block_copy_wait_inflight_reqs(s, start, bytes); | ||
122 | - block_copy_inflight_req_begin(s, &req, start, end); | ||
123 | + block_copy_inflight_req_begin(s, &req, start, bytes); | ||
124 | |||
125 | - while (start < end) { | ||
126 | - int64_t next_zero, chunk_end, status_bytes; | ||
127 | + while (bytes) { | ||
128 | + int64_t next_zero, cur_bytes, status_bytes; | ||
129 | |||
130 | if (!bdrv_dirty_bitmap_get(s->copy_bitmap, start)) { | ||
131 | trace_block_copy_skip(s, start); | ||
132 | start += s->cluster_size; | ||
133 | + bytes -= s->cluster_size; | ||
134 | continue; /* already copied */ | ||
135 | } | ||
136 | |||
137 | - chunk_end = MIN(end, start + s->copy_size); | ||
138 | + cur_bytes = MIN(bytes, s->copy_size); | ||
139 | |||
140 | next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, start, | ||
141 | - chunk_end - start); | ||
142 | + cur_bytes); | ||
143 | if (next_zero >= 0) { | ||
144 | assert(next_zero > start); /* start is dirty */ | ||
145 | - assert(next_zero < chunk_end); /* no need to do MIN() */ | ||
146 | - chunk_end = next_zero; | ||
147 | + assert(next_zero < start + cur_bytes); /* no need to do MIN() */ | ||
148 | + cur_bytes = next_zero - start; | ||
149 | } | ||
150 | |||
151 | - ret = block_copy_block_status(s, start, chunk_end - start, | ||
152 | - &status_bytes); | ||
153 | + ret = block_copy_block_status(s, start, cur_bytes, &status_bytes); | ||
154 | if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) { | ||
155 | bdrv_reset_dirty_bitmap(s->copy_bitmap, start, status_bytes); | ||
156 | progress_set_remaining(s->progress, | ||
157 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, | ||
158 | s->in_flight_bytes); | ||
159 | trace_block_copy_skip_range(s, start, status_bytes); | ||
160 | start += status_bytes; | ||
161 | + bytes -= status_bytes; | ||
162 | continue; | ||
163 | } | ||
164 | |||
165 | - chunk_end = MIN(chunk_end, start + status_bytes); | ||
166 | + cur_bytes = MIN(cur_bytes, status_bytes); | ||
167 | |||
168 | trace_block_copy_process(s, start); | ||
169 | |||
170 | - bdrv_reset_dirty_bitmap(s->copy_bitmap, start, chunk_end - start); | ||
171 | - s->in_flight_bytes += chunk_end - start; | ||
172 | + bdrv_reset_dirty_bitmap(s->copy_bitmap, start, cur_bytes); | ||
173 | + s->in_flight_bytes += cur_bytes; | ||
174 | |||
175 | - co_get_from_shres(s->mem, chunk_end - start); | ||
176 | - ret = block_copy_do_copy(s, start, chunk_end, ret & BDRV_BLOCK_ZERO, | ||
177 | + co_get_from_shres(s->mem, cur_bytes); | ||
178 | + ret = block_copy_do_copy(s, start, cur_bytes, ret & BDRV_BLOCK_ZERO, | ||
179 | error_is_read); | ||
180 | - co_put_to_shres(s->mem, chunk_end - start); | ||
181 | - s->in_flight_bytes -= chunk_end - start; | ||
182 | + co_put_to_shres(s->mem, cur_bytes); | ||
183 | + s->in_flight_bytes -= cur_bytes; | ||
184 | if (ret < 0) { | ||
185 | - bdrv_set_dirty_bitmap(s->copy_bitmap, start, chunk_end - start); | ||
186 | + bdrv_set_dirty_bitmap(s->copy_bitmap, start, cur_bytes); | ||
187 | break; | ||
188 | } | ||
189 | |||
190 | - progress_work_done(s->progress, chunk_end - start); | ||
191 | - s->progress_bytes_callback(chunk_end - start, s->progress_opaque); | ||
192 | - start = chunk_end; | ||
193 | - ret = 0; | ||
194 | + progress_work_done(s->progress, cur_bytes); | ||
195 | + s->progress_bytes_callback(cur_bytes, s->progress_opaque); | ||
196 | + start += cur_bytes; | ||
197 | + bytes -= cur_bytes; | ||
198 | } | ||
199 | |||
200 | block_copy_inflight_req_end(&req); | ||
201 | diff --git a/include/block/block-copy.h b/include/block/block-copy.h | ||
202 | index XXXXXXX..XXXXXXX 100644 | ||
203 | --- a/include/block/block-copy.h | ||
204 | +++ b/include/block/block-copy.h | ||
205 | @@ -XXX,XX +XXX,XX @@ | ||
206 | #include "qemu/co-shared-resource.h" | ||
207 | |||
208 | typedef struct BlockCopyInFlightReq { | ||
209 | - int64_t start_byte; | ||
210 | - int64_t end_byte; | ||
211 | + int64_t start; | ||
212 | + int64_t bytes; | ||
213 | QLIST_ENTRY(BlockCopyInFlightReq) list; | ||
214 | CoQueue wait_queue; /* coroutines blocked on this request */ | ||
215 | } BlockCopyInFlightReq; | ||
216 | @@ -XXX,XX +XXX,XX @@ void block_copy_state_free(BlockCopyState *s); | ||
217 | int64_t block_copy_reset_unallocated(BlockCopyState *s, | ||
218 | int64_t offset, int64_t *count); | ||
219 | |||
220 | -int coroutine_fn block_copy(BlockCopyState *s, int64_t start, uint64_t bytes, | ||
221 | +int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes, | ||
222 | bool *error_is_read); | ||
223 | |||
224 | #endif /* BLOCK_COPY_H */ | ||
225 | -- | ||
226 | 2.24.1 | ||
227 | |||
228 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
2 | 1 | ||
3 | offset/bytes pair is more usual naming in block layer, let's use it. | ||
4 | |||
5 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | ||
6 | Reviewed-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com> | ||
7 | Reviewed-by: Max Reitz <mreitz@redhat.com> | ||
8 | Message-Id: <20200311103004.7649-8-vsementsov@virtuozzo.com> | ||
9 | Signed-off-by: Max Reitz <mreitz@redhat.com> | ||
10 | --- | ||
11 | block/block-copy.c | 82 +++++++++++++++++++------------------- | ||
12 | include/block/block-copy.h | 4 +- | ||
13 | 2 files changed, 43 insertions(+), 43 deletions(-) | ||
14 | |||
15 | diff --git a/block/block-copy.c b/block/block-copy.c | ||
16 | index XXXXXXX..XXXXXXX 100644 | ||
17 | --- a/block/block-copy.c | ||
18 | +++ b/block/block-copy.c | ||
19 | @@ -XXX,XX +XXX,XX @@ | ||
20 | #define BLOCK_COPY_MAX_MEM (128 * MiB) | ||
21 | |||
22 | static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s, | ||
23 | - int64_t start, | ||
24 | + int64_t offset, | ||
25 | int64_t bytes) | ||
26 | { | ||
27 | BlockCopyInFlightReq *req; | ||
28 | |||
29 | QLIST_FOREACH(req, &s->inflight_reqs, list) { | ||
30 | - if (start + bytes > req->start && start < req->start + req->bytes) { | ||
31 | + if (offset + bytes > req->offset && offset < req->offset + req->bytes) { | ||
32 | return req; | ||
33 | } | ||
34 | } | ||
35 | @@ -XXX,XX +XXX,XX @@ static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s, | ||
36 | } | ||
37 | |||
38 | static void coroutine_fn block_copy_wait_inflight_reqs(BlockCopyState *s, | ||
39 | - int64_t start, | ||
40 | + int64_t offset, | ||
41 | int64_t bytes) | ||
42 | { | ||
43 | BlockCopyInFlightReq *req; | ||
44 | |||
45 | - while ((req = find_conflicting_inflight_req(s, start, bytes))) { | ||
46 | + while ((req = find_conflicting_inflight_req(s, offset, bytes))) { | ||
47 | qemu_co_queue_wait(&req->wait_queue, NULL); | ||
48 | } | ||
49 | } | ||
50 | |||
51 | static void block_copy_inflight_req_begin(BlockCopyState *s, | ||
52 | BlockCopyInFlightReq *req, | ||
53 | - int64_t start, int64_t bytes) | ||
54 | + int64_t offset, int64_t bytes) | ||
55 | { | ||
56 | - req->start = start; | ||
57 | + req->offset = offset; | ||
58 | req->bytes = bytes; | ||
59 | qemu_co_queue_init(&req->wait_queue); | ||
60 | QLIST_INSERT_HEAD(&s->inflight_reqs, req, list); | ||
61 | @@ -XXX,XX +XXX,XX @@ void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm) | ||
62 | * Returns 0 on success. | ||
63 | */ | ||
64 | static int coroutine_fn block_copy_do_copy(BlockCopyState *s, | ||
65 | - int64_t start, int64_t bytes, | ||
66 | + int64_t offset, int64_t bytes, | ||
67 | bool zeroes, bool *error_is_read) | ||
68 | { | ||
69 | int ret; | ||
70 | - int64_t nbytes = MIN(start + bytes, s->len) - start; | ||
71 | + int64_t nbytes = MIN(offset + bytes, s->len) - offset; | ||
72 | void *bounce_buffer = NULL; | ||
73 | |||
74 | - assert(start >= 0 && bytes > 0 && INT64_MAX - start >= bytes); | ||
75 | - assert(QEMU_IS_ALIGNED(start, s->cluster_size)); | ||
76 | + assert(offset >= 0 && bytes > 0 && INT64_MAX - offset >= bytes); | ||
77 | + assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); | ||
78 | assert(QEMU_IS_ALIGNED(bytes, s->cluster_size)); | ||
79 | - assert(start < s->len); | ||
80 | - assert(start + bytes <= s->len || | ||
81 | - start + bytes == QEMU_ALIGN_UP(s->len, s->cluster_size)); | ||
82 | + assert(offset < s->len); | ||
83 | + assert(offset + bytes <= s->len || | ||
84 | + offset + bytes == QEMU_ALIGN_UP(s->len, s->cluster_size)); | ||
85 | assert(nbytes < INT_MAX); | ||
86 | |||
87 | if (zeroes) { | ||
88 | - ret = bdrv_co_pwrite_zeroes(s->target, start, nbytes, s->write_flags & | ||
89 | + ret = bdrv_co_pwrite_zeroes(s->target, offset, nbytes, s->write_flags & | ||
90 | ~BDRV_REQ_WRITE_COMPRESSED); | ||
91 | if (ret < 0) { | ||
92 | - trace_block_copy_write_zeroes_fail(s, start, ret); | ||
93 | + trace_block_copy_write_zeroes_fail(s, offset, ret); | ||
94 | if (error_is_read) { | ||
95 | *error_is_read = false; | ||
96 | } | ||
97 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_do_copy(BlockCopyState *s, | ||
98 | } | ||
99 | |||
100 | if (s->use_copy_range) { | ||
101 | - ret = bdrv_co_copy_range(s->source, start, s->target, start, nbytes, | ||
102 | + ret = bdrv_co_copy_range(s->source, offset, s->target, offset, nbytes, | ||
103 | 0, s->write_flags); | ||
104 | if (ret < 0) { | ||
105 | - trace_block_copy_copy_range_fail(s, start, ret); | ||
106 | + trace_block_copy_copy_range_fail(s, offset, ret); | ||
107 | s->use_copy_range = false; | ||
108 | s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER); | ||
109 | /* Fallback to read+write with allocated buffer */ | ||
110 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_do_copy(BlockCopyState *s, | ||
111 | |||
112 | bounce_buffer = qemu_blockalign(s->source->bs, nbytes); | ||
113 | |||
114 | - ret = bdrv_co_pread(s->source, start, nbytes, bounce_buffer, 0); | ||
115 | + ret = bdrv_co_pread(s->source, offset, nbytes, bounce_buffer, 0); | ||
116 | if (ret < 0) { | ||
117 | - trace_block_copy_read_fail(s, start, ret); | ||
118 | + trace_block_copy_read_fail(s, offset, ret); | ||
119 | if (error_is_read) { | ||
120 | *error_is_read = true; | ||
121 | } | ||
122 | goto out; | ||
123 | } | ||
124 | |||
125 | - ret = bdrv_co_pwrite(s->target, start, nbytes, bounce_buffer, | ||
126 | + ret = bdrv_co_pwrite(s->target, offset, nbytes, bounce_buffer, | ||
127 | s->write_flags); | ||
128 | if (ret < 0) { | ||
129 | - trace_block_copy_write_fail(s, start, ret); | ||
130 | + trace_block_copy_write_fail(s, offset, ret); | ||
131 | if (error_is_read) { | ||
132 | *error_is_read = false; | ||
133 | } | ||
134 | @@ -XXX,XX +XXX,XX @@ int64_t block_copy_reset_unallocated(BlockCopyState *s, | ||
135 | } | ||
136 | |||
137 | int coroutine_fn block_copy(BlockCopyState *s, | ||
138 | - int64_t start, int64_t bytes, | ||
139 | + int64_t offset, int64_t bytes, | ||
140 | bool *error_is_read) | ||
141 | { | ||
142 | int ret = 0; | ||
143 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, | ||
144 | assert(bdrv_get_aio_context(s->source->bs) == | ||
145 | bdrv_get_aio_context(s->target->bs)); | ||
146 | |||
147 | - assert(QEMU_IS_ALIGNED(start, s->cluster_size)); | ||
148 | + assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); | ||
149 | assert(QEMU_IS_ALIGNED(bytes, s->cluster_size)); | ||
150 | |||
151 | - block_copy_wait_inflight_reqs(s, start, bytes); | ||
152 | - block_copy_inflight_req_begin(s, &req, start, bytes); | ||
153 | + block_copy_wait_inflight_reqs(s, offset, bytes); | ||
154 | + block_copy_inflight_req_begin(s, &req, offset, bytes); | ||
155 | |||
156 | while (bytes) { | ||
157 | int64_t next_zero, cur_bytes, status_bytes; | ||
158 | |||
159 | - if (!bdrv_dirty_bitmap_get(s->copy_bitmap, start)) { | ||
160 | - trace_block_copy_skip(s, start); | ||
161 | - start += s->cluster_size; | ||
162 | + if (!bdrv_dirty_bitmap_get(s->copy_bitmap, offset)) { | ||
163 | + trace_block_copy_skip(s, offset); | ||
164 | + offset += s->cluster_size; | ||
165 | bytes -= s->cluster_size; | ||
166 | continue; /* already copied */ | ||
167 | } | ||
168 | |||
169 | cur_bytes = MIN(bytes, s->copy_size); | ||
170 | |||
171 | - next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, start, | ||
172 | + next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, offset, | ||
173 | cur_bytes); | ||
174 | if (next_zero >= 0) { | ||
175 | - assert(next_zero > start); /* start is dirty */ | ||
176 | - assert(next_zero < start + cur_bytes); /* no need to do MIN() */ | ||
177 | - cur_bytes = next_zero - start; | ||
178 | + assert(next_zero > offset); /* offset is dirty */ | ||
179 | + assert(next_zero < offset + cur_bytes); /* no need to do MIN() */ | ||
180 | + cur_bytes = next_zero - offset; | ||
181 | } | ||
182 | |||
183 | - ret = block_copy_block_status(s, start, cur_bytes, &status_bytes); | ||
184 | + ret = block_copy_block_status(s, offset, cur_bytes, &status_bytes); | ||
185 | if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) { | ||
186 | - bdrv_reset_dirty_bitmap(s->copy_bitmap, start, status_bytes); | ||
187 | + bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, status_bytes); | ||
188 | progress_set_remaining(s->progress, | ||
189 | bdrv_get_dirty_count(s->copy_bitmap) + | ||
190 | s->in_flight_bytes); | ||
191 | - trace_block_copy_skip_range(s, start, status_bytes); | ||
192 | - start += status_bytes; | ||
193 | + trace_block_copy_skip_range(s, offset, status_bytes); | ||
194 | + offset += status_bytes; | ||
195 | bytes -= status_bytes; | ||
196 | continue; | ||
197 | } | ||
198 | |||
199 | cur_bytes = MIN(cur_bytes, status_bytes); | ||
200 | |||
201 | - trace_block_copy_process(s, start); | ||
202 | + trace_block_copy_process(s, offset); | ||
203 | |||
204 | - bdrv_reset_dirty_bitmap(s->copy_bitmap, start, cur_bytes); | ||
205 | + bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, cur_bytes); | ||
206 | s->in_flight_bytes += cur_bytes; | ||
207 | |||
208 | co_get_from_shres(s->mem, cur_bytes); | ||
209 | - ret = block_copy_do_copy(s, start, cur_bytes, ret & BDRV_BLOCK_ZERO, | ||
210 | + ret = block_copy_do_copy(s, offset, cur_bytes, ret & BDRV_BLOCK_ZERO, | ||
211 | error_is_read); | ||
212 | co_put_to_shres(s->mem, cur_bytes); | ||
213 | s->in_flight_bytes -= cur_bytes; | ||
214 | if (ret < 0) { | ||
215 | - bdrv_set_dirty_bitmap(s->copy_bitmap, start, cur_bytes); | ||
216 | + bdrv_set_dirty_bitmap(s->copy_bitmap, offset, cur_bytes); | ||
217 | break; | ||
218 | } | ||
219 | |||
220 | progress_work_done(s->progress, cur_bytes); | ||
221 | s->progress_bytes_callback(cur_bytes, s->progress_opaque); | ||
222 | - start += cur_bytes; | ||
223 | + offset += cur_bytes; | ||
224 | bytes -= cur_bytes; | ||
225 | } | ||
226 | |||
227 | diff --git a/include/block/block-copy.h b/include/block/block-copy.h | ||
228 | index XXXXXXX..XXXXXXX 100644 | ||
229 | --- a/include/block/block-copy.h | ||
230 | +++ b/include/block/block-copy.h | ||
231 | @@ -XXX,XX +XXX,XX @@ | ||
232 | #include "qemu/co-shared-resource.h" | ||
233 | |||
234 | typedef struct BlockCopyInFlightReq { | ||
235 | - int64_t start; | ||
236 | + int64_t offset; | ||
237 | int64_t bytes; | ||
238 | QLIST_ENTRY(BlockCopyInFlightReq) list; | ||
239 | CoQueue wait_queue; /* coroutines blocked on this request */ | ||
240 | @@ -XXX,XX +XXX,XX @@ void block_copy_state_free(BlockCopyState *s); | ||
241 | int64_t block_copy_reset_unallocated(BlockCopyState *s, | ||
242 | int64_t offset, int64_t *count); | ||
243 | |||
244 | -int coroutine_fn block_copy(BlockCopyState *s, int64_t start, int64_t bytes, | ||
245 | +int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes, | ||
246 | bool *error_is_read); | ||
247 | |||
248 | #endif /* BLOCK_COPY_H */ | ||
249 | -- | ||
250 | 2.24.1 | ||
251 | |||
252 | diff view generated by jsdifflib |
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 1 | From: Nicolas Saenz Julienne <nsaenzju@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Currently, block_copy operation lock the whole requested region. But | 3 | 'event-loop-base' provides basic property handling for all 'AioContext' |
4 | there is no reason to lock clusters, which are already copied, it will | 4 | based event loops. So let's define a new 'MainLoopClass' that inherits |
5 | disturb other parallel block_copy requests for no reason. | 5 | from it. This will permit tweaking the main loop's properties through |
6 | 6 | qapi as well as through the command line using the '-object' keyword[1]. | |
7 | Let's instead do the following: | 7 | Only one instance of 'MainLoopClass' might be created at any time. |
8 | 8 | ||
9 | Lock only sub-region, which we are going to operate on. Then, after | 9 | 'EventLoopBaseClass' learns a new callback, 'can_be_deleted()' so as to |
10 | copying all dirty sub-regions, we should wait for intersecting | 10 | mark 'MainLoop' as non-deletable. |
11 | requests block-copy, if they failed, we should retry these new dirty | 11 | |
12 | clusters. | 12 | [1] For example: |
13 | 13 | -object main-loop,id=main-loop,aio-max-batch=<value> | |
14 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 14 | |
15 | Reviewed-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com> | 15 | Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com> |
16 | Message-Id: <20200311103004.7649-9-vsementsov@virtuozzo.com> | 16 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> |
17 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 17 | Acked-by: Markus Armbruster <armbru@redhat.com> |
18 | Message-id: 20220425075723.20019-3-nsaenzju@redhat.com | ||
19 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
18 | --- | 20 | --- |
19 | block/block-copy.c | 129 ++++++++++++++++++++++++++++++++++++--------- | 21 | qapi/qom.json | 13 ++++++++ |
20 | 1 file changed, 105 insertions(+), 24 deletions(-) | 22 | meson.build | 3 +- |
21 | 23 | include/qemu/main-loop.h | 10 ++++++ | |
22 | diff --git a/block/block-copy.c b/block/block-copy.c | 24 | include/sysemu/event-loop-base.h | 1 + |
23 | index XXXXXXX..XXXXXXX 100644 | 25 | event-loop-base.c | 13 ++++++++ |
24 | --- a/block/block-copy.c | 26 | util/main-loop.c | 56 ++++++++++++++++++++++++++++++++ |
25 | +++ b/block/block-copy.c | 27 | 6 files changed, 95 insertions(+), 1 deletion(-) |
26 | @@ -XXX,XX +XXX,XX @@ static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s, | 28 | |
27 | return NULL; | 29 | diff --git a/qapi/qom.json b/qapi/qom.json |
30 | index XXXXXXX..XXXXXXX 100644 | ||
31 | --- a/qapi/qom.json | ||
32 | +++ b/qapi/qom.json | ||
33 | @@ -XXX,XX +XXX,XX @@ | ||
34 | '*poll-grow': 'int', | ||
35 | '*poll-shrink': 'int' } } | ||
36 | |||
37 | +## | ||
38 | +# @MainLoopProperties: | ||
39 | +# | ||
40 | +# Properties for the main-loop object. | ||
41 | +# | ||
42 | +# Since: 7.1 | ||
43 | +## | ||
44 | +{ 'struct': 'MainLoopProperties', | ||
45 | + 'base': 'EventLoopBaseProperties', | ||
46 | + 'data': {} } | ||
47 | + | ||
48 | ## | ||
49 | # @MemoryBackendProperties: | ||
50 | # | ||
51 | @@ -XXX,XX +XXX,XX @@ | ||
52 | { 'name': 'input-linux', | ||
53 | 'if': 'CONFIG_LINUX' }, | ||
54 | 'iothread', | ||
55 | + 'main-loop', | ||
56 | { 'name': 'memory-backend-epc', | ||
57 | 'if': 'CONFIG_LINUX' }, | ||
58 | 'memory-backend-file', | ||
59 | @@ -XXX,XX +XXX,XX @@ | ||
60 | 'input-linux': { 'type': 'InputLinuxProperties', | ||
61 | 'if': 'CONFIG_LINUX' }, | ||
62 | 'iothread': 'IothreadProperties', | ||
63 | + 'main-loop': 'MainLoopProperties', | ||
64 | 'memory-backend-epc': { 'type': 'MemoryBackendEpcProperties', | ||
65 | 'if': 'CONFIG_LINUX' }, | ||
66 | 'memory-backend-file': 'MemoryBackendFileProperties', | ||
67 | diff --git a/meson.build b/meson.build | ||
68 | index XXXXXXX..XXXXXXX 100644 | ||
69 | --- a/meson.build | ||
70 | +++ b/meson.build | ||
71 | @@ -XXX,XX +XXX,XX @@ libqemuutil = static_library('qemuutil', | ||
72 | sources: util_ss.sources() + stub_ss.sources() + genh, | ||
73 | dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc, pixman]) | ||
74 | qemuutil = declare_dependency(link_with: libqemuutil, | ||
75 | - sources: genh + version_res) | ||
76 | + sources: genh + version_res, | ||
77 | + dependencies: [event_loop_base]) | ||
78 | |||
79 | if have_system or have_user | ||
80 | decodetree = generator(find_program('scripts/decodetree.py'), | ||
81 | diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h | ||
82 | index XXXXXXX..XXXXXXX 100644 | ||
83 | --- a/include/qemu/main-loop.h | ||
84 | +++ b/include/qemu/main-loop.h | ||
85 | @@ -XXX,XX +XXX,XX @@ | ||
86 | #define QEMU_MAIN_LOOP_H | ||
87 | |||
88 | #include "block/aio.h" | ||
89 | +#include "qom/object.h" | ||
90 | +#include "sysemu/event-loop-base.h" | ||
91 | |||
92 | #define SIG_IPI SIGUSR1 | ||
93 | |||
94 | +#define TYPE_MAIN_LOOP "main-loop" | ||
95 | +OBJECT_DECLARE_TYPE(MainLoop, MainLoopClass, MAIN_LOOP) | ||
96 | + | ||
97 | +struct MainLoop { | ||
98 | + EventLoopBase parent_obj; | ||
99 | +}; | ||
100 | +typedef struct MainLoop MainLoop; | ||
101 | + | ||
102 | /** | ||
103 | * qemu_init_main_loop: Set up the process so that it can run the main loop. | ||
104 | * | ||
105 | diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h | ||
106 | index XXXXXXX..XXXXXXX 100644 | ||
107 | --- a/include/sysemu/event-loop-base.h | ||
108 | +++ b/include/sysemu/event-loop-base.h | ||
109 | @@ -XXX,XX +XXX,XX @@ struct EventLoopBaseClass { | ||
110 | |||
111 | void (*init)(EventLoopBase *base, Error **errp); | ||
112 | void (*update_params)(EventLoopBase *base, Error **errp); | ||
113 | + bool (*can_be_deleted)(EventLoopBase *base); | ||
114 | }; | ||
115 | |||
116 | struct EventLoopBase { | ||
117 | diff --git a/event-loop-base.c b/event-loop-base.c | ||
118 | index XXXXXXX..XXXXXXX 100644 | ||
119 | --- a/event-loop-base.c | ||
120 | +++ b/event-loop-base.c | ||
121 | @@ -XXX,XX +XXX,XX @@ static void event_loop_base_complete(UserCreatable *uc, Error **errp) | ||
122 | } | ||
28 | } | 123 | } |
29 | 124 | ||
30 | -static void coroutine_fn block_copy_wait_inflight_reqs(BlockCopyState *s, | 125 | +static bool event_loop_base_can_be_deleted(UserCreatable *uc) |
31 | - int64_t offset, | 126 | +{ |
32 | - int64_t bytes) | 127 | + EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc); |
33 | +/* | 128 | + EventLoopBase *backend = EVENT_LOOP_BASE(uc); |
34 | + * If there are no intersecting requests return false. Otherwise, wait for the | 129 | + |
35 | + * first found intersecting request to finish and return true. | 130 | + if (bc->can_be_deleted) { |
36 | + */ | 131 | + return bc->can_be_deleted(backend); |
37 | +static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset, | 132 | + } |
38 | + int64_t bytes) | 133 | + |
134 | + return true; | ||
135 | +} | ||
136 | + | ||
137 | static void event_loop_base_class_init(ObjectClass *klass, void *class_data) | ||
39 | { | 138 | { |
40 | - BlockCopyInFlightReq *req; | 139 | UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass); |
41 | + BlockCopyInFlightReq *req = find_conflicting_inflight_req(s, offset, bytes); | 140 | ucc->complete = event_loop_base_complete; |
42 | 141 | + ucc->can_be_deleted = event_loop_base_can_be_deleted; | |
43 | - while ((req = find_conflicting_inflight_req(s, offset, bytes))) { | 142 | |
44 | - qemu_co_queue_wait(&req->wait_queue, NULL); | 143 | object_class_property_add(klass, "aio-max-batch", "int", |
45 | + if (!req) { | 144 | event_loop_base_get_param, |
46 | + return false; | 145 | diff --git a/util/main-loop.c b/util/main-loop.c |
47 | } | 146 | index XXXXXXX..XXXXXXX 100644 |
48 | + | 147 | --- a/util/main-loop.c |
49 | + qemu_co_queue_wait(&req->wait_queue, NULL); | 148 | +++ b/util/main-loop.c |
50 | + | 149 | @@ -XXX,XX +XXX,XX @@ |
51 | + return true; | 150 | #include "qemu/error-report.h" |
151 | #include "qemu/queue.h" | ||
152 | #include "qemu/compiler.h" | ||
153 | +#include "qom/object.h" | ||
154 | |||
155 | #ifndef _WIN32 | ||
156 | #include <sys/wait.h> | ||
157 | @@ -XXX,XX +XXX,XX @@ int qemu_init_main_loop(Error **errp) | ||
158 | return 0; | ||
52 | } | 159 | } |
53 | 160 | ||
54 | +/* Called only on full-dirty region */ | 161 | +static void main_loop_update_params(EventLoopBase *base, Error **errp) |
55 | static void block_copy_inflight_req_begin(BlockCopyState *s, | 162 | +{ |
56 | BlockCopyInFlightReq *req, | 163 | + if (!qemu_aio_context) { |
57 | int64_t offset, int64_t bytes) | 164 | + error_setg(errp, "qemu aio context not ready"); |
58 | { | ||
59 | + assert(!find_conflicting_inflight_req(s, offset, bytes)); | ||
60 | + | ||
61 | + bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes); | ||
62 | + s->in_flight_bytes += bytes; | ||
63 | + | ||
64 | req->offset = offset; | ||
65 | req->bytes = bytes; | ||
66 | qemu_co_queue_init(&req->wait_queue); | ||
67 | QLIST_INSERT_HEAD(&s->inflight_reqs, req, list); | ||
68 | } | ||
69 | |||
70 | -static void coroutine_fn block_copy_inflight_req_end(BlockCopyInFlightReq *req) | ||
71 | +/* | ||
72 | + * block_copy_inflight_req_shrink | ||
73 | + * | ||
74 | + * Drop the tail of the request to be handled later. Set dirty bits back and | ||
75 | + * wake up all requests waiting for us (may be some of them are not intersecting | ||
76 | + * with shrunk request) | ||
77 | + */ | ||
78 | +static void coroutine_fn block_copy_inflight_req_shrink(BlockCopyState *s, | ||
79 | + BlockCopyInFlightReq *req, int64_t new_bytes) | ||
80 | { | ||
81 | + if (new_bytes == req->bytes) { | ||
82 | + return; | 165 | + return; |
83 | + } | 166 | + } |
84 | + | 167 | + |
85 | + assert(new_bytes > 0 && new_bytes < req->bytes); | 168 | + aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp); |
86 | + | 169 | +} |
87 | + s->in_flight_bytes -= req->bytes - new_bytes; | 170 | + |
88 | + bdrv_set_dirty_bitmap(s->copy_bitmap, | 171 | +MainLoop *mloop; |
89 | + req->offset + new_bytes, req->bytes - new_bytes); | 172 | + |
90 | + | 173 | +static void main_loop_init(EventLoopBase *base, Error **errp) |
91 | + req->bytes = new_bytes; | 174 | +{ |
92 | + qemu_co_queue_restart_all(&req->wait_queue); | 175 | + MainLoop *m = MAIN_LOOP(base); |
93 | +} | 176 | + |
94 | + | 177 | + if (mloop) { |
95 | +static void coroutine_fn block_copy_inflight_req_end(BlockCopyState *s, | 178 | + error_setg(errp, "only one main-loop instance allowed"); |
96 | + BlockCopyInFlightReq *req, | 179 | + return; |
97 | + int ret) | ||
98 | +{ | ||
99 | + s->in_flight_bytes -= req->bytes; | ||
100 | + if (ret < 0) { | ||
101 | + bdrv_set_dirty_bitmap(s->copy_bitmap, req->offset, req->bytes); | ||
102 | + } | 180 | + } |
103 | QLIST_REMOVE(req, list); | 181 | + |
104 | qemu_co_queue_restart_all(&req->wait_queue); | 182 | + main_loop_update_params(base, errp); |
105 | } | 183 | + |
106 | @@ -XXX,XX +XXX,XX @@ int64_t block_copy_reset_unallocated(BlockCopyState *s, | 184 | + mloop = m; |
107 | return ret; | 185 | + return; |
108 | } | 186 | +} |
109 | 187 | + | |
110 | -int coroutine_fn block_copy(BlockCopyState *s, | 188 | +static bool main_loop_can_be_deleted(EventLoopBase *base) |
111 | - int64_t offset, int64_t bytes, | 189 | +{ |
112 | - bool *error_is_read) | 190 | + return false; |
113 | +/* | 191 | +} |
114 | + * block_copy_dirty_clusters | 192 | + |
115 | + * | 193 | +static void main_loop_class_init(ObjectClass *oc, void *class_data) |
116 | + * Copy dirty clusters in @offset/@bytes range. | 194 | +{ |
117 | + * Returns 1 if dirty clusters found and successfully copied, 0 if no dirty | 195 | + EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(oc); |
118 | + * clusters found and -errno on failure. | 196 | + |
119 | + */ | 197 | + bc->init = main_loop_init; |
120 | +static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s, | 198 | + bc->update_params = main_loop_update_params; |
121 | + int64_t offset, int64_t bytes, | 199 | + bc->can_be_deleted = main_loop_can_be_deleted; |
122 | + bool *error_is_read) | 200 | +} |
123 | { | 201 | + |
124 | int ret = 0; | 202 | +static const TypeInfo main_loop_info = { |
125 | - BlockCopyInFlightReq req; | 203 | + .name = TYPE_MAIN_LOOP, |
126 | + bool found_dirty = false; | 204 | + .parent = TYPE_EVENT_LOOP_BASE, |
127 | 205 | + .class_init = main_loop_class_init, | |
128 | /* | 206 | + .instance_size = sizeof(MainLoop), |
129 | * block_copy() user is responsible for keeping source and target in same | 207 | +}; |
130 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, | 208 | + |
131 | assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); | 209 | +static void main_loop_register_types(void) |
132 | assert(QEMU_IS_ALIGNED(bytes, s->cluster_size)); | 210 | +{ |
133 | 211 | + type_register_static(&main_loop_info); | |
134 | - block_copy_wait_inflight_reqs(s, offset, bytes); | 212 | +} |
135 | - block_copy_inflight_req_begin(s, &req, offset, bytes); | 213 | + |
136 | - | 214 | +type_init(main_loop_register_types) |
137 | while (bytes) { | 215 | + |
138 | + BlockCopyInFlightReq req; | 216 | static int max_priority; |
139 | int64_t next_zero, cur_bytes, status_bytes; | 217 | |
140 | 218 | #ifndef _WIN32 | |
141 | if (!bdrv_dirty_bitmap_get(s->copy_bitmap, offset)) { | ||
142 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, | ||
143 | continue; /* already copied */ | ||
144 | } | ||
145 | |||
146 | + found_dirty = true; | ||
147 | + | ||
148 | cur_bytes = MIN(bytes, s->copy_size); | ||
149 | |||
150 | next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, offset, | ||
151 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, | ||
152 | assert(next_zero < offset + cur_bytes); /* no need to do MIN() */ | ||
153 | cur_bytes = next_zero - offset; | ||
154 | } | ||
155 | + block_copy_inflight_req_begin(s, &req, offset, cur_bytes); | ||
156 | |||
157 | ret = block_copy_block_status(s, offset, cur_bytes, &status_bytes); | ||
158 | + assert(ret >= 0); /* never fail */ | ||
159 | + cur_bytes = MIN(cur_bytes, status_bytes); | ||
160 | + block_copy_inflight_req_shrink(s, &req, cur_bytes); | ||
161 | if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) { | ||
162 | - bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, status_bytes); | ||
163 | + block_copy_inflight_req_end(s, &req, 0); | ||
164 | progress_set_remaining(s->progress, | ||
165 | bdrv_get_dirty_count(s->copy_bitmap) + | ||
166 | s->in_flight_bytes); | ||
167 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, | ||
168 | continue; | ||
169 | } | ||
170 | |||
171 | - cur_bytes = MIN(cur_bytes, status_bytes); | ||
172 | - | ||
173 | trace_block_copy_process(s, offset); | ||
174 | |||
175 | - bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, cur_bytes); | ||
176 | - s->in_flight_bytes += cur_bytes; | ||
177 | - | ||
178 | co_get_from_shres(s->mem, cur_bytes); | ||
179 | ret = block_copy_do_copy(s, offset, cur_bytes, ret & BDRV_BLOCK_ZERO, | ||
180 | error_is_read); | ||
181 | co_put_to_shres(s->mem, cur_bytes); | ||
182 | - s->in_flight_bytes -= cur_bytes; | ||
183 | + block_copy_inflight_req_end(s, &req, ret); | ||
184 | if (ret < 0) { | ||
185 | - bdrv_set_dirty_bitmap(s->copy_bitmap, offset, cur_bytes); | ||
186 | - break; | ||
187 | + return ret; | ||
188 | } | ||
189 | |||
190 | progress_work_done(s->progress, cur_bytes); | ||
191 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, | ||
192 | bytes -= cur_bytes; | ||
193 | } | ||
194 | |||
195 | - block_copy_inflight_req_end(&req); | ||
196 | + return found_dirty; | ||
197 | +} | ||
198 | + | ||
199 | +/* | ||
200 | + * block_copy | ||
201 | + * | ||
202 | + * Copy requested region, accordingly to dirty bitmap. | ||
203 | + * Collaborate with parallel block_copy requests: if they succeed it will help | ||
204 | + * us. If they fail, we will retry not-copied regions. So, if we return error, | ||
205 | + * it means that some I/O operation failed in context of _this_ block_copy call, | ||
206 | + * not some parallel operation. | ||
207 | + */ | ||
208 | +int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes, | ||
209 | + bool *error_is_read) | ||
210 | +{ | ||
211 | + int ret; | ||
212 | + | ||
213 | + do { | ||
214 | + ret = block_copy_dirty_clusters(s, offset, bytes, error_is_read); | ||
215 | + | ||
216 | + if (ret == 0) { | ||
217 | + ret = block_copy_wait_one(s, offset, bytes); | ||
218 | + } | ||
219 | + | ||
220 | + /* | ||
221 | + * We retry in two cases: | ||
222 | + * 1. Some progress done | ||
223 | + * Something was copied, which means that there were yield points | ||
224 | + * and some new dirty bits may have appeared (due to failed parallel | ||
225 | + * block-copy requests). | ||
226 | + * 2. We have waited for some intersecting block-copy request | ||
227 | + * It may have failed and produced new dirty bits. | ||
228 | + */ | ||
229 | + } while (ret > 0); | ||
230 | |||
231 | return ret; | ||
232 | } | ||
233 | -- | 219 | -- |
234 | 2.24.1 | 220 | 2.35.1 |
235 | |||
236 | diff view generated by jsdifflib |
1 | From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 1 | From: Nicolas Saenz Julienne <nsaenzju@redhat.com> |
---|---|---|---|
2 | 2 | ||
3 | Hide structure definitions and add explicit API instead, to keep an | 3 | The thread pool regulates itself: when idle, it kills threads until |
4 | eye on the scope of the shared fields. | 4 | empty, when in demand, it creates new threads until full. This behaviour |
5 | doesn't play well with latency sensitive workloads where the price of | ||
6 | creating a new thread is too high. For example, when paired with qemu's | ||
7 | '-mlock', or using safety features like SafeStack, creating a new thread | ||
8 | has been measured take multiple milliseconds. | ||
5 | 9 | ||
6 | Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> | 10 | In order to mitigate this let's introduce a new 'EventLoopBase' |
7 | Reviewed-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com> | 11 | property to set the thread pool size. The threads will be created during |
8 | Reviewed-by: Max Reitz <mreitz@redhat.com> | 12 | the pool's initialization or upon updating the property's value, remain |
9 | Message-Id: <20200311103004.7649-10-vsementsov@virtuozzo.com> | 13 | available during its lifetime regardless of demand, and destroyed upon |
10 | Signed-off-by: Max Reitz <mreitz@redhat.com> | 14 | freeing it. A properly characterized workload will then be able to |
15 | configure the pool to avoid any latency spikes. | ||
16 | |||
17 | Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com> | ||
18 | Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
19 | Acked-by: Markus Armbruster <armbru@redhat.com> | ||
20 | Message-id: 20220425075723.20019-4-nsaenzju@redhat.com | ||
21 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
11 | --- | 22 | --- |
12 | block/backup-top.c | 6 ++-- | 23 | qapi/qom.json | 10 +++++- |
13 | block/backup.c | 25 ++++++++-------- | 24 | include/block/aio.h | 10 ++++++ |
14 | block/block-copy.c | 59 ++++++++++++++++++++++++++++++++++++++ | 25 | include/block/thread-pool.h | 3 ++ |
15 | include/block/block-copy.h | 52 +++------------------------------ | 26 | include/sysemu/event-loop-base.h | 4 +++ |
16 | 4 files changed, 80 insertions(+), 62 deletions(-) | 27 | event-loop-base.c | 23 +++++++++++++ |
28 | iothread.c | 3 ++ | ||
29 | util/aio-posix.c | 1 + | ||
30 | util/async.c | 20 ++++++++++++ | ||
31 | util/main-loop.c | 9 ++++++ | ||
32 | util/thread-pool.c | 55 +++++++++++++++++++++++++++++--- | ||
33 | 10 files changed, 133 insertions(+), 5 deletions(-) | ||
17 | 34 | ||
18 | diff --git a/block/backup-top.c b/block/backup-top.c | 35 | diff --git a/qapi/qom.json b/qapi/qom.json |
19 | index XXXXXXX..XXXXXXX 100644 | 36 | index XXXXXXX..XXXXXXX 100644 |
20 | --- a/block/backup-top.c | 37 | --- a/qapi/qom.json |
21 | +++ b/block/backup-top.c | 38 | +++ b/qapi/qom.json |
22 | @@ -XXX,XX +XXX,XX @@ typedef struct BDRVBackupTopState { | 39 | @@ -XXX,XX +XXX,XX @@ |
23 | BlockCopyState *bcs; | 40 | # 0 means that the engine will use its default. |
24 | BdrvChild *target; | 41 | # (default: 0) |
25 | bool active; | 42 | # |
26 | + int64_t cluster_size; | 43 | +# @thread-pool-min: minimum number of threads reserved in the thread pool |
27 | } BDRVBackupTopState; | 44 | +# (default:0) |
28 | 45 | +# | |
29 | static coroutine_fn int backup_top_co_preadv( | 46 | +# @thread-pool-max: maximum number of threads the thread pool can contain |
30 | @@ -XXX,XX +XXX,XX @@ static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset, | 47 | +# (default:64) |
31 | return 0; | 48 | +# |
32 | } | 49 | # Since: 7.1 |
33 | 50 | ## | |
34 | - off = QEMU_ALIGN_DOWN(offset, s->bcs->cluster_size); | 51 | { 'struct': 'EventLoopBaseProperties', |
35 | - end = QEMU_ALIGN_UP(offset + bytes, s->bcs->cluster_size); | 52 | - 'data': { '*aio-max-batch': 'int' } } |
36 | + off = QEMU_ALIGN_DOWN(offset, s->cluster_size); | 53 | + 'data': { '*aio-max-batch': 'int', |
37 | + end = QEMU_ALIGN_UP(offset + bytes, s->cluster_size); | 54 | + '*thread-pool-min': 'int', |
38 | 55 | + '*thread-pool-max': 'int' } } | |
39 | return block_copy(s->bcs, off, end - off, NULL); | 56 | |
40 | } | 57 | ## |
41 | @@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, | 58 | # @IothreadProperties: |
42 | goto fail; | 59 | diff --git a/include/block/aio.h b/include/block/aio.h |
43 | } | 60 | index XXXXXXX..XXXXXXX 100644 |
44 | 61 | --- a/include/block/aio.h | |
45 | + state->cluster_size = cluster_size; | 62 | +++ b/include/block/aio.h |
46 | state->bcs = block_copy_state_new(top->backing, state->target, | 63 | @@ -XXX,XX +XXX,XX @@ struct AioContext { |
47 | cluster_size, write_flags, &local_err); | 64 | QSLIST_HEAD(, Coroutine) scheduled_coroutines; |
48 | if (local_err) { | 65 | QEMUBH *co_schedule_bh; |
49 | diff --git a/block/backup.c b/block/backup.c | 66 | |
50 | index XXXXXXX..XXXXXXX 100644 | 67 | + int thread_pool_min; |
51 | --- a/block/backup.c | 68 | + int thread_pool_max; |
52 | +++ b/block/backup.c | 69 | /* Thread pool for performing work and receiving completion callbacks. |
53 | @@ -XXX,XX +XXX,XX @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) | 70 | * Has its own locking. |
54 | 71 | */ | |
55 | if (ret < 0 && job->bitmap_mode == BITMAP_SYNC_MODE_ALWAYS) { | 72 | @@ -XXX,XX +XXX,XX @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, |
56 | /* If we failed and synced, merge in the bits we didn't copy: */ | 73 | void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch, |
57 | - bdrv_dirty_bitmap_merge_internal(bm, job->bcs->copy_bitmap, | 74 | Error **errp); |
58 | + bdrv_dirty_bitmap_merge_internal(bm, block_copy_dirty_bitmap(job->bcs), | 75 | |
59 | NULL, true); | 76 | +/** |
60 | } | 77 | + * aio_context_set_thread_pool_params: |
61 | } | 78 | + * @ctx: the aio context |
62 | @@ -XXX,XX +XXX,XX @@ void backup_do_checkpoint(BlockJob *job, Error **errp) | 79 | + * @min: min number of threads to have readily available in the thread pool |
80 | + * @min: max number of threads the thread pool can contain | ||
81 | + */ | ||
82 | +void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min, | ||
83 | + int64_t max, Error **errp); | ||
84 | #endif | ||
85 | diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h | ||
86 | index XXXXXXX..XXXXXXX 100644 | ||
87 | --- a/include/block/thread-pool.h | ||
88 | +++ b/include/block/thread-pool.h | ||
89 | @@ -XXX,XX +XXX,XX @@ | ||
90 | |||
91 | #include "block/block.h" | ||
92 | |||
93 | +#define THREAD_POOL_MAX_THREADS_DEFAULT 64 | ||
94 | + | ||
95 | typedef int ThreadPoolFunc(void *opaque); | ||
96 | |||
97 | typedef struct ThreadPool ThreadPool; | ||
98 | @@ -XXX,XX +XXX,XX @@ BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool, | ||
99 | int coroutine_fn thread_pool_submit_co(ThreadPool *pool, | ||
100 | ThreadPoolFunc *func, void *arg); | ||
101 | void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg); | ||
102 | +void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx); | ||
103 | |||
104 | #endif | ||
105 | diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h | ||
106 | index XXXXXXX..XXXXXXX 100644 | ||
107 | --- a/include/sysemu/event-loop-base.h | ||
108 | +++ b/include/sysemu/event-loop-base.h | ||
109 | @@ -XXX,XX +XXX,XX @@ struct EventLoopBase { | ||
110 | |||
111 | /* AioContext AIO engine parameters */ | ||
112 | int64_t aio_max_batch; | ||
113 | + | ||
114 | + /* AioContext thread pool parameters */ | ||
115 | + int64_t thread_pool_min; | ||
116 | + int64_t thread_pool_max; | ||
117 | }; | ||
118 | #endif | ||
119 | diff --git a/event-loop-base.c b/event-loop-base.c | ||
120 | index XXXXXXX..XXXXXXX 100644 | ||
121 | --- a/event-loop-base.c | ||
122 | +++ b/event-loop-base.c | ||
123 | @@ -XXX,XX +XXX,XX @@ | ||
124 | #include "qemu/osdep.h" | ||
125 | #include "qom/object_interfaces.h" | ||
126 | #include "qapi/error.h" | ||
127 | +#include "block/thread-pool.h" | ||
128 | #include "sysemu/event-loop-base.h" | ||
129 | |||
130 | typedef struct { | ||
131 | @@ -XXX,XX +XXX,XX @@ typedef struct { | ||
132 | ptrdiff_t offset; /* field's byte offset in EventLoopBase struct */ | ||
133 | } EventLoopBaseParamInfo; | ||
134 | |||
135 | +static void event_loop_base_instance_init(Object *obj) | ||
136 | +{ | ||
137 | + EventLoopBase *base = EVENT_LOOP_BASE(obj); | ||
138 | + | ||
139 | + base->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT; | ||
140 | +} | ||
141 | + | ||
142 | static EventLoopBaseParamInfo aio_max_batch_info = { | ||
143 | "aio-max-batch", offsetof(EventLoopBase, aio_max_batch), | ||
144 | }; | ||
145 | +static EventLoopBaseParamInfo thread_pool_min_info = { | ||
146 | + "thread-pool-min", offsetof(EventLoopBase, thread_pool_min), | ||
147 | +}; | ||
148 | +static EventLoopBaseParamInfo thread_pool_max_info = { | ||
149 | + "thread-pool-max", offsetof(EventLoopBase, thread_pool_max), | ||
150 | +}; | ||
151 | |||
152 | static void event_loop_base_get_param(Object *obj, Visitor *v, | ||
153 | const char *name, void *opaque, Error **errp) | ||
154 | @@ -XXX,XX +XXX,XX @@ static void event_loop_base_class_init(ObjectClass *klass, void *class_data) | ||
155 | event_loop_base_get_param, | ||
156 | event_loop_base_set_param, | ||
157 | NULL, &aio_max_batch_info); | ||
158 | + object_class_property_add(klass, "thread-pool-min", "int", | ||
159 | + event_loop_base_get_param, | ||
160 | + event_loop_base_set_param, | ||
161 | + NULL, &thread_pool_min_info); | ||
162 | + object_class_property_add(klass, "thread-pool-max", "int", | ||
163 | + event_loop_base_get_param, | ||
164 | + event_loop_base_set_param, | ||
165 | + NULL, &thread_pool_max_info); | ||
166 | } | ||
167 | |||
168 | static const TypeInfo event_loop_base_info = { | ||
169 | .name = TYPE_EVENT_LOOP_BASE, | ||
170 | .parent = TYPE_OBJECT, | ||
171 | .instance_size = sizeof(EventLoopBase), | ||
172 | + .instance_init = event_loop_base_instance_init, | ||
173 | .class_size = sizeof(EventLoopBaseClass), | ||
174 | .class_init = event_loop_base_class_init, | ||
175 | .abstract = true, | ||
176 | diff --git a/iothread.c b/iothread.c | ||
177 | index XXXXXXX..XXXXXXX 100644 | ||
178 | --- a/iothread.c | ||
179 | +++ b/iothread.c | ||
180 | @@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp) | ||
181 | aio_context_set_aio_params(iothread->ctx, | ||
182 | iothread->parent_obj.aio_max_batch, | ||
183 | errp); | ||
184 | + | ||
185 | + aio_context_set_thread_pool_params(iothread->ctx, base->thread_pool_min, | ||
186 | + base->thread_pool_max, errp); | ||
187 | } | ||
188 | |||
189 | |||
190 | diff --git a/util/aio-posix.c b/util/aio-posix.c | ||
191 | index XXXXXXX..XXXXXXX 100644 | ||
192 | --- a/util/aio-posix.c | ||
193 | +++ b/util/aio-posix.c | ||
194 | @@ -XXX,XX +XXX,XX @@ | ||
195 | |||
196 | #include "qemu/osdep.h" | ||
197 | #include "block/block.h" | ||
198 | +#include "block/thread-pool.h" | ||
199 | #include "qemu/main-loop.h" | ||
200 | #include "qemu/rcu.h" | ||
201 | #include "qemu/rcu_queue.h" | ||
202 | diff --git a/util/async.c b/util/async.c | ||
203 | index XXXXXXX..XXXXXXX 100644 | ||
204 | --- a/util/async.c | ||
205 | +++ b/util/async.c | ||
206 | @@ -XXX,XX +XXX,XX @@ AioContext *aio_context_new(Error **errp) | ||
207 | |||
208 | ctx->aio_max_batch = 0; | ||
209 | |||
210 | + ctx->thread_pool_min = 0; | ||
211 | + ctx->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT; | ||
212 | + | ||
213 | return ctx; | ||
214 | fail: | ||
215 | g_source_destroy(&ctx->source); | ||
216 | @@ -XXX,XX +XXX,XX @@ void qemu_set_current_aio_context(AioContext *ctx) | ||
217 | assert(!get_my_aiocontext()); | ||
218 | set_my_aiocontext(ctx); | ||
219 | } | ||
220 | + | ||
221 | +void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min, | ||
222 | + int64_t max, Error **errp) | ||
223 | +{ | ||
224 | + | ||
225 | + if (min > max || !max || min > INT_MAX || max > INT_MAX) { | ||
226 | + error_setg(errp, "bad thread-pool-min/thread-pool-max values"); | ||
227 | + return; | ||
228 | + } | ||
229 | + | ||
230 | + ctx->thread_pool_min = min; | ||
231 | + ctx->thread_pool_max = max; | ||
232 | + | ||
233 | + if (ctx->thread_pool) { | ||
234 | + thread_pool_update_params(ctx->thread_pool, ctx); | ||
235 | + } | ||
236 | +} | ||
237 | diff --git a/util/main-loop.c b/util/main-loop.c | ||
238 | index XXXXXXX..XXXXXXX 100644 | ||
239 | --- a/util/main-loop.c | ||
240 | +++ b/util/main-loop.c | ||
241 | @@ -XXX,XX +XXX,XX @@ | ||
242 | #include "sysemu/replay.h" | ||
243 | #include "qemu/main-loop.h" | ||
244 | #include "block/aio.h" | ||
245 | +#include "block/thread-pool.h" | ||
246 | #include "qemu/error-report.h" | ||
247 | #include "qemu/queue.h" | ||
248 | #include "qemu/compiler.h" | ||
249 | @@ -XXX,XX +XXX,XX @@ int qemu_init_main_loop(Error **errp) | ||
250 | |||
251 | static void main_loop_update_params(EventLoopBase *base, Error **errp) | ||
252 | { | ||
253 | + ERRP_GUARD(); | ||
254 | + | ||
255 | if (!qemu_aio_context) { | ||
256 | error_setg(errp, "qemu aio context not ready"); | ||
63 | return; | 257 | return; |
64 | } | 258 | } |
65 | 259 | ||
66 | - bdrv_set_dirty_bitmap(backup_job->bcs->copy_bitmap, 0, backup_job->len); | 260 | aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp); |
67 | + bdrv_set_dirty_bitmap(block_copy_dirty_bitmap(backup_job->bcs), 0, | 261 | + if (*errp) { |
68 | + backup_job->len); | 262 | + return; |
69 | } | 263 | + } |
70 | 264 | + | |
71 | static BlockErrorAction backup_error_action(BackupBlockJob *job, | 265 | + aio_context_set_thread_pool_params(qemu_aio_context, base->thread_pool_min, |
72 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_loop(BackupBlockJob *job) | 266 | + base->thread_pool_max, errp); |
73 | BdrvDirtyBitmapIter *bdbi; | 267 | } |
74 | int ret = 0; | 268 | |
75 | 269 | MainLoop *mloop; | |
76 | - bdbi = bdrv_dirty_iter_new(job->bcs->copy_bitmap); | 270 | diff --git a/util/thread-pool.c b/util/thread-pool.c |
77 | + bdbi = bdrv_dirty_iter_new(block_copy_dirty_bitmap(job->bcs)); | 271 | index XXXXXXX..XXXXXXX 100644 |
78 | while ((offset = bdrv_dirty_iter_next(bdbi)) != -1) { | 272 | --- a/util/thread-pool.c |
79 | do { | 273 | +++ b/util/thread-pool.c |
80 | if (yield_and_check(job)) { | 274 | @@ -XXX,XX +XXX,XX @@ struct ThreadPool { |
81 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_loop(BackupBlockJob *job) | 275 | QemuMutex lock; |
82 | return ret; | 276 | QemuCond worker_stopped; |
83 | } | 277 | QemuSemaphore sem; |
84 | 278 | - int max_threads; | |
85 | -static void backup_init_copy_bitmap(BackupBlockJob *job) | 279 | QEMUBH *new_thread_bh; |
86 | +static void backup_init_bcs_bitmap(BackupBlockJob *job) | 280 | |
281 | /* The following variables are only accessed from one AioContext. */ | ||
282 | @@ -XXX,XX +XXX,XX @@ struct ThreadPool { | ||
283 | int new_threads; /* backlog of threads we need to create */ | ||
284 | int pending_threads; /* threads created but not running yet */ | ||
285 | bool stopping; | ||
286 | + int min_threads; | ||
287 | + int max_threads; | ||
288 | }; | ||
289 | |||
290 | +static inline bool back_to_sleep(ThreadPool *pool, int ret) | ||
291 | +{ | ||
292 | + /* | ||
293 | + * The semaphore timed out, we should exit the loop except when: | ||
294 | + * - There is work to do, we raced with the signal. | ||
295 | + * - The max threads threshold just changed, we raced with the signal. | ||
296 | + * - The thread pool forces a minimum number of readily available threads. | ||
297 | + */ | ||
298 | + if (ret == -1 && (!QTAILQ_EMPTY(&pool->request_list) || | ||
299 | + pool->cur_threads > pool->max_threads || | ||
300 | + pool->cur_threads <= pool->min_threads)) { | ||
301 | + return true; | ||
302 | + } | ||
303 | + | ||
304 | + return false; | ||
305 | +} | ||
306 | + | ||
307 | static void *worker_thread(void *opaque) | ||
87 | { | 308 | { |
88 | bool ret; | 309 | ThreadPool *pool = opaque; |
89 | uint64_t estimate; | 310 | @@ -XXX,XX +XXX,XX @@ static void *worker_thread(void *opaque) |
90 | + BdrvDirtyBitmap *bcs_bitmap = block_copy_dirty_bitmap(job->bcs); | 311 | ret = qemu_sem_timedwait(&pool->sem, 10000); |
91 | 312 | qemu_mutex_lock(&pool->lock); | |
92 | if (job->sync_mode == MIRROR_SYNC_MODE_BITMAP) { | 313 | pool->idle_threads--; |
93 | - ret = bdrv_dirty_bitmap_merge_internal(job->bcs->copy_bitmap, | 314 | - } while (ret == -1 && !QTAILQ_EMPTY(&pool->request_list)); |
94 | - job->sync_bitmap, | 315 | - if (ret == -1 || pool->stopping) { |
95 | + ret = bdrv_dirty_bitmap_merge_internal(bcs_bitmap, job->sync_bitmap, | 316 | + } while (back_to_sleep(pool, ret)); |
96 | NULL, true); | 317 | + if (ret == -1 || pool->stopping || |
97 | assert(ret); | 318 | + pool->cur_threads > pool->max_threads) { |
98 | } else { | 319 | break; |
99 | @@ -XXX,XX +XXX,XX @@ static void backup_init_copy_bitmap(BackupBlockJob *job) | ||
100 | * We can't hog the coroutine to initialize this thoroughly. | ||
101 | * Set a flag and resume work when we are able to yield safely. | ||
102 | */ | ||
103 | - job->bcs->skip_unallocated = true; | ||
104 | + block_copy_set_skip_unallocated(job->bcs, true); | ||
105 | } | 320 | } |
106 | - bdrv_set_dirty_bitmap(job->bcs->copy_bitmap, 0, job->len); | 321 | |
107 | + bdrv_set_dirty_bitmap(bcs_bitmap, 0, job->len); | 322 | @@ -XXX,XX +XXX,XX @@ void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg) |
108 | } | 323 | thread_pool_submit_aio(pool, func, arg, NULL, NULL); |
109 | 324 | } | |
110 | - estimate = bdrv_get_dirty_count(job->bcs->copy_bitmap); | 325 | |
111 | + estimate = bdrv_get_dirty_count(bcs_bitmap); | 326 | +void thread_pool_update_params(ThreadPool *pool, AioContext *ctx) |
112 | job_progress_set_remaining(&job->common.job, estimate); | 327 | +{ |
113 | } | 328 | + qemu_mutex_lock(&pool->lock); |
114 | 329 | + | |
115 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_run(Job *job, Error **errp) | 330 | + pool->min_threads = ctx->thread_pool_min; |
116 | BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); | 331 | + pool->max_threads = ctx->thread_pool_max; |
117 | int ret = 0; | 332 | + |
118 | |||
119 | - backup_init_copy_bitmap(s); | ||
120 | + backup_init_bcs_bitmap(s); | ||
121 | |||
122 | if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { | ||
123 | int64_t offset = 0; | ||
124 | @@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_run(Job *job, Error **errp) | ||
125 | |||
126 | offset += count; | ||
127 | } | ||
128 | - s->bcs->skip_unallocated = false; | ||
129 | + block_copy_set_skip_unallocated(s->bcs, false); | ||
130 | } | ||
131 | |||
132 | if (s->sync_mode == MIRROR_SYNC_MODE_NONE) { | ||
133 | /* | ||
134 | - * All bits are set in copy_bitmap to allow any cluster to be copied. | ||
135 | + * All bits are set in bcs bitmap to allow any cluster to be copied. | ||
136 | * This does not actually require them to be copied. | ||
137 | */ | ||
138 | while (!job_is_cancelled(job)) { | ||
139 | diff --git a/block/block-copy.c b/block/block-copy.c | ||
140 | index XXXXXXX..XXXXXXX 100644 | ||
141 | --- a/block/block-copy.c | ||
142 | +++ b/block/block-copy.c | ||
143 | @@ -XXX,XX +XXX,XX @@ | ||
144 | #define BLOCK_COPY_MAX_BUFFER (1 * MiB) | ||
145 | #define BLOCK_COPY_MAX_MEM (128 * MiB) | ||
146 | |||
147 | +typedef struct BlockCopyInFlightReq { | ||
148 | + int64_t offset; | ||
149 | + int64_t bytes; | ||
150 | + QLIST_ENTRY(BlockCopyInFlightReq) list; | ||
151 | + CoQueue wait_queue; /* coroutines blocked on this request */ | ||
152 | +} BlockCopyInFlightReq; | ||
153 | + | ||
154 | +typedef struct BlockCopyState { | ||
155 | + /* | 333 | + /* |
156 | + * BdrvChild objects are not owned or managed by block-copy. They are | 334 | + * We either have to: |
157 | + * provided by block-copy user and user is responsible for appropriate | 335 | + * - Increase the number available of threads until over the min_threads |
158 | + * permissions on these children. | 336 | + * threshold. |
337 | + * - Decrease the number of available threads until under the max_threads | ||
338 | + * threshold. | ||
339 | + * - Do nothing. The current number of threads fall in between the min and | ||
340 | + * max thresholds. We'll let the pool manage itself. | ||
159 | + */ | 341 | + */ |
160 | + BdrvChild *source; | 342 | + for (int i = pool->cur_threads; i < pool->min_threads; i++) { |
161 | + BdrvChild *target; | 343 | + spawn_thread(pool); |
162 | + BdrvDirtyBitmap *copy_bitmap; | 344 | + } |
163 | + int64_t in_flight_bytes; | 345 | + |
164 | + int64_t cluster_size; | 346 | + for (int i = pool->cur_threads; i > pool->max_threads; i--) { |
165 | + bool use_copy_range; | 347 | + qemu_sem_post(&pool->sem); |
166 | + int64_t copy_size; | 348 | + } |
167 | + uint64_t len; | 349 | + |
168 | + QLIST_HEAD(, BlockCopyInFlightReq) inflight_reqs; | 350 | + qemu_mutex_unlock(&pool->lock); |
169 | + | ||
170 | + BdrvRequestFlags write_flags; | ||
171 | + | ||
172 | + /* | ||
173 | + * skip_unallocated: | ||
174 | + * | ||
175 | + * Used by sync=top jobs, which first scan the source node for unallocated | ||
176 | + * areas and clear them in the copy_bitmap. During this process, the bitmap | ||
177 | + * is thus not fully initialized: It may still have bits set for areas that | ||
178 | + * are unallocated and should actually not be copied. | ||
179 | + * | ||
180 | + * This is indicated by skip_unallocated. | ||
181 | + * | ||
182 | + * In this case, block_copy() will query the source’s allocation status, | ||
183 | + * skip unallocated regions, clear them in the copy_bitmap, and invoke | ||
184 | + * block_copy_reset_unallocated() every time it does. | ||
185 | + */ | ||
186 | + bool skip_unallocated; | ||
187 | + | ||
188 | + ProgressMeter *progress; | ||
189 | + /* progress_bytes_callback: called when some copying progress is done. */ | ||
190 | + ProgressBytesCallbackFunc progress_bytes_callback; | ||
191 | + void *progress_opaque; | ||
192 | + | ||
193 | + SharedResource *mem; | ||
194 | +} BlockCopyState; | ||
195 | + | ||
196 | static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s, | ||
197 | int64_t offset, | ||
198 | int64_t bytes) | ||
199 | @@ -XXX,XX +XXX,XX @@ int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes, | ||
200 | |||
201 | return ret; | ||
202 | } | ||
203 | + | ||
204 | +BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s) | ||
205 | +{ | ||
206 | + return s->copy_bitmap; | ||
207 | +} | 351 | +} |
208 | + | 352 | + |
209 | +void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip) | 353 | static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx) |
210 | +{ | 354 | { |
211 | + s->skip_unallocated = skip; | 355 | if (!ctx) { |
212 | +} | 356 | @@ -XXX,XX +XXX,XX @@ static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx) |
213 | diff --git a/include/block/block-copy.h b/include/block/block-copy.h | 357 | qemu_mutex_init(&pool->lock); |
214 | index XXXXXXX..XXXXXXX 100644 | 358 | qemu_cond_init(&pool->worker_stopped); |
215 | --- a/include/block/block-copy.h | 359 | qemu_sem_init(&pool->sem, 0); |
216 | +++ b/include/block/block-copy.h | 360 | - pool->max_threads = 64; |
217 | @@ -XXX,XX +XXX,XX @@ | 361 | pool->new_thread_bh = aio_bh_new(ctx, spawn_thread_bh_fn, pool); |
218 | #include "block/block.h" | 362 | |
219 | #include "qemu/co-shared-resource.h" | 363 | QLIST_INIT(&pool->head); |
220 | 364 | QTAILQ_INIT(&pool->request_list); | |
221 | -typedef struct BlockCopyInFlightReq { | 365 | + |
222 | - int64_t offset; | 366 | + thread_pool_update_params(pool, ctx); |
223 | - int64_t bytes; | 367 | } |
224 | - QLIST_ENTRY(BlockCopyInFlightReq) list; | 368 | |
225 | - CoQueue wait_queue; /* coroutines blocked on this request */ | 369 | ThreadPool *thread_pool_new(AioContext *ctx) |
226 | -} BlockCopyInFlightReq; | ||
227 | - | ||
228 | typedef void (*ProgressBytesCallbackFunc)(int64_t bytes, void *opaque); | ||
229 | -typedef struct BlockCopyState { | ||
230 | - /* | ||
231 | - * BdrvChild objects are not owned or managed by block-copy. They are | ||
232 | - * provided by block-copy user and user is responsible for appropriate | ||
233 | - * permissions on these children. | ||
234 | - */ | ||
235 | - BdrvChild *source; | ||
236 | - BdrvChild *target; | ||
237 | - BdrvDirtyBitmap *copy_bitmap; | ||
238 | - int64_t in_flight_bytes; | ||
239 | - int64_t cluster_size; | ||
240 | - bool use_copy_range; | ||
241 | - int64_t copy_size; | ||
242 | - uint64_t len; | ||
243 | - QLIST_HEAD(, BlockCopyInFlightReq) inflight_reqs; | ||
244 | - | ||
245 | - BdrvRequestFlags write_flags; | ||
246 | - | ||
247 | - /* | ||
248 | - * skip_unallocated: | ||
249 | - * | ||
250 | - * Used by sync=top jobs, which first scan the source node for unallocated | ||
251 | - * areas and clear them in the copy_bitmap. During this process, the bitmap | ||
252 | - * is thus not fully initialized: It may still have bits set for areas that | ||
253 | - * are unallocated and should actually not be copied. | ||
254 | - * | ||
255 | - * This is indicated by skip_unallocated. | ||
256 | - * | ||
257 | - * In this case, block_copy() will query the source’s allocation status, | ||
258 | - * skip unallocated regions, clear them in the copy_bitmap, and invoke | ||
259 | - * block_copy_reset_unallocated() every time it does. | ||
260 | - */ | ||
261 | - bool skip_unallocated; | ||
262 | - | ||
263 | - ProgressMeter *progress; | ||
264 | - /* progress_bytes_callback: called when some copying progress is done. */ | ||
265 | - ProgressBytesCallbackFunc progress_bytes_callback; | ||
266 | - void *progress_opaque; | ||
267 | - | ||
268 | - SharedResource *mem; | ||
269 | -} BlockCopyState; | ||
270 | +typedef struct BlockCopyState BlockCopyState; | ||
271 | |||
272 | BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target, | ||
273 | int64_t cluster_size, | ||
274 | @@ -XXX,XX +XXX,XX @@ int64_t block_copy_reset_unallocated(BlockCopyState *s, | ||
275 | int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes, | ||
276 | bool *error_is_read); | ||
277 | |||
278 | +BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s); | ||
279 | +void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip); | ||
280 | + | ||
281 | #endif /* BLOCK_COPY_H */ | ||
282 | -- | 370 | -- |
283 | 2.24.1 | 371 | 2.35.1 |
284 | |||
285 | diff view generated by jsdifflib |