xref: /qemu/migration/ram-compress.c (revision 8258f2fa)
1b5ca3368SLukas Straub /*
2b5ca3368SLukas Straub  * QEMU System Emulator
3b5ca3368SLukas Straub  *
4b5ca3368SLukas Straub  * Copyright (c) 2003-2008 Fabrice Bellard
5b5ca3368SLukas Straub  * Copyright (c) 2011-2015 Red Hat Inc
6b5ca3368SLukas Straub  *
7b5ca3368SLukas Straub  * Authors:
8b5ca3368SLukas Straub  *  Juan Quintela <quintela@redhat.com>
9b5ca3368SLukas Straub  *
10b5ca3368SLukas Straub  * Permission is hereby granted, free of charge, to any person obtaining a copy
11b5ca3368SLukas Straub  * of this software and associated documentation files (the "Software"), to deal
12b5ca3368SLukas Straub  * in the Software without restriction, including without limitation the rights
13b5ca3368SLukas Straub  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14b5ca3368SLukas Straub  * copies of the Software, and to permit persons to whom the Software is
15b5ca3368SLukas Straub  * furnished to do so, subject to the following conditions:
16b5ca3368SLukas Straub  *
17b5ca3368SLukas Straub  * The above copyright notice and this permission notice shall be included in
18b5ca3368SLukas Straub  * all copies or substantial portions of the Software.
19b5ca3368SLukas Straub  *
20b5ca3368SLukas Straub  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21b5ca3368SLukas Straub  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22b5ca3368SLukas Straub  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23b5ca3368SLukas Straub  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24b5ca3368SLukas Straub  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25b5ca3368SLukas Straub  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26b5ca3368SLukas Straub  * THE SOFTWARE.
27b5ca3368SLukas Straub  */
28b5ca3368SLukas Straub 
29b5ca3368SLukas Straub #include "qemu/osdep.h"
30b5ca3368SLukas Straub #include "qemu/cutils.h"
31b5ca3368SLukas Straub 
32b5ca3368SLukas Straub #include "ram-compress.h"
33b5ca3368SLukas Straub 
34b5ca3368SLukas Straub #include "qemu/error-report.h"
351fd03d41SJuan Quintela #include "qemu/stats64.h"
36b5ca3368SLukas Straub #include "migration.h"
37b5ca3368SLukas Straub #include "options.h"
38b5ca3368SLukas Straub #include "io/channel-null.h"
3952623f23SLukas Straub #include "exec/target_page.h"
4052623f23SLukas Straub #include "exec/ramblock.h"
411fd03d41SJuan Quintela #include "ram.h"
421fd03d41SJuan Quintela #include "migration-stats.h"
43b5ca3368SLukas Straub 
44fb36fb27SJuan Quintela static struct {
45fb36fb27SJuan Quintela     int64_t pages;
46fb36fb27SJuan Quintela     int64_t busy;
47fb36fb27SJuan Quintela     double busy_rate;
48fb36fb27SJuan Quintela     int64_t compressed_size;
49fb36fb27SJuan Quintela     double compression_rate;
50fb36fb27SJuan Quintela     /* compression statistics since the beginning of the period */
51fb36fb27SJuan Quintela     /* amount of count that no free thread to compress data */
52fb36fb27SJuan Quintela     uint64_t compress_thread_busy_prev;
53fb36fb27SJuan Quintela     /* amount bytes after compression */
54fb36fb27SJuan Quintela     uint64_t compressed_size_prev;
55fb36fb27SJuan Quintela     /* amount of compressed pages */
56fb36fb27SJuan Quintela     uint64_t compress_pages_prev;
57fb36fb27SJuan Quintela } compression_counters;
58b5ca3368SLukas Straub 
59b5ca3368SLukas Straub static CompressParam *comp_param;
60b5ca3368SLukas Straub static QemuThread *compress_threads;
61b5ca3368SLukas Straub /* comp_done_cond is used to wake up the migration thread when
62b5ca3368SLukas Straub  * one of the compression threads has finished the compression.
63b5ca3368SLukas Straub  * comp_done_lock is used to co-work with comp_done_cond.
64b5ca3368SLukas Straub  */
65b5ca3368SLukas Straub static QemuMutex comp_done_lock;
66b5ca3368SLukas Straub static QemuCond comp_done_cond;
67b5ca3368SLukas Straub 
68b1f17720SLukas Straub struct DecompressParam {
69b1f17720SLukas Straub     bool done;
70b1f17720SLukas Straub     bool quit;
71b1f17720SLukas Straub     QemuMutex mutex;
72b1f17720SLukas Straub     QemuCond cond;
73b1f17720SLukas Straub     void *des;
74b1f17720SLukas Straub     uint8_t *compbuf;
75b1f17720SLukas Straub     int len;
76b1f17720SLukas Straub     z_stream stream;
77b1f17720SLukas Straub };
78b1f17720SLukas Straub typedef struct DecompressParam DecompressParam;
79b1f17720SLukas Straub 
80b1f17720SLukas Straub static QEMUFile *decomp_file;
81b1f17720SLukas Straub static DecompressParam *decomp_param;
82b1f17720SLukas Straub static QemuThread *decompress_threads;
83b1f17720SLukas Straub static QemuMutex decomp_done_lock;
84b1f17720SLukas Straub static QemuCond decomp_done_cond;
85b1f17720SLukas Straub 
86b5ca3368SLukas Straub static CompressResult do_compress_ram_page(QEMUFile *f, z_stream *stream,
87b5ca3368SLukas Straub                                            RAMBlock *block, ram_addr_t offset,
88b5ca3368SLukas Straub                                            uint8_t *source_buf);
89b5ca3368SLukas Straub 
do_data_compress(void * opaque)90b5ca3368SLukas Straub static void *do_data_compress(void *opaque)
91b5ca3368SLukas Straub {
92b5ca3368SLukas Straub     CompressParam *param = opaque;
93b5ca3368SLukas Straub     RAMBlock *block;
94b5ca3368SLukas Straub     ram_addr_t offset;
95b5ca3368SLukas Straub     CompressResult result;
96b5ca3368SLukas Straub 
97b5ca3368SLukas Straub     qemu_mutex_lock(&param->mutex);
98b5ca3368SLukas Straub     while (!param->quit) {
99b5ca3368SLukas Straub         if (param->trigger) {
100b5ca3368SLukas Straub             block = param->block;
101b5ca3368SLukas Straub             offset = param->offset;
102b5ca3368SLukas Straub             param->trigger = false;
103b5ca3368SLukas Straub             qemu_mutex_unlock(&param->mutex);
104b5ca3368SLukas Straub 
105b5ca3368SLukas Straub             result = do_compress_ram_page(param->file, &param->stream,
106b5ca3368SLukas Straub                                           block, offset, param->originbuf);
107b5ca3368SLukas Straub 
108b5ca3368SLukas Straub             qemu_mutex_lock(&comp_done_lock);
109b5ca3368SLukas Straub             param->done = true;
110b5ca3368SLukas Straub             param->result = result;
111b5ca3368SLukas Straub             qemu_cond_signal(&comp_done_cond);
112b5ca3368SLukas Straub             qemu_mutex_unlock(&comp_done_lock);
113b5ca3368SLukas Straub 
114b5ca3368SLukas Straub             qemu_mutex_lock(&param->mutex);
115b5ca3368SLukas Straub         } else {
116b5ca3368SLukas Straub             qemu_cond_wait(&param->cond, &param->mutex);
117b5ca3368SLukas Straub         }
118b5ca3368SLukas Straub     }
119b5ca3368SLukas Straub     qemu_mutex_unlock(&param->mutex);
120b5ca3368SLukas Straub 
121b5ca3368SLukas Straub     return NULL;
122b5ca3368SLukas Straub }
123b5ca3368SLukas Straub 
compress_threads_save_cleanup(void)124b5ca3368SLukas Straub void compress_threads_save_cleanup(void)
125b5ca3368SLukas Straub {
126b5ca3368SLukas Straub     int i, thread_count;
127b5ca3368SLukas Straub 
128b5ca3368SLukas Straub     if (!migrate_compress() || !comp_param) {
129b5ca3368SLukas Straub         return;
130b5ca3368SLukas Straub     }
131b5ca3368SLukas Straub 
132b5ca3368SLukas Straub     thread_count = migrate_compress_threads();
133b5ca3368SLukas Straub     for (i = 0; i < thread_count; i++) {
134b5ca3368SLukas Straub         /*
135b5ca3368SLukas Straub          * we use it as a indicator which shows if the thread is
136b5ca3368SLukas Straub          * properly init'd or not
137b5ca3368SLukas Straub          */
138b5ca3368SLukas Straub         if (!comp_param[i].file) {
139b5ca3368SLukas Straub             break;
140b5ca3368SLukas Straub         }
141b5ca3368SLukas Straub 
142b5ca3368SLukas Straub         qemu_mutex_lock(&comp_param[i].mutex);
143b5ca3368SLukas Straub         comp_param[i].quit = true;
144b5ca3368SLukas Straub         qemu_cond_signal(&comp_param[i].cond);
145b5ca3368SLukas Straub         qemu_mutex_unlock(&comp_param[i].mutex);
146b5ca3368SLukas Straub 
147b5ca3368SLukas Straub         qemu_thread_join(compress_threads + i);
148b5ca3368SLukas Straub         qemu_mutex_destroy(&comp_param[i].mutex);
149b5ca3368SLukas Straub         qemu_cond_destroy(&comp_param[i].cond);
150b5ca3368SLukas Straub         deflateEnd(&comp_param[i].stream);
151b5ca3368SLukas Straub         g_free(comp_param[i].originbuf);
152b5ca3368SLukas Straub         qemu_fclose(comp_param[i].file);
153b5ca3368SLukas Straub         comp_param[i].file = NULL;
154b5ca3368SLukas Straub     }
155b5ca3368SLukas Straub     qemu_mutex_destroy(&comp_done_lock);
156b5ca3368SLukas Straub     qemu_cond_destroy(&comp_done_cond);
157b5ca3368SLukas Straub     g_free(compress_threads);
158b5ca3368SLukas Straub     g_free(comp_param);
159b5ca3368SLukas Straub     compress_threads = NULL;
160b5ca3368SLukas Straub     comp_param = NULL;
161b5ca3368SLukas Straub }
162b5ca3368SLukas Straub 
compress_threads_save_setup(void)163b5ca3368SLukas Straub int compress_threads_save_setup(void)
164b5ca3368SLukas Straub {
165b5ca3368SLukas Straub     int i, thread_count;
166b5ca3368SLukas Straub 
167b5ca3368SLukas Straub     if (!migrate_compress()) {
168b5ca3368SLukas Straub         return 0;
169b5ca3368SLukas Straub     }
170b5ca3368SLukas Straub     thread_count = migrate_compress_threads();
171b5ca3368SLukas Straub     compress_threads = g_new0(QemuThread, thread_count);
172b5ca3368SLukas Straub     comp_param = g_new0(CompressParam, thread_count);
173b5ca3368SLukas Straub     qemu_cond_init(&comp_done_cond);
174b5ca3368SLukas Straub     qemu_mutex_init(&comp_done_lock);
175b5ca3368SLukas Straub     for (i = 0; i < thread_count; i++) {
17652623f23SLukas Straub         comp_param[i].originbuf = g_try_malloc(qemu_target_page_size());
177b5ca3368SLukas Straub         if (!comp_param[i].originbuf) {
178b5ca3368SLukas Straub             goto exit;
179b5ca3368SLukas Straub         }
180b5ca3368SLukas Straub 
181b5ca3368SLukas Straub         if (deflateInit(&comp_param[i].stream,
182b5ca3368SLukas Straub                         migrate_compress_level()) != Z_OK) {
183b5ca3368SLukas Straub             g_free(comp_param[i].originbuf);
184b5ca3368SLukas Straub             goto exit;
185b5ca3368SLukas Straub         }
186b5ca3368SLukas Straub 
187b5ca3368SLukas Straub         /* comp_param[i].file is just used as a dummy buffer to save data,
188b5ca3368SLukas Straub          * set its ops to empty.
189b5ca3368SLukas Straub          */
190b5ca3368SLukas Straub         comp_param[i].file = qemu_file_new_output(
191b5ca3368SLukas Straub             QIO_CHANNEL(qio_channel_null_new()));
192b5ca3368SLukas Straub         comp_param[i].done = true;
193b5ca3368SLukas Straub         comp_param[i].quit = false;
194b5ca3368SLukas Straub         qemu_mutex_init(&comp_param[i].mutex);
195b5ca3368SLukas Straub         qemu_cond_init(&comp_param[i].cond);
196b5ca3368SLukas Straub         qemu_thread_create(compress_threads + i, "compress",
197b5ca3368SLukas Straub                            do_data_compress, comp_param + i,
198b5ca3368SLukas Straub                            QEMU_THREAD_JOINABLE);
199b5ca3368SLukas Straub     }
200b5ca3368SLukas Straub     return 0;
201b5ca3368SLukas Straub 
202b5ca3368SLukas Straub exit:
203b5ca3368SLukas Straub     compress_threads_save_cleanup();
204b5ca3368SLukas Straub     return -1;
205b5ca3368SLukas Straub }
206b5ca3368SLukas Straub 
do_compress_ram_page(QEMUFile * f,z_stream * stream,RAMBlock * block,ram_addr_t offset,uint8_t * source_buf)207b5ca3368SLukas Straub static CompressResult do_compress_ram_page(QEMUFile *f, z_stream *stream,
208b5ca3368SLukas Straub                                            RAMBlock *block, ram_addr_t offset,
209b5ca3368SLukas Straub                                            uint8_t *source_buf)
210b5ca3368SLukas Straub {
211b5ca3368SLukas Straub     uint8_t *p = block->host + offset;
21252623f23SLukas Straub     size_t page_size = qemu_target_page_size();
213b5ca3368SLukas Straub     int ret;
214b5ca3368SLukas Straub 
2154024cc85SLukas Straub     assert(qemu_file_buffer_empty(f));
2164024cc85SLukas Straub 
21752623f23SLukas Straub     if (buffer_is_zero(p, page_size)) {
218b5ca3368SLukas Straub         return RES_ZEROPAGE;
219b5ca3368SLukas Straub     }
220b5ca3368SLukas Straub 
221b5ca3368SLukas Straub     /*
222b5ca3368SLukas Straub      * copy it to a internal buffer to avoid it being modified by VM
223b5ca3368SLukas Straub      * so that we can catch up the error during compression and
224b5ca3368SLukas Straub      * decompression
225b5ca3368SLukas Straub      */
22652623f23SLukas Straub     memcpy(source_buf, p, page_size);
22752623f23SLukas Straub     ret = qemu_put_compression_data(f, stream, source_buf, page_size);
228b5ca3368SLukas Straub     if (ret < 0) {
229b5ca3368SLukas Straub         qemu_file_set_error(migrate_get_current()->to_dst_file, ret);
230b5ca3368SLukas Straub         error_report("compressed data failed!");
2314024cc85SLukas Straub         qemu_fflush(f);
232b5ca3368SLukas Straub         return RES_NONE;
233b5ca3368SLukas Straub     }
234b5ca3368SLukas Straub     return RES_COMPRESS;
235b5ca3368SLukas Straub }
236b5ca3368SLukas Straub 
compress_reset_result(CompressParam * param)237b5ca3368SLukas Straub static inline void compress_reset_result(CompressParam *param)
238b5ca3368SLukas Straub {
239b5ca3368SLukas Straub     param->result = RES_NONE;
240b5ca3368SLukas Straub     param->block = NULL;
241b5ca3368SLukas Straub     param->offset = 0;
242b5ca3368SLukas Straub }
243b5ca3368SLukas Straub 
compress_flush_data(void)244f639cfe5SJuan Quintela void compress_flush_data(void)
245b5ca3368SLukas Straub {
246bef4e2edSJuan Quintela     int thread_count = migrate_compress_threads();
247b5ca3368SLukas Straub 
248f639cfe5SJuan Quintela     if (!migrate_compress()) {
249f639cfe5SJuan Quintela         return;
250f639cfe5SJuan Quintela     }
251f639cfe5SJuan Quintela 
252b5ca3368SLukas Straub     qemu_mutex_lock(&comp_done_lock);
253bef4e2edSJuan Quintela     for (int i = 0; i < thread_count; i++) {
254bef4e2edSJuan Quintela         while (!comp_param[i].done) {
255b5ca3368SLukas Straub             qemu_cond_wait(&comp_done_cond, &comp_done_lock);
256b5ca3368SLukas Straub         }
257b5ca3368SLukas Straub     }
258b5ca3368SLukas Straub     qemu_mutex_unlock(&comp_done_lock);
259b5ca3368SLukas Straub 
260bef4e2edSJuan Quintela     for (int i = 0; i < thread_count; i++) {
261bef4e2edSJuan Quintela         qemu_mutex_lock(&comp_param[i].mutex);
262bef4e2edSJuan Quintela         if (!comp_param[i].quit) {
263bef4e2edSJuan Quintela             CompressParam *param = &comp_param[i];
264f639cfe5SJuan Quintela             compress_send_queued_data(param);
2654024cc85SLukas Straub             assert(qemu_file_buffer_empty(param->file));
266b5ca3368SLukas Straub             compress_reset_result(param);
267b5ca3368SLukas Straub         }
268bef4e2edSJuan Quintela         qemu_mutex_unlock(&comp_param[i].mutex);
269b5ca3368SLukas Straub     }
270b5ca3368SLukas Straub }
271b5ca3368SLukas Straub 
set_compress_params(CompressParam * param,RAMBlock * block,ram_addr_t offset)272b5ca3368SLukas Straub static inline void set_compress_params(CompressParam *param, RAMBlock *block,
273b5ca3368SLukas Straub                                        ram_addr_t offset)
274b5ca3368SLukas Straub {
275b5ca3368SLukas Straub     param->block = block;
276b5ca3368SLukas Straub     param->offset = offset;
277b5ca3368SLukas Straub     param->trigger = true;
278b5ca3368SLukas Straub }
279b5ca3368SLukas Straub 
28083df387dSJuan Quintela /*
28183df387dSJuan Quintela  * Return true when it compress a page
28283df387dSJuan Quintela  */
compress_page_with_multi_thread(RAMBlock * block,ram_addr_t offset,int (send_queued_data (CompressParam *)))28383df387dSJuan Quintela bool compress_page_with_multi_thread(RAMBlock *block, ram_addr_t offset,
284b5ca3368SLukas Straub                                      int (send_queued_data(CompressParam *)))
285b5ca3368SLukas Straub {
28683df387dSJuan Quintela     int thread_count;
287b5ca3368SLukas Straub     bool wait = migrate_compress_wait_thread();
288b5ca3368SLukas Straub 
289b5ca3368SLukas Straub     thread_count = migrate_compress_threads();
290b5ca3368SLukas Straub     qemu_mutex_lock(&comp_done_lock);
291b6e19b6dSJuan Quintela 
292b6e19b6dSJuan Quintela     while (true) {
293bef4e2edSJuan Quintela         for (int i = 0; i < thread_count; i++) {
294bef4e2edSJuan Quintela             if (comp_param[i].done) {
295bef4e2edSJuan Quintela                 CompressParam *param = &comp_param[i];
296b5ca3368SLukas Straub                 qemu_mutex_lock(&param->mutex);
297b5ca3368SLukas Straub                 param->done = false;
298b5ca3368SLukas Straub                 send_queued_data(param);
2994024cc85SLukas Straub                 assert(qemu_file_buffer_empty(param->file));
300b5ca3368SLukas Straub                 compress_reset_result(param);
301b5ca3368SLukas Straub                 set_compress_params(param, block, offset);
302b5ca3368SLukas Straub 
303b5ca3368SLukas Straub                 qemu_cond_signal(&param->cond);
304b5ca3368SLukas Straub                 qemu_mutex_unlock(&param->mutex);
30583df387dSJuan Quintela                 qemu_mutex_unlock(&comp_done_lock);
30683df387dSJuan Quintela                 return true;
307b5ca3368SLukas Straub             }
308b5ca3368SLukas Straub         }
309b6e19b6dSJuan Quintela         if (!wait) {
310b5ca3368SLukas Straub             qemu_mutex_unlock(&comp_done_lock);
311250b1d7eSJuan Quintela             compression_counters.busy++;
31283df387dSJuan Quintela             return false;
313b5ca3368SLukas Straub         }
314b6e19b6dSJuan Quintela         /*
315b6e19b6dSJuan Quintela          * wait for a free thread if the user specifies
316b6e19b6dSJuan Quintela          * 'compress-wait-thread', otherwise we will post the page out
317b6e19b6dSJuan Quintela          * in the main thread as normal page.
318b6e19b6dSJuan Quintela          */
319b6e19b6dSJuan Quintela         qemu_cond_wait(&comp_done_cond, &comp_done_lock);
320b6e19b6dSJuan Quintela     }
321b6e19b6dSJuan Quintela }
322b1f17720SLukas Straub 
323b1f17720SLukas Straub /* return the size after decompression, or negative value on error */
324b1f17720SLukas Straub static int
qemu_uncompress_data(z_stream * stream,uint8_t * dest,size_t dest_len,const uint8_t * source,size_t source_len)325b1f17720SLukas Straub qemu_uncompress_data(z_stream *stream, uint8_t *dest, size_t dest_len,
326b1f17720SLukas Straub                      const uint8_t *source, size_t source_len)
327b1f17720SLukas Straub {
328b1f17720SLukas Straub     int err;
329b1f17720SLukas Straub 
330b1f17720SLukas Straub     err = inflateReset(stream);
331b1f17720SLukas Straub     if (err != Z_OK) {
332b1f17720SLukas Straub         return -1;
333b1f17720SLukas Straub     }
334b1f17720SLukas Straub 
335b1f17720SLukas Straub     stream->avail_in = source_len;
336b1f17720SLukas Straub     stream->next_in = (uint8_t *)source;
337b1f17720SLukas Straub     stream->avail_out = dest_len;
338b1f17720SLukas Straub     stream->next_out = dest;
339b1f17720SLukas Straub 
340b1f17720SLukas Straub     err = inflate(stream, Z_NO_FLUSH);
341b1f17720SLukas Straub     if (err != Z_STREAM_END) {
342b1f17720SLukas Straub         return -1;
343b1f17720SLukas Straub     }
344b1f17720SLukas Straub 
345b1f17720SLukas Straub     return stream->total_out;
346b1f17720SLukas Straub }
347b1f17720SLukas Straub 
do_data_decompress(void * opaque)348b1f17720SLukas Straub static void *do_data_decompress(void *opaque)
349b1f17720SLukas Straub {
350b1f17720SLukas Straub     DecompressParam *param = opaque;
351b1f17720SLukas Straub     unsigned long pagesize;
352b1f17720SLukas Straub     uint8_t *des;
353b1f17720SLukas Straub     int len, ret;
354b1f17720SLukas Straub 
355b1f17720SLukas Straub     qemu_mutex_lock(&param->mutex);
356b1f17720SLukas Straub     while (!param->quit) {
357b1f17720SLukas Straub         if (param->des) {
358b1f17720SLukas Straub             des = param->des;
359b1f17720SLukas Straub             len = param->len;
360b1f17720SLukas Straub             param->des = 0;
361b1f17720SLukas Straub             qemu_mutex_unlock(&param->mutex);
362b1f17720SLukas Straub 
36352623f23SLukas Straub             pagesize = qemu_target_page_size();
364b1f17720SLukas Straub 
365b1f17720SLukas Straub             ret = qemu_uncompress_data(&param->stream, des, pagesize,
366b1f17720SLukas Straub                                        param->compbuf, len);
367b1f17720SLukas Straub             if (ret < 0 && migrate_get_current()->decompress_error_check) {
368b1f17720SLukas Straub                 error_report("decompress data failed");
369b1f17720SLukas Straub                 qemu_file_set_error(decomp_file, ret);
370b1f17720SLukas Straub             }
371b1f17720SLukas Straub 
372b1f17720SLukas Straub             qemu_mutex_lock(&decomp_done_lock);
373b1f17720SLukas Straub             param->done = true;
374b1f17720SLukas Straub             qemu_cond_signal(&decomp_done_cond);
375b1f17720SLukas Straub             qemu_mutex_unlock(&decomp_done_lock);
376b1f17720SLukas Straub 
377b1f17720SLukas Straub             qemu_mutex_lock(&param->mutex);
378b1f17720SLukas Straub         } else {
379b1f17720SLukas Straub             qemu_cond_wait(&param->cond, &param->mutex);
380b1f17720SLukas Straub         }
381b1f17720SLukas Straub     }
382b1f17720SLukas Straub     qemu_mutex_unlock(&param->mutex);
383b1f17720SLukas Straub 
384b1f17720SLukas Straub     return NULL;
385b1f17720SLukas Straub }
386b1f17720SLukas Straub 
wait_for_decompress_done(void)387b1f17720SLukas Straub int wait_for_decompress_done(void)
388b1f17720SLukas Straub {
389b1f17720SLukas Straub     if (!migrate_compress()) {
390b1f17720SLukas Straub         return 0;
391b1f17720SLukas Straub     }
392b1f17720SLukas Straub 
393bef4e2edSJuan Quintela     int thread_count = migrate_decompress_threads();
394b1f17720SLukas Straub     qemu_mutex_lock(&decomp_done_lock);
395bef4e2edSJuan Quintela     for (int i = 0; i < thread_count; i++) {
396bef4e2edSJuan Quintela         while (!decomp_param[i].done) {
397b1f17720SLukas Straub             qemu_cond_wait(&decomp_done_cond, &decomp_done_lock);
398b1f17720SLukas Straub         }
399b1f17720SLukas Straub     }
400b1f17720SLukas Straub     qemu_mutex_unlock(&decomp_done_lock);
401b1f17720SLukas Straub     return qemu_file_get_error(decomp_file);
402b1f17720SLukas Straub }
403b1f17720SLukas Straub 
compress_threads_load_cleanup(void)404b1f17720SLukas Straub void compress_threads_load_cleanup(void)
405b1f17720SLukas Straub {
406b1f17720SLukas Straub     int i, thread_count;
407b1f17720SLukas Straub 
408b1f17720SLukas Straub     if (!migrate_compress()) {
409b1f17720SLukas Straub         return;
410b1f17720SLukas Straub     }
411b1f17720SLukas Straub     thread_count = migrate_decompress_threads();
412b1f17720SLukas Straub     for (i = 0; i < thread_count; i++) {
413b1f17720SLukas Straub         /*
414b1f17720SLukas Straub          * we use it as a indicator which shows if the thread is
415b1f17720SLukas Straub          * properly init'd or not
416b1f17720SLukas Straub          */
417b1f17720SLukas Straub         if (!decomp_param[i].compbuf) {
418b1f17720SLukas Straub             break;
419b1f17720SLukas Straub         }
420b1f17720SLukas Straub 
421b1f17720SLukas Straub         qemu_mutex_lock(&decomp_param[i].mutex);
422b1f17720SLukas Straub         decomp_param[i].quit = true;
423b1f17720SLukas Straub         qemu_cond_signal(&decomp_param[i].cond);
424b1f17720SLukas Straub         qemu_mutex_unlock(&decomp_param[i].mutex);
425b1f17720SLukas Straub     }
426b1f17720SLukas Straub     for (i = 0; i < thread_count; i++) {
427b1f17720SLukas Straub         if (!decomp_param[i].compbuf) {
428b1f17720SLukas Straub             break;
429b1f17720SLukas Straub         }
430b1f17720SLukas Straub 
431b1f17720SLukas Straub         qemu_thread_join(decompress_threads + i);
432b1f17720SLukas Straub         qemu_mutex_destroy(&decomp_param[i].mutex);
433b1f17720SLukas Straub         qemu_cond_destroy(&decomp_param[i].cond);
434b1f17720SLukas Straub         inflateEnd(&decomp_param[i].stream);
435b1f17720SLukas Straub         g_free(decomp_param[i].compbuf);
436b1f17720SLukas Straub         decomp_param[i].compbuf = NULL;
437b1f17720SLukas Straub     }
438b1f17720SLukas Straub     g_free(decompress_threads);
439b1f17720SLukas Straub     g_free(decomp_param);
440b1f17720SLukas Straub     decompress_threads = NULL;
441b1f17720SLukas Straub     decomp_param = NULL;
442b1f17720SLukas Straub     decomp_file = NULL;
443b1f17720SLukas Straub }
444b1f17720SLukas Straub 
compress_threads_load_setup(QEMUFile * f)445b1f17720SLukas Straub int compress_threads_load_setup(QEMUFile *f)
446b1f17720SLukas Straub {
447b1f17720SLukas Straub     int i, thread_count;
448b1f17720SLukas Straub 
449b1f17720SLukas Straub     if (!migrate_compress()) {
450b1f17720SLukas Straub         return 0;
451b1f17720SLukas Straub     }
452b1f17720SLukas Straub 
453809f188aSJuan Quintela     /*
454809f188aSJuan Quintela      * set compression_counters memory to zero for a new migration
455809f188aSJuan Quintela      */
456809f188aSJuan Quintela     memset(&compression_counters, 0, sizeof(compression_counters));
457809f188aSJuan Quintela 
458b1f17720SLukas Straub     thread_count = migrate_decompress_threads();
459b1f17720SLukas Straub     decompress_threads = g_new0(QemuThread, thread_count);
460b1f17720SLukas Straub     decomp_param = g_new0(DecompressParam, thread_count);
461b1f17720SLukas Straub     qemu_mutex_init(&decomp_done_lock);
462b1f17720SLukas Straub     qemu_cond_init(&decomp_done_cond);
463b1f17720SLukas Straub     decomp_file = f;
464b1f17720SLukas Straub     for (i = 0; i < thread_count; i++) {
465b1f17720SLukas Straub         if (inflateInit(&decomp_param[i].stream) != Z_OK) {
466b1f17720SLukas Straub             goto exit;
467b1f17720SLukas Straub         }
468b1f17720SLukas Straub 
46952623f23SLukas Straub         size_t compbuf_size = compressBound(qemu_target_page_size());
47052623f23SLukas Straub         decomp_param[i].compbuf = g_malloc0(compbuf_size);
471b1f17720SLukas Straub         qemu_mutex_init(&decomp_param[i].mutex);
472b1f17720SLukas Straub         qemu_cond_init(&decomp_param[i].cond);
473b1f17720SLukas Straub         decomp_param[i].done = true;
474b1f17720SLukas Straub         decomp_param[i].quit = false;
475b1f17720SLukas Straub         qemu_thread_create(decompress_threads + i, "decompress",
476b1f17720SLukas Straub                            do_data_decompress, decomp_param + i,
477b1f17720SLukas Straub                            QEMU_THREAD_JOINABLE);
478b1f17720SLukas Straub     }
479b1f17720SLukas Straub     return 0;
480b1f17720SLukas Straub exit:
481b1f17720SLukas Straub     compress_threads_load_cleanup();
482b1f17720SLukas Straub     return -1;
483b1f17720SLukas Straub }
484b1f17720SLukas Straub 
decompress_data_with_multi_threads(QEMUFile * f,void * host,int len)485b1f17720SLukas Straub void decompress_data_with_multi_threads(QEMUFile *f, void *host, int len)
486b1f17720SLukas Straub {
487bef4e2edSJuan Quintela     int thread_count = migrate_decompress_threads();
488b1f17720SLukas Straub     QEMU_LOCK_GUARD(&decomp_done_lock);
489b1f17720SLukas Straub     while (true) {
490bef4e2edSJuan Quintela         for (int i = 0; i < thread_count; i++) {
491bef4e2edSJuan Quintela             if (decomp_param[i].done) {
492bef4e2edSJuan Quintela                 decomp_param[i].done = false;
493bef4e2edSJuan Quintela                 qemu_mutex_lock(&decomp_param[i].mutex);
494bef4e2edSJuan Quintela                 qemu_get_buffer(f, decomp_param[i].compbuf, len);
495bef4e2edSJuan Quintela                 decomp_param[i].des = host;
496bef4e2edSJuan Quintela                 decomp_param[i].len = len;
497bef4e2edSJuan Quintela                 qemu_cond_signal(&decomp_param[i].cond);
498bef4e2edSJuan Quintela                 qemu_mutex_unlock(&decomp_param[i].mutex);
4994703d195SJuan Quintela                 return;
500b1f17720SLukas Straub             }
501b1f17720SLukas Straub         }
502b1f17720SLukas Straub         qemu_cond_wait(&decomp_done_cond, &decomp_done_lock);
503b1f17720SLukas Straub     }
504b1f17720SLukas Straub }
5056f609005SJuan Quintela 
populate_compress(MigrationInfo * info)5066f609005SJuan Quintela void populate_compress(MigrationInfo *info)
5076f609005SJuan Quintela {
5086f609005SJuan Quintela     if (!migrate_compress()) {
5096f609005SJuan Quintela         return;
5106f609005SJuan Quintela     }
5116f609005SJuan Quintela     info->compression = g_malloc0(sizeof(*info->compression));
5126f609005SJuan Quintela     info->compression->pages = compression_counters.pages;
5136f609005SJuan Quintela     info->compression->busy = compression_counters.busy;
5146f609005SJuan Quintela     info->compression->busy_rate = compression_counters.busy_rate;
5156f609005SJuan Quintela     info->compression->compressed_size = compression_counters.compressed_size;
5166f609005SJuan Quintela     info->compression->compression_rate = compression_counters.compression_rate;
5176f609005SJuan Quintela }
518f504789dSJuan Quintela 
compress_ram_pages(void)5198258f2faSJuan Quintela uint64_t compress_ram_pages(void)
520f504789dSJuan Quintela {
521f504789dSJuan Quintela     return compression_counters.pages;
522f504789dSJuan Quintela }
523f504789dSJuan Quintela 
update_compress_thread_counts(const CompressParam * param,int bytes_xmit)5241fd03d41SJuan Quintela void update_compress_thread_counts(const CompressParam *param, int bytes_xmit)
5251fd03d41SJuan Quintela {
5261fd03d41SJuan Quintela     ram_transferred_add(bytes_xmit);
5271fd03d41SJuan Quintela 
5281fd03d41SJuan Quintela     if (param->result == RES_ZEROPAGE) {
5291fd03d41SJuan Quintela         stat64_add(&mig_stats.zero_pages, 1);
5301fd03d41SJuan Quintela         return;
5311fd03d41SJuan Quintela     }
5321fd03d41SJuan Quintela 
5331fd03d41SJuan Quintela     /* 8 means a header with RAM_SAVE_FLAG_CONTINUE. */
5341fd03d41SJuan Quintela     compression_counters.compressed_size += bytes_xmit - 8;
5351fd03d41SJuan Quintela     compression_counters.pages++;
5361fd03d41SJuan Quintela }
5371fd03d41SJuan Quintela 
compress_update_rates(uint64_t page_count)538fb36fb27SJuan Quintela void compress_update_rates(uint64_t page_count)
539fb36fb27SJuan Quintela {
540fb36fb27SJuan Quintela     if (!migrate_compress()) {
541fb36fb27SJuan Quintela         return;
542fb36fb27SJuan Quintela     }
543fb36fb27SJuan Quintela     compression_counters.busy_rate = (double)(compression_counters.busy -
544fb36fb27SJuan Quintela             compression_counters.compress_thread_busy_prev) / page_count;
545fb36fb27SJuan Quintela     compression_counters.compress_thread_busy_prev =
546fb36fb27SJuan Quintela             compression_counters.busy;
547fb36fb27SJuan Quintela 
548fb36fb27SJuan Quintela     double compressed_size = compression_counters.compressed_size -
549fb36fb27SJuan Quintela         compression_counters.compressed_size_prev;
550fb36fb27SJuan Quintela     if (compressed_size) {
551fb36fb27SJuan Quintela         double uncompressed_size = (compression_counters.pages -
552fb36fb27SJuan Quintela                                     compression_counters.compress_pages_prev) *
553fb36fb27SJuan Quintela             qemu_target_page_size();
554fb36fb27SJuan Quintela 
555fb36fb27SJuan Quintela         /* Compression-Ratio = Uncompressed-size / Compressed-size */
556fb36fb27SJuan Quintela         compression_counters.compression_rate =
557fb36fb27SJuan Quintela             uncompressed_size / compressed_size;
558fb36fb27SJuan Quintela 
559fb36fb27SJuan Quintela         compression_counters.compress_pages_prev =
560fb36fb27SJuan Quintela             compression_counters.pages;
561fb36fb27SJuan Quintela         compression_counters.compressed_size_prev =
562fb36fb27SJuan Quintela             compression_counters.compressed_size;
563fb36fb27SJuan Quintela     }
564fb36fb27SJuan Quintela }
565