Files
xen/tools/libxc/xc_sr_save_x86_hvm.c
T
Juergen Gross 91e204d37f libxc: try to find last used pfn when migrating
For migration the last used pfn of a guest is needed to size the
logdirty bitmap and as an upper bound of the page loop. Unfortunately
there are pv-kernels advertising a much higher maximum pfn as they
are really using in order to support memory hotplug. This will lead
to allocation of much more memory in Xen tools during migration as
really needed.

Try to find the last used guest pfn of a pv-domu by scanning the p2m
tree from the last entry towards it's start and search for an entry
not being invalid.

Normally the mid pages of the p2m tree containing all invalid entries
are being reused, so we can just scan the top page for identical
entries and skip them but the first one.

Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Wei Liu <wei.liu2@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
[ ijc -- added errno = E2BIG to one error path ]
Acked-by: Ian Campbell <ian.campbell@citrix.com>
2015-12-08 16:56:18 +00:00

236 lines
5.4 KiB
C

#include <assert.h>
#include "xc_sr_common_x86.h"
#include <xen/hvm/params.h>
/*
* Query for the HVM context and write an HVM_CONTEXT record into the stream.
*/
static int write_hvm_context(struct xc_sr_context *ctx)
{
xc_interface *xch = ctx->xch;
int rc, hvm_buf_size;
struct xc_sr_record hvm_rec =
{
.type = REC_TYPE_HVM_CONTEXT,
};
hvm_buf_size = xc_domain_hvm_getcontext(xch, ctx->domid, 0, 0);
if ( hvm_buf_size < 0 )
{
PERROR("Couldn't get HVM context size from Xen");
rc = -1;
goto out;
}
hvm_rec.data = malloc(hvm_buf_size);
if ( !hvm_rec.data )
{
PERROR("Couldn't allocate memory");
rc = -1;
goto out;
}
hvm_buf_size = xc_domain_hvm_getcontext(xch, ctx->domid,
hvm_rec.data, hvm_buf_size);
if ( hvm_buf_size < 0 )
{
PERROR("Couldn't get HVM context from Xen");
rc = -1;
goto out;
}
hvm_rec.length = hvm_buf_size;
rc = write_record(ctx, &hvm_rec);
if ( rc < 0 )
{
PERROR("error write HVM_CONTEXT record");
goto out;
}
out:
free(hvm_rec.data);
return rc;
}
/*
* Query for a range of HVM parameters and write an HVM_PARAMS record into the
* stream.
*/
static int write_hvm_params(struct xc_sr_context *ctx)
{
static const unsigned int params[] = {
HVM_PARAM_STORE_PFN,
HVM_PARAM_IOREQ_PFN,
HVM_PARAM_BUFIOREQ_PFN,
HVM_PARAM_PAGING_RING_PFN,
HVM_PARAM_MONITOR_RING_PFN,
HVM_PARAM_SHARING_RING_PFN,
HVM_PARAM_VM86_TSS,
HVM_PARAM_CONSOLE_PFN,
HVM_PARAM_ACPI_IOPORTS_LOCATION,
HVM_PARAM_VIRIDIAN,
HVM_PARAM_IDENT_PT,
HVM_PARAM_PAE_ENABLED,
HVM_PARAM_VM_GENERATION_ID_ADDR,
HVM_PARAM_IOREQ_SERVER_PFN,
HVM_PARAM_NR_IOREQ_SERVER_PAGES,
};
xc_interface *xch = ctx->xch;
struct xc_sr_rec_hvm_params_entry entries[ARRAY_SIZE(params)];
struct xc_sr_rec_hvm_params hdr = {
.count = 0,
};
struct xc_sr_record rec = {
.type = REC_TYPE_HVM_PARAMS,
.length = sizeof(hdr),
.data = &hdr,
};
unsigned int i;
int rc;
for ( i = 0; i < ARRAY_SIZE(params); i++ )
{
uint32_t index = params[i];
uint64_t value;
rc = xc_hvm_param_get(xch, ctx->domid, index, &value);
if ( rc )
{
PERROR("Failed to get HVMPARAM at index %u", index);
return rc;
}
if ( value != 0 )
{
entries[hdr.count].index = index;
entries[hdr.count].value = value;
hdr.count++;
}
}
rc = write_split_record(ctx, &rec, entries, hdr.count * sizeof(*entries));
if ( rc )
PERROR("Failed to write HVM_PARAMS record");
return rc;
}
static xen_pfn_t x86_hvm_pfn_to_gfn(const struct xc_sr_context *ctx,
xen_pfn_t pfn)
{
/* identity map */
return pfn;
}
static int x86_hvm_normalise_page(struct xc_sr_context *ctx,
xen_pfn_t type, void **page)
{
/* no-op */
return 0;
}
static int x86_hvm_setup(struct xc_sr_context *ctx)
{
xc_interface *xch = ctx->xch;
xen_pfn_t nr_pfns;
if ( xc_domain_nr_gpfns(xch, ctx->domid, &nr_pfns) < 0 )
{
PERROR("Unable to obtain the guest p2m size");
return -1;
}
if ( nr_pfns > ~XEN_DOMCTL_PFINFO_LTAB_MASK )
{
errno = E2BIG;
PERROR("Cannot save this big a guest");
return -1;
}
ctx->save.p2m_size = nr_pfns;
if ( ctx->save.callbacks->switch_qemu_logdirty(
ctx->domid, 1, ctx->save.callbacks->data) )
{
PERROR("Couldn't enable qemu log-dirty mode");
return -1;
}
ctx->x86_hvm.save.qemu_enabled_logdirty = true;
return 0;
}
static int x86_hvm_start_of_stream(struct xc_sr_context *ctx)
{
/* no-op */
return 0;
}
static int x86_hvm_start_of_checkpoint(struct xc_sr_context *ctx)
{
/* no-op */
return 0;
}
static int x86_hvm_end_of_checkpoint(struct xc_sr_context *ctx)
{
int rc;
/* Write the TSC record. */
rc = write_tsc_info(ctx);
if ( rc )
return rc;
/* Write the HVM_CONTEXT record. */
rc = write_hvm_context(ctx);
if ( rc )
return rc;
/* Write HVM_PARAMS record contains applicable HVM params. */
rc = write_hvm_params(ctx);
if ( rc )
return rc;
return 0;
}
static int x86_hvm_cleanup(struct xc_sr_context *ctx)
{
xc_interface *xch = ctx->xch;
/* If qemu successfully enabled logdirty mode, attempt to disable. */
if ( ctx->x86_hvm.save.qemu_enabled_logdirty &&
ctx->save.callbacks->switch_qemu_logdirty(
ctx->domid, 0, ctx->save.callbacks->data) )
{
PERROR("Couldn't disable qemu log-dirty mode");
return -1;
}
return 0;
}
struct xc_sr_save_ops save_ops_x86_hvm =
{
.pfn_to_gfn = x86_hvm_pfn_to_gfn,
.normalise_page = x86_hvm_normalise_page,
.setup = x86_hvm_setup,
.start_of_stream = x86_hvm_start_of_stream,
.start_of_checkpoint = x86_hvm_start_of_checkpoint,
.end_of_checkpoint = x86_hvm_end_of_checkpoint,
.cleanup = x86_hvm_cleanup,
};
/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/