[Spice-devel] [spice-gtk PATCH v2] spice-channel: check message queue length
Fabiano FidĂȘncio
fidencio at redhat.com
Fri Oct 16 07:02:11 PDT 2015
Victor,
On Mon, Oct 12, 2015 at 10:51 AM, Victor Toso <lists at victortoso.com> wrote:
> CC hans about this one as well.
>
> Btw, this was tested on windows client (related to rhbz#1201387) and it
> seems to improve well.
>
> https://bugzilla.redhat.com/show_bug.cgi?id=1201387
>
> Thanks for any input,
>
> On Fri, Oct 09, 2015 at 02:59:05PM +0200, Victor Toso wrote:
>> When channel wants to send much more data then the wire can handle, the
>> queue grows fast. This patch does not limit the queue growth but
>> introduces an internal API to check if queue size is too big.
>>
>> In the case of usbredir, video devices on high latency can easily
>> trigger this situation.
>>
>> An easy way to test locally is sharing and webcam and simulating high
>> latency with tc:
>> tc qdisc add dev lo root netem delay 100ms
>> tc qdisc change dev lo root netem delay 1000ms
>> tc qdisc del dev lo root netem
>>
>> Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1264156
>> ---
>> src/channel-usbredir.c | 7 +++++++
>> src/spice-channel-priv.h | 2 ++
>> src/spice-channel.c | 27 ++++++++++++++++++++++++++-
>> 3 files changed, 35 insertions(+), 1 deletion(-)
>>
>> diff --git a/src/channel-usbredir.c b/src/channel-usbredir.c
>> index 89f5c9d..fb7bd07 100644
>> --- a/src/channel-usbredir.c
>> +++ b/src/channel-usbredir.c
>> @@ -475,6 +475,13 @@ static void usbredir_write_flush_callback(void *user_data)
>> if (!priv->host)
>> return;
>>
>> + if (spice_channel_has_full_queue (SPICE_CHANNEL(channel))) {
>> + g_warning ("device %04x:%04x is loosing data due high traffic",
>> + spice_usb_device_get_vid(priv->spice_device),
>> + spice_usb_device_get_pid(priv->spice_device));
>> + return;
>> + }
>> +
>> usbredirhost_write_guest_data(priv->host);
>> }
>>
>> diff --git a/src/spice-channel-priv.h b/src/spice-channel-priv.h
>> index 436a521..ae8d01e 100644
>> --- a/src/spice-channel-priv.h
>> +++ b/src/spice-channel-priv.h
>> @@ -111,6 +111,7 @@ struct _SpiceChannelPrivate {
>> gboolean xmit_queue_blocked;
>> STATIC_MUTEX xmit_queue_lock;
>> guint xmit_queue_wakeup_id;
>> + guint64 queue_size;
>>
>> char name[16];
>> enum spice_channel_state state;
>> @@ -171,6 +172,7 @@ void spice_channel_wakeup(SpiceChannel *channel, gboolean cancel);
>>
>> SpiceSession* spice_channel_get_session(SpiceChannel *channel);
>> enum spice_channel_state spice_channel_get_state(SpiceChannel *channel);
>> +gboolean spice_channel_has_full_queue (SpiceChannel *channel);
>>
>> /* coroutine context */
>> typedef void (*handler_msg_in)(SpiceChannel *channel, SpiceMsgIn *msg, gpointer data);
>> diff --git a/src/spice-channel.c b/src/spice-channel.c
>> index 2ce52c7..d574b90 100644
>> --- a/src/spice-channel.c
>> +++ b/src/spice-channel.c
>> @@ -697,10 +697,12 @@ void spice_msg_out_send(SpiceMsgOut *out)
>> {
>> SpiceChannelPrivate *c;
>> gboolean was_empty;
>> + guint32 size;
>>
>> g_return_if_fail(out != NULL);
>> g_return_if_fail(out->channel != NULL);
>> c = out->channel->priv;
>> + size = spice_marshaller_get_total_size(out->marshaller);
>>
>> STATIC_MUTEX_LOCK(c->xmit_queue_lock);
>> if (c->xmit_queue_blocked) {
>> @@ -710,6 +712,7 @@ void spice_msg_out_send(SpiceMsgOut *out)
>>
>> was_empty = g_queue_is_empty(&c->xmit_queue);
>> g_queue_push_tail(&c->xmit_queue, out);
>> + c->queue_size = (was_empty) ? size : c->queue_size + size;
>>
>> /* One wakeup is enough to empty the entire queue -> only do a wakeup
>> if the queue was empty, and there isn't one pending already. */
>> @@ -2104,8 +2107,11 @@ static void spice_channel_iterate_write(SpiceChannel *channel)
>> STATIC_MUTEX_LOCK(c->xmit_queue_lock);
>> out = g_queue_pop_head(&c->xmit_queue);
>> STATIC_MUTEX_UNLOCK(c->xmit_queue_lock);
>> - if (out)
>> + if (out) {
>> + guint32 size = spice_marshaller_get_total_size(out->marshaller);
>> + c->queue_size = (c->queue_size < size) ? 0 : c->queue_size - size;
>> spice_channel_write_msg(channel, out);
>> + }
>> } while (out);
>>
>> spice_channel_flushed(channel, TRUE);
>> @@ -2813,6 +2819,25 @@ enum spice_channel_state spice_channel_get_state(SpiceChannel *channel)
>> return channel->priv->state;
>> }
>>
>> +#define QUEUE_MEMORY_LIMIT_MB 10
>> +
>> +G_GNUC_INTERNAL
>> +gboolean spice_channel_has_full_queue (SpiceChannel *channel)
>> +{
>> + SpiceChannelPrivate *c = channel->priv;
>> + gdouble ret_mb;
>> + guint mb_limit;
>> + const gchar *env = g_getenv("SPICE_CHANNEL_QUEUE_SIZE");
>> + mb_limit = (env == NULL) ? QUEUE_MEMORY_LIMIT_MB : strtoul(env, NULL, 10);
>> +
>> + STATIC_MUTEX_LOCK(c->xmit_queue_lock);
>> + ret_mb = c->queue_size/1024.0/1024.0;
>> + STATIC_MUTEX_UNLOCK(c->xmit_queue_lock);
>> + CHANNEL_DEBUG(channel, "queue length: %u with total size: %4.4f MB (max = %u MB)",
>> + g_queue_get_length(&c->xmit_queue), ret_mb, mb_limit);
>> + return (ret_mb > mb_limit);
>> +}
>> +
>> G_GNUC_INTERNAL
>> void spice_channel_swap(SpiceChannel *channel, SpiceChannel *swap, gboolean swap_msgs)
>> {
>> --
>> 2.5.0
>>
>> _______________________________________________
>> Spice-devel mailing list
>> Spice-devel at lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/spice-devel
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
I had a private conversation with Hans (which he agreed to share in
this email) and here is his suggestion:
15:51 <hansg> Hi, ah yes that one, I had it on my radar at one point...
15:51 <hansg> Overall I think the idea is good, but the dropping of
usbredir data may only be done for isoc packets, not for any other
packets, and data must be dropped a whole packet at a time
15:52 <hansg> So you likely will need to have some code in
usbredirparser or usbredirhost to take care of this for you
15:53 <hansg> Also with video data (what this is about) you really do
not want to drop every other packet, as then you will just end up
generating only unusable frames.
15:54 <hansg> What you want to do is buffer upto a threshold and then
stop accepting packets until the queue has been drained to a
second much lower threshold
15:55 <hansg> Thinking more about this, I think that what you need to
do is add a new callback which usbredirhost can call which is
called "is_space_available" or some such, and have that
return a boolean
15:55 <hansg> Then in usbredirhost when receiving an isoc packet from
an usb device call this callback (if defined) and if it returns
falls drop the packet
15:56 <hansg> Then in spice-gtk defined the callback and have it
return true until the queue grows to above a certain threshold
15:56 <hansg> Once the queue is above the threshold have the callback
return falls till the queue is drained till the
15:56 <hansg> second much lower threshold
15:57 <hansg> Does that make sense ?
15:58 <fidencio> yes, it does.
15:58 <fidencio> do you mind if I c&p this conversation to that thread?
15:58 <hansg> No I do not mind, please do copy and paste it, thanks
15:58 <fidencio> thanks a lot!
Best Regards,
--
Fabiano FidĂȘncio
More information about the Spice-devel
mailing list