2 * Multifd UADK compression accelerator implementation
4 * Copyright (c) 2024 Huawei Technologies R & D (UK) Ltd
7 * Shameer Kolothum <shameerali.kolothum.thodi@huawei.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"
14 #include "qemu/module.h"
15 #include "qapi/error.h"
16 #include "exec/ramblock.h"
17 #include "migration.h"
20 #include "qemu/error-report.h"
21 #include "uadk/wd_comp.h"
22 #include "uadk/wd_sched.h"
30 static bool uadk_hw_init(void)
35 ret
= wd_comp_init2(alg
, SCHED_POLICY_RR
, TASK_HW
);
36 if (ret
&& ret
!= -WD_EEXIST
) {
43 static struct wd_data
*multifd_uadk_init_sess(uint32_t count
,
45 bool compress
, Error
**errp
)
47 struct wd_comp_sess_setup ss
= {0};
48 struct sched_params param
= {0};
49 uint32_t size
= count
* page_size
;
52 wd
= g_new0(struct wd_data
, 1);
55 ss
.alg_type
= WD_ZLIB
;
57 ss
.op_type
= WD_DIR_COMPRESS
;
58 /* Add an additional page for handling output > input */
61 ss
.op_type
= WD_DIR_DECOMPRESS
;
63 /* We use default level 1 compression and 4K window size */
64 param
.type
= ss
.op_type
;
65 ss
.sched_param
= ¶m
;
67 wd
->handle
= wd_comp_alloc_sess(&ss
);
69 error_setg(errp
, "multifd: failed wd_comp_alloc_sess");
74 warn_report_once("UADK hardware not available. Switch to no compression mode");
77 wd
->buf
= g_try_malloc(size
);
79 error_setg(errp
, "multifd: out of mem for uadk buf");
82 wd
->buf_hdr
= g_new0(uint32_t, count
);
87 wd_comp_free_sess(wd
->handle
);
95 static void multifd_uadk_uninit_sess(struct wd_data
*wd
)
98 wd_comp_free_sess(wd
->handle
);
107 * multifd_uadk_send_setup: setup send side
109 * Returns 0 for success or -1 for error
111 * @p: Params for the channel that we are using
112 * @errp: pointer to an error
114 static int multifd_uadk_send_setup(MultiFDSendParams
*p
, Error
**errp
)
118 wd
= multifd_uadk_init_sess(p
->page_count
, p
->page_size
, true, errp
);
123 p
->compress_data
= wd
;
124 assert(p
->iov
== NULL
);
126 * Each page will be compressed independently and sent using an IOV. The
127 * additional two IOVs are used to store packet header and compressed data
131 p
->iov
= g_new0(struct iovec
, p
->page_count
+ 2);
136 * multifd_uadk_send_cleanup: cleanup send side
138 * Close the channel and return memory.
140 * @p: Params for the channel that we are using
141 * @errp: pointer to an error
143 static void multifd_uadk_send_cleanup(MultiFDSendParams
*p
, Error
**errp
)
145 struct wd_data
*wd
= p
->compress_data
;
147 multifd_uadk_uninit_sess(wd
);
148 p
->compress_data
= NULL
;
151 static inline void prepare_next_iov(MultiFDSendParams
*p
, void *base
,
154 p
->iov
[p
->iovs_num
].iov_base
= (uint8_t *)base
;
155 p
->iov
[p
->iovs_num
].iov_len
= len
;
156 p
->next_packet_size
+= len
;
161 * multifd_uadk_send_prepare: prepare data to be able to send
163 * Create a compressed buffer with all the pages that we are going to
166 * Returns 0 for success or -1 for error
168 * @p: Params for the channel that we are using
169 * @errp: pointer to an error
171 static int multifd_uadk_send_prepare(MultiFDSendParams
*p
, Error
**errp
)
173 struct wd_data
*uadk_data
= p
->compress_data
;
175 uint8_t *buf
= uadk_data
->buf
;
178 if (!multifd_send_prepare_common(p
)) {
182 hdr_size
= p
->pages
->normal_num
* sizeof(uint32_t);
183 /* prepare the header that stores the lengths of all compressed data */
184 prepare_next_iov(p
, uadk_data
->buf_hdr
, hdr_size
);
186 for (int i
= 0; i
< p
->pages
->normal_num
; i
++) {
187 struct wd_comp_req creq
= {
188 .op_type
= WD_DIR_COMPRESS
,
189 .src
= p
->pages
->block
->host
+ p
->pages
->offset
[i
],
190 .src_len
= p
->page_size
,
192 /* Set dst_len to double the src in case compressed out >= page_size */
193 .dst_len
= p
->page_size
* 2,
196 if (uadk_data
->handle
) {
197 ret
= wd_do_comp_sync(uadk_data
->handle
, &creq
);
198 if (ret
|| creq
.status
) {
199 error_setg(errp
, "multifd %u: failed compression, ret %d status %d",
200 p
->id
, ret
, creq
.status
);
203 if (creq
.dst_len
< p
->page_size
) {
204 uadk_data
->buf_hdr
[i
] = cpu_to_be32(creq
.dst_len
);
205 prepare_next_iov(p
, buf
, creq
.dst_len
);
210 * Send raw data if no UADK hardware or if compressed out >= page_size.
211 * We might be better off sending raw data if output is slightly less
212 * than page_size as well because at the receive end we can skip the
213 * decompression. But it is tricky to find the right number here.
215 if (!uadk_data
->handle
|| creq
.dst_len
>= p
->page_size
) {
216 uadk_data
->buf_hdr
[i
] = cpu_to_be32(p
->page_size
);
217 prepare_next_iov(p
, p
->pages
->block
->host
+ p
->pages
->offset
[i
],
223 p
->flags
|= MULTIFD_FLAG_UADK
;
224 multifd_send_fill_packet(p
);
229 * multifd_uadk_recv_setup: setup receive side
231 * Create the compressed channel and buffer.
233 * Returns 0 for success or -1 for error
235 * @p: Params for the channel that we are using
236 * @errp: pointer to an error
238 static int multifd_uadk_recv_setup(MultiFDRecvParams
*p
, Error
**errp
)
242 wd
= multifd_uadk_init_sess(p
->page_count
, p
->page_size
, false, errp
);
246 p
->compress_data
= wd
;
251 * multifd_uadk_recv_cleanup: cleanup receive side
253 * Close the channel and return memory.
255 * @p: Params for the channel that we are using
257 static void multifd_uadk_recv_cleanup(MultiFDRecvParams
*p
)
259 struct wd_data
*wd
= p
->compress_data
;
261 multifd_uadk_uninit_sess(wd
);
262 p
->compress_data
= NULL
;
266 * multifd_uadk_recv: read the data from the channel into actual pages
268 * Read the compressed buffer, and uncompress it into the actual
271 * Returns 0 for success or -1 for error
273 * @p: Params for the channel that we are using
274 * @errp: pointer to an error
276 static int multifd_uadk_recv(MultiFDRecvParams
*p
, Error
**errp
)
278 struct wd_data
*uadk_data
= p
->compress_data
;
279 uint32_t in_size
= p
->next_packet_size
;
280 uint32_t flags
= p
->flags
& MULTIFD_FLAG_COMPRESSION_MASK
;
281 uint32_t hdr_len
= p
->normal_num
* sizeof(uint32_t);
282 uint32_t data_len
= 0;
283 uint8_t *buf
= uadk_data
->buf
;
286 if (flags
!= MULTIFD_FLAG_UADK
) {
287 error_setg(errp
, "multifd %u: flags received %x flags expected %x",
288 p
->id
, flags
, MULTIFD_FLAG_ZLIB
);
292 multifd_recv_zero_page_process(p
);
293 if (!p
->normal_num
) {
294 assert(in_size
== 0);
298 /* read compressed data lengths */
299 assert(hdr_len
< in_size
);
300 ret
= qio_channel_read_all(p
->c
, (void *) uadk_data
->buf_hdr
,
306 for (int i
= 0; i
< p
->normal_num
; i
++) {
307 uadk_data
->buf_hdr
[i
] = be32_to_cpu(uadk_data
->buf_hdr
[i
]);
308 data_len
+= uadk_data
->buf_hdr
[i
];
309 assert(uadk_data
->buf_hdr
[i
] <= p
->page_size
);
312 /* read compressed data */
313 assert(in_size
== hdr_len
+ data_len
);
314 ret
= qio_channel_read_all(p
->c
, (void *)buf
, data_len
, errp
);
319 for (int i
= 0; i
< p
->normal_num
; i
++) {
320 struct wd_comp_req creq
= {
321 .op_type
= WD_DIR_DECOMPRESS
,
323 .src_len
= uadk_data
->buf_hdr
[i
],
324 .dst
= p
->host
+ p
->normal
[i
],
325 .dst_len
= p
->page_size
,
328 if (uadk_data
->buf_hdr
[i
] == p
->page_size
) {
329 memcpy(p
->host
+ p
->normal
[i
], buf
, p
->page_size
);
334 if (unlikely(!uadk_data
->handle
)) {
335 error_setg(errp
, "multifd %u: UADK HW not available for decompression",
340 ret
= wd_do_comp_sync(uadk_data
->handle
, &creq
);
341 if (ret
|| creq
.status
) {
342 error_setg(errp
, "multifd %u: failed decompression, ret %d status %d",
343 p
->id
, ret
, creq
.status
);
346 if (creq
.dst_len
!= p
->page_size
) {
347 error_setg(errp
, "multifd %u: decompressed length error", p
->id
);
350 buf
+= uadk_data
->buf_hdr
[i
];
356 static MultiFDMethods multifd_uadk_ops
= {
357 .send_setup
= multifd_uadk_send_setup
,
358 .send_cleanup
= multifd_uadk_send_cleanup
,
359 .send_prepare
= multifd_uadk_send_prepare
,
360 .recv_setup
= multifd_uadk_recv_setup
,
361 .recv_cleanup
= multifd_uadk_recv_cleanup
,
362 .recv
= multifd_uadk_recv
,
365 static void multifd_uadk_register(void)
367 multifd_register_ops(MULTIFD_COMPRESSION_UADK
, &multifd_uadk_ops
);
369 migration_init(multifd_uadk_register
);