[Spice-devel] [server PATCH] LZ4 compression is now available at the Spicevmc channel

Snir Sheriber ssheribe at redhat.com
Sun Apr 3 15:41:12 UTC 2016


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



More information about the Spice-devel mailing list