xref: /openbmc/qemu/block/qcow2-threads.c (revision 0f5636c51c76fed01e2ed02fbb7a46da869f0489)
1 /*
2  * Threaded data processing for Qcow2: compression, encryption
3  *
4  * Copyright (c) 2004-2006 Fabrice Bellard
5  * Copyright (c) 2018 Virtuozzo International GmbH. All rights reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 
26 #include "qemu/osdep.h"
27 
28 #define ZLIB_CONST
29 #include <zlib.h>
30 
31 #include "qcow2.h"
32 #include "block/thread-pool.h"
33 
34 #define MAX_COMPRESS_THREADS 4
35 
36 typedef ssize_t (*Qcow2CompressFunc)(void *dest, size_t dest_size,
37                                      const void *src, size_t src_size);
38 typedef struct Qcow2CompressData {
39     void *dest;
40     size_t dest_size;
41     const void *src;
42     size_t src_size;
43     ssize_t ret;
44 
45     Qcow2CompressFunc func;
46 } Qcow2CompressData;
47 
48 /*
49  * qcow2_compress()
50  *
51  * @dest - destination buffer, @dest_size bytes
52  * @src - source buffer, @src_size bytes
53  *
54  * Returns: compressed size on success
55  *          -ENOMEM destination buffer is not enough to store compressed data
56  *          -EIO    on any other error
57  */
58 static ssize_t qcow2_compress(void *dest, size_t dest_size,
59                               const void *src, size_t src_size)
60 {
61     ssize_t ret;
62     z_stream strm;
63 
64     /* best compression, small window, no zlib header */
65     memset(&strm, 0, sizeof(strm));
66     ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
67                        -12, 9, Z_DEFAULT_STRATEGY);
68     if (ret != Z_OK) {
69         return -EIO;
70     }
71 
72     /*
73      * strm.next_in is not const in old zlib versions, such as those used on
74      * OpenBSD/NetBSD, so cast the const away
75      */
76     strm.avail_in = src_size;
77     strm.next_in = (void *) src;
78     strm.avail_out = dest_size;
79     strm.next_out = dest;
80 
81     ret = deflate(&strm, Z_FINISH);
82     if (ret == Z_STREAM_END) {
83         ret = dest_size - strm.avail_out;
84     } else {
85         ret = (ret == Z_OK ? -ENOMEM : -EIO);
86     }
87 
88     deflateEnd(&strm);
89 
90     return ret;
91 }
92 
93 /*
94  * qcow2_decompress()
95  *
96  * Decompress some data (not more than @src_size bytes) to produce exactly
97  * @dest_size bytes.
98  *
99  * @dest - destination buffer, @dest_size bytes
100  * @src - source buffer, @src_size bytes
101  *
102  * Returns: 0 on success
103  *          -1 on fail
104  */
105 static ssize_t qcow2_decompress(void *dest, size_t dest_size,
106                                 const void *src, size_t src_size)
107 {
108     int ret = 0;
109     z_stream strm;
110 
111     memset(&strm, 0, sizeof(strm));
112     strm.avail_in = src_size;
113     strm.next_in = (void *) src;
114     strm.avail_out = dest_size;
115     strm.next_out = dest;
116 
117     ret = inflateInit2(&strm, -12);
118     if (ret != Z_OK) {
119         return -1;
120     }
121 
122     ret = inflate(&strm, Z_FINISH);
123     if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) || strm.avail_out != 0) {
124         /*
125          * We approve Z_BUF_ERROR because we need @dest buffer to be filled, but
126          * @src buffer may be processed partly (because in qcow2 we know size of
127          * compressed data with precision of one sector)
128          */
129         ret = -1;
130     }
131 
132     inflateEnd(&strm);
133 
134     return ret;
135 }
136 
137 static int qcow2_compress_pool_func(void *opaque)
138 {
139     Qcow2CompressData *data = opaque;
140 
141     data->ret = data->func(data->dest, data->dest_size,
142                            data->src, data->src_size);
143 
144     return 0;
145 }
146 
147 static ssize_t coroutine_fn
148 qcow2_co_do_compress(BlockDriverState *bs, void *dest, size_t dest_size,
149                      const void *src, size_t src_size, Qcow2CompressFunc func)
150 {
151     BDRVQcow2State *s = bs->opaque;
152     ThreadPool *pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
153     Qcow2CompressData arg = {
154         .dest = dest,
155         .dest_size = dest_size,
156         .src = src,
157         .src_size = src_size,
158         .func = func,
159     };
160 
161     qemu_co_mutex_lock(&s->lock);
162     while (s->nb_compress_threads >= MAX_COMPRESS_THREADS) {
163         qemu_co_queue_wait(&s->compress_wait_queue, &s->lock);
164     }
165     s->nb_compress_threads++;
166     qemu_co_mutex_unlock(&s->lock);
167 
168     thread_pool_submit_co(pool, qcow2_compress_pool_func, &arg);
169 
170     qemu_co_mutex_lock(&s->lock);
171     s->nb_compress_threads--;
172     qemu_co_queue_next(&s->compress_wait_queue);
173     qemu_co_mutex_unlock(&s->lock);
174 
175     return arg.ret;
176 }
177 
178 ssize_t coroutine_fn
179 qcow2_co_compress(BlockDriverState *bs, void *dest, size_t dest_size,
180                   const void *src, size_t src_size)
181 {
182     return qcow2_co_do_compress(bs, dest, dest_size, src, src_size,
183                                 qcow2_compress);
184 }
185 
186 ssize_t coroutine_fn
187 qcow2_co_decompress(BlockDriverState *bs, void *dest, size_t dest_size,
188                     const void *src, size_t src_size)
189 {
190     return qcow2_co_do_compress(bs, dest, dest_size, src, src_size,
191                                 qcow2_decompress);
192 }
193