diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2016-12-21 00:31:36 +0800 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2017-01-16 17:52:35 +0100 |
commit | 0987d735a3a42c92f6e7e0caa8bab1b0139e3b54 (patch) | |
tree | 82eee4bc187b2e03305b67b89c0476142d88431b /include/exec | |
parent | fb5e19d2e1472e96d72d5e4d89c20033f8ab345c (diff) |
ramblock-notifier: new
This adds a notify interface of ram block additions and removals.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'include/exec')
-rw-r--r-- | include/exec/memory.h | 6 | ||||
-rw-r--r-- | include/exec/ram_addr.h | 46 | ||||
-rw-r--r-- | include/exec/ramlist.h | 72 |
3 files changed, 75 insertions, 49 deletions
diff --git a/include/exec/memory.h b/include/exec/memory.h index bec9756667..a10044f08f 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -16,16 +16,12 @@ #ifndef CONFIG_USER_ONLY -#define DIRTY_MEMORY_VGA 0 -#define DIRTY_MEMORY_CODE 1 -#define DIRTY_MEMORY_MIGRATION 2 -#define DIRTY_MEMORY_NUM 3 /* num of dirty bits */ - #include "exec/cpu-common.h" #ifndef CONFIG_USER_ONLY #include "exec/hwaddr.h" #endif #include "exec/memattrs.h" +#include "exec/ramlist.h" #include "qemu/queue.h" #include "qemu/int128.h" #include "qemu/notify.h" diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 54d7108a9e..3e79466a44 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -21,6 +21,7 @@ #ifndef CONFIG_USER_ONLY #include "hw/xen/xen.h" +#include "exec/ramlist.h" struct RAMBlock { struct rcu_head rcu; @@ -35,6 +36,7 @@ struct RAMBlock { char idstr[256]; /* RCU-enabled, writes protected by the ramlist lock */ QLIST_ENTRY(RAMBlock) next; + QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers; int fd; size_t page_size; }; @@ -50,51 +52,7 @@ static inline void *ramblock_ptr(RAMBlock *block, ram_addr_t offset) return (char *)block->host + offset; } -/* The dirty memory bitmap is split into fixed-size blocks to allow growth - * under RCU. The bitmap for a block can be accessed as follows: - * - * rcu_read_lock(); - * - * DirtyMemoryBlocks *blocks = - * atomic_rcu_read(&ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION]); - * - * ram_addr_t idx = (addr >> TARGET_PAGE_BITS) / DIRTY_MEMORY_BLOCK_SIZE; - * unsigned long *block = blocks.blocks[idx]; - * ...access block bitmap... - * - * rcu_read_unlock(); - * - * Remember to check for the end of the block when accessing a range of - * addresses. Move on to the next block if you reach the end. - * - * Organization into blocks allows dirty memory to grow (but not shrink) under - * RCU. When adding new RAMBlocks requires the dirty memory to grow, a new - * DirtyMemoryBlocks array is allocated with pointers to existing blocks kept - * the same. Other threads can safely access existing blocks while dirty - * memory is being grown. When no threads are using the old DirtyMemoryBlocks - * anymore it is freed by RCU (but the underlying blocks stay because they are - * pointed to from the new DirtyMemoryBlocks). - */ -#define DIRTY_MEMORY_BLOCK_SIZE ((ram_addr_t)256 * 1024 * 8) -typedef struct { - struct rcu_head rcu; - unsigned long *blocks[]; -} DirtyMemoryBlocks; - -typedef struct RAMList { - QemuMutex mutex; - RAMBlock *mru_block; - /* RCU-enabled, writes protected by the ramlist lock. */ - QLIST_HEAD(, RAMBlock) blocks; - DirtyMemoryBlocks *dirty_memory[DIRTY_MEMORY_NUM]; - uint32_t version; -} RAMList; -extern RAMList ram_list; - ram_addr_t last_ram_offset(void); -void qemu_mutex_lock_ramlist(void); -void qemu_mutex_unlock_ramlist(void); - RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, bool share, const char *mem_path, Error **errp); diff --git a/include/exec/ramlist.h b/include/exec/ramlist.h new file mode 100644 index 0000000000..c59880de82 --- /dev/null +++ b/include/exec/ramlist.h @@ -0,0 +1,72 @@ +#ifndef RAMLIST_H +#define RAMLIST_H + +#include "qemu/queue.h" +#include "qemu/thread.h" +#include "qemu/rcu.h" + +typedef struct RAMBlockNotifier RAMBlockNotifier; + +#define DIRTY_MEMORY_VGA 0 +#define DIRTY_MEMORY_CODE 1 +#define DIRTY_MEMORY_MIGRATION 2 +#define DIRTY_MEMORY_NUM 3 /* num of dirty bits */ + +/* The dirty memory bitmap is split into fixed-size blocks to allow growth + * under RCU. The bitmap for a block can be accessed as follows: + * + * rcu_read_lock(); + * + * DirtyMemoryBlocks *blocks = + * atomic_rcu_read(&ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION]); + * + * ram_addr_t idx = (addr >> TARGET_PAGE_BITS) / DIRTY_MEMORY_BLOCK_SIZE; + * unsigned long *block = blocks.blocks[idx]; + * ...access block bitmap... + * + * rcu_read_unlock(); + * + * Remember to check for the end of the block when accessing a range of + * addresses. Move on to the next block if you reach the end. + * + * Organization into blocks allows dirty memory to grow (but not shrink) under + * RCU. When adding new RAMBlocks requires the dirty memory to grow, a new + * DirtyMemoryBlocks array is allocated with pointers to existing blocks kept + * the same. Other threads can safely access existing blocks while dirty + * memory is being grown. When no threads are using the old DirtyMemoryBlocks + * anymore it is freed by RCU (but the underlying blocks stay because they are + * pointed to from the new DirtyMemoryBlocks). + */ +#define DIRTY_MEMORY_BLOCK_SIZE ((ram_addr_t)256 * 1024 * 8) +typedef struct { + struct rcu_head rcu; + unsigned long *blocks[]; +} DirtyMemoryBlocks; + +typedef struct RAMList { + QemuMutex mutex; + RAMBlock *mru_block; + /* RCU-enabled, writes protected by the ramlist lock. */ + QLIST_HEAD(, RAMBlock) blocks; + DirtyMemoryBlocks *dirty_memory[DIRTY_MEMORY_NUM]; + uint32_t version; + QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers; +} RAMList; +extern RAMList ram_list; + +void qemu_mutex_lock_ramlist(void); +void qemu_mutex_unlock_ramlist(void); + +struct RAMBlockNotifier { + void (*ram_block_added)(RAMBlockNotifier *n, void *host, size_t size); + void (*ram_block_removed)(RAMBlockNotifier *n, void *host, size_t size); + QLIST_ENTRY(RAMBlockNotifier) next; +}; + +void ram_block_notifier_add(RAMBlockNotifier *n); +void ram_block_notifier_remove(RAMBlockNotifier *n); +void ram_block_notify_add(void *host, size_t size); +void ram_block_notify_remove(void *host, size_t size); + + +#endif /* RAMLIST_H */ |