1 /* 2 * Multifd UADK compression accelerator implementation 3 * 4 * Copyright (c) 2024 Huawei Technologies R & D (UK) Ltd 5 * 6 * Authors: 7 * Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> 8 * 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. 11 */ 12 13 #include "qemu/osdep.h" 14 #include "qemu/module.h" 15 #include "qapi/error.h" 16 #include "exec/ramblock.h" 17 #include "migration.h" 18 #include "multifd.h" 19 #include "options.h" 20 #include "uadk/wd_comp.h" 21 #include "uadk/wd_sched.h" 22 23 struct wd_data { 24 handle_t handle; 25 uint8_t *buf; 26 uint32_t *buf_hdr; 27 }; 28 29 static bool uadk_hw_init(void) 30 { 31 char alg[] = "zlib"; 32 int ret; 33 34 ret = wd_comp_init2(alg, SCHED_POLICY_RR, TASK_HW); 35 if (ret && ret != -WD_EEXIST) { 36 return false; 37 } else { 38 return true; 39 } 40 } 41 42 static struct wd_data *multifd_uadk_init_sess(uint32_t count, 43 uint32_t page_size, 44 bool compress, Error **errp) 45 { 46 struct wd_comp_sess_setup ss = {0}; 47 struct sched_params param = {0}; 48 uint32_t size = count * page_size; 49 struct wd_data *wd; 50 51 if (!uadk_hw_init()) { 52 error_setg(errp, "multifd: UADK hardware not available"); 53 return NULL; 54 } 55 56 wd = g_new0(struct wd_data, 1); 57 ss.alg_type = WD_ZLIB; 58 if (compress) { 59 ss.op_type = WD_DIR_COMPRESS; 60 /* Add an additional page for handling output > input */ 61 size += page_size; 62 } else { 63 ss.op_type = WD_DIR_DECOMPRESS; 64 } 65 66 /* We use default level 1 compression and 4K window size */ 67 param.type = ss.op_type; 68 ss.sched_param = ¶m; 69 70 wd->handle = wd_comp_alloc_sess(&ss); 71 if (!wd->handle) { 72 error_setg(errp, "multifd: failed wd_comp_alloc_sess"); 73 goto out; 74 } 75 76 wd->buf = g_try_malloc(size); 77 if (!wd->buf) { 78 error_setg(errp, "multifd: out of mem for uadk buf"); 79 goto out_free_sess; 80 } 81 wd->buf_hdr = g_new0(uint32_t, count); 82 return wd; 83 84 out_free_sess: 85 wd_comp_free_sess(wd->handle); 86 out: 87 wd_comp_uninit2(); 88 g_free(wd); 89 return NULL; 90 } 91 92 static void multifd_uadk_uninit_sess(struct wd_data *wd) 93 { 94 wd_comp_free_sess(wd->handle); 95 wd_comp_uninit2(); 96 g_free(wd->buf); 97 g_free(wd->buf_hdr); 98 g_free(wd); 99 } 100 101 /** 102 * multifd_uadk_send_setup: setup send side 103 * 104 * Returns 0 for success or -1 for error 105 * 106 * @p: Params for the channel that we are using 107 * @errp: pointer to an error 108 */ 109 static int multifd_uadk_send_setup(MultiFDSendParams *p, Error **errp) 110 { 111 struct wd_data *wd; 112 113 wd = multifd_uadk_init_sess(p->page_count, p->page_size, true, errp); 114 if (!wd) { 115 return -1; 116 } 117 118 p->compress_data = wd; 119 assert(p->iov == NULL); 120 /* 121 * Each page will be compressed independently and sent using an IOV. The 122 * additional two IOVs are used to store packet header and compressed data 123 * length 124 */ 125 126 p->iov = g_new0(struct iovec, p->page_count + 2); 127 return 0; 128 } 129 130 /** 131 * multifd_uadk_send_cleanup: cleanup send side 132 * 133 * Close the channel and return memory. 134 * 135 * @p: Params for the channel that we are using 136 * @errp: pointer to an error 137 */ 138 static void multifd_uadk_send_cleanup(MultiFDSendParams *p, Error **errp) 139 { 140 struct wd_data *wd = p->compress_data; 141 142 multifd_uadk_uninit_sess(wd); 143 p->compress_data = NULL; 144 } 145 146 static inline void prepare_next_iov(MultiFDSendParams *p, void *base, 147 uint32_t len) 148 { 149 p->iov[p->iovs_num].iov_base = (uint8_t *)base; 150 p->iov[p->iovs_num].iov_len = len; 151 p->next_packet_size += len; 152 p->iovs_num++; 153 } 154 155 /** 156 * multifd_uadk_send_prepare: prepare data to be able to send 157 * 158 * Create a compressed buffer with all the pages that we are going to 159 * send. 160 * 161 * Returns 0 for success or -1 for error 162 * 163 * @p: Params for the channel that we are using 164 * @errp: pointer to an error 165 */ 166 static int multifd_uadk_send_prepare(MultiFDSendParams *p, Error **errp) 167 { 168 struct wd_data *uadk_data = p->compress_data; 169 uint32_t hdr_size; 170 uint8_t *buf = uadk_data->buf; 171 int ret = 0; 172 173 if (!multifd_send_prepare_common(p)) { 174 goto out; 175 } 176 177 hdr_size = p->pages->normal_num * sizeof(uint32_t); 178 /* prepare the header that stores the lengths of all compressed data */ 179 prepare_next_iov(p, uadk_data->buf_hdr, hdr_size); 180 181 for (int i = 0; i < p->pages->normal_num; i++) { 182 struct wd_comp_req creq = { 183 .op_type = WD_DIR_COMPRESS, 184 .src = p->pages->block->host + p->pages->offset[i], 185 .src_len = p->page_size, 186 .dst = buf, 187 /* Set dst_len to double the src in case compressed out >= page_size */ 188 .dst_len = p->page_size * 2, 189 }; 190 191 ret = wd_do_comp_sync(uadk_data->handle, &creq); 192 if (ret || creq.status) { 193 error_setg(errp, "multifd %u: failed compression, ret %d status %d", 194 p->id, ret, creq.status); 195 return -1; 196 } 197 if (creq.dst_len < p->page_size) { 198 uadk_data->buf_hdr[i] = cpu_to_be32(creq.dst_len); 199 prepare_next_iov(p, buf, creq.dst_len); 200 buf += creq.dst_len; 201 } else { 202 /* 203 * Send raw data if compressed out >= page_size. We might be better 204 * off sending raw data if output is slightly less than page_size 205 * as well because at the receive end we can skip the decompression. 206 * But it is tricky to find the right number here. 207 */ 208 uadk_data->buf_hdr[i] = cpu_to_be32(p->page_size); 209 prepare_next_iov(p, p->pages->block->host + p->pages->offset[i], 210 p->page_size); 211 buf += p->page_size; 212 } 213 } 214 out: 215 p->flags |= MULTIFD_FLAG_UADK; 216 multifd_send_fill_packet(p); 217 return 0; 218 } 219 220 /** 221 * multifd_uadk_recv_setup: setup receive side 222 * 223 * Create the compressed channel and buffer. 224 * 225 * Returns 0 for success or -1 for error 226 * 227 * @p: Params for the channel that we are using 228 * @errp: pointer to an error 229 */ 230 static int multifd_uadk_recv_setup(MultiFDRecvParams *p, Error **errp) 231 { 232 struct wd_data *wd; 233 234 wd = multifd_uadk_init_sess(p->page_count, p->page_size, false, errp); 235 if (!wd) { 236 return -1; 237 } 238 p->compress_data = wd; 239 return 0; 240 } 241 242 /** 243 * multifd_uadk_recv_cleanup: cleanup receive side 244 * 245 * Close the channel and return memory. 246 * 247 * @p: Params for the channel that we are using 248 */ 249 static void multifd_uadk_recv_cleanup(MultiFDRecvParams *p) 250 { 251 struct wd_data *wd = p->compress_data; 252 253 multifd_uadk_uninit_sess(wd); 254 p->compress_data = NULL; 255 } 256 257 /** 258 * multifd_uadk_recv: read the data from the channel into actual pages 259 * 260 * Read the compressed buffer, and uncompress it into the actual 261 * pages. 262 * 263 * Returns 0 for success or -1 for error 264 * 265 * @p: Params for the channel that we are using 266 * @errp: pointer to an error 267 */ 268 static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp) 269 { 270 struct wd_data *uadk_data = p->compress_data; 271 uint32_t in_size = p->next_packet_size; 272 uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; 273 uint32_t hdr_len = p->normal_num * sizeof(uint32_t); 274 uint32_t data_len = 0; 275 uint8_t *buf = uadk_data->buf; 276 int ret = 0; 277 278 if (flags != MULTIFD_FLAG_UADK) { 279 error_setg(errp, "multifd %u: flags received %x flags expected %x", 280 p->id, flags, MULTIFD_FLAG_ZLIB); 281 return -1; 282 } 283 284 multifd_recv_zero_page_process(p); 285 if (!p->normal_num) { 286 assert(in_size == 0); 287 return 0; 288 } 289 290 /* read compressed data lengths */ 291 assert(hdr_len < in_size); 292 ret = qio_channel_read_all(p->c, (void *) uadk_data->buf_hdr, 293 hdr_len, errp); 294 if (ret != 0) { 295 return ret; 296 } 297 298 for (int i = 0; i < p->normal_num; i++) { 299 uadk_data->buf_hdr[i] = be32_to_cpu(uadk_data->buf_hdr[i]); 300 data_len += uadk_data->buf_hdr[i]; 301 assert(uadk_data->buf_hdr[i] <= p->page_size); 302 } 303 304 /* read compressed data */ 305 assert(in_size == hdr_len + data_len); 306 ret = qio_channel_read_all(p->c, (void *)buf, data_len, errp); 307 if (ret != 0) { 308 return ret; 309 } 310 311 for (int i = 0; i < p->normal_num; i++) { 312 struct wd_comp_req creq = { 313 .op_type = WD_DIR_DECOMPRESS, 314 .src = buf, 315 .src_len = uadk_data->buf_hdr[i], 316 .dst = p->host + p->normal[i], 317 .dst_len = p->page_size, 318 }; 319 320 if (uadk_data->buf_hdr[i] == p->page_size) { 321 memcpy(p->host + p->normal[i], buf, p->page_size); 322 buf += p->page_size; 323 continue; 324 } 325 326 ret = wd_do_comp_sync(uadk_data->handle, &creq); 327 if (ret || creq.status) { 328 error_setg(errp, "multifd %u: failed decompression, ret %d status %d", 329 p->id, ret, creq.status); 330 return -1; 331 } 332 if (creq.dst_len != p->page_size) { 333 error_setg(errp, "multifd %u: decompressed length error", p->id); 334 return -1; 335 } 336 buf += uadk_data->buf_hdr[i]; 337 } 338 339 return 0; 340 } 341 342 static MultiFDMethods multifd_uadk_ops = { 343 .send_setup = multifd_uadk_send_setup, 344 .send_cleanup = multifd_uadk_send_cleanup, 345 .send_prepare = multifd_uadk_send_prepare, 346 .recv_setup = multifd_uadk_recv_setup, 347 .recv_cleanup = multifd_uadk_recv_cleanup, 348 .recv = multifd_uadk_recv, 349 }; 350 351 static void multifd_uadk_register(void) 352 { 353 multifd_register_ops(MULTIFD_COMPRESSION_UADK, &multifd_uadk_ops); 354 } 355 migration_init(multifd_uadk_register); 356