xref: /openbmc/qemu/migration/multifd-uadk.c (revision f5827eb0ac3af024918fb702690188210c31202c)
1f3d8bb75SShameer Kolothum /*
2f3d8bb75SShameer Kolothum  * Multifd UADK compression accelerator implementation
3f3d8bb75SShameer Kolothum  *
4f3d8bb75SShameer Kolothum  * Copyright (c) 2024 Huawei Technologies R & D (UK) Ltd
5f3d8bb75SShameer Kolothum  *
6f3d8bb75SShameer Kolothum  * Authors:
7f3d8bb75SShameer Kolothum  *  Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
8f3d8bb75SShameer Kolothum  *
9f3d8bb75SShameer Kolothum  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10f3d8bb75SShameer Kolothum  * See the COPYING file in the top-level directory.
11f3d8bb75SShameer Kolothum  */
12f3d8bb75SShameer Kolothum 
13f3d8bb75SShameer Kolothum #include "qemu/osdep.h"
14f3d8bb75SShameer Kolothum #include "qemu/module.h"
15819dd206SShameer Kolothum #include "qapi/error.h"
163c49191aSShameer Kolothum #include "exec/ramblock.h"
17819dd206SShameer Kolothum #include "migration.h"
18819dd206SShameer Kolothum #include "multifd.h"
19819dd206SShameer Kolothum #include "options.h"
20c1dfd121SShameer Kolothum #include "qemu/error-report.h"
21819dd206SShameer Kolothum #include "uadk/wd_comp.h"
22819dd206SShameer Kolothum #include "uadk/wd_sched.h"
23819dd206SShameer Kolothum 
24819dd206SShameer Kolothum struct wd_data {
25819dd206SShameer Kolothum     handle_t handle;
26819dd206SShameer Kolothum     uint8_t *buf;
27819dd206SShameer Kolothum     uint32_t *buf_hdr;
28819dd206SShameer Kolothum };
29819dd206SShameer Kolothum 
uadk_hw_init(void)30819dd206SShameer Kolothum static bool uadk_hw_init(void)
31819dd206SShameer Kolothum {
32819dd206SShameer Kolothum     char alg[] = "zlib";
33819dd206SShameer Kolothum     int ret;
34819dd206SShameer Kolothum 
35819dd206SShameer Kolothum     ret = wd_comp_init2(alg, SCHED_POLICY_RR, TASK_HW);
36819dd206SShameer Kolothum     if (ret && ret != -WD_EEXIST) {
37819dd206SShameer Kolothum         return false;
38819dd206SShameer Kolothum     } else {
39819dd206SShameer Kolothum         return true;
40819dd206SShameer Kolothum     }
41819dd206SShameer Kolothum }
42819dd206SShameer Kolothum 
multifd_uadk_init_sess(uint32_t count,uint32_t page_size,bool compress,Error ** errp)43819dd206SShameer Kolothum static struct wd_data *multifd_uadk_init_sess(uint32_t count,
44819dd206SShameer Kolothum                                               uint32_t page_size,
45819dd206SShameer Kolothum                                               bool compress, Error **errp)
46819dd206SShameer Kolothum {
47819dd206SShameer Kolothum     struct wd_comp_sess_setup ss = {0};
48819dd206SShameer Kolothum     struct sched_params param = {0};
49819dd206SShameer Kolothum     uint32_t size = count * page_size;
50819dd206SShameer Kolothum     struct wd_data *wd;
51819dd206SShameer Kolothum 
52819dd206SShameer Kolothum     wd = g_new0(struct wd_data, 1);
53c1dfd121SShameer Kolothum 
54c1dfd121SShameer Kolothum     if (uadk_hw_init()) {
55819dd206SShameer Kolothum         ss.alg_type = WD_ZLIB;
56819dd206SShameer Kolothum         if (compress) {
57819dd206SShameer Kolothum             ss.op_type = WD_DIR_COMPRESS;
58819dd206SShameer Kolothum             /* Add an additional page for handling output > input */
59819dd206SShameer Kolothum             size += page_size;
60819dd206SShameer Kolothum         } else {
61819dd206SShameer Kolothum             ss.op_type = WD_DIR_DECOMPRESS;
62819dd206SShameer Kolothum         }
63819dd206SShameer Kolothum         /* We use default level 1 compression and 4K window size */
64819dd206SShameer Kolothum         param.type = ss.op_type;
65819dd206SShameer Kolothum         ss.sched_param = &param;
66819dd206SShameer Kolothum 
67819dd206SShameer Kolothum         wd->handle = wd_comp_alloc_sess(&ss);
68819dd206SShameer Kolothum         if (!wd->handle) {
69819dd206SShameer Kolothum             error_setg(errp, "multifd: failed wd_comp_alloc_sess");
70819dd206SShameer Kolothum             goto out;
71819dd206SShameer Kolothum         }
72c1dfd121SShameer Kolothum     } else {
73c1dfd121SShameer Kolothum         /* For CI test use */
74c1dfd121SShameer Kolothum         warn_report_once("UADK hardware not available. Switch to no compression mode");
75c1dfd121SShameer Kolothum     }
76819dd206SShameer Kolothum 
77819dd206SShameer Kolothum     wd->buf = g_try_malloc(size);
78819dd206SShameer Kolothum     if (!wd->buf) {
79819dd206SShameer Kolothum         error_setg(errp, "multifd: out of mem for uadk buf");
80819dd206SShameer Kolothum         goto out_free_sess;
81819dd206SShameer Kolothum     }
82819dd206SShameer Kolothum     wd->buf_hdr = g_new0(uint32_t, count);
83819dd206SShameer Kolothum     return wd;
84819dd206SShameer Kolothum 
85819dd206SShameer Kolothum out_free_sess:
86c1dfd121SShameer Kolothum     if (wd->handle) {
87819dd206SShameer Kolothum         wd_comp_free_sess(wd->handle);
88c1dfd121SShameer Kolothum     }
89819dd206SShameer Kolothum out:
90819dd206SShameer Kolothum     wd_comp_uninit2();
91819dd206SShameer Kolothum     g_free(wd);
92819dd206SShameer Kolothum     return NULL;
93819dd206SShameer Kolothum }
94819dd206SShameer Kolothum 
multifd_uadk_uninit_sess(struct wd_data * wd)95819dd206SShameer Kolothum static void multifd_uadk_uninit_sess(struct wd_data *wd)
96819dd206SShameer Kolothum {
97c1dfd121SShameer Kolothum     if (wd->handle) {
98819dd206SShameer Kolothum         wd_comp_free_sess(wd->handle);
99c1dfd121SShameer Kolothum     }
100819dd206SShameer Kolothum     wd_comp_uninit2();
101819dd206SShameer Kolothum     g_free(wd->buf);
102819dd206SShameer Kolothum     g_free(wd->buf_hdr);
103819dd206SShameer Kolothum     g_free(wd);
104819dd206SShameer Kolothum }
105819dd206SShameer Kolothum 
multifd_uadk_send_setup(MultiFDSendParams * p,Error ** errp)106819dd206SShameer Kolothum static int multifd_uadk_send_setup(MultiFDSendParams *p, Error **errp)
107819dd206SShameer Kolothum {
108819dd206SShameer Kolothum     struct wd_data *wd;
10990fa121cSFabiano Rosas     uint32_t page_size = multifd_ram_page_size();
11090fa121cSFabiano Rosas     uint32_t page_count = multifd_ram_page_count();
111819dd206SShameer Kolothum 
11290fa121cSFabiano Rosas     wd = multifd_uadk_init_sess(page_count, page_size, true, errp);
113819dd206SShameer Kolothum     if (!wd) {
114819dd206SShameer Kolothum         return -1;
115819dd206SShameer Kolothum     }
116819dd206SShameer Kolothum 
117819dd206SShameer Kolothum     p->compress_data = wd;
118819dd206SShameer Kolothum     assert(p->iov == NULL);
119819dd206SShameer Kolothum     /*
120819dd206SShameer Kolothum      * Each page will be compressed independently and sent using an IOV. The
121819dd206SShameer Kolothum      * additional two IOVs are used to store packet header and compressed data
122819dd206SShameer Kolothum      * length
123819dd206SShameer Kolothum      */
124819dd206SShameer Kolothum 
12590fa121cSFabiano Rosas     p->iov = g_new0(struct iovec, page_count + 2);
126819dd206SShameer Kolothum     return 0;
127819dd206SShameer Kolothum }
128819dd206SShameer Kolothum 
multifd_uadk_send_cleanup(MultiFDSendParams * p,Error ** errp)129819dd206SShameer Kolothum static void multifd_uadk_send_cleanup(MultiFDSendParams *p, Error **errp)
130819dd206SShameer Kolothum {
131819dd206SShameer Kolothum     struct wd_data *wd = p->compress_data;
132819dd206SShameer Kolothum 
133819dd206SShameer Kolothum     multifd_uadk_uninit_sess(wd);
134819dd206SShameer Kolothum     p->compress_data = NULL;
135405e352dSFabiano Rosas     g_free(p->iov);
136405e352dSFabiano Rosas     p->iov = NULL;
137819dd206SShameer Kolothum }
138819dd206SShameer Kolothum 
prepare_next_iov(MultiFDSendParams * p,void * base,uint32_t len)1393c49191aSShameer Kolothum static inline void prepare_next_iov(MultiFDSendParams *p, void *base,
1403c49191aSShameer Kolothum                                     uint32_t len)
1413c49191aSShameer Kolothum {
1423c49191aSShameer Kolothum     p->iov[p->iovs_num].iov_base = (uint8_t *)base;
1433c49191aSShameer Kolothum     p->iov[p->iovs_num].iov_len = len;
1443c49191aSShameer Kolothum     p->next_packet_size += len;
1453c49191aSShameer Kolothum     p->iovs_num++;
1463c49191aSShameer Kolothum }
1473c49191aSShameer Kolothum 
multifd_uadk_send_prepare(MultiFDSendParams * p,Error ** errp)148819dd206SShameer Kolothum static int multifd_uadk_send_prepare(MultiFDSendParams *p, Error **errp)
149819dd206SShameer Kolothum {
1503c49191aSShameer Kolothum     struct wd_data *uadk_data = p->compress_data;
1513c49191aSShameer Kolothum     uint32_t hdr_size;
15290fa121cSFabiano Rosas     uint32_t page_size = multifd_ram_page_size();
1533c49191aSShameer Kolothum     uint8_t *buf = uadk_data->buf;
1543c49191aSShameer Kolothum     int ret = 0;
1559f0e1089SFabiano Rosas     MultiFDPages_t *pages = &p->data->u.ram;
1563c49191aSShameer Kolothum 
1573c49191aSShameer Kolothum     if (!multifd_send_prepare_common(p)) {
1583c49191aSShameer Kolothum         goto out;
1593c49191aSShameer Kolothum     }
1603c49191aSShameer Kolothum 
161bc112a6cSFabiano Rosas     hdr_size = pages->normal_num * sizeof(uint32_t);
1623c49191aSShameer Kolothum     /* prepare the header that stores the lengths of all compressed data */
1633c49191aSShameer Kolothum     prepare_next_iov(p, uadk_data->buf_hdr, hdr_size);
1643c49191aSShameer Kolothum 
165bc112a6cSFabiano Rosas     for (int i = 0; i < pages->normal_num; i++) {
1663c49191aSShameer Kolothum         struct wd_comp_req creq = {
1673c49191aSShameer Kolothum             .op_type = WD_DIR_COMPRESS,
168bc112a6cSFabiano Rosas             .src     = pages->block->host + pages->offset[i],
16990fa121cSFabiano Rosas             .src_len = page_size,
1703c49191aSShameer Kolothum             .dst     = buf,
1713c49191aSShameer Kolothum             /* Set dst_len to double the src in case compressed out >= page_size */
172*f5827eb0SShameer Kolothum             .dst_len = page_size * 2,
1733c49191aSShameer Kolothum         };
1743c49191aSShameer Kolothum 
175c1dfd121SShameer Kolothum         if (uadk_data->handle) {
1763c49191aSShameer Kolothum             ret = wd_do_comp_sync(uadk_data->handle, &creq);
1773c49191aSShameer Kolothum             if (ret || creq.status) {
1783c49191aSShameer Kolothum                 error_setg(errp, "multifd %u: failed compression, ret %d status %d",
1793c49191aSShameer Kolothum                            p->id, ret, creq.status);
180819dd206SShameer Kolothum                 return -1;
181819dd206SShameer Kolothum             }
18290fa121cSFabiano Rosas             if (creq.dst_len < page_size) {
1833c49191aSShameer Kolothum                 uadk_data->buf_hdr[i] = cpu_to_be32(creq.dst_len);
1843c49191aSShameer Kolothum                 prepare_next_iov(p, buf, creq.dst_len);
1853c49191aSShameer Kolothum                 buf += creq.dst_len;
186c1dfd121SShameer Kolothum             }
187c1dfd121SShameer Kolothum         }
1883c49191aSShameer Kolothum         /*
189c1dfd121SShameer Kolothum          * Send raw data if no UADK hardware or if compressed out >= page_size.
190c1dfd121SShameer Kolothum          * We might be better off sending raw data if output is slightly less
191c1dfd121SShameer Kolothum          * than page_size as well because at the receive end we can skip the
192c1dfd121SShameer Kolothum          * decompression. But it is tricky to find the right number here.
1933c49191aSShameer Kolothum          */
19490fa121cSFabiano Rosas         if (!uadk_data->handle || creq.dst_len >= page_size) {
19590fa121cSFabiano Rosas             uadk_data->buf_hdr[i] = cpu_to_be32(page_size);
196bc112a6cSFabiano Rosas             prepare_next_iov(p, pages->block->host + pages->offset[i],
19790fa121cSFabiano Rosas                              page_size);
19890fa121cSFabiano Rosas             buf += page_size;
1993c49191aSShameer Kolothum         }
2003c49191aSShameer Kolothum     }
2013c49191aSShameer Kolothum out:
2023c49191aSShameer Kolothum     p->flags |= MULTIFD_FLAG_UADK;
2033c49191aSShameer Kolothum     multifd_send_fill_packet(p);
2043c49191aSShameer Kolothum     return 0;
2053c49191aSShameer Kolothum }
206819dd206SShameer Kolothum 
multifd_uadk_recv_setup(MultiFDRecvParams * p,Error ** errp)207819dd206SShameer Kolothum static int multifd_uadk_recv_setup(MultiFDRecvParams *p, Error **errp)
208819dd206SShameer Kolothum {
209819dd206SShameer Kolothum     struct wd_data *wd;
21090fa121cSFabiano Rosas     uint32_t page_size = multifd_ram_page_size();
21190fa121cSFabiano Rosas     uint32_t page_count = multifd_ram_page_count();
212819dd206SShameer Kolothum 
21390fa121cSFabiano Rosas     wd = multifd_uadk_init_sess(page_count, page_size, false, errp);
214819dd206SShameer Kolothum     if (!wd) {
215819dd206SShameer Kolothum         return -1;
216819dd206SShameer Kolothum     }
217819dd206SShameer Kolothum     p->compress_data = wd;
218819dd206SShameer Kolothum     return 0;
219819dd206SShameer Kolothum }
220819dd206SShameer Kolothum 
multifd_uadk_recv_cleanup(MultiFDRecvParams * p)221819dd206SShameer Kolothum static void multifd_uadk_recv_cleanup(MultiFDRecvParams *p)
222819dd206SShameer Kolothum {
223819dd206SShameer Kolothum     struct wd_data *wd = p->compress_data;
224819dd206SShameer Kolothum 
225819dd206SShameer Kolothum     multifd_uadk_uninit_sess(wd);
226819dd206SShameer Kolothum     p->compress_data = NULL;
227819dd206SShameer Kolothum }
228819dd206SShameer Kolothum 
multifd_uadk_recv(MultiFDRecvParams * p,Error ** errp)229819dd206SShameer Kolothum static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp)
230819dd206SShameer Kolothum {
2313c49191aSShameer Kolothum     struct wd_data *uadk_data = p->compress_data;
2323c49191aSShameer Kolothum     uint32_t in_size = p->next_packet_size;
2333c49191aSShameer Kolothum     uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK;
2343c49191aSShameer Kolothum     uint32_t hdr_len = p->normal_num * sizeof(uint32_t);
2353c49191aSShameer Kolothum     uint32_t data_len = 0;
23690fa121cSFabiano Rosas     uint32_t page_size = multifd_ram_page_size();
2373c49191aSShameer Kolothum     uint8_t *buf = uadk_data->buf;
2383c49191aSShameer Kolothum     int ret = 0;
2393c49191aSShameer Kolothum 
2403c49191aSShameer Kolothum     if (flags != MULTIFD_FLAG_UADK) {
2413c49191aSShameer Kolothum         error_setg(errp, "multifd %u: flags received %x flags expected %x",
2423c49191aSShameer Kolothum                    p->id, flags, MULTIFD_FLAG_ZLIB);
243819dd206SShameer Kolothum         return -1;
244819dd206SShameer Kolothum     }
245819dd206SShameer Kolothum 
2463c49191aSShameer Kolothum     multifd_recv_zero_page_process(p);
2473c49191aSShameer Kolothum     if (!p->normal_num) {
2483c49191aSShameer Kolothum         assert(in_size == 0);
2493c49191aSShameer Kolothum         return 0;
2503c49191aSShameer Kolothum     }
2513c49191aSShameer Kolothum 
2523c49191aSShameer Kolothum     /* read compressed data lengths */
2533c49191aSShameer Kolothum     assert(hdr_len < in_size);
2543c49191aSShameer Kolothum     ret = qio_channel_read_all(p->c, (void *) uadk_data->buf_hdr,
2553c49191aSShameer Kolothum                                hdr_len, errp);
2563c49191aSShameer Kolothum     if (ret != 0) {
2573c49191aSShameer Kolothum         return ret;
2583c49191aSShameer Kolothum     }
2593c49191aSShameer Kolothum 
2603c49191aSShameer Kolothum     for (int i = 0; i < p->normal_num; i++) {
2613c49191aSShameer Kolothum         uadk_data->buf_hdr[i] = be32_to_cpu(uadk_data->buf_hdr[i]);
2623c49191aSShameer Kolothum         data_len += uadk_data->buf_hdr[i];
26390fa121cSFabiano Rosas         assert(uadk_data->buf_hdr[i] <= page_size);
2643c49191aSShameer Kolothum     }
2653c49191aSShameer Kolothum 
2663c49191aSShameer Kolothum     /* read compressed data */
2673c49191aSShameer Kolothum     assert(in_size == hdr_len + data_len);
2683c49191aSShameer Kolothum     ret = qio_channel_read_all(p->c, (void *)buf, data_len, errp);
2693c49191aSShameer Kolothum     if (ret != 0) {
2703c49191aSShameer Kolothum         return ret;
2713c49191aSShameer Kolothum     }
2723c49191aSShameer Kolothum 
2733c49191aSShameer Kolothum     for (int i = 0; i < p->normal_num; i++) {
2743c49191aSShameer Kolothum         struct wd_comp_req creq = {
2753c49191aSShameer Kolothum             .op_type = WD_DIR_DECOMPRESS,
2763c49191aSShameer Kolothum             .src     = buf,
2773c49191aSShameer Kolothum             .src_len = uadk_data->buf_hdr[i],
2783c49191aSShameer Kolothum             .dst     = p->host + p->normal[i],
27990fa121cSFabiano Rosas             .dst_len = page_size,
2803c49191aSShameer Kolothum         };
2813c49191aSShameer Kolothum 
28290fa121cSFabiano Rosas         if (uadk_data->buf_hdr[i] == page_size) {
28390fa121cSFabiano Rosas             memcpy(p->host + p->normal[i], buf, page_size);
28490fa121cSFabiano Rosas             buf += page_size;
2853c49191aSShameer Kolothum             continue;
2863c49191aSShameer Kolothum         }
2873c49191aSShameer Kolothum 
288c1dfd121SShameer Kolothum         if (unlikely(!uadk_data->handle)) {
289c1dfd121SShameer Kolothum             error_setg(errp, "multifd %u: UADK HW not available for decompression",
290c1dfd121SShameer Kolothum                        p->id);
291c1dfd121SShameer Kolothum             return -1;
292c1dfd121SShameer Kolothum         }
293c1dfd121SShameer Kolothum 
2943c49191aSShameer Kolothum         ret = wd_do_comp_sync(uadk_data->handle, &creq);
2953c49191aSShameer Kolothum         if (ret || creq.status) {
2963c49191aSShameer Kolothum             error_setg(errp, "multifd %u: failed decompression, ret %d status %d",
2973c49191aSShameer Kolothum                        p->id, ret, creq.status);
2983c49191aSShameer Kolothum             return -1;
2993c49191aSShameer Kolothum         }
30090fa121cSFabiano Rosas         if (creq.dst_len != page_size) {
3013c49191aSShameer Kolothum             error_setg(errp, "multifd %u: decompressed length error", p->id);
3023c49191aSShameer Kolothum             return -1;
3033c49191aSShameer Kolothum         }
3043c49191aSShameer Kolothum         buf += uadk_data->buf_hdr[i];
3053c49191aSShameer Kolothum      }
3063c49191aSShameer Kolothum 
3073c49191aSShameer Kolothum     return 0;
3083c49191aSShameer Kolothum }
3093c49191aSShameer Kolothum 
310308d165cSFabiano Rosas static const MultiFDMethods multifd_uadk_ops = {
311819dd206SShameer Kolothum     .send_setup = multifd_uadk_send_setup,
312819dd206SShameer Kolothum     .send_cleanup = multifd_uadk_send_cleanup,
313819dd206SShameer Kolothum     .send_prepare = multifd_uadk_send_prepare,
314819dd206SShameer Kolothum     .recv_setup = multifd_uadk_recv_setup,
315819dd206SShameer Kolothum     .recv_cleanup = multifd_uadk_recv_cleanup,
316819dd206SShameer Kolothum     .recv = multifd_uadk_recv,
317819dd206SShameer Kolothum };
318f3d8bb75SShameer Kolothum 
multifd_uadk_register(void)319f3d8bb75SShameer Kolothum static void multifd_uadk_register(void)
320f3d8bb75SShameer Kolothum {
321819dd206SShameer Kolothum     multifd_register_ops(MULTIFD_COMPRESSION_UADK, &multifd_uadk_ops);
322f3d8bb75SShameer Kolothum }
323f3d8bb75SShameer Kolothum migration_init(multifd_uadk_register);
324