/* * Multifd common functions * * Copyright (c) 2019-2020 Red Hat Inc * * Authors: * Juan Quintela * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ #ifndef QEMU_MIGRATION_MULTIFD_H #define QEMU_MIGRATION_MULTIFD_H #include "ram.h" typedef struct MultiFDRecvData MultiFDRecvData; bool multifd_send_setup(void); void multifd_send_shutdown(void); void multifd_send_channel_created(void); int multifd_recv_setup(Error **errp); void multifd_recv_cleanup(void); void multifd_recv_shutdown(void); bool multifd_recv_all_channels_created(void); void multifd_recv_new_channel(QIOChannel *ioc, Error **errp); void multifd_recv_sync_main(void); int multifd_send_sync_main(void); bool multifd_queue_page(RAMBlock *block, ram_addr_t offset); bool multifd_recv(void); MultiFDRecvData *multifd_get_recv_data(void); /* Multifd Compression flags */ #define MULTIFD_FLAG_SYNC (1 << 0) /* We reserve 3 bits for compression methods */ #define MULTIFD_FLAG_COMPRESSION_MASK (7 << 1) /* we need to be compatible. Before compression value was 0 */ #define MULTIFD_FLAG_NOCOMP (0 << 1) #define MULTIFD_FLAG_ZLIB (1 << 1) #define MULTIFD_FLAG_ZSTD (2 << 1) /* This value needs to be a multiple of qemu_target_page_size() */ #define MULTIFD_PACKET_SIZE (512 * 1024) typedef struct { uint32_t magic; uint32_t version; uint32_t flags; /* maximum number of allocated pages */ uint32_t pages_alloc; /* non zero pages */ uint32_t normal_pages; /* size of the next packet that contains pages */ uint32_t next_packet_size; uint64_t packet_num; /* zero pages */ uint32_t zero_pages; uint32_t unused32[1]; /* Reserved for future use */ uint64_t unused64[3]; /* Reserved for future use */ char ramblock[256]; /* * This array contains the pointers to: * - normal pages (initial normal_pages entries) * - zero pages (following zero_pages entries) */ uint64_t offset[]; } __attribute__((packed)) MultiFDPacket_t; typedef struct { /* number of used pages */ uint32_t num; /* number of normal pages */ uint32_t normal_num; /* number of allocated pages */ uint32_t allocated; /* offset of each page */ ram_addr_t *offset; RAMBlock *block; } MultiFDPages_t; struct MultiFDRecvData { void *opaque; size_t size; /* for preadv */ off_t file_offset; }; typedef struct { /* Fields are only written at creating/deletion time */ /* No lock required for them, they are read only */ /* channel number */ uint8_t id; /* channel thread name */ char *name; /* channel thread id */ QemuThread thread; bool thread_created; QemuThread tls_thread; bool tls_thread_created; /* communication channel */ QIOChannel *c; /* packet allocated len */ uint32_t packet_len; /* guest page size */ uint32_t page_size; /* number of pages in a full packet */ uint32_t page_count; /* multifd flags for sending ram */ int write_flags; /* sem where to wait for more work */ QemuSemaphore sem; /* syncs main thread and channels */ QemuSemaphore sem_sync; /* multifd flags for each packet */ uint32_t flags; /* * The sender thread has work to do if either of below boolean is set. * * @pending_job: a job is pending * @pending_sync: a sync request is pending * * For both of these fields, they're only set by the requesters, and * cleared by the multifd sender threads. */ bool pending_job; bool pending_sync; /* array of pages to sent. * The owner of 'pages' depends of 'pending_job' value: * pending_job == 0 -> migration_thread can use it. * pending_job != 0 -> multifd_channel can use it. */ MultiFDPages_t *pages; /* thread local variables. No locking required */ /* pointer to the packet */ MultiFDPacket_t *packet; /* size of the next packet that contains pages */ uint32_t next_packet_size; /* packets sent through this channel */ uint64_t packets_sent; /* non zero pages sent through this channel */ uint64_t total_normal_pages; /* zero pages sent through this channel */ uint64_t total_zero_pages; /* buffers to send */ struct iovec *iov; /* number of iovs used */ uint32_t iovs_num; /* used for compression methods */ void *compress_data; } MultiFDSendParams; typedef struct { /* Fields are only written at creating/deletion time */ /* No lock required for them, they are read only */ /* channel number */ uint8_t id; /* channel thread name */ char *name; /* channel thread id */ QemuThread thread; bool thread_created; /* communication channel */ QIOChannel *c; /* packet allocated len */ uint32_t packet_len; /* guest page size */ uint32_t page_size; /* number of pages in a full packet */ uint32_t page_count; /* syncs main thread and channels */ QemuSemaphore sem_sync; /* sem where to wait for more work */ QemuSemaphore sem; /* this mutex protects the following parameters */ QemuMutex mutex; /* should this thread finish */ bool quit; /* multifd flags for each packet */ uint32_t flags; /* global number of generated multifd packets */ uint64_t packet_num; int pending_job; MultiFDRecvData *data; /* thread local variables. No locking required */ /* pointer to the packet */ MultiFDPacket_t *packet; /* size of the next packet that contains pages */ uint32_t next_packet_size; /* packets received through this channel */ uint64_t packets_recved; /* ramblock */ RAMBlock *block; /* ramblock host address */ uint8_t *host; /* non zero pages recv through this channel */ uint64_t total_normal_pages; /* zero pages recv through this channel */ uint64_t total_zero_pages; /* buffers to recv */ struct iovec *iov; /* Pages that are not zero */ ram_addr_t *normal; /* num of non zero pages */ uint32_t normal_num; /* Pages that are zero */ ram_addr_t *zero; /* num of zero pages */ uint32_t zero_num; /* used for de-compression methods */ void *compress_data; } MultiFDRecvParams; typedef struct { /* Setup for sending side */ int (*send_setup)(MultiFDSendParams *p, Error **errp); /* Cleanup for sending side */ void (*send_cleanup)(MultiFDSendParams *p, Error **errp); /* Prepare the send packet */ int (*send_prepare)(MultiFDSendParams *p, Error **errp); /* Setup for receiving side */ int (*recv_setup)(MultiFDRecvParams *p, Error **errp); /* Cleanup for receiving side */ void (*recv_cleanup)(MultiFDRecvParams *p); /* Read all data */ int (*recv)(MultiFDRecvParams *p, Error **errp); } MultiFDMethods; void multifd_register_ops(int method, MultiFDMethods *ops); void multifd_send_fill_packet(MultiFDSendParams *p); bool multifd_send_prepare_common(MultiFDSendParams *p); void multifd_send_zero_page_detect(MultiFDSendParams *p); void multifd_recv_zero_page_process(MultiFDRecvParams *p); static inline void multifd_send_prepare_header(MultiFDSendParams *p) { p->iov[0].iov_len = p->packet_len; p->iov[0].iov_base = p->packet; p->iovs_num++; } void multifd_channel_connect(MultiFDSendParams *p, QIOChannel *ioc); #endif