Page request: Process incoming page request

On receiving MIG_RPCOMM_REQ_PAGES look up the address and
queue the page.

Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Reviewed-by: Amit Shah <amit.shah@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
This commit is contained in:
Dr. David Alan Gilbert 2015-11-05 18:11:08 +00:00 committed by Juan Quintela
parent 1e2d90ebc5
commit 6c595cdee1
4 changed files with 138 additions and 1 deletions

View File

@ -105,6 +105,18 @@ MigrationIncomingState *migration_incoming_get_current(void);
MigrationIncomingState *migration_incoming_state_new(QEMUFile *f);
void migration_incoming_state_destroy(void);
/*
* An outstanding page request, on the source, having been received
* and queued
*/
struct MigrationSrcPageRequest {
RAMBlock *rb;
hwaddr offset;
hwaddr len;
QSIMPLEQ_ENTRY(MigrationSrcPageRequest) next_req;
};
struct MigrationState
{
int64_t bandwidth_limit;
@ -141,6 +153,12 @@ struct MigrationState
/* Flag set once the migration thread is running (and needs joining) */
bool migration_thread_running;
/* Queue of outstanding page requests from the destination */
QemuMutex src_page_req_mutex;
QSIMPLEQ_HEAD(src_page_requests, MigrationSrcPageRequest) src_page_requests;
/* The RAMBlock used in the last src_page_request */
RAMBlock *last_req_rb;
};
void process_incoming_migration(QEMUFile *f);
@ -288,6 +306,10 @@ void savevm_skip_configuration(void);
int global_state_store(void);
void global_state_store_running(void);
void flush_page_queue(MigrationState *ms);
int ram_save_queue_pages(MigrationState *ms, const char *rbname,
ram_addr_t start, ram_addr_t len);
PostcopyState postcopy_state_get(void);
/* Set the state and return the old state */
PostcopyState postcopy_state_set(PostcopyState new_state);

View File

@ -21,6 +21,7 @@
#include "sysemu/sysemu.h"
#include "block/block.h"
#include "qapi/qmp/qerror.h"
#include "qapi/util.h"
#include "qemu/sockets.h"
#include "qemu/rcu.h"
#include "migration/block.h"
@ -28,9 +29,10 @@
#include "qemu/thread.h"
#include "qmp-commands.h"
#include "trace.h"
#include "qapi/util.h"
#include "qapi-event.h"
#include "qom/cpu.h"
#include "exec/memory.h"
#include "exec/address-spaces.h"
#define MAX_THROTTLE (32 << 20) /* Migration transfer speed throttling */
@ -72,6 +74,7 @@ static PostcopyState incoming_postcopy_state;
/* For outgoing */
MigrationState *migrate_get_current(void)
{
static bool once;
static MigrationState current_migration = {
.state = MIGRATION_STATUS_NONE,
.bandwidth_limit = MAX_THROTTLE,
@ -89,6 +92,10 @@ MigrationState *migrate_get_current(void)
DEFAULT_MIGRATE_X_CPU_THROTTLE_INCREMENT,
};
if (!once) {
qemu_mutex_init(&current_migration.src_page_req_mutex);
once = true;
}
return &current_migration;
}
@ -771,6 +778,8 @@ static void migrate_fd_cleanup(void *opaque)
qemu_bh_delete(s->cleanup_bh);
s->cleanup_bh = NULL;
flush_page_queue(s);
if (s->file) {
trace_migrate_fd_cleanup();
qemu_mutex_unlock_iothread();
@ -903,6 +912,8 @@ MigrationState *migrate_init(const MigrationParams *params)
s->bandwidth_limit = bandwidth_limit;
migrate_set_state(s, MIGRATION_STATUS_NONE, MIGRATION_STATUS_SETUP);
QSIMPLEQ_INIT(&s->src_page_requests);
s->total_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
return s;
}
@ -1193,7 +1204,25 @@ static struct rp_cmd_args {
static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname,
ram_addr_t start, size_t len)
{
long our_host_ps = getpagesize();
trace_migrate_handle_rp_req_pages(rbname, start, len);
/*
* Since we currently insist on matching page sizes, just sanity check
* we're being asked for whole host pages.
*/
if (start & (our_host_ps-1) ||
(len & (our_host_ps-1))) {
error_report("%s: Misaligned page request, start: " RAM_ADDR_FMT
" len: %zd", __func__, start, len);
mark_source_rp_bad(ms);
return;
}
if (ram_save_queue_pages(ms, rbname, start, len)) {
mark_source_rp_bad(ms);
}
}
/*

View File

@ -1015,6 +1015,91 @@ static bool find_dirty_block(QEMUFile *f, PageSearchStatus *pss,
}
}
/**
* flush_page_queue: Flush any remaining pages in the ram request queue
* it should be empty at the end anyway, but in error cases there may be
* some left.
*
* ms: MigrationState
*/
void flush_page_queue(MigrationState *ms)
{
struct MigrationSrcPageRequest *mspr, *next_mspr;
/* This queue generally should be empty - but in the case of a failed
* migration might have some droppings in.
*/
rcu_read_lock();
QSIMPLEQ_FOREACH_SAFE(mspr, &ms->src_page_requests, next_req, next_mspr) {
memory_region_unref(mspr->rb->mr);
QSIMPLEQ_REMOVE_HEAD(&ms->src_page_requests, next_req);
g_free(mspr);
}
rcu_read_unlock();
}
/**
* Queue the pages for transmission, e.g. a request from postcopy destination
* ms: MigrationStatus in which the queue is held
* rbname: The RAMBlock the request is for - may be NULL (to mean reuse last)
* start: Offset from the start of the RAMBlock
* len: Length (in bytes) to send
* Return: 0 on success
*/
int ram_save_queue_pages(MigrationState *ms, const char *rbname,
ram_addr_t start, ram_addr_t len)
{
RAMBlock *ramblock;
rcu_read_lock();
if (!rbname) {
/* Reuse last RAMBlock */
ramblock = ms->last_req_rb;
if (!ramblock) {
/*
* Shouldn't happen, we can't reuse the last RAMBlock if
* it's the 1st request.
*/
error_report("ram_save_queue_pages no previous block");
goto err;
}
} else {
ramblock = qemu_ram_block_by_name(rbname);
if (!ramblock) {
/* We shouldn't be asked for a non-existent RAMBlock */
error_report("ram_save_queue_pages no block '%s'", rbname);
goto err;
}
ms->last_req_rb = ramblock;
}
trace_ram_save_queue_pages(ramblock->idstr, start, len);
if (start+len > ramblock->used_length) {
error_report("%s request overrun start=%zx len=%zx blocklen=%zx",
__func__, start, len, ramblock->used_length);
goto err;
}
struct MigrationSrcPageRequest *new_entry =
g_malloc0(sizeof(struct MigrationSrcPageRequest));
new_entry->rb = ramblock;
new_entry->offset = start;
new_entry->len = len;
memory_region_ref(ramblock->mr);
qemu_mutex_lock(&ms->src_page_req_mutex);
QSIMPLEQ_INSERT_TAIL(&ms->src_page_requests, new_entry, next_req);
qemu_mutex_unlock(&ms->src_page_req_mutex);
rcu_read_unlock();
return 0;
err:
rcu_read_unlock();
return -1;
}
/**
* ram_find_and_save_block: Finds a dirty page and sends it to f
*

View File

@ -1256,6 +1256,7 @@ migration_bitmap_sync_start(void) ""
migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64""
migration_throttle(void) ""
ram_postcopy_send_discard_bitmap(void) ""
ram_save_queue_pages(const char *rbname, size_t start, size_t len) "%s: start: %zx len: %zx"
# hw/display/qxl.c
disable qxl_interface_set_mm_time(int qid, uint32_t mm_time) "%d %d"