[Spice-devel] [PATCH spice 06/11] server/red_channel: support network monitoring
Yonit Halperin
yhalperi at redhat.com
Tue Apr 10 05:53:17 PDT 2012
On 04/10/2012 03:34 PM, Christophe Fergeau wrote:
> 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?
>
th=threshold. The minimal size we require for sending QOS_QUERY
>> + } 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?
>
Since the timeout reached, and we no message reached the size limit, I
chose to decrease the limit (for example, if the resolution is very
small, we will never reach the threshold). 10000 was arbitrary choice.
>> + rcc->channel->core->timer_start(rcc->net_monitor.qos.timer, NET_MONITOR_TIMEOUT_MS / 2);
>
> Why half the timeout?
>
In order not to wait too much time.
>> + 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
More information about the Spice-devel
mailing list