mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2026-01-09 22:36:29 +00:00
main: restore state from migration data
Also removed old migration leftovers.
This commit is contained in:
parent
fa9bfd01f1
commit
a180fc5e0b
@ -500,31 +500,26 @@ static void main_channel_marshall_migrate_data_item(RedChannelClient *rcc,
|
||||
reds_marshall_migrate_data(m); // TODO: from reds split. ugly separation.
|
||||
}
|
||||
|
||||
static uint64_t main_channel_handle_migrate_data_get_serial(RedChannelClient *base,
|
||||
static int main_channel_handle_migrate_data(RedChannelClient *rcc,
|
||||
uint32_t size, void *message)
|
||||
{
|
||||
MainMigrateData *data = message;
|
||||
MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
|
||||
SpiceMigrateDataHeader *header = (SpiceMigrateDataHeader *)message;
|
||||
|
||||
if (size < sizeof(*data)) {
|
||||
spice_printerr("bad message size");
|
||||
return 0;
|
||||
}
|
||||
return data->serial;
|
||||
}
|
||||
/* not supported with multi-clients */
|
||||
spice_assert(rcc->channel->clients_num == 1);
|
||||
|
||||
static int main_channel_handle_migrate_data(RedChannelClient *base,
|
||||
uint32_t size, void *message)
|
||||
{
|
||||
MainChannelClient *mcc = SPICE_CONTAINEROF(base, MainChannelClient, base);
|
||||
MainMigrateData *data = message;
|
||||
|
||||
if (size < sizeof(*data)) {
|
||||
spice_printerr("bad message size");
|
||||
if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataMain)) {
|
||||
spice_printerr("bad message size %u", size);
|
||||
return FALSE;
|
||||
}
|
||||
mcc->ping_id = data->ping_id;
|
||||
reds_on_main_receive_migrate_data(data, ((uint8_t*)message) + size);
|
||||
return TRUE;
|
||||
if (!migration_protocol_validate_header(header,
|
||||
SPICE_MIGRATE_DATA_MAIN_MAGIC,
|
||||
SPICE_MIGRATE_DATA_MAIN_VERSION)) {
|
||||
spice_error("bad header");
|
||||
return FALSE;
|
||||
}
|
||||
return reds_handle_migrate_data(mcc, (SpiceMigrateDataMain *)(header + 1), size);
|
||||
}
|
||||
|
||||
void main_channel_push_init(MainChannelClient *mcc,
|
||||
@ -1173,7 +1168,6 @@ MainChannel* main_channel_init(void)
|
||||
channel_cbs.release_recv_buf = main_channel_release_msg_rcv_buf;
|
||||
channel_cbs.handle_migrate_flush_mark = main_channel_handle_migrate_flush_mark;
|
||||
channel_cbs.handle_migrate_data = main_channel_handle_migrate_data;
|
||||
channel_cbs.handle_migrate_data_get_serial = main_channel_handle_migrate_data_get_serial;
|
||||
|
||||
// TODO: set the migration flag of the channel
|
||||
channel = red_channel_create_parser(sizeof(MainChannel), core,
|
||||
|
||||
@ -24,27 +24,6 @@
|
||||
#include "reds.h"
|
||||
#include "red_channel.h"
|
||||
|
||||
/* This is a temporary measure for reds/main split - should not be in a header,
|
||||
* but private (although only reds.c includes main_channel.h) */
|
||||
struct MainMigrateData {
|
||||
uint32_t version;
|
||||
uint32_t serial;
|
||||
uint32_t ping_id;
|
||||
|
||||
uint32_t agent_connected;
|
||||
uint32_t client_agent_started;
|
||||
uint32_t num_client_tokens;
|
||||
uint32_t send_tokens;
|
||||
|
||||
uint32_t read_state;
|
||||
VDIChunkHeader vdi_chunk_header;
|
||||
uint32_t recive_len;
|
||||
uint32_t message_recive_len;
|
||||
uint32_t read_buf_len;
|
||||
|
||||
uint32_t write_queue_size;
|
||||
};
|
||||
|
||||
// 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.
|
||||
#define REDS_AGENT_WINDOW_SIZE 10
|
||||
|
||||
162
server/reds.c
162
server/reds.c
@ -181,6 +181,9 @@ typedef struct VDIPortState {
|
||||
AgentMsgFilter read_filter;
|
||||
|
||||
VDIChunkHeader vdi_chunk_header;
|
||||
|
||||
SpiceMigrateDataMain *mig_data; /* storing it when migration data arrives
|
||||
before agent is attached */
|
||||
} VDIPortState;
|
||||
|
||||
/* messages that are addressed to the agent and are created in the server */
|
||||
@ -716,6 +719,8 @@ void reds_client_disconnect(RedClient *client)
|
||||
* messages read from the agent */
|
||||
reds->agent_state.read_filter.result = AGENT_MSG_FILTER_DISCARD;
|
||||
reds->agent_state.read_filter.discard_all = TRUE;
|
||||
free(reds->agent_state.mig_data);
|
||||
reds->agent_state.mig_data = NULL;
|
||||
|
||||
reds_mig_cleanup();
|
||||
}
|
||||
@ -793,8 +798,8 @@ static void reds_agent_remove(void)
|
||||
|
||||
vdagent = NULL;
|
||||
reds_update_mouse_mode();
|
||||
|
||||
if (reds_main_channel_connected()) {
|
||||
if (reds_main_channel_connected() &&
|
||||
!red_channel_waits_for_migrate_data(&reds->main_channel->base)) {
|
||||
main_channel_push_agent_disconnected(reds->main_channel);
|
||||
}
|
||||
}
|
||||
@ -1343,9 +1348,114 @@ void reds_marshall_migrate_data(SpiceMarshaller *m)
|
||||
agent_state->write_filter.result);
|
||||
}
|
||||
|
||||
void reds_on_main_receive_migrate_data(MainMigrateData *data, uint8_t *end)
|
||||
static int reds_agent_state_restore(SpiceMigrateDataMain *mig_data)
|
||||
{
|
||||
spice_warning("not implemented");
|
||||
VDIPortState *agent_state = &reds->agent_state;
|
||||
uint32_t chunk_header_remaining;
|
||||
|
||||
agent_state->vdi_chunk_header = mig_data->agent2client.chunk_header;
|
||||
spice_assert(mig_data->agent2client.chunk_header_size <= sizeof(VDIChunkHeader));
|
||||
chunk_header_remaining = sizeof(VDIChunkHeader) - mig_data->agent2client.chunk_header_size;
|
||||
if (chunk_header_remaining) {
|
||||
agent_state->read_state = VDI_PORT_READ_STATE_READ_HEADER;
|
||||
agent_state->recive_pos = (uint8_t *)&agent_state->vdi_chunk_header +
|
||||
mig_data->agent2client.chunk_header_size;
|
||||
agent_state->recive_len = chunk_header_remaining;
|
||||
} else {
|
||||
agent_state->message_recive_len = agent_state->vdi_chunk_header.size;
|
||||
}
|
||||
|
||||
if (!mig_data->agent2client.msg_header_done) {
|
||||
uint8_t *partial_msg_header;
|
||||
|
||||
if (!chunk_header_remaining) {
|
||||
uint32_t cur_buf_size;
|
||||
|
||||
agent_state->read_state = VDI_PORT_READ_STATE_READ_DATA;
|
||||
agent_state->current_read_buf = vdi_port_read_buf_get();
|
||||
spice_assert(agent_state->current_read_buf);
|
||||
partial_msg_header = (uint8_t *)mig_data + mig_data->agent2client.msg_header_ptr -
|
||||
sizeof(SpiceMiniDataHeader);
|
||||
memcpy(agent_state->current_read_buf->data,
|
||||
partial_msg_header,
|
||||
mig_data->agent2client.msg_header_partial_len);
|
||||
agent_state->recive_pos = agent_state->current_read_buf->data +
|
||||
mig_data->agent2client.msg_header_partial_len;
|
||||
cur_buf_size = sizeof(agent_state->current_read_buf->data) -
|
||||
mig_data->agent2client.msg_header_partial_len;
|
||||
agent_state->recive_len = MIN(agent_state->message_recive_len, cur_buf_size);
|
||||
agent_state->current_read_buf->len = agent_state->recive_len +
|
||||
mig_data->agent2client.msg_header_partial_len;
|
||||
agent_state->message_recive_len -= agent_state->recive_len;
|
||||
} else {
|
||||
spice_assert(mig_data->agent2client.msg_header_partial_len == 0);
|
||||
}
|
||||
} else {
|
||||
agent_state->read_state = VDI_PORT_READ_STATE_GET_BUFF;
|
||||
agent_state->current_read_buf = NULL;
|
||||
agent_state->recive_pos = NULL;
|
||||
agent_state->read_filter.msg_data_to_read = mig_data->agent2client.msg_remaining;
|
||||
agent_state->read_filter.result = mig_data->agent2client.msg_filter_result;
|
||||
}
|
||||
|
||||
agent_state->read_filter.discard_all = FALSE;
|
||||
agent_state->write_filter.discard_all = !mig_data->client_agent_started;
|
||||
agent_state->client_agent_started = mig_data->client_agent_started;
|
||||
|
||||
agent_state->write_filter.msg_data_to_read = mig_data->client2agent.msg_remaining;
|
||||
agent_state->write_filter.result = mig_data->client2agent.msg_filter_result;
|
||||
|
||||
spice_debug("to agent filter: discard all %d, wait_msg %u, msg_filter_result %d",
|
||||
agent_state->write_filter.discard_all,
|
||||
agent_state->write_filter.msg_data_to_read,
|
||||
agent_state->write_filter.result);
|
||||
spice_debug("from agent filter: discard all %d, wait_msg %u, msg_filter_result %d",
|
||||
agent_state->read_filter.discard_all,
|
||||
agent_state->read_filter.msg_data_to_read,
|
||||
agent_state->read_filter.result);
|
||||
return spice_char_device_state_restore(agent_state->base, &mig_data->agent_base);
|
||||
}
|
||||
|
||||
/*
|
||||
* The agent device is not attached to the dest before migration is completed. It is
|
||||
* attached only after the vm is started. It might be attached before or after
|
||||
* the migration data has reached the server.
|
||||
*/
|
||||
int reds_handle_migrate_data(MainChannelClient *mcc, SpiceMigrateDataMain *mig_data, uint32_t size)
|
||||
{
|
||||
VDIPortState *agent_state = &reds->agent_state;
|
||||
|
||||
if (mig_data->agent_base.connected) {
|
||||
if (agent_state->base) { // agent was attached before migration data has arrived
|
||||
if (!vdagent) {
|
||||
spice_assert(agent_state->plug_generation > 0);
|
||||
main_channel_push_agent_disconnected(reds->main_channel);
|
||||
spice_debug("agent is no longer connected");
|
||||
} else {
|
||||
if (agent_state->plug_generation > 1) {
|
||||
/* spice_char_device_state_reset takes care of not making the device wait for migration data */
|
||||
spice_debug("agent has been detached and reattached before receiving migration data");
|
||||
main_channel_push_agent_disconnected(reds->main_channel);
|
||||
main_channel_push_agent_connected(reds->main_channel);
|
||||
} else {
|
||||
return reds_agent_state_restore(mig_data);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* restore agent starte when the agent gets attached */
|
||||
spice_assert(agent_state->plug_generation == 0);
|
||||
agent_state->mig_data = spice_memdup(mig_data, size);
|
||||
}
|
||||
} else {
|
||||
if (vdagent) {
|
||||
/* spice_char_device_client_remove disables waiting for migration data */
|
||||
spice_char_device_client_remove(agent_state->base,
|
||||
main_channel_client_get_base(mcc)->client);
|
||||
main_channel_push_agent_connected(reds->main_channel);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int sync_write(RedsStream *stream, const void *in_buf, size_t n)
|
||||
@ -1568,6 +1678,18 @@ static int reds_find_client(RedClient *client)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* should be used only when there is one client */
|
||||
static RedClient *reds_get_client(void)
|
||||
{
|
||||
spice_assert(reds->num_clients <= 1);
|
||||
|
||||
if (reds->num_clients == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return SPICE_CONTAINEROF(ring_get_head(&reds->clients), RedClient, link);
|
||||
}
|
||||
|
||||
// TODO: now that main is a separate channel this should
|
||||
// actually be joined with reds_handle_other_links, become reds_handle_link
|
||||
static void reds_handle_main_link(RedLinkInfo *link)
|
||||
@ -1624,6 +1746,9 @@ static void reds_handle_main_link(RedLinkInfo *link)
|
||||
red_client_set_main(client, mcc);
|
||||
|
||||
if (vdagent) {
|
||||
if (mig_target) {
|
||||
spice_warning("unexpected: vdagent attached to destination during migration");
|
||||
}
|
||||
reds->agent_state.read_filter.discard_all = FALSE;
|
||||
reds->agent_state.plug_generation++;
|
||||
}
|
||||
@ -3320,13 +3445,10 @@ static void reds_mig_remove_wait_disconnect_client(RedClient *client)
|
||||
|
||||
static void reds_migrate_channels_seamless(void)
|
||||
{
|
||||
RingItem *client_item;
|
||||
RedClient *client;
|
||||
|
||||
/* seamless migration is supported for only one client for now */
|
||||
spice_assert(reds->num_clients == 1);
|
||||
client_item = ring_get_head(&reds->clients);
|
||||
client = SPICE_CONTAINEROF(client_item, RedClient, link);
|
||||
client = reds_get_client();
|
||||
red_client_migrate(client);
|
||||
}
|
||||
|
||||
@ -3446,9 +3568,27 @@ static SpiceCharDeviceState *attach_to_red_agent(SpiceCharDeviceInstance *sin)
|
||||
state->read_filter.discard_all = FALSE;
|
||||
reds->agent_state.plug_generation++;
|
||||
|
||||
/* we will assoicate the client with the char device, upon reds_on_main_agent_start,
|
||||
* in response to MSGC_AGENT_START */
|
||||
main_channel_push_agent_connected(reds->main_channel);
|
||||
if (reds->agent_state.mig_data) {
|
||||
spice_assert(reds->agent_state.plug_generation == 1);
|
||||
reds_agent_state_restore(reds->agent_state.mig_data);
|
||||
free(reds->agent_state.mig_data);
|
||||
reds->agent_state.mig_data = NULL;
|
||||
} else if (!red_channel_waits_for_migrate_data(&reds->main_channel->base)) {
|
||||
/* we will assoicate the client with the char device, upon reds_on_main_agent_start,
|
||||
* in response to MSGC_AGENT_START */
|
||||
main_channel_push_agent_connected(reds->main_channel);
|
||||
} else {
|
||||
spice_debug("waiting for migration data");
|
||||
if (!spice_char_device_client_exists(reds->agent_state.base, reds_get_client())) {
|
||||
spice_char_device_client_add(reds->agent_state.base,
|
||||
reds_get_client(),
|
||||
TRUE, /* flow control */
|
||||
REDS_VDI_PORT_NUM_RECEIVE_BUFFS,
|
||||
REDS_AGENT_WINDOW_SIZE,
|
||||
~0,
|
||||
TRUE);
|
||||
}
|
||||
}
|
||||
return state->base;
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
#include "common/messages.h"
|
||||
#include "spice.h"
|
||||
#include "red_channel.h"
|
||||
#include "migration_protocol.h"
|
||||
|
||||
#define SPICE_GNUC_VISIBLE __attribute__ ((visibility ("default")))
|
||||
|
||||
@ -152,7 +153,8 @@ void reds_release_agent_data_buffer(uint8_t *buf);
|
||||
void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size);
|
||||
void reds_on_main_migrate_connected(int seamless); //should be called when all the clients
|
||||
// are connected to the target
|
||||
void reds_on_main_receive_migrate_data(MainMigrateData *data, uint8_t *end);
|
||||
int reds_handle_migrate_data(MainChannelClient *mcc,
|
||||
SpiceMigrateDataMain *mig_data, uint32_t size);
|
||||
void reds_on_main_mouse_mode_request(void *message, size_t size);
|
||||
/* migration dest side: returns whether it can support seamless migration
|
||||
* with the given src migration protocol version */
|
||||
|
||||
Loading…
Reference in New Issue
Block a user