ramblock-notifier: new
This adds a notify interface of ram block additions and removals. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
		
							parent
							
								
									fb5e19d2e1
								
							
						
					
					
						commit
						0987d735a3
					
				
							
								
								
									
										5
									
								
								exec.c
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								exec.c
									
									
									
									
									
								
							| @ -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); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -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; | ||||
|  | ||||
| @ -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" | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
							
								
								
									
										72
									
								
								include/exec/ramlist.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								include/exec/ramlist.h
									
									
									
									
									
										Normal file
									
								
							| @ -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 */ | ||||
							
								
								
									
										29
									
								
								numa.c
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								numa.c
									
									
									
									
									
								
							| @ -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" | ||||
| @ -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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -163,6 +163,7 @@ static void xen_remap_bucket(MapCacheEntry *entry, | ||||
|     err = g_malloc0(nb_pfn * sizeof (int)); | ||||
| 
 | ||||
|     if (entry->vaddr_base != NULL) { | ||||
|         ram_block_notify_remove(entry->vaddr_base, entry->size); | ||||
|         if (munmap(entry->vaddr_base, entry->size) != 0) { | ||||
|             perror("unmap fails"); | ||||
|             exit(-1); | ||||
| @ -188,6 +189,7 @@ static void xen_remap_bucket(MapCacheEntry *entry, | ||||
|     entry->valid_mapping = (unsigned long *) g_malloc0(sizeof(unsigned long) * | ||||
|             BITS_TO_LONGS(size >> XC_PAGE_SHIFT)); | ||||
| 
 | ||||
|     ram_block_notify_add(entry->vaddr_base, entry->size); | ||||
|     bitmap_zero(entry->valid_mapping, nb_pfn); | ||||
|     for (i = 0; i < nb_pfn; i++) { | ||||
|         if (!err[i]) { | ||||
| @ -397,6 +399,7 @@ static void xen_invalidate_map_cache_entry_unlocked(uint8_t *buffer) | ||||
|     } | ||||
| 
 | ||||
|     pentry->next = entry->next; | ||||
|     ram_block_notify_remove(entry->vaddr_base, entry->size); | ||||
|     if (munmap(entry->vaddr_base, entry->size) != 0) { | ||||
|         perror("unmap fails"); | ||||
|         exit(-1); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Paolo Bonzini
						Paolo Bonzini