Compressed message type is CompressedData which contains compression type (1 byte) followed by the uncompressed data size (4 bytes) followed by the compressed data size (4 bytes) followed by the compressed data If SPICE_USBREDIR_CAP_DATA_COMPRESS_LZ4 capability is available && data_size > COMPRESS_THRESHOLD data will be sent compressed otherwise data will be sent uncompressed (also if compression has failed) --- server/spicevmc.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 114 insertions(+), 11 deletions(-) diff --git a/server/spicevmc.c b/server/spicevmc.c index 1050fde..0466df4 100644 --- a/server/spicevmc.c +++ b/server/spicevmc.c @@ -34,6 +34,9 @@ #include "red-channel.h" #include "reds.h" #include "migration-protocol.h" +#ifdef USE_LZ4 +#include <lz4.h> +#endif /* todo: add flow control. i.e., * (a) limit the tokens available for the client @@ -41,11 +44,14 @@ */ /* 64K should be enough for all but the largest writes + 32 bytes hdr */ #define BUF_SIZE (64 * 1024 + 32) +#define COMPRESS_THRESHOLD 1000 typedef struct SpiceVmcPipeItem { PipeItem base; uint32_t refs; + SpiceDataCompressionType type; + uint32_t uncompressed_data_size; /* writes which don't fit this will get split, this is not a problem */ uint8_t buf[BUF_SIZE]; uint32_t buf_used; @@ -79,6 +85,15 @@ enum { PIPE_ITEM_TYPE_PORT_EVENT, }; +static uint8_t *spicevmc_red_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, + uint16_t type, + uint32_t size); + +static void spicevmc_red_channel_release_msg_rcv_buf(RedChannelClient *rcc, + uint16_t type, + uint32_t size, + uint8_t *msg); + static SpiceVmcPipeItem *spicevmc_pipe_item_ref(SpiceVmcPipeItem *item) { item->refs++; @@ -121,6 +136,7 @@ static SpiceCharDeviceMsgToClient *spicevmc_chardev_read_msg_from_dev(SpiceCharD if (!state->pipe_item) { msg_item = spice_new0(SpiceVmcPipeItem, 1); msg_item->refs = 1; + msg_item->type = SPICE_DATA_COMPRESSION_TYPE_NONE; pipe_item_init(&msg_item->base, PIPE_ITEM_TYPE_SPICEVMC_DATA); } else { spice_assert(state->pipe_item->buf_used == 0); @@ -132,6 +148,34 @@ static SpiceCharDeviceMsgToClient *spicevmc_chardev_read_msg_from_dev(SpiceCharD sizeof(msg_item->buf)); if (n > 0) { spice_debug("read from dev %d", n); +#ifdef USE_LZ4 + SpiceVmcPipeItem *msg_item_compressed; + int bound, compressed_data_count; + + if (n > COMPRESS_THRESHOLD && + red_channel_test_remote_cap(&state->channel, SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4) && + ((bound = LZ4_compressBound(n)) != 0)) { + if (bound < BUF_SIZE){ + msg_item_compressed = spice_new0(SpiceVmcPipeItem, 1); + msg_item_compressed->refs = 1; + pipe_item_init(&msg_item_compressed->base, PIPE_ITEM_TYPE_SPICEVMC_DATA); + compressed_data_count = LZ4_compress_default((char*)&msg_item->buf, + (char*)&msg_item_compressed->buf, n, bound); + + if (compressed_data_count < 1) {/*LZ4 compression failed-fallback a non-compressed data is to be sent*/ + spice_warning("Compress Error"); + free(msg_item_compressed); + } else { + msg_item_compressed->type = SPICE_DATA_COMPRESSION_TYPE_LZ4; + msg_item_compressed->uncompressed_data_size = n; + msg_item_compressed->buf_used = compressed_data_count; + free(msg_item); + return msg_item_compressed; + } + } + } +#endif + msg_item->uncompressed_data_size = 0; msg_item->buf_used = n; return msg_item; } else { @@ -278,10 +322,10 @@ static int spicevmc_channel_client_handle_migrate_data(RedChannelClient *rcc, return spice_char_device_state_restore(state->chardev_st, &mig_data->base); } -static int spicevmc_red_channel_client_handle_message(RedChannelClient *rcc, - uint16_t type, +static int spicevmc_red_channel_client_handle_message_parsed(RedChannelClient *rcc, uint32_t size, - uint8_t *msg) + uint16_t type, + void *msg) { SpiceVmcState *state; SpiceCharDeviceInterface *sif; @@ -296,18 +340,55 @@ static int spicevmc_red_channel_client_handle_message(RedChannelClient *rcc, spice_char_device_write_buffer_add(state->chardev_st, state->recv_from_client_buf); state->recv_from_client_buf = NULL; break; + case SPICE_MSGC_SPICEVMC_COMPRESSED_DATA: { + /*NOTE: msg free by free() (when cb to spicevmc_red_channel_release_msg_rcv_buf + * with the compressed msg type), decompressed is free by the char-device */ + uint32_t decompressed_size; + char* decompressed; + SpiceMsgCompressedData *compressed_data_msg = (SpiceMsgCompressedData*)msg; + + decompressed = (char*)spicevmc_red_channel_alloc_msg_rcv_buf(rcc,SPICE_MSGC_SPICEVMC_DATA, + compressed_data_msg->uncompressed_size); + switch (compressed_data_msg->type) { +#ifdef USE_LZ4 + case SPICE_DATA_COMPRESSION_TYPE_LZ4: + decompressed_size = LZ4_decompress_safe ((char*)compressed_data_msg->compressed_data, + decompressed, + compressed_data_msg->compressed_size, + compressed_data_msg->uncompressed_size); + break; +#endif + default: + spice_warning("Invalid Compression Type"); + spicevmc_red_channel_release_msg_rcv_buf(rcc, SPICE_MSGC_SPICEVMC_DATA, + compressed_data_msg->uncompressed_size, + (uint8_t*)decompressed); + return FALSE; + } + if (decompressed_size != compressed_data_msg->uncompressed_size) { + spice_warning("Decompression Error"); + spicevmc_red_channel_release_msg_rcv_buf(rcc, SPICE_MSGC_SPICEVMC_DATA, + compressed_data_msg->uncompressed_size, + (uint8_t*)decompressed); + return FALSE; + } + spice_assert(state->recv_from_client_buf->buf == (uint8_t*)decompressed); + state->recv_from_client_buf->buf_used = decompressed_size; + spice_char_device_write_buffer_add(state->chardev_st, state->recv_from_client_buf); + state->recv_from_client_buf = NULL; + break; + } case SPICE_MSGC_PORT_EVENT: if (size != sizeof(uint8_t)) { spice_warning("bad port event message size"); return FALSE; } if (sif->base.minor_version >= 2 && sif->event != NULL) - sif->event(state->chardev_sin, *msg); + sif->event(state->chardev_sin, *(uint8_t*)msg); break; default: - return red_channel_client_handle_message(rcc, size, type, msg); + return red_channel_client_handle_message(rcc, size, type, (uint8_t*)msg); } - return TRUE; } @@ -371,8 +452,27 @@ static void spicevmc_red_channel_send_data(RedChannelClient *rcc, { SpiceVmcPipeItem *i = SPICE_CONTAINEROF(item, SpiceVmcPipeItem, base); - red_channel_client_init_send_data(rcc, SPICE_MSG_SPICEVMC_DATA, item); - spice_marshaller_add_ref(m, i->buf, i->buf_used); + switch (i->type){ + case SPICE_DATA_COMPRESSION_TYPE_NONE: + red_channel_client_init_send_data(rcc, SPICE_MSG_SPICEVMC_DATA, item); + spice_marshaller_add_ref(m, i->buf, i->buf_used); + break; + case SPICE_DATA_COMPRESSION_TYPE_LZ4: { + SpiceMsgCompressedData compressed_msg; + + red_channel_client_init_send_data(rcc, SPICE_MSG_SPICEVMC_COMPRESSED_DATA, item); + compressed_msg.type = SPICE_DATA_COMPRESSION_TYPE_LZ4; + compressed_msg.uncompressed_size = i->uncompressed_data_size; + compressed_msg.compressed_size = i->buf_used; + + spice_marshall_SpiceMsgCompressedData(m, &compressed_msg); + spice_marshaller_add_ref(m, i->buf, i->buf_used); + break; + } + default: + spice_warning("Invalid Compression Type"); + + } } static void spicevmc_red_channel_send_migrate_data(RedChannelClient *rcc, @@ -519,17 +619,20 @@ SpiceCharDeviceState *spicevmc_device_connect(RedsState *reds, channel_cbs.handle_migrate_flush_mark = spicevmc_channel_client_handle_migrate_flush_mark; channel_cbs.handle_migrate_data = spicevmc_channel_client_handle_migrate_data; - state = (SpiceVmcState*)red_channel_create(sizeof(SpiceVmcState), reds, + state = (SpiceVmcState*)red_channel_create_parser(sizeof(SpiceVmcState), reds, reds_get_core_interface(reds), channel_type, id[channel_type]++, FALSE /* handle_acks */, - spicevmc_red_channel_client_handle_message, + spice_get_client_channel_parser(SPICE_CHANNEL_USBREDIR, NULL), + spicevmc_red_channel_client_handle_message_parsed, &channel_cbs, SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER); red_channel_init_outgoing_messages_window(&state->channel); client_cbs.connect = spicevmc_connect; red_channel_register_client_cbs(&state->channel, &client_cbs); - +#ifdef USE_LZ4 + red_channel_set_cap(&state->channel, SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4); +#endif char_dev_cbs.read_one_msg_from_device = spicevmc_chardev_read_msg_from_dev; char_dev_cbs.ref_msg_to_client = spicevmc_chardev_ref_msg_to_client; char_dev_cbs.unref_msg_to_client = spicevmc_chardev_unref_msg_to_client; -- 2.4.11 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel