Loading exec.c +5 −0 Original line number Diff line number Diff line Loading @@ -1687,6 +1687,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_HUGEPAGE); /* MADV_DONTFORK is also needed by KVM in absence of synchronous MMU */ qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_DONTFORK); ram_block_notify_add(new_block->host, new_block->max_length); } } Loading Loading @@ -1817,6 +1818,10 @@ void qemu_ram_free(RAMBlock *block) return; } if (block->host) { ram_block_notify_remove(block->host, block->max_length); } qemu_mutex_lock_ramlist(); QLIST_REMOVE_RCU(block, next); ram_list.mru_block = NULL; Loading include/exec/memory.h +1 −5 Original line number Diff line number Diff line Loading @@ -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" Loading include/exec/ram_addr.h +2 −44 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #ifndef CONFIG_USER_ONLY #include "hw/xen/xen.h" #include "exec/ramlist.h" struct RAMBlock { struct rcu_head rcu; Loading @@ -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; }; Loading @@ -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); Loading include/exec/ramlist.h 0 → 100644 +72 −0 Original line number Diff line number Diff line #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 */ numa.c +29 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "sysemu/numa.h" #include "exec/cpu-common.h" #include "exec/ramlist.h" #include "qemu/bitmap.h" #include "qom/cpu.h" #include "qemu/error-report.h" Loading Loading @@ -572,3 +573,31 @@ int numa_get_node_for_cpu(int idx) } return i; } void ram_block_notifier_add(RAMBlockNotifier *n) { QLIST_INSERT_HEAD(&ram_list.ramblock_notifiers, n, next); } void ram_block_notifier_remove(RAMBlockNotifier *n) { QLIST_REMOVE(n, next); } void ram_block_notify_add(void *host, size_t size) { RAMBlockNotifier *notifier; QLIST_FOREACH(notifier, &ram_list.ramblock_notifiers, next) { notifier->ram_block_added(notifier, host, size); } } void ram_block_notify_remove(void *host, size_t size) { RAMBlockNotifier *notifier; QLIST_FOREACH(notifier, &ram_list.ramblock_notifiers, next) { notifier->ram_block_removed(notifier, host, size); } } Loading
exec.c +5 −0 Original line number Diff line number Diff line Loading @@ -1687,6 +1687,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_HUGEPAGE); /* MADV_DONTFORK is also needed by KVM in absence of synchronous MMU */ qemu_madvise(new_block->host, new_block->max_length, QEMU_MADV_DONTFORK); ram_block_notify_add(new_block->host, new_block->max_length); } } Loading Loading @@ -1817,6 +1818,10 @@ void qemu_ram_free(RAMBlock *block) return; } if (block->host) { ram_block_notify_remove(block->host, block->max_length); } qemu_mutex_lock_ramlist(); QLIST_REMOVE_RCU(block, next); ram_list.mru_block = NULL; Loading
include/exec/memory.h +1 −5 Original line number Diff line number Diff line Loading @@ -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" Loading
include/exec/ram_addr.h +2 −44 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #ifndef CONFIG_USER_ONLY #include "hw/xen/xen.h" #include "exec/ramlist.h" struct RAMBlock { struct rcu_head rcu; Loading @@ -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; }; Loading @@ -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); Loading
include/exec/ramlist.h 0 → 100644 +72 −0 Original line number Diff line number Diff line #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 */
numa.c +29 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "sysemu/numa.h" #include "exec/cpu-common.h" #include "exec/ramlist.h" #include "qemu/bitmap.h" #include "qom/cpu.h" #include "qemu/error-report.h" Loading Loading @@ -572,3 +573,31 @@ int numa_get_node_for_cpu(int idx) } return i; } void ram_block_notifier_add(RAMBlockNotifier *n) { QLIST_INSERT_HEAD(&ram_list.ramblock_notifiers, n, next); } void ram_block_notifier_remove(RAMBlockNotifier *n) { QLIST_REMOVE(n, next); } void ram_block_notify_add(void *host, size_t size) { RAMBlockNotifier *notifier; QLIST_FOREACH(notifier, &ram_list.ramblock_notifiers, next) { notifier->ram_block_added(notifier, host, size); } } void ram_block_notify_remove(void *host, size_t size) { RAMBlockNotifier *notifier; QLIST_FOREACH(notifier, &ram_list.ramblock_notifiers, next) { notifier->ram_block_removed(notifier, host, size); } }