[Spice-devel] [RFC] Adding compression to the webdav folder sharing

Snir Sheriber ssheribe at redhat.com
Sun Aug 21 15:16:16 UTC 2016


Adding lz4 compression same as in spicevmc channel.
Compression will be enabled only if LZ4 is in use && socket
is not AF_LOCAL type (local) && data size is under threshold
&& host has lz4 compression capability.

-handling compressed msgs is implemented in src/channel-webdav.c
-compression of msgs is implemented in src/vmcstream.c
---
 src/channel-webdav.c | 57 ++++++++++++++++++++++++++++++++++++++++++
 src/vmcstream.c      | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 127 insertions(+)

diff --git a/src/channel-webdav.c b/src/channel-webdav.c
index 4ecc769..39598a5 100644
--- a/src/channel-webdav.c
+++ b/src/channel-webdav.c
@@ -25,6 +25,10 @@
 #include "vmcstream.h"
 #include "giopipe.h"
 
+#ifdef USE_LZ4
+#include <lz4.h>
+#endif
+
 /**
  * SECTION:channel-webdav
  * @short_description: exports a directory
@@ -531,6 +535,9 @@ static void spice_webdav_channel_init(SpiceWebdavChannel *channel)
 
     GOutputStream *ostream = g_io_stream_get_output_stream(G_IO_STREAM(c->stream));
     c->queue = output_queue_new(ostream);
+#ifdef USE_LZ4
+    spice_channel_set_capability(channel, SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4);
+#endif
 }
 
 static void spice_webdav_channel_finalize(GObject *object)
@@ -593,6 +600,50 @@ static void webdav_handle_msg(SpiceChannel *channel, SpiceMsgIn *in)
         buf, size);
 }
 
+/* coroutine context */
+static int webdav_try_handle_compressed_msg(SpiceChannel *channel, SpiceMsgIn *in)
+{
+    SpiceWebdavChannel *self = SPICE_WEBDAV_CHANNEL(channel);
+    SpiceWebdavChannelPrivate *c = self->priv;
+    int size;
+    int decompressed_size = 0;
+    char *decompressed = NULL;
+
+    SpiceMsgCompressedData *compressed_data_msg = spice_msg_in_parsed(in);
+
+    if (compressed_data_msg->uncompressed_size == 0) {
+        spice_warning("Invalid uncompressed_size");
+        return FALSE;
+    }
+
+    switch (compressed_data_msg->type) {
+#ifdef USE_LZ4
+    case SPICE_DATA_COMPRESSION_TYPE_LZ4:
+        decompressed = g_malloc(compressed_data_msg->uncompressed_size);
+        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("Unknown Compression Type");
+        return FALSE;
+    }
+    if (decompressed_size != compressed_data_msg->uncompressed_size) {
+        spice_warning("Decompress Error decompressed_size=%d expected=%u",
+                      decompressed_size, compressed_data_msg->uncompressed_size);
+        g_free(decompressed);
+        return FALSE;
+    }
+
+    size = decompressed_size;
+    spice_vmc_input_stream_co_data(
+        SPICE_VMC_INPUT_STREAM(g_io_stream_get_input_stream(G_IO_STREAM(c->stream))),
+                              (uint8_t*)decompressed, size);
+    g_free(decompressed);
+    return TRUE;
+}
 
 /* coroutine context */
 static void spice_webdav_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
@@ -604,6 +655,12 @@ static void spice_webdav_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
 
     if (type == SPICE_MSG_SPICEVMC_DATA)
         webdav_handle_msg(channel, msg);
+    else if (type == SPICE_MSG_SPICEVMC_COMPRESSED_DATA) {
+        if(!webdav_try_handle_compressed_msg(channel, msg)) {
+            spice_warning("handling compressed message failed");
+            g_return_if_reached();
+        }
+    }
     else if (parent_class->handle_msg)
         parent_class->handle_msg(channel, msg);
     else
diff --git a/src/vmcstream.c b/src/vmcstream.c
index 0634bce..57f3758 100644
--- a/src/vmcstream.c
+++ b/src/vmcstream.c
@@ -23,6 +23,11 @@
 #include "spice-channel-priv.h"
 #include "gio-coroutine.h"
 
+#ifdef USE_LZ4
+#include <lz4.h>
+#define COMPRESS_THRESHOLD 1000
+#endif
+
 struct _SpiceVmcInputStream
 {
     GInputStream parent_instance;
@@ -43,6 +48,9 @@ struct _SpiceVmcInputStreamClass
     GInputStreamClass parent_class;
 };
 
+#ifdef USE_LZ4
+static int try_write_compress_LZ4(SpiceChannel *channel, const uint8_t *data, int count);
+#endif
 static gssize   spice_vmc_input_stream_read        (GInputStream        *stream,
                                                     void                *buffer,
                                                     gsize                count,
@@ -373,6 +381,63 @@ spice_vmc_output_stream_new(SpiceChannel *channel)
     return self;
 }
 
+#ifdef USE_LZ4
+static int try_write_compress_LZ4(SpiceChannel *channel, const uint8_t *data, int count)
+{
+    SpiceChannelPrivate *c;
+    SpiceMsgOut *msg_out_compressed;
+    int bound, compressed_data_count;
+    uint8_t *compressed_buf;
+    SpiceMsgCompressedData compressed_data_msg = {
+        .type = SPICE_DATA_COMPRESSION_TYPE_LZ4,
+        .uncompressed_size = count
+    };
+
+    c = SPICE_CHANNEL(channel)->priv;
+    if (g_socket_get_family(c->sock) == G_SOCKET_FAMILY_UNIX) {
+        /* AF_LOCAL socket - data will not be compressed */
+        return FALSE;
+    }
+    if (count <= COMPRESS_THRESHOLD) {
+        /* Not enough data to compress */
+        return FALSE;
+    }
+    if (!spice_channel_test_capability(SPICE_CHANNEL(channel),
+                                       SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4)) {
+        /* No server compression capability - data will not be compressed */
+        return FALSE;
+    }
+    bound = LZ4_compressBound(count);
+    if (bound == 0) {
+        /* Invalid bound - data will not be compressed */
+        return FALSE;
+    }
+
+    compressed_buf = g_malloc(bound);
+    compressed_data_count = LZ4_compress_default((char*)data,
+                                                 (char*)compressed_buf,
+                                                 count,
+                                                 bound);
+    if (compressed_data_count > 0 && compressed_data_count < count) {
+        compressed_data_msg.compressed_data = compressed_buf;
+        msg_out_compressed = spice_msg_out_new(SPICE_CHANNEL(channel),
+                                               SPICE_MSGC_SPICEVMC_COMPRESSED_DATA);
+        msg_out_compressed->marshallers->msg_SpiceMsgCompressedData(msg_out_compressed->marshaller,
+                                                                    &compressed_data_msg);
+        spice_marshaller_add_ref_full(msg_out_compressed->marshaller,
+                                      compressed_data_msg.compressed_data,
+                                      compressed_data_count,
+                                      (spice_marshaller_item_free_func)g_free,
+                                      channel);
+        spice_msg_out_send(msg_out_compressed);
+        return TRUE;
+    }
+    /* if not - free & fallback to sending the message uncompressed */
+    g_free(compressed_buf);
+    return FALSE;
+}
+#endif
+
 static gssize
 spice_vmc_output_stream_write_fn(GOutputStream   *stream,
                                  const void      *buffer,
@@ -383,6 +448,11 @@ spice_vmc_output_stream_write_fn(GOutputStream   *stream,
     SpiceVmcOutputStream *self = SPICE_VMC_OUTPUT_STREAM(stream);
     SpiceMsgOut *msg_out;
 
+#ifdef USE_LZ4
+    if (try_write_compress_LZ4(SPICE_CHANNEL(self->channel), buffer, count)) {
+        return count;
+    }
+#endif
     msg_out = spice_msg_out_new(SPICE_CHANNEL(self->channel),
                                 SPICE_MSGC_SPICEVMC_DATA);
     spice_marshaller_add(msg_out->marshaller, buffer, count);
-- 
2.5.5



More information about the Spice-devel mailing list