[Spice-devel] [PATCH spice 06/11] server/red_channel: support network monitoring

Yonit Halperin yhalperi at redhat.com
Sun Apr 8 08:43:15 PDT 2012


If the client's channel has SPICE_COMMON_CAP_QOS_QUERY, the server
channel's can send it SPICE_MSG_QOS_QUERY. The client is supposed to
respond by SPICE_MSG_QOS_ACK right after it receives the one message
that follows SPICE_MSG_QOS_QUERY.
For channels that are characterized mainly by transmissions from the
server to the client, the time it takes to send "large enough" messages
can be used to calculate the current available bandwidth of the channel.

This patch also contains the corresponding update to the spice-common
submodule.

Signed-off-by: Yonit Halperin <yhalperi at redhat.com>
---
 server/inputs_channel.c    |    1 +
 server/main_channel.c      |    5 +-
 server/red_channel.c       |  117 ++++++++++++++++++++++++++++++++++++++++++++
 server/red_channel.h       |   23 +++++++++
 server/red_tunnel_worker.c |    1 +
 server/red_worker.c        |    2 +-
 server/smartcard.c         |    1 +
 server/spicevmc.c          |    1 +
 spice-common               |    2 +-
 9 files changed, 149 insertions(+), 4 deletions(-)

diff --git a/server/inputs_channel.c b/server/inputs_channel.c
index ad247f4..cc0db94 100644
--- a/server/inputs_channel.c
+++ b/server/inputs_channel.c
@@ -500,6 +500,7 @@ static void inputs_connect(RedChannel *channel, RedClient *client,
                                                           channel,
                                                           client,
                                                           stream,
+                                                          FALSE,
                                                           num_common_caps, common_caps,
                                                           num_caps, caps);
     icc->motion_count = 0;
diff --git a/server/main_channel.c b/server/main_channel.c
index 713f121..624b56d 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -996,8 +996,9 @@ static MainChannelClient *main_channel_client_create(MainChannel *main_chan, Red
 {
     MainChannelClient *mcc = (MainChannelClient*)
                              red_channel_client_create(sizeof(MainChannelClient), &main_chan->base,
-                                                       client, stream, num_common_caps,
-                                                       common_caps, num_caps, caps);
+                                                       client, stream, FALSE,
+                                                       num_common_caps, common_caps,
+                                                       num_caps, caps);
 
     mcc->connection_id = connection_id;
     mcc->bitrate_per_sec = ~0;
diff --git a/server/red_channel.c b/server/red_channel.c
index 4858bb5..c43c233 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -24,11 +24,15 @@
 
 #include <stdio.h>
 #include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
+#include <time.h>
+#include <spice/protocol.h>
 
 #include "common/generated_server_marshallers.h"
 #include "common/ring.h"
@@ -37,6 +41,10 @@
 #include "red_channel.h"
 #include "reds.h"
 
+#define NET_MONITOR_TIMEOUT_MS 15000
+#define NET_MONITOR_QOS_QUERY_SIZE 100000
+#define NET_MONITOR_QOS_QUERY_SIZE_MIN 25000
+
 static void red_channel_client_event(int fd, int event, void *data);
 static void red_client_add_channel(RedClient *client, RedChannelClient *rcc);
 static void red_client_remove_channel(RedChannelClient *rcc);
@@ -514,8 +522,28 @@ int red_channel_client_test_remote_cap(RedChannelClient *rcc, uint32_t cap)
                           cap);
 }
 
+static void red_channel_client_net_monitor_qos_timer(void *opaque)
+{
+    RedChannelClient *rcc = opaque;
+    int *qos_state = &rcc->net_monitor.qos.state;
+
+    spice_assert(*qos_state != NET_MONITOR_STATE_INVALID);
+
+    if (*qos_state == NET_MONITOR_STATE_COMPLETE) {
+        *qos_state = NET_MONITOR_STATE_PENDING;
+        rcc->net_monitor.qos.size_th = NET_MONITOR_QOS_QUERY_SIZE;
+    } else {
+        spice_assert(*qos_state == NET_MONITOR_STATE_PENDING);
+        rcc->net_monitor.qos.size_th = MAX(NET_MONITOR_QOS_QUERY_SIZE_MIN,
+                                           (rcc->net_monitor.qos.size_th - 10000));
+        rcc->channel->core->timer_start(rcc->net_monitor.qos.timer, NET_MONITOR_TIMEOUT_MS / 2);
+        spice_debug("size_th=%d", rcc->net_monitor.qos.size_th);
+    }
+}
+
 RedChannelClient *red_channel_client_create(int size, RedChannel *channel, RedClient  *client,
                                             RedsStream *stream,
+                                            int monitor_net,
                                             int num_common_caps, uint32_t *common_caps,
                                             int num_caps, uint32_t *caps)
 {
@@ -570,6 +598,29 @@ RedChannelClient *red_channel_client_create(int size, RedChannel *channel, RedCl
     rcc->id = channel->clients_num;
     red_channel_add_client(channel, rcc);
     red_client_add_channel(client, rcc);
+
+
+    if (monitor_net) {
+        spice_assert(channel->core->timer_add);
+        spice_assert(channel->core->timer_remove);
+        spice_assert(channel->core->timer_start);
+        spice_assert(channel->core->timer_cancel);
+
+        if (red_channel_client_test_remote_common_cap(rcc, SPICE_COMMON_CAP_QOS_QUERY)) {
+            rcc->net_monitor.qos.timer = channel->core->timer_add(
+                red_channel_client_net_monitor_qos_timer, rcc);
+            spice_debug("qos monitoring active");
+            rcc->net_monitor.qos.state = NET_MONITOR_STATE_PENDING;
+            rcc->net_monitor.qos.size_th = NET_MONITOR_QOS_QUERY_SIZE;
+            channel->core->timer_start(rcc->net_monitor.qos.timer, NET_MONITOR_TIMEOUT_MS);
+        } else {
+            spice_printerr("channel %d id %d client %p: qos cap unavailable",
+                           rcc->channel->type,
+                           rcc->channel->id,
+                           rcc->client);
+        }
+    }
+
     return rcc;
 error:
     free(rcc);
@@ -906,6 +957,11 @@ static void red_channel_client_init_outgoing_messages_window(RedChannelClient *r
     red_channel_client_push(rcc);
 }
 
+uint64_t red_channel_client_get_qos_bit_rate(RedChannelClient *rcc)
+{
+    return rcc->net_monitor.qos.bit_rate;
+}
+
 // TODO: this function doesn't make sense because the window should be client (WAN/LAN)
 // specific
 void red_channel_init_outgoing_messages_window(RedChannel *channel)
@@ -944,6 +1000,28 @@ static void red_channel_handle_migrate_data(RedChannelClient *rcc, uint32_t size
     rcc->channel->channel_cbs.handle_migrate_data(rcc, size, message);
 }
 
+static void red_channel_client_handle_qos_ack(RedChannelClient *rcc)
+{
+    struct timespec now;
+    uint64_t send_time;
+
+    spice_debug(NULL);
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    send_time = (now.tv_sec * 1000000000LL + now.tv_nsec) - rcc->net_monitor.qos.start_time;
+    rcc->net_monitor.qos.bit_rate = ((rcc->net_monitor.qos.size * 8) * 1000 * 1000 * 1000) /
+                                     send_time;
+    rcc->net_monitor.qos.timestamp = now.tv_sec * 1000 + (now.tv_nsec / 1000 /1000);
+
+    spice_debug("QOS    %lu (%p, %d, %d): bit rate %.2f (Mbps), size %lu time %lu (ns)",
+                rcc->net_monitor.qos.timestamp,
+                rcc->client, rcc->channel->type,
+                rcc->channel->id, (rcc->net_monitor.qos.bit_rate + 0.0) / 1024 / 1024,
+                rcc->net_monitor.qos.size,
+                send_time);
+    rcc->net_monitor.qos.state = NET_MONITOR_STATE_COMPLETE;
+    rcc->channel->core->timer_start(rcc->net_monitor.qos.timer, NET_MONITOR_TIMEOUT_MS);
+}
+
 int red_channel_client_handle_message(RedChannelClient *rcc, uint32_t size,
                                       uint16_t type, void *message)
 {
@@ -969,6 +1047,9 @@ int red_channel_client_handle_message(RedChannelClient *rcc, uint32_t size,
     case SPICE_MSGC_MIGRATE_DATA:
         red_channel_handle_migrate_data(rcc, size, message);
         break;
+    case SPICE_MSGC_QOS_ACK:
+        red_channel_client_handle_qos_ack(rcc);
+        break;
     default:
         spice_printerr("invalid message type %u", type);
         return FALSE;
@@ -999,6 +1080,30 @@ void red_channel_client_init_send_data(RedChannelClient *rcc, uint16_t msg_type,
     }
 }
 
+static void red_channel_client_send_qos_query(RedChannelClient *rcc, int size)
+{
+    struct timespec now;
+    int so_unsend_size = 0;
+
+    /* retrieving the occupied size of the socket's tcp snd buffer */
+    if (ioctl(rcc->stream->socket, TIOCOUTQ, &so_unsend_size) == -1) {
+        spice_printerr("ioctl(TIOCOUTQ) failed, %s", strerror(errno));
+        so_unsend_size = 0;
+    }
+
+    spice_debug(NULL);
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    rcc->net_monitor.qos.state = NET_MONITOR_STATE_STARTED;
+    rcc->net_monitor.qos.start_time = now.tv_sec *1000 *1000 *1000 + now.tv_nsec;
+    spice_debug("new message size %d occupied tcp snd buf size %d", size, so_unsend_size);
+    rcc->net_monitor.qos.size = size + so_unsend_size;
+    rcc->channel->core->timer_cancel(rcc->net_monitor.qos.timer);
+
+    red_channel_client_switch_to_urgent_sender(rcc);
+    red_channel_client_init_send_data(rcc, SPICE_MSG_QOS_QUERY, NULL);
+    red_channel_client_begin_send_message(rcc);
+}
+
 void red_channel_client_begin_send_message(RedChannelClient *rcc)
 {
     SpiceMarshaller *m = rcc->send_data.marshaller;
@@ -1008,6 +1113,15 @@ void red_channel_client_begin_send_message(RedChannelClient *rcc)
         spice_printerr("BUG: header->type == 0");
         return;
     }
+
+    if (spice_marshaller_get_total_size(m) > rcc->net_monitor.qos.size_th) {
+        if (rcc->net_monitor.qos.state == NET_MONITOR_STATE_PENDING &&
+            !red_channel_client_urgent_marshaller_is_active(rcc)) {
+            red_channel_client_send_qos_query(rcc, spice_marshaller_get_total_size(m));
+            return;
+        }
+    }
+
     spice_marshaller_flush(m);
     rcc->send_data.size = spice_marshaller_get_total_size(m);
     rcc->send_data.header.set_msg_size(&rcc->send_data.header,
@@ -1199,6 +1313,9 @@ void red_channel_client_disconnect(RedChannelClient *rcc)
     }
     reds_stream_free(rcc->stream);
     rcc->stream = NULL;
+    if (rcc->net_monitor.qos.timer) {
+        rcc->channel->core->timer_remove(rcc->net_monitor.qos.timer);
+    }
     red_channel_remove_client(rcc);
     rcc->channel->channel_cbs.on_disconnect(rcc);
 }
diff --git a/server/red_channel.h b/server/red_channel.h
index 5418210..5c718fb 100644
--- a/server/red_channel.h
+++ b/server/red_channel.h
@@ -219,6 +219,13 @@ typedef struct RedChannelCapabilities {
 
 int test_capabilty(uint32_t *caps, int num_caps, uint32_t cap);
 
+enum NetMonitorState {
+    NET_MONITOR_STATE_INVALID,
+    NET_MONITOR_STATE_PENDING,
+    NET_MONITOR_STATE_STARTED,
+    NET_MONITOR_STATE_COMPLETE,
+};
+
 struct RedChannelClient {
     RingItem channel_link;
     RingItem client_link;
@@ -252,6 +259,18 @@ struct RedChannelClient {
         } urgent;
     } send_data;
 
+    struct {
+        struct {
+            int state;
+            uint64_t bit_rate;
+            uint64_t timestamp;
+            uint64_t start_time;
+            uint64_t size;
+            int size_th;
+            SpiceTimer *timer;
+        } qos;
+    } net_monitor;
+
     OutgoingHandler outgoing;
     IncomingHandler incoming;
     int during_send;
@@ -328,6 +347,7 @@ void red_channel_set_data(RedChannel *channel, void *data);
 
 RedChannelClient *red_channel_client_create(int size, RedChannel *channel, RedClient *client,
                                             RedsStream *stream,
+                                            int monitor_net,
                                             int num_common_caps, uint32_t *common_caps,
                                             int num_caps, uint32_t *caps);
 // TODO: tmp, for channels that don't use RedChannel yet (e.g., snd channel), but
@@ -393,6 +413,9 @@ void red_channel_client_begin_send_message(RedChannelClient *rcc);
  */
 SpiceMarshaller *red_channel_client_switch_to_urgent_sender(RedChannelClient *rcc);
 
+/* returns 0 if the bit rate has never been estimated */
+uint64_t red_channel_client_get_qos_bit_rate(RedChannelClient *rcc);
+
 void red_channel_pipe_item_init(RedChannel *channel, PipeItem *item, int type);
 
 // TODO: add back the channel_pipe_add functionality - by adding reference counting
diff --git a/server/red_tunnel_worker.c b/server/red_tunnel_worker.c
index 384c36d..9d77e5a 100644
--- a/server/red_tunnel_worker.c
+++ b/server/red_tunnel_worker.c
@@ -3451,6 +3451,7 @@ static void handle_tunnel_channel_link(RedChannel *channel, RedClient *client,
 
     tcc = (TunnelChannelClient*)red_channel_client_create(sizeof(TunnelChannelClient),
                                                           channel, client, stream,
+                                                          FALSE,
                                                           0, NULL, 0, NULL);
 
     tcc->worker = worker;
diff --git a/server/red_worker.c b/server/red_worker.c
index fd0e32e..9fa3afd 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -9720,7 +9720,7 @@ static CommonChannelClient *common_channel_client_create(int size,
 {
     MainChannelClient *mcc = red_client_get_main(client);
     RedChannelClient *rcc =
-        red_channel_client_create(size, &common->base, client, stream,
+        red_channel_client_create(size, &common->base, client, stream, FALSE,
                                   num_common_caps, common_caps, num_caps, caps);
     CommonChannelClient *common_cc = (CommonChannelClient*)rcc;
     common_cc->worker = common->worker;
diff --git a/server/smartcard.c b/server/smartcard.c
index 894053e..cd51d19 100644
--- a/server/smartcard.c
+++ b/server/smartcard.c
@@ -499,6 +499,7 @@ static void smartcard_connect(RedChannel *channel, RedClient *client,
     RedChannelClient *rcc;
 
     rcc = red_channel_client_create(sizeof(RedChannelClient), channel, client, stream,
+                                    FALSE,
                                     num_common_caps, common_caps,
                                     num_caps, caps);
     red_channel_client_ack_zero_messages_window(rcc);
diff --git a/server/spicevmc.c b/server/spicevmc.c
index 27123d4..67ede47 100644
--- a/server/spicevmc.c
+++ b/server/spicevmc.c
@@ -232,6 +232,7 @@ static void spicevmc_connect(RedChannel *channel, RedClient *client,
     }
 
     rcc = red_channel_client_create(sizeof(RedChannelClient), channel, client, stream,
+                                    FALSE,
                                     num_common_caps, common_caps,
                                     num_caps, caps);
     if (!rcc) {
diff --git a/spice-common b/spice-common
index cc613c0..5b11895 160000
--- a/spice-common
+++ b/spice-common
@@ -1 +1 @@
-Subproject commit cc613c0c3c1ba3fe7017528d0da998a210a35d75
+Subproject commit 5b1189540ad62f2c79815040e93e2ad567304cdb
-- 
1.7.7.6



More information about the Spice-devel mailing list