mirror of https://github.com/xqemu/xqemu.git
memory: add API for observing updates to the physical memory map
Add an API that allows a client to observe changes in the global memory map: - region added (possibly with logging enabled) - region removed (possibly with logging enabled) - logging started on a region - logging stopped on a region - global logging started - global logging removed This API will eventually replace cpu_register_physical_memory_client(). Signed-off-by: Avi Kivity <avi@redhat.com>
This commit is contained in:
parent
86e775c654
commit
7664e80c84
5
exec.c
5
exec.c
|
@ -1762,6 +1762,11 @@ static int cpu_notify_sync_dirty_bitmap(target_phys_addr_t start,
|
||||||
static int cpu_notify_migration_log(int enable)
|
static int cpu_notify_migration_log(int enable)
|
||||||
{
|
{
|
||||||
CPUPhysMemoryClient *client;
|
CPUPhysMemoryClient *client;
|
||||||
|
if (enable) {
|
||||||
|
memory_global_dirty_log_start();
|
||||||
|
} else {
|
||||||
|
memory_global_dirty_log_stop();
|
||||||
|
}
|
||||||
QLIST_FOREACH(client, &memory_client_list, list) {
|
QLIST_FOREACH(client, &memory_client_list, list) {
|
||||||
int r = client->migration_log(client, enable);
|
int r = client->migration_log(client, enable);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|
93
memory.c
93
memory.c
|
@ -23,6 +23,10 @@
|
||||||
|
|
||||||
unsigned memory_region_transaction_depth = 0;
|
unsigned memory_region_transaction_depth = 0;
|
||||||
static bool memory_region_update_pending = false;
|
static bool memory_region_update_pending = false;
|
||||||
|
static bool global_dirty_log = false;
|
||||||
|
|
||||||
|
static QLIST_HEAD(, MemoryListener) memory_listeners
|
||||||
|
= QLIST_HEAD_INITIALIZER(memory_listeners);
|
||||||
|
|
||||||
typedef struct AddrRange AddrRange;
|
typedef struct AddrRange AddrRange;
|
||||||
|
|
||||||
|
@ -697,6 +701,32 @@ static void address_space_update_ioeventfds(AddressSpace *as)
|
||||||
as->ioeventfd_nb = ioeventfd_nb;
|
as->ioeventfd_nb = ioeventfd_nb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef void ListenerCallback(MemoryListener *listener,
|
||||||
|
MemoryRegionSection *mrs);
|
||||||
|
|
||||||
|
/* Want "void (&MemoryListener::*callback)(const MemoryRegionSection& s)" */
|
||||||
|
static void memory_listener_update_region(FlatRange *fr, AddressSpace *as,
|
||||||
|
size_t callback_offset)
|
||||||
|
{
|
||||||
|
MemoryRegionSection section = {
|
||||||
|
.mr = fr->mr,
|
||||||
|
.address_space = as->root,
|
||||||
|
.offset_within_region = fr->offset_in_region,
|
||||||
|
.size = int128_get64(fr->addr.size),
|
||||||
|
.offset_within_address_space = int128_get64(fr->addr.start),
|
||||||
|
};
|
||||||
|
MemoryListener *listener;
|
||||||
|
|
||||||
|
QLIST_FOREACH(listener, &memory_listeners, link) {
|
||||||
|
ListenerCallback *callback
|
||||||
|
= *(ListenerCallback **)((void *)listener + callback_offset);
|
||||||
|
callback(listener, §ion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MEMORY_LISTENER_UPDATE_REGION(fr, as, callback) \
|
||||||
|
memory_listener_update_region(fr, as, offsetof(MemoryListener, callback))
|
||||||
|
|
||||||
static void address_space_update_topology_pass(AddressSpace *as,
|
static void address_space_update_topology_pass(AddressSpace *as,
|
||||||
FlatView old_view,
|
FlatView old_view,
|
||||||
FlatView new_view,
|
FlatView new_view,
|
||||||
|
@ -729,6 +759,7 @@ static void address_space_update_topology_pass(AddressSpace *as,
|
||||||
/* In old, but (not in new, or in new but attributes changed). */
|
/* In old, but (not in new, or in new but attributes changed). */
|
||||||
|
|
||||||
if (!adding) {
|
if (!adding) {
|
||||||
|
MEMORY_LISTENER_UPDATE_REGION(frold, as, region_del);
|
||||||
as->ops->range_del(as, frold);
|
as->ops->range_del(as, frold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -738,9 +769,11 @@ static void address_space_update_topology_pass(AddressSpace *as,
|
||||||
|
|
||||||
if (adding) {
|
if (adding) {
|
||||||
if (frold->dirty_log_mask && !frnew->dirty_log_mask) {
|
if (frold->dirty_log_mask && !frnew->dirty_log_mask) {
|
||||||
|
MEMORY_LISTENER_UPDATE_REGION(frold, as, log_stop);
|
||||||
as->ops->log_stop(as, frnew);
|
as->ops->log_stop(as, frnew);
|
||||||
} else if (frnew->dirty_log_mask && !frold->dirty_log_mask) {
|
} else if (frnew->dirty_log_mask && !frold->dirty_log_mask) {
|
||||||
as->ops->log_start(as, frnew);
|
as->ops->log_start(as, frnew);
|
||||||
|
MEMORY_LISTENER_UPDATE_REGION(frold, as, log_start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -751,6 +784,7 @@ static void address_space_update_topology_pass(AddressSpace *as,
|
||||||
|
|
||||||
if (adding) {
|
if (adding) {
|
||||||
as->ops->range_add(as, frnew);
|
as->ops->range_add(as, frnew);
|
||||||
|
MEMORY_LISTENER_UPDATE_REGION(frold, as, region_add);
|
||||||
}
|
}
|
||||||
|
|
||||||
++inew;
|
++inew;
|
||||||
|
@ -1130,6 +1164,7 @@ void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
|
||||||
|
|
||||||
FOR_EACH_FLAT_RANGE(fr, &address_space_memory.current_map) {
|
FOR_EACH_FLAT_RANGE(fr, &address_space_memory.current_map) {
|
||||||
if (fr->mr == mr) {
|
if (fr->mr == mr) {
|
||||||
|
MEMORY_LISTENER_UPDATE_REGION(fr, &address_space_memory, log_sync);
|
||||||
cpu_physical_sync_dirty_bitmap(int128_get64(fr->addr.start),
|
cpu_physical_sync_dirty_bitmap(int128_get64(fr->addr.start),
|
||||||
int128_get64(addrrange_end(fr->addr)));
|
int128_get64(addrrange_end(fr->addr)));
|
||||||
}
|
}
|
||||||
|
@ -1449,7 +1484,65 @@ MemoryRegionSection memory_region_find(MemoryRegion *address_space,
|
||||||
|
|
||||||
void memory_global_sync_dirty_bitmap(MemoryRegion *address_space)
|
void memory_global_sync_dirty_bitmap(MemoryRegion *address_space)
|
||||||
{
|
{
|
||||||
|
AddressSpace *as = memory_region_to_address_space(address_space);
|
||||||
|
FlatRange *fr;
|
||||||
|
|
||||||
cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX);
|
cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX);
|
||||||
|
FOR_EACH_FLAT_RANGE(fr, &as->current_map) {
|
||||||
|
MEMORY_LISTENER_UPDATE_REGION(fr, as, log_sync);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory_global_dirty_log_start(void)
|
||||||
|
{
|
||||||
|
MemoryListener *listener;
|
||||||
|
|
||||||
|
global_dirty_log = true;
|
||||||
|
QLIST_FOREACH(listener, &memory_listeners, link) {
|
||||||
|
listener->log_global_start(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory_global_dirty_log_stop(void)
|
||||||
|
{
|
||||||
|
MemoryListener *listener;
|
||||||
|
|
||||||
|
global_dirty_log = false;
|
||||||
|
QLIST_FOREACH(listener, &memory_listeners, link) {
|
||||||
|
listener->log_global_stop(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void listener_add_address_space(MemoryListener *listener,
|
||||||
|
AddressSpace *as)
|
||||||
|
{
|
||||||
|
FlatRange *fr;
|
||||||
|
|
||||||
|
if (global_dirty_log) {
|
||||||
|
listener->log_global_start(listener);
|
||||||
|
}
|
||||||
|
FOR_EACH_FLAT_RANGE(fr, &as->current_map) {
|
||||||
|
MemoryRegionSection section = {
|
||||||
|
.mr = fr->mr,
|
||||||
|
.address_space = as->root,
|
||||||
|
.offset_within_region = fr->offset_in_region,
|
||||||
|
.size = int128_get64(fr->addr.size),
|
||||||
|
.offset_within_address_space = int128_get64(fr->addr.start),
|
||||||
|
};
|
||||||
|
listener->region_add(listener, §ion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory_listener_register(MemoryListener *listener)
|
||||||
|
{
|
||||||
|
QLIST_INSERT_HEAD(&memory_listeners, listener, link);
|
||||||
|
listener_add_address_space(listener, &address_space_memory);
|
||||||
|
listener_add_address_space(listener, &address_space_io);
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory_listener_unregister(MemoryListener *listener)
|
||||||
|
{
|
||||||
|
QLIST_REMOVE(listener, link);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_system_memory_map(MemoryRegion *mr)
|
void set_system_memory_map(MemoryRegion *mr)
|
||||||
|
|
47
memory.h
47
memory.h
|
@ -154,6 +154,7 @@ typedef struct MemoryRegionSection MemoryRegionSection;
|
||||||
* MemoryRegionSection: describes a fragment of a #MemoryRegion
|
* MemoryRegionSection: describes a fragment of a #MemoryRegion
|
||||||
*
|
*
|
||||||
* @mr: the region, or %NULL if empty
|
* @mr: the region, or %NULL if empty
|
||||||
|
* @address_space: the address space the region is mapped in
|
||||||
* @offset_within_region: the beginning of the section, relative to @mr's start
|
* @offset_within_region: the beginning of the section, relative to @mr's start
|
||||||
* @size: the size of the section; will not exceed @mr's boundaries
|
* @size: the size of the section; will not exceed @mr's boundaries
|
||||||
* @offset_within_address_space: the address of the first byte of the section
|
* @offset_within_address_space: the address of the first byte of the section
|
||||||
|
@ -161,11 +162,31 @@ typedef struct MemoryRegionSection MemoryRegionSection;
|
||||||
*/
|
*/
|
||||||
struct MemoryRegionSection {
|
struct MemoryRegionSection {
|
||||||
MemoryRegion *mr;
|
MemoryRegion *mr;
|
||||||
|
MemoryRegion *address_space;
|
||||||
target_phys_addr_t offset_within_region;
|
target_phys_addr_t offset_within_region;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
target_phys_addr_t offset_within_address_space;
|
target_phys_addr_t offset_within_address_space;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct MemoryListener MemoryListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MemoryListener: callbacks structure for updates to the physical memory map
|
||||||
|
*
|
||||||
|
* Allows a component to adjust to changes in the guest-visible memory map.
|
||||||
|
* Use with memory_listener_register() and memory_listener_unregister().
|
||||||
|
*/
|
||||||
|
struct MemoryListener {
|
||||||
|
void (*region_add)(MemoryListener *listener, MemoryRegionSection *section);
|
||||||
|
void (*region_del)(MemoryListener *listener, MemoryRegionSection *section);
|
||||||
|
void (*log_start)(MemoryListener *listener, MemoryRegionSection *section);
|
||||||
|
void (*log_stop)(MemoryListener *listener, MemoryRegionSection *section);
|
||||||
|
void (*log_sync)(MemoryListener *listener, MemoryRegionSection *section);
|
||||||
|
void (*log_global_start)(MemoryListener *listener);
|
||||||
|
void (*log_global_stop)(MemoryListener *listener);
|
||||||
|
QLIST_ENTRY(MemoryListener) link;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* memory_region_init: Initialize a memory region
|
* memory_region_init: Initialize a memory region
|
||||||
*
|
*
|
||||||
|
@ -631,6 +652,32 @@ void memory_region_transaction_begin(void);
|
||||||
*/
|
*/
|
||||||
void memory_region_transaction_commit(void);
|
void memory_region_transaction_commit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memory_listener_register: register callbacks to be called when memory
|
||||||
|
* sections are mapped or unmapped into an address
|
||||||
|
* space
|
||||||
|
*
|
||||||
|
* @listener: an object containing the callbacks to be called
|
||||||
|
*/
|
||||||
|
void memory_listener_register(MemoryListener *listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memory_listener_unregister: undo the effect of memory_listener_register()
|
||||||
|
*
|
||||||
|
* @listener: an object containing the callbacks to be removed
|
||||||
|
*/
|
||||||
|
void memory_listener_unregister(MemoryListener *listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memory_global_dirty_log_start: begin dirty logging for all regions
|
||||||
|
*/
|
||||||
|
void memory_global_dirty_log_start(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memory_global_dirty_log_stop: begin dirty logging for all regions
|
||||||
|
*/
|
||||||
|
void memory_global_dirty_log_stop(void);
|
||||||
|
|
||||||
void mtree_info(fprintf_function mon_printf, void *f);
|
void mtree_info(fprintf_function mon_printf, void *f);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue