| /* | 
 |  * Multifd common functions | 
 |  * | 
 |  * Copyright (c) 2019-2020 Red Hat Inc | 
 |  * | 
 |  * Authors: | 
 |  *  Juan Quintela <quintela@redhat.com> | 
 |  * | 
 |  * 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; | 
 |  | 
 | typedef enum { | 
 |     /* No sync request */ | 
 |     MULTIFD_SYNC_NONE = 0, | 
 |     /* Sync locally on the sender threads without pushing messages */ | 
 |     MULTIFD_SYNC_LOCAL, | 
 |     /* | 
 |      * Sync not only on the sender threads, but also push MULTIFD_FLAG_SYNC | 
 |      * message to the wire for each iochannel (which is for a remote sync). | 
 |      * | 
 |      * When remote sync is used, need to be paired with a follow up | 
 |      * RAM_SAVE_FLAG_EOS / RAM_SAVE_FLAG_MULTIFD_FLUSH message on the main | 
 |      * channel. | 
 |      */ | 
 |     MULTIFD_SYNC_ALL, | 
 | } MultiFDSyncReq; | 
 |  | 
 | 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(MultiFDSyncReq req); | 
 | 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) | 
 |  | 
 | /* | 
 |  * If set it means that this packet contains device state | 
 |  * (MultiFDPacketDeviceState_t), not RAM data (MultiFDPacket_t). | 
 |  */ | 
 | #define MULTIFD_FLAG_DEVICE_STATE (32 << 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; | 
 | } __attribute__((packed)) MultiFDPacketHdr_t; | 
 |  | 
 | typedef struct { | 
 |     MultiFDPacketHdr_t hdr; | 
 |  | 
 |     /* 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 { | 
 |     MultiFDPacketHdr_t hdr; | 
 |  | 
 |     char idstr[256]; | 
 |     uint32_t instance_id; | 
 |  | 
 |     /* size of the next packet that contains the actual data */ | 
 |     uint32_t next_packet_size; | 
 | } __attribute__((packed)) MultiFDPacketDeviceState_t; | 
 |  | 
 | typedef struct { | 
 |     /* number of used pages */ | 
 |     uint32_t num; | 
 |     /* number of normal pages */ | 
 |     uint32_t normal_num; | 
 |     /* | 
 |      * Pointer to the ramblock.  NOTE: it's caller's responsibility to make | 
 |      * sure the pointer is always valid! | 
 |      */ | 
 |     RAMBlock *block; | 
 |     /* offset array of each page, managed by multifd */ | 
 |     ram_addr_t *offset; | 
 | } MultiFDPages_t; | 
 |  | 
 | struct MultiFDRecvData { | 
 |     void *opaque; | 
 |     size_t size; | 
 |     /* for preadv */ | 
 |     off_t file_offset; | 
 | }; | 
 |  | 
 | typedef struct { | 
 |     char *idstr; | 
 |     uint32_t instance_id; | 
 |     char *buf; | 
 |     size_t buf_len; | 
 | } MultiFDDeviceState_t; | 
 |  | 
 | typedef enum { | 
 |     MULTIFD_PAYLOAD_NONE, | 
 |     MULTIFD_PAYLOAD_RAM, | 
 |     MULTIFD_PAYLOAD_DEVICE_STATE, | 
 | } MultiFDPayloadType; | 
 |  | 
 | typedef struct MultiFDPayload { | 
 |     MultiFDPages_t ram; | 
 |     MultiFDDeviceState_t device_state; | 
 | } MultiFDPayload; | 
 |  | 
 | struct MultiFDSendData { | 
 |     MultiFDPayloadType type; | 
 |     MultiFDPayload u; | 
 | }; | 
 |  | 
 | static inline bool multifd_payload_empty(MultiFDSendData *data) | 
 | { | 
 |     return data->type == MULTIFD_PAYLOAD_NONE; | 
 | } | 
 |  | 
 | static inline bool multifd_payload_device_state(MultiFDSendData *data) | 
 | { | 
 |     return data->type == MULTIFD_PAYLOAD_DEVICE_STATE; | 
 | } | 
 |  | 
 | static inline void multifd_set_payload_type(MultiFDSendData *data, | 
 |                                             MultiFDPayloadType type) | 
 | { | 
 |     assert(multifd_payload_empty(data)); | 
 |     assert(type != MULTIFD_PAYLOAD_NONE); | 
 |  | 
 |     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 field 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; | 
 |     MultiFDSyncReq pending_sync; | 
 |  | 
 |     MultiFDSendData *data; | 
 |  | 
 |     /* thread local variables. No locking required */ | 
 |  | 
 |     /* pointers to the possible packet types */ | 
 |     MultiFDPacket_t *packet; | 
 |     MultiFDPacketDeviceState_t *packet_device_state; | 
 |     /* 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 */ | 
 |  | 
 |     /* pointers to the possible packet types */ | 
 |     MultiFDPacket_t *packet; | 
 |     MultiFDPacketDeviceState_t *packet_dev_state; | 
 |     /* 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; | 
 |     /* Flags for the QIOChannel */ | 
 |     int read_flags; | 
 | } 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); | 
 |  | 
 | void multifd_channel_connect(MultiFDSendParams *p, QIOChannel *ioc); | 
 | bool multifd_send(MultiFDSendData **send_data); | 
 | MultiFDSendData *multifd_send_data_alloc(void); | 
 | void multifd_send_data_clear(MultiFDSendData *data); | 
 | void multifd_send_data_free(MultiFDSendData *data); | 
 |  | 
 | 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(QEMUFile *f); | 
 | bool multifd_ram_sync_per_round(void); | 
 | bool multifd_ram_sync_per_section(void); | 
 | void multifd_ram_payload_alloc(MultiFDPages_t *pages); | 
 | void multifd_ram_payload_free(MultiFDPages_t *pages); | 
 | void multifd_ram_fill_packet(MultiFDSendParams *p); | 
 | int multifd_ram_unfill_packet(MultiFDRecvParams *p, Error **errp); | 
 |  | 
 | void multifd_send_data_clear_device_state(MultiFDDeviceState_t *device_state); | 
 |  | 
 | void multifd_device_state_send_setup(void); | 
 | void multifd_device_state_send_cleanup(void); | 
 |  | 
 | void multifd_device_state_send_prepare(MultiFDSendParams *p); | 
 |  | 
 | #endif |