2 * Multifd zlib compression implementation
4 * Copyright (c) 2020 Red Hat Inc
7 * Juan Quintela <quintela@redhat.com>
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
13 #include "qemu/osdep.h"
16 #include "exec/ramblock.h"
17 #include "exec/target_page.h"
18 #include "qapi/error.h"
19 #include "migration.h"
25 /* stream for compression */
27 /* compressed buffer */
29 /* size of compressed buffer */
31 /* uncompressed buffer of size qemu_target_page_size() */
35 /* Multifd zlib compression */
37 static int multifd_zlib_send_setup(MultiFDSendParams
*p
, Error
**errp
)
39 struct zlib_data
*z
= g_new0(struct zlib_data
, 1);
40 z_stream
*zs
= &z
->zs
;
46 if (deflateInit(zs
, migrate_multifd_zlib_level()) != Z_OK
) {
47 err_msg
= "deflate init failed";
50 /* This is the maximum size of the compressed buffer */
51 z
->zbuff_len
= compressBound(MULTIFD_PACKET_SIZE
);
52 z
->zbuff
= g_try_malloc(z
->zbuff_len
);
54 err_msg
= "out of memory for zbuff";
57 z
->buf
= g_try_malloc(qemu_target_page_size());
59 err_msg
= "out of memory for buf";
64 /* Needs 2 IOVs, one for packet header and one for compressed data */
65 p
->iov
= g_new0(struct iovec
, 2);
75 error_setg(errp
, "multifd %u: %s", p
->id
, err_msg
);
79 static void multifd_zlib_send_cleanup(MultiFDSendParams
*p
, Error
**errp
)
81 struct zlib_data
*z
= p
->compress_data
;
88 g_free(p
->compress_data
);
89 p
->compress_data
= NULL
;
95 static int multifd_zlib_send_prepare(MultiFDSendParams
*p
, Error
**errp
)
97 MultiFDPages_t
*pages
= &p
->data
->u
.ram
;
98 struct zlib_data
*z
= p
->compress_data
;
99 z_stream
*zs
= &z
->zs
;
100 uint32_t out_size
= 0;
101 uint32_t page_size
= multifd_ram_page_size();
105 if (!multifd_send_prepare_common(p
)) {
109 for (i
= 0; i
< pages
->normal_num
; i
++) {
110 uint32_t available
= z
->zbuff_len
- out_size
;
111 int flush
= Z_NO_FLUSH
;
113 if (i
== pages
->normal_num
- 1) {
114 flush
= Z_SYNC_FLUSH
;
118 * Since the VM might be running, the page may be changing concurrently
119 * with compression. zlib does not guarantee that this is safe,
120 * therefore copy the page before calling deflate().
122 memcpy(z
->buf
, pages
->block
->host
+ pages
->offset
[i
], page_size
);
123 zs
->avail_in
= page_size
;
124 zs
->next_in
= z
->buf
;
126 zs
->avail_out
= available
;
127 zs
->next_out
= z
->zbuff
+ out_size
;
130 * Welcome to deflate semantics
132 * We need to loop while:
134 * - there are stuff to be compressed
135 * - there are output space free
138 ret
= deflate(zs
, flush
);
139 } while (ret
== Z_OK
&& zs
->avail_in
&& zs
->avail_out
);
140 if (ret
== Z_OK
&& zs
->avail_in
) {
141 error_setg(errp
, "multifd %u: deflate failed to compress all input",
146 error_setg(errp
, "multifd %u: deflate returned %d instead of Z_OK",
150 out_size
+= available
- zs
->avail_out
;
152 p
->iov
[p
->iovs_num
].iov_base
= z
->zbuff
;
153 p
->iov
[p
->iovs_num
].iov_len
= out_size
;
155 p
->next_packet_size
= out_size
;
158 p
->flags
|= MULTIFD_FLAG_ZLIB
;
159 multifd_send_fill_packet(p
);
163 static int multifd_zlib_recv_setup(MultiFDRecvParams
*p
, Error
**errp
)
165 struct zlib_data
*z
= g_new0(struct zlib_data
, 1);
166 z_stream
*zs
= &z
->zs
;
168 p
->compress_data
= z
;
173 zs
->next_in
= Z_NULL
;
174 if (inflateInit(zs
) != Z_OK
) {
175 error_setg(errp
, "multifd %u: inflate init failed", p
->id
);
178 /* To be safe, we reserve twice the size of the packet */
179 z
->zbuff_len
= MULTIFD_PACKET_SIZE
* 2;
180 z
->zbuff
= g_try_malloc(z
->zbuff_len
);
183 error_setg(errp
, "multifd %u: out of memory for zbuff", p
->id
);
189 static void multifd_zlib_recv_cleanup(MultiFDRecvParams
*p
)
191 struct zlib_data
*z
= p
->compress_data
;
196 g_free(p
->compress_data
);
197 p
->compress_data
= NULL
;
200 static int multifd_zlib_recv(MultiFDRecvParams
*p
, Error
**errp
)
202 struct zlib_data
*z
= p
->compress_data
;
203 z_stream
*zs
= &z
->zs
;
204 uint32_t in_size
= p
->next_packet_size
;
205 /* we measure the change of total_out */
206 uint32_t out_size
= zs
->total_out
;
207 uint32_t page_size
= multifd_ram_page_size();
208 uint32_t expected_size
= p
->normal_num
* page_size
;
209 uint32_t flags
= p
->flags
& MULTIFD_FLAG_COMPRESSION_MASK
;
213 if (flags
!= MULTIFD_FLAG_ZLIB
) {
214 error_setg(errp
, "multifd %u: flags received %x flags expected %x",
215 p
->id
, flags
, MULTIFD_FLAG_ZLIB
);
219 multifd_recv_zero_page_process(p
);
221 if (!p
->normal_num
) {
222 assert(in_size
== 0);
226 ret
= qio_channel_read_all(p
->c
, (void *)z
->zbuff
, in_size
, errp
);
232 zs
->avail_in
= in_size
;
233 zs
->next_in
= z
->zbuff
;
235 for (i
= 0; i
< p
->normal_num
; i
++) {
236 int flush
= Z_NO_FLUSH
;
237 unsigned long start
= zs
->total_out
;
239 ramblock_recv_bitmap_set_offset(p
->block
, p
->normal
[i
]);
240 if (i
== p
->normal_num
- 1) {
241 flush
= Z_SYNC_FLUSH
;
244 zs
->avail_out
= page_size
;
245 zs
->next_out
= p
->host
+ p
->normal
[i
];
248 * Welcome to inflate semantics
250 * We need to loop while:
252 * - there are input available
253 * - we haven't completed a full page
256 ret
= inflate(zs
, flush
);
257 } while (ret
== Z_OK
&& zs
->avail_in
258 && (zs
->total_out
- start
) < page_size
);
259 if (ret
== Z_OK
&& (zs
->total_out
- start
) < page_size
) {
260 error_setg(errp
, "multifd %u: inflate generated too few output",
265 error_setg(errp
, "multifd %u: inflate returned %d instead of Z_OK",
270 out_size
= zs
->total_out
- out_size
;
271 if (out_size
!= expected_size
) {
272 error_setg(errp
, "multifd %u: packet size received %u size expected %u",
273 p
->id
, out_size
, expected_size
);
280 static const MultiFDMethods multifd_zlib_ops
= {
281 .send_setup
= multifd_zlib_send_setup
,
282 .send_cleanup
= multifd_zlib_send_cleanup
,
283 .send_prepare
= multifd_zlib_send_prepare
,
284 .recv_setup
= multifd_zlib_recv_setup
,
285 .recv_cleanup
= multifd_zlib_recv_cleanup
,
286 .recv
= multifd_zlib_recv
289 static void multifd_zlib_register(void)
291 multifd_register_ops(MULTIFD_COMPRESSION_ZLIB
, &multifd_zlib_ops
);
294 migration_init(multifd_zlib_register
);