/* * 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 "exec/target_page.h" #include "ram.h" typedef struct MultiFDRecvData MultiFDRecvData; typedef struct MultiFDSendData MultiFDSendData; 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 5 bits for compression methods */ #define MULTIFD_FLAG_COMPRESSION_MASK (0x1f << 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) #define MULTIFD_FLAG_QPL (4 << 1) #define MULTIFD_FLAG_UADK (8 << 1) #define MULTIFD_FLAG_QATZIP (16 << 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; RAMBlock *block; /* offset of each page */ ram_addr_t offset[]; } MultiFDPages_t; struct MultiFDRecvData { void *opaque; size_t size; /* for preadv */ off_t file_offset; }; typedef enum { MULTIFD_PAYLOAD_NONE, MULTIFD_PAYLOAD_RAM, } MultiFDPayloadType; typedef union MultiFDPayload { MultiFDPages_t ram; } MultiFDPayload; struct MultiFDSendData { MultiFDPayloadType type; MultiFDPayload u; }; static inline bool multifd_payload_empty(MultiFDSendData *data) { return data->type == MULTIFD_PAYLOAD_NONE; } static inline void multifd_set_payload_type(MultiFDSendData *data, MultiFDPayloadType type) { data->type = type; } 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; /* 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; MultiFDSendData *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 sent through this channel */ uint64_t packets_sent; /* 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; /* 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; /* 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 { /* * The send_setup, send_cleanup, send_prepare are only called on * the QEMU instance at the migration source. */ /* * Setup for sending side. Called once per channel during channel * setup phase. * * Must allocate p->iov. If packets are in use (default), one * extra iovec must be allocated for the packet header. Any memory * allocated in this hook must be released at send_cleanup. * * p->write_flags may be used for passing flags to the QIOChannel. * * p->compression_data may be used by compression methods to store * compression data. */ int (*send_setup)(MultiFDSendParams *p, Error **errp); /* * Cleanup for sending side. Called once per channel during * channel cleanup phase. */ void (*send_cleanup)(MultiFDSendParams *p, Error **errp); /* * Prepare the send packet. Called as a result of multifd_send() * on the client side, with p pointing to the MultiFDSendParams of * a channel that is currently idle. * * Must populate p->iov with the data to be sent, increment * p->iovs_num to match the amount of iovecs used and set * p->next_packet_size with the amount of data currently present * in p->iov. * * Must indicate whether this is a compression packet by setting * p->flags. * * As a last step, if packets are in use (default), must prepare * the packet by calling multifd_send_fill_packet(). */ int (*send_prepare)(MultiFDSendParams *p, Error **errp); /* * The recv_setup, recv_cleanup, recv are only called on the QEMU * instance at the migration destination. */ /* * Setup for receiving side. Called once per channel during * channel setup phase. May be empty. * * May allocate data structures for the receiving of data. May use * p->iov. Compression methods may use p->compress_data. */ int (*recv_setup)(MultiFDRecvParams *p, Error **errp); /* * Cleanup for receiving side. Called once per channel during * channel cleanup phase. May be empty. */ void (*recv_cleanup)(MultiFDRecvParams *p); /* * Data receive method. Called as a result of multifd_recv() on * the client side, with p pointing to the MultiFDRecvParams of a * channel that is currently idle. Only called if there is data * available to receive. * * Must validate p->flags according to what was set at * send_prepare. * * Must read the data from the QIOChannel p->c. */ int (*recv)(MultiFDRecvParams *p, Error **errp); } MultiFDMethods; void multifd_register_ops(int method, const 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); bool multifd_send(MultiFDSendData **send_data); MultiFDSendData *multifd_send_data_alloc(void); static inline uint32_t multifd_ram_page_size(void) { return qemu_target_page_size(); } static inline uint32_t multifd_ram_page_count(void) { return MULTIFD_PACKET_SIZE / qemu_target_page_size(); } void multifd_ram_save_setup(void); void multifd_ram_save_cleanup(void); int multifd_ram_flush_and_sync(void); size_t multifd_ram_payload_size(void); void multifd_ram_fill_packet(MultiFDSendParams *p); int multifd_ram_unfill_packet(MultiFDRecvParams *p, Error **errp); #endif