Preparation for converting to GObject --- server/Makefile.am | 2 + server/inputs-channel.c | 2 +- server/main-channel-client.c | 552 ++++++++++++++++++++++++++++++++++++++ server/main-channel-client.h | 163 ++++++++++++ server/main-channel.c | 616 +++---------------------------------------- server/main-channel.h | 27 +- server/red-worker.c | 1 + server/reds.c | 1 + server/sound.c | 1 + server/stream.c | 1 + 10 files changed, 764 insertions(+), 602 deletions(-) create mode 100644 server/main-channel-client.c create mode 100644 server/main-channel-client.h diff --git a/server/Makefile.am b/server/Makefile.am index a119c86..786cd42 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -86,6 +86,8 @@ libserver_la_SOURCES = \ lz4-encoder.h \ main-channel.c \ main-channel.h \ + main-channel-client.c \ + main-channel-client.h \ mjpeg-encoder.c \ mjpeg-encoder.h \ red-channel.c \ diff --git a/server/inputs-channel.c b/server/inputs-channel.c index da26cf6..b768c95 100644 --- a/server/inputs-channel.c +++ b/server/inputs-channel.c @@ -39,7 +39,7 @@ #include "reds.h" #include "reds-stream.h" #include "red-channel.h" -#include "main-channel.h" +#include "main-channel-client.h" #include "inputs-channel.h" #include "migration-protocol.h" #include "utils.h" diff --git a/server/main-channel-client.c b/server/main-channel-client.c new file mode 100644 index 0000000..bed0f55 --- /dev/null +++ b/server/main-channel-client.c @@ -0,0 +1,552 @@ +/* + Copyright (C) 2009-2015 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <inttypes.h> +#include "main-channel-client.h" +#include "main-channel-client.h" +#include "main-channel.h" +#include "reds.h" + +#define NET_TEST_WARMUP_BYTES 0 +#define NET_TEST_BYTES (1024 * 250) + +enum NetTestStage { + NET_TEST_STAGE_INVALID, + NET_TEST_STAGE_WARMUP, + NET_TEST_STAGE_LATENCY, + NET_TEST_STAGE_RATE, + NET_TEST_STAGE_COMPLETE, +}; + +#define CLIENT_CONNECTIVITY_TIMEOUT (MSEC_PER_SEC * 30) +#define PING_INTERVAL (MSEC_PER_SEC * 10) + +struct MainChannelClient { + RedChannelClient base; + uint32_t connection_id; + uint32_t ping_id; + uint32_t net_test_id; + int net_test_stage; + uint64_t latency; + uint64_t bitrate_per_sec; +#ifdef RED_STATISTICS + SpiceTimer *ping_timer; + int ping_interval; +#endif + int mig_wait_connect; + int mig_connect_ok; + int mig_wait_prev_complete; + int mig_wait_prev_try_seamless; + int init_sent; + int seamless_mig_dst; +}; + +RedPipeItem *main_notify_item_new(void *data, int num) +{ + RedNotifyPipeItem *item = spice_malloc(sizeof(RedNotifyPipeItem)); + const char *msg = data; + + red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NOTIFY); + item->msg = spice_strdup(msg); + return &item->base; +} + +void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate) +{ + if (!mcc || mcc->net_test_id) { + return; + } + if (test_rate) { + if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES) + && main_channel_client_push_ping(mcc, 0) + && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) { + mcc->net_test_id = mcc->ping_id - 2; + mcc->net_test_stage = NET_TEST_STAGE_WARMUP; + } + } else { + red_channel_client_start_connectivity_monitoring(&mcc->base, CLIENT_CONNECTIVITY_TIMEOUT); + } +} + +static RedPipeItem *red_ping_item_new(int size) +{ + RedPingPipeItem *item = spice_malloc(sizeof(RedPingPipeItem)); + + red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_PING); + item->size = size; + return &item->base; +} + +int main_channel_client_push_ping(MainChannelClient *mcc, int size) +{ + RedPipeItem *item; + + if (mcc == NULL) { + return FALSE; + } + item = red_ping_item_new(size); + red_channel_client_pipe_add_push(&mcc->base, item); + return TRUE; +} + +static RedPipeItem *main_agent_tokens_item_new(uint32_t num_tokens) +{ + RedTokensPipeItem *item = spice_malloc(sizeof(RedTokensPipeItem)); + + red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN); + item->tokens = num_tokens; + return &item->base; +} + + +void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens) +{ + RedPipeItem *item = main_agent_tokens_item_new(num_tokens); + + red_channel_client_pipe_add_push(&mcc->base, item); +} + +static RedPipeItem *main_agent_data_item_new(uint8_t* data, size_t len, + spice_marshaller_item_free_func free_data, + void *opaque) +{ + RedAgentDataPipeItem *item = spice_malloc(sizeof(RedAgentDataPipeItem)); + + red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA); + item->data = data; + item->len = len; + item->free_data = free_data; + item->opaque = opaque; + return &item->base; +} + +void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len, + spice_marshaller_item_free_func free_data, void *opaque) +{ + RedPipeItem *item; + + item = main_agent_data_item_new(data, len, free_data, opaque); + red_channel_client_pipe_add_push(&mcc->base, item); +} + +static RedPipeItem *main_init_item_new(int connection_id, + int display_channels_hint, + int current_mouse_mode, + int is_client_mouse_allowed, + int multi_media_time, + int ram_hint) +{ + RedInitPipeItem *item = spice_malloc(sizeof(RedInitPipeItem)); + + red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_INIT); + item->connection_id = connection_id; + item->display_channels_hint = display_channels_hint; + item->current_mouse_mode = current_mouse_mode; + item->is_client_mouse_allowed = is_client_mouse_allowed; + item->multi_media_time = multi_media_time; + item->ram_hint = ram_hint; + return &item->base; +} + +void main_channel_client_push_init(MainChannelClient *mcc, + int display_channels_hint, + int current_mouse_mode, + int is_client_mouse_allowed, + int multi_media_time, + int ram_hint) +{ + RedPipeItem *item; + + item = main_init_item_new(mcc->connection_id, display_channels_hint, + current_mouse_mode, is_client_mouse_allowed, + multi_media_time, ram_hint); + red_channel_client_pipe_add_push(&mcc->base, item); +} + +static RedPipeItem *main_name_item_new(const char *name) +{ + RedNamePipeItem *item = spice_malloc(sizeof(RedNamePipeItem) + strlen(name) + 1); + + red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NAME); + item->msg.name_len = strlen(name) + 1; + memcpy(&item->msg.name, name, item->msg.name_len); + + return &item->base; +} + +void main_channel_client_push_name(MainChannelClient *mcc, const char *name) +{ + RedPipeItem *item; + + if (!red_channel_client_test_remote_cap(&mcc->base, + SPICE_MAIN_CAP_NAME_AND_UUID)) + return; + + item = main_name_item_new(name); + red_channel_client_pipe_add_push(&mcc->base, item); +} + +static RedPipeItem *main_uuid_item_new(const uint8_t uuid[16]) +{ + RedUuidPipeItem *item = spice_malloc(sizeof(RedUuidPipeItem)); + + red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_UUID); + memcpy(item->msg.uuid, uuid, sizeof(item->msg.uuid)); + + return &item->base; +} + +void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16]) +{ + RedPipeItem *item; + + if (!red_channel_client_test_remote_cap(&mcc->base, + SPICE_MAIN_CAP_NAME_AND_UUID)) + return; + + item = main_uuid_item_new(uuid); + red_channel_client_pipe_add_push(&mcc->base, item); +} + +void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg) +{ + RedPipeItem *item = main_notify_item_new((void *)msg, 1); + red_channel_client_pipe_add_push(&mcc->base, item); +} + +void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, + int success, + int seamless) +{ + spice_printerr("client %p connected: %d seamless %d", mcc->base.client, success, seamless); + if (mcc->mig_wait_connect) { + MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel, MainChannel, base); + + mcc->mig_wait_connect = FALSE; + mcc->mig_connect_ok = success; + spice_assert(main_channel->num_clients_mig_wait); + spice_assert(!seamless || main_channel->num_clients_mig_wait == 1); + if (!--main_channel->num_clients_mig_wait) { + reds_on_main_migrate_connected(mcc->base.channel->reds, seamless && success); + } + } else { + if (success) { + spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client); + red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL); + } + } +} + +void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc, + uint32_t src_version) +{ + if (reds_on_migrate_dst_set_seamless(mcc->base.channel->reds, mcc, src_version)) { + mcc->seamless_mig_dst = TRUE; + red_channel_client_pipe_add_empty_msg(&mcc->base, + SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK); + } else { + red_channel_client_pipe_add_empty_msg(&mcc->base, + SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK); + } +} +void main_channel_client_handle_pong(MainChannelClient *mcc, SpiceMsgPing *ping, uint32_t size) +{ + uint64_t roundtrip; + RedChannelClient* rcc = (RedChannelClient*)mcc; + + roundtrip = g_get_monotonic_time() - ping->timestamp; + + if (ping->id == mcc->net_test_id) { + switch (mcc->net_test_stage) { + case NET_TEST_STAGE_WARMUP: + mcc->net_test_id++; + mcc->net_test_stage = NET_TEST_STAGE_LATENCY; + mcc->latency = roundtrip; + break; + case NET_TEST_STAGE_LATENCY: + mcc->net_test_id++; + mcc->net_test_stage = NET_TEST_STAGE_RATE; + mcc->latency = MIN(mcc->latency, roundtrip); + break; + case NET_TEST_STAGE_RATE: + mcc->net_test_id = 0; + if (roundtrip <= mcc->latency) { + // probably high load on client or server result with incorrect values + spice_printerr("net test: invalid values, latency %" PRIu64 + " roundtrip %" PRIu64 ". assuming high" + "bandwidth", mcc->latency, roundtrip); + mcc->latency = 0; + mcc->net_test_stage = NET_TEST_STAGE_INVALID; + red_channel_client_start_connectivity_monitoring(&mcc->base, + CLIENT_CONNECTIVITY_TIMEOUT); + break; + } + mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000 + / (roundtrip - mcc->latency); + mcc->net_test_stage = NET_TEST_STAGE_COMPLETE; + spice_printerr("net test: latency %f ms, bitrate %"PRIu64" bps (%f Mbps)%s", + (double)mcc->latency / 1000, + mcc->bitrate_per_sec, + (double)mcc->bitrate_per_sec / 1024 / 1024, + main_channel_client_is_low_bandwidth(mcc) ? " LOW BANDWIDTH" : ""); + red_channel_client_start_connectivity_monitoring(&mcc->base, + CLIENT_CONNECTIVITY_TIMEOUT); + break; + default: + spice_printerr("invalid net test stage, ping id %d test id %d stage %d", + ping->id, + mcc->net_test_id, + mcc->net_test_stage); + mcc->net_test_stage = NET_TEST_STAGE_INVALID; + } + return; + } else { + /* + * channel client monitors the connectivity using ping-pong messages + */ + red_channel_client_handle_message(rcc, size, SPICE_MSGC_PONG, ping); + } +#ifdef RED_STATISTICS + stat_update_value(rcc->channel->reds, roundtrip); +#endif +} + +gboolean main_channel_client_get_seamless_migration(MainChannelClient *mcc) +{ + return mcc->seamless_mig_dst; +} + +void main_channel_client_handle_migrate_end(MainChannelClient *mcc) +{ + if (!red_client_during_migrate_at_target(mcc->base.client)) { + spice_printerr("unexpected SPICE_MSGC_MIGRATE_END"); + return; + } + if (!red_channel_client_test_remote_cap(&mcc->base, + SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) { + spice_printerr("unexpected SPICE_MSGC_MIGRATE_END, " + "client does not support semi-seamless migration"); + return; + } + red_client_semi_seamless_migrate_complete(mcc->base.client); +} + +void main_channel_client_migrate_cancel_wait(MainChannelClient *mcc) +{ + if (mcc->mig_wait_connect) { + spice_printerr("client %p cancel wait connect", mcc->base.client); + mcc->mig_wait_connect = FALSE; + mcc->mig_connect_ok = FALSE; + } + mcc->mig_wait_prev_complete = FALSE; +} + +void main_channel_client_migrate_dst_complete(MainChannelClient *mcc) +{ + if (mcc->mig_wait_prev_complete) { + if (mcc->mig_wait_prev_try_seamless) { + spice_assert(mcc->base.channel->clients_num == 1); + red_channel_client_pipe_add_type(&mcc->base, + RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS); + } else { + red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN); + } + mcc->mig_wait_connect = TRUE; + mcc->mig_wait_prev_complete = FALSE; + } +} + +gboolean main_channel_client_migrate_src_complete(MainChannelClient *mcc, + gboolean success) +{ + gboolean ret = FALSE; + int semi_seamless_support = red_channel_client_test_remote_cap(&mcc->base, + SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE); + if (semi_seamless_support && mcc->mig_connect_ok) { + if (success) { + spice_printerr("client %p MIGRATE_END", mcc->base.client); + red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_END); + ret = TRUE; + } else { + spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client); + red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL); + } + } else { + if (success) { + spice_printerr("client %p SWITCH_HOST", mcc->base.client); + red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST); + } + } + mcc->mig_connect_ok = FALSE; + mcc->mig_wait_connect = FALSE; + + return ret; +} + +uint32_t main_channel_client_get_link_id(MainChannelClient *mcc) +{ + return mcc->connection_id; +} + +#ifdef RED_STATISTICS +static void do_ping_client(MainChannelClient *mcc, + const char *opt, int has_interval, int interval) +{ + spice_printerr(""); + if (!opt) { + main_channel_client_push_ping(mcc, 0); + } else if (!strcmp(opt, "on")) { + if (has_interval && interval > 0) { + mcc->ping_interval = interval * MSEC_PER_SEC; + } + reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc->ping_interval); + } else if (!strcmp(opt, "off")) { + reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer); + } else { + return; + } +} + +static void ping_timer_cb(void *opaque) +{ + MainChannelClient *mcc = opaque; + + if (!red_channel_client_is_connected(&mcc->base)) { + spice_printerr("not connected to peer, ping off"); + reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer); + return; + } + do_ping_client(mcc, NULL, 0, 0); + reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc->ping_interval); +} +#endif /* RED_STATISTICS */ + +MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client, + RedsStream *stream, uint32_t connection_id, + int num_common_caps, uint32_t *common_caps, + int num_caps, uint32_t *caps) +{ + MainChannelClient *mcc = (MainChannelClient*) + red_channel_client_create(sizeof(MainChannelClient), &main_chan->base, + client, stream, FALSE, num_common_caps, + common_caps, num_caps, caps); + spice_assert(mcc != NULL); + mcc->connection_id = connection_id; + mcc->bitrate_per_sec = ~0; +#ifdef RED_STATISTICS + if (!(mcc->ping_timer = reds_core_timer_add(red_channel_get_server(&main_chan->base), ping_timer_cb, mcc))) { + spice_error("ping timer create failed"); + } + mcc->ping_interval = PING_INTERVAL; +#endif + return mcc; +} + +int main_channel_client_is_network_info_initialized(MainChannelClient *mcc) +{ + return mcc->net_test_stage == NET_TEST_STAGE_COMPLETE; +} + +int main_channel_client_is_low_bandwidth(MainChannelClient *mcc) +{ + // TODO: configurable? + return mcc->bitrate_per_sec < 10 * 1024 * 1024; +} + +uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc) +{ + return mcc->bitrate_per_sec; +} + +uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc) +{ + return mcc->latency / 1000; +} + +void main_channel_client_migrate(RedChannelClient *rcc) +{ + reds_on_main_channel_migrate(rcc->channel->reds, SPICE_CONTAINEROF(rcc, MainChannelClient, base)); + red_channel_client_default_migrate(rcc); +} + +gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc) +{ + RedChannelClient *rcc = main_channel_client_get_base(mcc); + MainChannel* main_channel = SPICE_CONTAINEROF(rcc->channel, MainChannel, base); + if (red_channel_client_test_remote_cap(rcc, + SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) { + RedClient *client = red_channel_client_get_client(rcc); + if (red_client_during_migrate_at_target(client)) { + spice_printerr("client %p: wait till previous migration completes", client); + mcc->mig_wait_prev_complete = TRUE; + mcc->mig_wait_prev_try_seamless = FALSE; + } else { + red_channel_client_pipe_add_type(rcc, + RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN); + mcc->mig_wait_connect = TRUE; + } + mcc->mig_connect_ok = FALSE; + main_channel->num_clients_mig_wait++; + return TRUE; + } + return FALSE; +} + +void main_channel_client_connect_seamless(MainChannelClient *mcc) +{ + spice_assert(red_channel_client_test_remote_cap(&mcc->base, + SPICE_MAIN_CAP_SEAMLESS_MIGRATE)); + if (red_client_during_migrate_at_target(mcc->base.client)) { + spice_printerr("client %p: wait till previous migration completes", mcc->base.client); + mcc->mig_wait_prev_complete = TRUE; + mcc->mig_wait_prev_try_seamless = TRUE; + } else { + red_channel_client_pipe_add_type(&mcc->base, + RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS); + mcc->mig_wait_connect = TRUE; + } + mcc->mig_connect_ok = FALSE; +} + +RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc) +{ + spice_assert(mcc); + return &mcc->base; +} + +uint32_t main_channel_client_get_connection_id(MainChannelClient *mcc) +{ + return mcc->connection_id; +} + +uint32_t main_channel_client_next_ping_id(MainChannelClient *mcc) +{ + return ++mcc->ping_id; +} + +void main_channel_client_on_send_init(MainChannelClient *mcc) +{ + mcc->init_sent = TRUE; +} + +gboolean main_channel_client_get_init_sent(MainChannelClient *mcc) +{ + return mcc->init_sent; +} diff --git a/server/main-channel-client.h b/server/main-channel-client.h new file mode 100644 index 0000000..e103edc --- /dev/null +++ b/server/main-channel-client.h @@ -0,0 +1,163 @@ +/* + Copyright (C) 2009-2015 Red Hat, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ +#ifndef __MAIN_CHANNEL_CLIENT_H__ +#define __MAIN_CHANNEL_CLIENT_H__ + +#include "red-channel.h" + +typedef struct MainChannel MainChannel; +typedef struct MainChannelClient MainChannelClient; + +MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client, + RedsStream *stream, uint32_t connection_id, + int num_common_caps, uint32_t *common_caps, + int num_caps, uint32_t *caps); + +void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens); +void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len, + spice_marshaller_item_free_func free_data, void *opaque); +void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate); +// TODO: huge. Consider making a reds_* interface for these functions +// and calling from main. +void main_channel_client_push_init(MainChannelClient *mcc, + int display_channels_hint, + int current_mouse_mode, + int is_client_mouse_allowed, + int multi_media_time, + int ram_hint); +void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg); +void main_channel_client_migrate(RedChannelClient *rcc); +gboolean main_channel_client_connect_semi_seamless(MainChannelClient *mcc); +void main_channel_client_connect_seamless(MainChannelClient *mcc); +void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, + int success, + int seamless); +void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc, + uint32_t src_version); +void main_channel_client_handle_migrate_end(MainChannelClient *mcc); +void main_channel_client_migrate_cancel_wait(MainChannelClient *mcc); +void main_channel_client_migrate_dst_complete(MainChannelClient *mcc); +gboolean main_channel_client_migrate_src_complete(MainChannelClient *mcc, + gboolean success); + +int main_channel_client_push_ping(MainChannelClient *mcc, int size); +void main_channel_client_handle_pong(MainChannelClient *mcc, SpiceMsgPing *ping, uint32_t size); + +uint32_t main_channel_client_get_link_id(MainChannelClient *mcc); + +/* + * return TRUE if network test had been completed successfully. + * If FALSE, bitrate_per_sec is set to MAX_UINT64 and the roundtrip is set to 0 + */ +int main_channel_client_is_network_info_initialized(MainChannelClient *mcc); +int main_channel_client_is_low_bandwidth(MainChannelClient *mcc); +uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc); +uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc); + +RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc); + +void main_channel_client_push_name(MainChannelClient *mcc, const char *name); +void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16]); + +uint32_t main_channel_client_get_connection_id(MainChannelClient *mcc); +uint32_t main_channel_client_next_ping_id(MainChannelClient *mcc); + +gboolean main_channel_client_get_seamless_migration(MainChannelClient *mcc); +void main_channel_client_on_send_init(MainChannelClient *mcc); +gboolean main_channel_client_get_init_sent(MainChannelClient *mcc); + +enum { + RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST = RED_PIPE_ITEM_TYPE_CHANNEL_BASE, + RED_PIPE_ITEM_TYPE_MAIN_PING, + RED_PIPE_ITEM_TYPE_MAIN_MOUSE_MODE, + RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED, + RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN, + RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA, + RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA, + RED_PIPE_ITEM_TYPE_MAIN_INIT, + RED_PIPE_ITEM_TYPE_MAIN_NOTIFY, + RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN, + RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS, + RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST, + RED_PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME, + RED_PIPE_ITEM_TYPE_MAIN_NAME, + RED_PIPE_ITEM_TYPE_MAIN_UUID, + RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS, +}; + +typedef struct RefsPipeItem { + RedPipeItem base; + int *refs; +} RefsPipeItem; + +typedef struct RedPingPipeItem { + RedPipeItem base; + int size; +} RedPingPipeItem; + +typedef struct RedMouseModePipeItem { + RedPipeItem base; + int current_mode; + int is_client_mouse_allowed; +} RedMouseModePipeItem; + +typedef struct RedTokensPipeItem { + RedPipeItem base; + int tokens; +} RedTokensPipeItem; + +typedef struct RedAgentDataPipeItem { + RedPipeItem base; + uint8_t* data; + size_t len; + spice_marshaller_item_free_func free_data; + void *opaque; +} RedAgentDataPipeItem; + +typedef struct RedInitPipeItem { + RedPipeItem base; + int connection_id; + int display_channels_hint; + int current_mouse_mode; + int is_client_mouse_allowed; + int multi_media_time; + int ram_hint; +} RedInitPipeItem; + +typedef struct RedNamePipeItem { + RedPipeItem base; + SpiceMsgMainName msg; +} RedNamePipeItem; + +typedef struct RedUuidPipeItem { + RedPipeItem base; + SpiceMsgMainUuid msg; +} RedUuidPipeItem; + +typedef struct RedNotifyPipeItem { + RedPipeItem base; + char *msg; +} RedNotifyPipeItem; + +typedef struct RedMultiMediaTimePipeItem { + RedPipeItem base; + int time; +} RedMultiMediaTimePipeItem; + +RedPipeItem *main_notify_item_new(void *data, int num); + +#endif /* __MAIN_CHANNEL_CLIENT_H__ */ diff --git a/server/main-channel.c b/server/main-channel.c index 6fc21b7..f762b18 100644 --- a/server/main-channel.c +++ b/server/main-channel.c @@ -41,6 +41,7 @@ #include "demarshallers.h" #include "main-channel.h" +#include "main-channel-client.h" #include "red-channel.h" #include "red-common.h" #include "reds.h" @@ -50,121 +51,8 @@ #define ZERO_BUF_SIZE 4096 -#define NET_TEST_WARMUP_BYTES 0 -#define NET_TEST_BYTES (1024 * 250) - -#define PING_INTERVAL (MSEC_PER_SEC * 10) - -#define CLIENT_CONNECTIVITY_TIMEOUT (MSEC_PER_SEC * 30) - static const uint8_t zero_page[ZERO_BUF_SIZE] = {0}; -enum { - RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST = RED_PIPE_ITEM_TYPE_CHANNEL_BASE, - RED_PIPE_ITEM_TYPE_MAIN_PING, - RED_PIPE_ITEM_TYPE_MAIN_MOUSE_MODE, - RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED, - RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN, - RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA, - RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA, - RED_PIPE_ITEM_TYPE_MAIN_INIT, - RED_PIPE_ITEM_TYPE_MAIN_NOTIFY, - RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN, - RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS, - RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST, - RED_PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME, - RED_PIPE_ITEM_TYPE_MAIN_NAME, - RED_PIPE_ITEM_TYPE_MAIN_UUID, - RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS, -}; - -typedef struct RedRefsPipeItem { - RedPipeItem base; - int *refs; -} RedRefsPipeItem; - -typedef struct RedPingPipeItem { - RedPipeItem base; - int size; -} RedPingPipeItem; - -typedef struct RedMouseModePipeItem { - RedPipeItem base; - int current_mode; - int is_client_mouse_allowed; -} RedMouseModePipeItem; - -typedef struct RedTokensPipeItem { - RedPipeItem base; - int tokens; -} RedTokensPipeItem; - -typedef struct RedAgentDataPipeItem { - RedPipeItem base; - uint8_t* data; - size_t len; - spice_marshaller_item_free_func free_data; - void *opaque; -} RedAgentDataPipeItem; - -typedef struct RedInitPipeItem { - RedPipeItem base; - int connection_id; - int display_channels_hint; - int current_mouse_mode; - int is_client_mouse_allowed; - int multi_media_time; - int ram_hint; -} RedInitPipeItem; - -typedef struct RedNamePipeItem { - RedPipeItem base; - SpiceMsgMainName msg; -} RedNamePipeItem; - -typedef struct RedUuidPipeItem { - RedPipeItem base; - SpiceMsgMainUuid msg; -} RedUuidPipeItem; - -typedef struct RedNotifyPipeItem { - RedPipeItem base; - char *msg; -} RedNotifyPipeItem; - -typedef struct RedMultiMediaTimePipeItem { - RedPipeItem base; - int time; -} RedMultiMediaTimePipeItem; - -struct MainChannelClient { - RedChannelClient base; - uint32_t connection_id; - uint32_t ping_id; - uint32_t net_test_id; - int net_test_stage; - uint64_t latency; - uint64_t bitrate_per_sec; -#ifdef RED_STATISTICS - SpiceTimer *ping_timer; - int ping_interval; -#endif - int mig_wait_connect; - int mig_connect_ok; - int mig_wait_prev_complete; - int mig_wait_prev_try_seamless; - int init_sent; - int seamless_mig_dst; -}; - -enum NetTestStage { - NET_TEST_STAGE_INVALID, - NET_TEST_STAGE_WARMUP, - NET_TEST_STAGE_LATENCY, - NET_TEST_STAGE_RATE, - NET_TEST_STAGE_COMPLETE, -}; - static void main_channel_release_pipe_item(RedChannelClient *rcc, RedPipeItem *base, int item_pushed); @@ -187,35 +75,18 @@ RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t c { RingItem *link; MainChannelClient *mcc; + RedChannelClient *rcc; RING_FOREACH(link, &main_chan->base.clients) { - mcc = SPICE_CONTAINEROF(link, MainChannelClient, base.channel_link); - if (mcc->connection_id == connection_id) { - return mcc->base.client; + rcc = SPICE_CONTAINEROF(link, RedChannelClient, channel_link); + mcc = (MainChannelClient*) rcc; + if (main_channel_client_get_connection_id(mcc) == connection_id) { + return rcc->client; } } return NULL; } -static int main_channel_client_push_ping(MainChannelClient *mcc, int size); - -void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate) -{ - if (!mcc || mcc->net_test_id) { - return; - } - if (test_rate) { - if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES) - && main_channel_client_push_ping(mcc, 0) - && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) { - mcc->net_test_id = mcc->ping_id - 2; - mcc->net_test_stage = NET_TEST_STAGE_WARMUP; - } - } else { - red_channel_client_start_connectivity_monitoring(&mcc->base, CLIENT_CONNECTIVITY_TIMEOUT); - } -} - typedef struct MainMouseModeItemInfo { int current_mode; int is_client_mouse_allowed; @@ -232,88 +103,8 @@ static RedPipeItem *main_mouse_mode_item_new(RedChannelClient *rcc, void *data, return &item->base; } -static RedPipeItem *red_ping_item_new(MainChannelClient *mcc, int size) -{ - RedPingPipeItem *item = spice_malloc(sizeof(RedPingPipeItem)); - - red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_PING); - item->size = size; - return &item->base; -} - -static RedPipeItem *main_agent_tokens_item_new(RedChannelClient *rcc, uint32_t num_tokens) -{ - RedTokensPipeItem *item = spice_malloc(sizeof(RedTokensPipeItem)); - - red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN); - item->tokens = num_tokens; - return &item->base; -} - -static RedPipeItem *main_agent_data_item_new(RedChannelClient *rcc, uint8_t* data, size_t len, - spice_marshaller_item_free_func free_data, - void *opaque) -{ - RedAgentDataPipeItem *item = spice_malloc(sizeof(RedAgentDataPipeItem)); - - red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_AGENT_DATA); - item->data = data; - item->len = len; - item->free_data = free_data; - item->opaque = opaque; - return &item->base; -} - -static RedPipeItem *main_init_item_new(MainChannelClient *mcc, - int connection_id, int display_channels_hint, int current_mouse_mode, - int is_client_mouse_allowed, int multi_media_time, - int ram_hint) -{ - RedInitPipeItem *item = spice_malloc(sizeof(RedInitPipeItem)); - - red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_INIT); - item->connection_id = connection_id; - item->display_channels_hint = display_channels_hint; - item->current_mouse_mode = current_mouse_mode; - item->is_client_mouse_allowed = is_client_mouse_allowed; - item->multi_media_time = multi_media_time; - item->ram_hint = ram_hint; - return &item->base; -} - -static RedPipeItem *main_name_item_new(MainChannelClient *mcc, const char *name) -{ - RedNamePipeItem *item = spice_malloc(sizeof(RedNamePipeItem) + strlen(name) + 1); - - red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NAME); - item->msg.name_len = strlen(name) + 1; - memcpy(&item->msg.name, name, item->msg.name_len); - - return &item->base; -} - -static RedPipeItem *main_uuid_item_new(MainChannelClient *mcc, const uint8_t uuid[16]) -{ - RedUuidPipeItem *item = spice_malloc(sizeof(RedUuidPipeItem)); - - red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_UUID); - memcpy(item->msg.uuid, uuid, sizeof(item->msg.uuid)); - - return &item->base; -} - -static RedPipeItem *main_notify_item_new(RedChannelClient *rcc, void *data, int num) -{ - RedNotifyPipeItem *item = spice_malloc(sizeof(RedNotifyPipeItem)); - const char *msg = data; - - red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_NOTIFY); - item->msg = spice_strdup(msg); - return &item->base; -} - -static RedPipeItem *main_multi_media_time_item_new( - RedChannelClient *rcc, void *data, int num) +static RedPipeItem *main_multi_media_time_item_new(RedChannelClient *rcc, + void *data, int num) { RedMultiMediaTimePipeItem *item, *info = data; @@ -325,12 +116,12 @@ static RedPipeItem *main_multi_media_time_item_new( static void main_channel_push_channels(MainChannelClient *mcc) { - if (red_client_during_migrate_at_target(mcc->base.client)) { + if (red_client_during_migrate_at_target((main_channel_client_get_base(mcc))->client)) { spice_printerr("warning: ignoring unexpected SPICE_MSGC_MAIN_ATTACH_CHANNELS" "during migration"); return; } - red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST); + red_channel_client_pipe_add_type(main_channel_client_get_base(mcc), RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST); } static void main_channel_marshall_channels(RedChannelClient *rcc, @@ -345,28 +136,16 @@ static void main_channel_marshall_channels(RedChannelClient *rcc, free(channels_info); } -int main_channel_client_push_ping(MainChannelClient *mcc, int size) -{ - RedPipeItem *item; - - if (mcc == NULL) { - return FALSE; - } - item = red_ping_item_new(mcc, size); - red_channel_client_pipe_add_push(&mcc->base, item); - return TRUE; -} - static void main_channel_marshall_ping(RedChannelClient *rcc, SpiceMarshaller *m, RedPingPipeItem *item) { - MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base); + MainChannelClient *mcc = (MainChannelClient*)rcc; SpiceMsgPing ping; int size_left = item->size; red_channel_client_init_send_data(rcc, SPICE_MSG_PING, &item->base); - ping.id = ++(mcc->ping_id); + ping.id = main_channel_client_next_ping_id(mcc); ping.timestamp = g_get_monotonic_time(); spice_marshall_msg_ping(m, &ping); @@ -440,13 +219,6 @@ static void main_channel_marshall_agent_disconnected(RedChannelClient *rcc, spice_marshall_msg_main_agent_disconnected(m, &disconnect); } -void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens) -{ - RedPipeItem *item = main_agent_tokens_item_new(&mcc->base, num_tokens); - - red_channel_client_pipe_add_push(&mcc->base, item); -} - static void main_channel_marshall_tokens(RedChannelClient *rcc, SpiceMarshaller *m, RedTokensPipeItem *item) { @@ -457,15 +229,6 @@ static void main_channel_marshall_tokens(RedChannelClient *rcc, spice_marshall_msg_main_agent_token(m, &tokens); } -void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len, - spice_marshaller_item_free_func free_data, void *opaque) -{ - RedPipeItem *item; - - item = main_agent_data_item_new(&mcc->base, data, len, free_data, opaque); - red_channel_client_pipe_add_push(&mcc->base, item); -} - static void main_channel_marshall_agent_data(RedChannelClient *rcc, SpiceMarshaller *m, RedAgentDataPipeItem *item) @@ -490,7 +253,7 @@ static void main_channel_marshall_migrate_data_item(RedChannelClient *rcc, static int main_channel_handle_migrate_data(RedChannelClient *rcc, uint32_t size, void *message) { - MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base); + MainChannelClient *mcc = (MainChannelClient*)rcc; SpiceMigrateDataHeader *header = (SpiceMigrateDataHeader *)message; /* not supported with multi-clients */ @@ -509,21 +272,6 @@ static int main_channel_handle_migrate_data(RedChannelClient *rcc, return reds_handle_migrate_data(rcc->channel->reds, mcc, (SpiceMigrateDataMain *)(header + 1), size); } -void main_channel_client_push_init(MainChannelClient *mcc, - int display_channels_hint, - int current_mouse_mode, - int is_client_mouse_allowed, - int multi_media_time, - int ram_hint) -{ - RedPipeItem *item; - - item = main_init_item_new(mcc, - mcc->connection_id, display_channels_hint, current_mouse_mode, - is_client_mouse_allowed, multi_media_time, ram_hint); - red_channel_client_pipe_add_push(&mcc->base, item); -} - static void main_channel_marshall_init(RedChannelClient *rcc, SpiceMarshaller *m, RedInitPipeItem *item) @@ -546,36 +294,6 @@ static void main_channel_marshall_init(RedChannelClient *rcc, spice_marshall_msg_main_init(m, &init); } -void main_channel_client_push_name(MainChannelClient *mcc, const char *name) -{ - RedPipeItem *item; - - if (!red_channel_client_test_remote_cap(&mcc->base, - SPICE_MAIN_CAP_NAME_AND_UUID)) - return; - - item = main_name_item_new(mcc, name); - red_channel_client_pipe_add_push(&mcc->base, item); -} - -void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16]) -{ - RedPipeItem *item; - - if (!red_channel_client_test_remote_cap(&mcc->base, - SPICE_MAIN_CAP_NAME_AND_UUID)) - return; - - item = main_uuid_item_new(mcc, uuid); - red_channel_client_pipe_add_push(&mcc->base, item); -} - -void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg) -{ - RedPipeItem *item = main_notify_item_new(&mcc->base, (void *)msg, 1); - red_channel_client_pipe_add_push(&mcc->base, item); -} - static void main_channel_marshall_notify(RedChannelClient *rcc, SpiceMarshaller *m, RedNotifyPipeItem *item) { @@ -701,14 +419,16 @@ static void main_channel_marshall_multi_media_time(RedChannelClient *rcc, static void main_channel_send_item(RedChannelClient *rcc, RedPipeItem *base) { - MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base); + MainChannelClient *mcc = (MainChannelClient*)rcc; SpiceMarshaller *m = red_channel_client_get_marshaller(rcc); /* In semi-seamless migration (dest side), the connection is started from scratch, and * we ignore any pipe item that arrives before the INIT msg is sent. * For seamless we don't send INIT, and the connection continues from the same place * it stopped on the src side. */ - if (!mcc->init_sent && !mcc->seamless_mig_dst && base->type != RED_PIPE_ITEM_TYPE_MAIN_INIT) { + if (!main_channel_client_get_init_sent(mcc) && + !main_channel_client_get_seamless_migration(mcc) && + base->type != RED_PIPE_ITEM_TYPE_MAIN_INIT) { spice_printerr("Init msg for client %p was not sent yet " "(client is probably during semi-seamless migration). Ignoring msg type %d", rcc->client, base->type); @@ -745,7 +465,7 @@ static void main_channel_send_item(RedChannelClient *rcc, RedPipeItem *base) main_channel_marshall_migrate_data_item(rcc, m, base); break; case RED_PIPE_ITEM_TYPE_MAIN_INIT: - mcc->init_sent = TRUE; + main_channel_client_on_send_init(mcc); main_channel_marshall_init(rcc, m, SPICE_CONTAINEROF(base, RedInitPipeItem, base)); break; @@ -804,77 +524,11 @@ static void main_channel_release_pipe_item(RedChannelClient *rcc, free(base); } -static void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, - int success, - int seamless) -{ - spice_printerr("client %p connected: %d seamless %d", mcc->base.client, success, seamless); - if (mcc->mig_wait_connect) { - MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel, MainChannel, base); - - mcc->mig_wait_connect = FALSE; - mcc->mig_connect_ok = success; - spice_assert(main_channel->num_clients_mig_wait); - spice_assert(!seamless || main_channel->num_clients_mig_wait == 1); - if (!--main_channel->num_clients_mig_wait) { - reds_on_main_migrate_connected(mcc->base.channel->reds, seamless && success); - } - } else { - if (success) { - spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client); - red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL); - } - } -} - -void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc, - uint32_t src_version) -{ - if (reds_on_migrate_dst_set_seamless(mcc->base.channel->reds, mcc, src_version)) { - mcc->seamless_mig_dst = TRUE; - red_channel_client_pipe_add_empty_msg(&mcc->base, - SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK); - } else { - red_channel_client_pipe_add_empty_msg(&mcc->base, - SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK); - } -} - -void main_channel_client_handle_migrate_end(MainChannelClient *mcc) -{ - if (!red_client_during_migrate_at_target(mcc->base.client)) { - spice_printerr("unexpected SPICE_MSGC_MIGRATE_END"); - return; - } - if (!red_channel_client_test_remote_cap(&mcc->base, - SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) { - spice_printerr("unexpected SPICE_MSGC_MIGRATE_END, " - "client does not support semi-seamless migration"); - return; - } - red_client_semi_seamless_migrate_complete(mcc->base.client); -} - -void main_channel_client_migrate_dst_complete(MainChannelClient *mcc) -{ - if (mcc->mig_wait_prev_complete) { - if (mcc->mig_wait_prev_try_seamless) { - spice_assert(mcc->base.channel->clients_num == 1); - red_channel_client_pipe_add_type(&mcc->base, - RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS); - } else { - red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN); - } - mcc->mig_wait_connect = TRUE; - mcc->mig_wait_prev_complete = FALSE; - } -} - static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type, void *message) { MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base); - MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base); + MainChannelClient *mcc = (MainChannelClient*)rcc; switch (type) { case SPICE_MSGC_MAIN_AGENT_START: { @@ -923,64 +577,7 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint reds_on_main_mouse_mode_request(rcc->channel->reds, message, size); break; case SPICE_MSGC_PONG: { - SpiceMsgPing *ping = (SpiceMsgPing *)message; - uint64_t roundtrip; - - roundtrip = g_get_monotonic_time() - ping->timestamp; - - if (ping->id == mcc->net_test_id) { - switch (mcc->net_test_stage) { - case NET_TEST_STAGE_WARMUP: - mcc->net_test_id++; - mcc->net_test_stage = NET_TEST_STAGE_LATENCY; - mcc->latency = roundtrip; - break; - case NET_TEST_STAGE_LATENCY: - mcc->net_test_id++; - mcc->net_test_stage = NET_TEST_STAGE_RATE; - mcc->latency = MIN(mcc->latency, roundtrip); - break; - case NET_TEST_STAGE_RATE: - mcc->net_test_id = 0; - if (roundtrip <= mcc->latency) { - // probably high load on client or server result with incorrect values - spice_printerr("net test: invalid values, latency %" PRIu64 - " roundtrip %" PRIu64 ". assuming high" - " bandwidth", mcc->latency, roundtrip); - mcc->latency = 0; - mcc->net_test_stage = NET_TEST_STAGE_INVALID; - red_channel_client_start_connectivity_monitoring(&mcc->base, - CLIENT_CONNECTIVITY_TIMEOUT); - break; - } - mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000 - / (roundtrip - mcc->latency); - mcc->net_test_stage = NET_TEST_STAGE_COMPLETE; - spice_printerr("net test: latency %f ms, bitrate %"PRIu64" bps (%f Mbps)%s", - (double)mcc->latency / 1000, - mcc->bitrate_per_sec, - (double)mcc->bitrate_per_sec / 1024 / 1024, - main_channel_client_is_low_bandwidth(mcc) ? " LOW BANDWIDTH" : ""); - red_channel_client_start_connectivity_monitoring(&mcc->base, - CLIENT_CONNECTIVITY_TIMEOUT); - break; - default: - spice_printerr("invalid net test stage, ping id %d test id %d stage %d", - ping->id, - mcc->net_test_id, - mcc->net_test_stage); - mcc->net_test_stage = NET_TEST_STAGE_INVALID; - } - break; - } else { - /* - * channel client monitors the connectivity using ping-pong messages - */ - red_channel_client_handle_message(rcc, size, type, message); - } -#ifdef RED_STATISTICS - stat_update_value(rcc->channel->reds, roundtrip); -#endif + main_channel_client_handle_pong(mcc, (SpiceMsgPing *)message, size); break; } case SPICE_MSGC_DISCONNECTING: @@ -999,7 +596,7 @@ static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc, uint32_t size) { MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base); - MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base); + MainChannelClient *mcc = (MainChannelClient*)rcc; if (type == SPICE_MSGC_MAIN_AGENT_DATA) { return reds_get_agent_data_buffer(rcc->channel->reds, mcc, size); @@ -1035,60 +632,6 @@ static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc) return TRUE; } -#ifdef RED_STATISTICS -static void do_ping_client(MainChannelClient *mcc, - const char *opt, int has_interval, int interval) -{ - spice_printerr(""); - if (!opt) { - main_channel_client_push_ping(mcc, 0); - } else if (!strcmp(opt, "on")) { - if (has_interval && interval > 0) { - mcc->ping_interval = interval * MSEC_PER_SEC; - } - reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc->ping_interval); - } else if (!strcmp(opt, "off")) { - reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer); - } else { - return; - } -} - -static void ping_timer_cb(void *opaque) -{ - MainChannelClient *mcc = opaque; - - if (!red_channel_client_is_connected(&mcc->base)) { - spice_printerr("not connected to peer, ping off"); - reds_core_timer_cancel(mcc->base.channel->reds, mcc->ping_timer); - return; - } - do_ping_client(mcc, NULL, 0, 0); - reds_core_timer_start(mcc->base.channel->reds, mcc->ping_timer, mcc->ping_interval); -} -#endif /* RED_STATISTICS */ - -static MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client, - RedsStream *stream, uint32_t connection_id, - int num_common_caps, uint32_t *common_caps, - int num_caps, uint32_t *caps) -{ - MainChannelClient *mcc = (MainChannelClient*) - red_channel_client_create(sizeof(MainChannelClient), &main_chan->base, - client, stream, FALSE, num_common_caps, - common_caps, num_caps, caps); - spice_assert(mcc != NULL); - mcc->connection_id = connection_id; - mcc->bitrate_per_sec = ~0; -#ifdef RED_STATISTICS - if (!(mcc->ping_timer = reds_core_timer_add(red_channel_get_server(&main_chan->base), ping_timer_cb, mcc))) { - spice_error("ping timer create failed"); - } - mcc->ping_interval = PING_INTERVAL; -#endif - return mcc; -} - MainChannelClient *main_channel_link(MainChannel *channel, RedClient *client, RedsStream *stream, uint32_t connection_id, int migration, int num_common_caps, uint32_t *common_caps, int num_caps, @@ -1128,33 +671,6 @@ void main_channel_close(MainChannel *main_chan) } } -int main_channel_client_is_network_info_initialized(MainChannelClient *mcc) -{ - return mcc->net_test_stage == NET_TEST_STAGE_COMPLETE; -} - -int main_channel_client_is_low_bandwidth(MainChannelClient *mcc) -{ - // TODO: configurable? - return mcc->bitrate_per_sec < 10 * 1024 * 1024; -} - -uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc) -{ - return mcc->bitrate_per_sec; -} - -uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc) -{ - return mcc->latency / 1000; -} - -static void main_channel_client_migrate(RedChannelClient *rcc) -{ - reds_on_main_channel_migrate(rcc->channel->reds, SPICE_CONTAINEROF(rcc, MainChannelClient, base)); - red_channel_client_default_migrate(rcc); -} - MainChannel* main_channel_new(RedsState *reds) { RedChannel *channel; @@ -1190,33 +706,16 @@ MainChannel* main_channel_new(RedsState *reds) return (MainChannel *)channel; } -RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc) -{ - spice_assert(mcc); - return &mcc->base; -} - static int main_channel_connect_semi_seamless(MainChannel *main_channel) { RingItem *client_link; RING_FOREACH(client_link, &main_channel->base.clients) { - MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, - base.channel_link); - if (red_channel_client_test_remote_cap(&mcc->base, - SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) { - if (red_client_during_migrate_at_target(mcc->base.client)) { - spice_printerr("client %p: wait till previous migration completes", mcc->base.client); - mcc->mig_wait_prev_complete = TRUE; - mcc->mig_wait_prev_try_seamless = FALSE; - } else { - red_channel_client_pipe_add_type(&mcc->base, - RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN); - mcc->mig_wait_connect = TRUE; - } - mcc->mig_connect_ok = FALSE; + RedChannelClient *rcc = SPICE_CONTAINEROF(client_link, RedChannelClient, + channel_link); + MainChannelClient *mcc = (MainChannelClient*)rcc; + if (main_channel_client_connect_semi_seamless(mcc)) main_channel->num_clients_mig_wait++; - } } return main_channel->num_clients_mig_wait; } @@ -1228,20 +727,10 @@ static int main_channel_connect_seamless(MainChannel *main_channel) spice_assert(main_channel->base.clients_num == 1); RING_FOREACH(client_link, &main_channel->base.clients) { - MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, - base.channel_link); - spice_assert(red_channel_client_test_remote_cap(&mcc->base, - SPICE_MAIN_CAP_SEAMLESS_MIGRATE)); - if (red_client_during_migrate_at_target(mcc->base.client)) { - spice_printerr("client %p: wait till previous migration completes", mcc->base.client); - mcc->mig_wait_prev_complete = TRUE; - mcc->mig_wait_prev_try_seamless = TRUE; - } else { - red_channel_client_pipe_add_type(&mcc->base, - RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS); - mcc->mig_wait_connect = TRUE; - } - mcc->mig_connect_ok = FALSE; + RedChannelClient *rcc = SPICE_CONTAINEROF(client_link, RedChannelClient, + channel_link); + MainChannelClient *mcc = (MainChannelClient*)rcc; + main_channel_client_connect_seamless(mcc); main_channel->num_clients_mig_wait++; } return main_channel->num_clients_mig_wait; @@ -1261,12 +750,12 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta return main_channel_connect_semi_seamless(main_channel); } else { RingItem *client_item; - MainChannelClient *mcc; + RedChannelClient *rcc; client_item = ring_get_head(&main_channel->base.clients); - mcc = SPICE_CONTAINEROF(client_item, MainChannelClient, base.channel_link); + rcc = SPICE_CONTAINEROF(client_item, RedChannelClient, channel_link); - if (!red_channel_client_test_remote_cap(&mcc->base, + if (!red_channel_client_test_remote_cap(rcc, SPICE_MAIN_CAP_SEAMLESS_MIGRATE)) { return main_channel_connect_semi_seamless(main_channel); } else { @@ -1281,15 +770,10 @@ void main_channel_migrate_cancel_wait(MainChannel *main_chan) RingItem *client_link; RING_FOREACH(client_link, &main_chan->base.clients) { - MainChannelClient *mcc; - - mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link); - if (mcc->mig_wait_connect) { - spice_printerr("client %p cancel wait connect", mcc->base.client); - mcc->mig_wait_connect = FALSE; - mcc->mig_connect_ok = FALSE; - } - mcc->mig_wait_prev_complete = FALSE; + RedChannelClient *rcc = SPICE_CONTAINEROF(client_link, RedChannelClient, + channel_link); + MainChannelClient *mcc = (MainChannelClient*)rcc; + main_channel_client_migrate_cancel_wait(mcc); } main_chan->num_clients_mig_wait = 0; } @@ -1307,29 +791,11 @@ int main_channel_migrate_src_complete(MainChannel *main_chan, int success) } RING_FOREACH(client_link, &main_chan->base.clients) { - MainChannelClient *mcc; - int semi_seamless_support; - - mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link); - semi_seamless_support = red_channel_client_test_remote_cap(&mcc->base, - SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE); - if (semi_seamless_support && mcc->mig_connect_ok) { - if (success) { - spice_printerr("client %p MIGRATE_END", mcc->base.client); - red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_END); - semi_seamless_count++; - } else { - spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client); - red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL); - } - } else { - if (success) { - spice_printerr("client %p SWITCH_HOST", mcc->base.client); - red_channel_client_pipe_add_type(&mcc->base, RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST); - } - } - mcc->mig_connect_ok = FALSE; - mcc->mig_wait_connect = FALSE; + RedChannelClient *rcc = SPICE_CONTAINEROF(client_link, RedChannelClient, + channel_link); + MainChannelClient *mcc = (MainChannelClient*)rcc; + if (main_channel_client_migrate_src_complete(mcc, success)) + semi_seamless_count++; } return semi_seamless_count; } diff --git a/server/main-channel.h b/server/main-channel.h index 34e91b0..43a2679 100644 --- a/server/main-channel.h +++ b/server/main-channel.h @@ -22,6 +22,7 @@ #include <spice/vd_agent.h> #include "common/marshaller.h" #include "red-channel.h" +#include "main-channel-client.h" // TODO: Defines used to calculate receive buffer size, and also by reds.c // other options: is to make a reds_main_consts.h, to duplicate defines. @@ -58,34 +59,11 @@ void main_channel_close(MainChannel *main_chan); // not destroy, just socket clo void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode, int is_client_mouse_allowed); void main_channel_push_agent_connected(MainChannel *main_chan); void main_channel_push_agent_disconnected(MainChannel *main_chan); -void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens); -void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len, - spice_marshaller_item_free_func free_data, void *opaque); -void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate); -// TODO: huge. Consider making a reds_* interface for these functions -// and calling from main. -void main_channel_client_push_init(MainChannelClient *mcc, - int display_channels_hint, - int current_mouse_mode, - int is_client_mouse_allowed, - int multi_media_time, - int ram_hint); -void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg); void main_channel_push_multi_media_time(MainChannel *main_chan, int time); int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen); int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen); -/* - * return TRUE if network test had been completed successfully. - * If FALSE, bitrate_per_sec is set to MAX_UINT64 and the roundtrip is set to 0 - */ -int main_channel_client_is_network_info_initialized(MainChannelClient *mcc); -int main_channel_client_is_low_bandwidth(MainChannelClient *mcc); -uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc); -uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc); - int main_channel_is_connected(MainChannel *main_chan); -RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc); /* switch host migration */ void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target); @@ -99,8 +77,5 @@ int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_ta void main_channel_migrate_cancel_wait(MainChannel *main_chan); /* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/ int main_channel_migrate_src_complete(MainChannel *main_chan, int success); -void main_channel_client_migrate_dst_complete(MainChannelClient *mcc); -void main_channel_client_push_name(MainChannelClient *mcc, const char *name); -void main_channel_client_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16]); #endif diff --git a/server/red-worker.c b/server/red-worker.c index aa04988..f21fefb 100644 --- a/server/red-worker.c +++ b/server/red-worker.c @@ -51,6 +51,7 @@ #include "red-worker.h" #include "cursor-channel.h" #include "tree.h" +#include "main-channel-client.h" #define CMD_RING_POLL_TIMEOUT 10 //milli #define CMD_RING_POLL_RETRIES 1 diff --git a/server/reds.c b/server/reds.c index efd1429..5551db7 100644 --- a/server/reds.c +++ b/server/reds.c @@ -57,6 +57,7 @@ #include "agent-msg-filter.h" #include "inputs-channel.h" #include "main-channel.h" +#include "main-channel-client.h" #include "red-qxl.h" #include "main-dispatcher.h" #include "sound.h" diff --git a/server/sound.c b/server/sound.c index aae841c..d790c7a 100644 --- a/server/sound.c +++ b/server/sound.c @@ -32,6 +32,7 @@ #include "spice.h" #include "red-common.h" #include "main-channel.h" +#include "main-channel-client.h" #include "reds.h" #include "red-qxl.h" #include "sound.h" diff --git a/server/stream.c b/server/stream.c index 5020eb0..35d2d4f 100644 --- a/server/stream.c +++ b/server/stream.c @@ -20,6 +20,7 @@ #include "stream.h" #include "display-channel.h" +#include "main-channel-client.h" #define FPS_TEST_INTERVAL 1 #define FOREACH_STREAMS(display, item) \ -- 2.4.11 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel