[Spice-devel] [PATCH spice 06/11] server/red_channel: support network monitoring
Christophe Fergeau
cfergeau at redhat.com
Tue Apr 10 05:34:51 PDT 2012
On Sun, Apr 08, 2012 at 06:43:15PM +0300, Yonit Halperin wrote:
> 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;
size_th ? what does that mean?
> + } 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));
Why the - 10000?
> + rcc->channel->core->timer_start(rcc->net_monitor.qos.timer, NET_MONITOR_TIMEOUT_MS / 2);
Why half the timeout?
> + 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;
I think it's so_unsent_size
> + }
> +
> + 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;
This could be unsigned I think.
Christophe
> + 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
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/spice-devel/attachments/20120410/6652d8c7/attachment.pgp>
More information about the Spice-devel
mailing list