[Spice-devel] [spice-gtk 2/2] Usbredir: enable lz4 compression
Victor Toso
victortoso at redhat.com
Thu Jul 7 15:19:24 UTC 2016
From: Snir Sheriber <ssheribe at redhat.com>
Compressed message type is CompressedData which contains compression
type (1 byte) followed by the uncompressed data size (4 bytes-exists
only if data was compressed) followed by the compressed data
If SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4 capability is available &&
data_size > COMPRESS_THRESHOLD && !AF_LOCAL data will be sent
compressed otherwise data will be sent uncompressed (as well if
compression has failed)
Update required spice-protocol to 0.12.12
Signed-off-by: Snir Sheriber <ssheribe at redhat.com>
Acked-by: Victor Toso <victortoso at redhat.com>
---
configure.ac | 2 +-
src/channel-usbredir.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 124 insertions(+), 7 deletions(-)
diff --git a/configure.ac b/configure.ac
index 6e65188..0373288 100644
--- a/configure.ac
+++ b/configure.ac
@@ -69,7 +69,7 @@ AC_CHECK_LIBM
AC_SUBST(LIBM)
AC_CONFIG_SUBDIRS([spice-common])
-PKG_CHECK_MODULES([SPICE_PROTOCOL], [spice-protocol >= 0.12.11])
+PKG_CHECK_MODULES([SPICE_PROTOCOL], [spice-protocol >= 0.12.12])
COMMON_CFLAGS='-I ${top_srcdir}/spice-common/ ${SPICE_PROTOCOL_CFLAGS}'
AC_SUBST(COMMON_CFLAGS)
diff --git a/src/channel-usbredir.c b/src/channel-usbredir.c
index 6595508..d420a96 100644
--- a/src/channel-usbredir.c
+++ b/src/channel-usbredir.c
@@ -24,6 +24,9 @@
#ifdef USE_USBREDIR
#include <glib/gi18n.h>
#include <usbredirhost.h>
+#ifdef USE_LZ4
+#include <lz4.h>
+#endif
#ifdef USE_POLKIT
#include "usb-acl-helper.h"
#endif
@@ -32,6 +35,7 @@
#include "usbutil.h"
#endif
+#include "common/log.h"
#include "spice-client.h"
#include "spice-common.h"
@@ -51,6 +55,7 @@
#ifdef USE_USBREDIR
+#define COMPRESS_THRESHOLD 1000
#define SPICE_USBREDIR_CHANNEL_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), SPICE_TYPE_USBREDIR_CHANNEL, SpiceUsbredirChannelPrivate))
@@ -239,6 +244,7 @@ static void channel_set_handlers(SpiceChannelClass *klass)
{
static const spice_msg_handler handlers[] = {
[ SPICE_MSG_SPICEVMC_DATA ] = usbredir_handle_msg,
+ [ SPICE_MSG_SPICEVMC_COMPRESSED_DATA ] = usbredir_handle_msg,
};
spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
@@ -275,6 +281,9 @@ void spice_usbredir_channel_set_context(SpiceUsbredirChannel *channel,
#if USBREDIR_VERSION >= 0x000701
usbredirhost_set_buffered_output_size_cb(priv->host, usbredir_buffered_output_size_callback);
#endif
+#ifdef USE_LZ4
+ spice_channel_set_capability(channel, SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4);
+#endif
}
static gboolean spice_usbredir_channel_open_device(
@@ -638,11 +647,71 @@ static void usbredir_free_write_cb_data(uint8_t *data, void *user_data)
usbredirhost_free_write_buffer(priv->host, data);
}
+#ifdef USE_LZ4
+static int try_write_compress_LZ4(SpiceUsbredirChannel *channel, 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 = (uint8_t*)spice_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,
+ usbredir_free_write_cb_data,
+ channel);
+ spice_msg_out_send(msg_out_compressed);
+ return TRUE;
+ }
+ /* if not - free & fallback to sending the message uncompressed */
+ free(compressed_buf);
+ return FALSE;
+}
+#endif
+
static int usbredir_write_callback(void *user_data, uint8_t *data, int count)
{
SpiceUsbredirChannel *channel = user_data;
SpiceMsgOut *msg_out;
+#ifdef USE_LZ4
+ if (try_write_compress_LZ4(channel, data, count))
+ return count;
+#endif
msg_out = spice_msg_out_new(SPICE_CHANNEL(channel),
SPICE_MSGC_SPICEVMC_DATA);
spice_marshaller_add_ref_full(msg_out->marshaller, data, count,
@@ -732,11 +801,49 @@ static void spice_usbredir_channel_up(SpiceChannel *c)
usbredirhost_write_guest_data(priv->host);
}
+static int try_handle_compressed_msg(SpiceMsgCompressedData *compressed_data_msg,
+ uint8_t **buf,
+ int *size) {
+ int decompressed_size = 0;
+ char *decompressed = NULL;
+
+ 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;
+ *buf = (uint8_t*)decompressed;
+ return TRUE;
+
+}
+
static void usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *in)
{
SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(c);
SpiceUsbredirChannelPrivate *priv = channel->priv;
- int r, size;
+ int r = 0, size;
uint8_t *buf;
g_return_if_fail(priv->host != NULL);
@@ -744,13 +851,23 @@ static void usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *in)
/* No recursion allowed! */
g_return_if_fail(priv->read_buf == NULL);
- buf = spice_msg_in_raw(in, &size);
- priv->read_buf = buf;
- priv->read_buf_size = size;
+ if (spice_msg_in_type(in) == SPICE_MSG_SPICEVMC_COMPRESSED_DATA) {
+ SpiceMsgCompressedData *compressed_data_msg = spice_msg_in_parsed(in);
+ if (try_handle_compressed_msg(compressed_data_msg, &buf, &size)) {
+ priv->read_buf_size = size;
+ priv->read_buf = buf;
+ } else {
+ r = usbredirhost_read_parse_error;
+ }
+ } else { /* Regular SPICE_MSG_SPICEVMC_DATA msg */
+ buf = spice_msg_in_raw(in, &size);
+ priv->read_buf_size = size;
+ priv->read_buf = buf;
+ }
spice_usbredir_channel_lock(channel);
-
- r = usbredirhost_read_guest_data(priv->host);
+ if (r == 0)
+ r = usbredirhost_read_guest_data(priv->host);
if (r != 0) {
SpiceUsbDevice *spice_device = priv->spice_device;
device_error_data err_data;
--
2.7.4
More information about the Spice-devel
mailing list