| #ifndef RAMLIST_H |
| #define RAMLIST_H |
| |
| #include "qemu/queue.h" |
| #include "qemu/thread.h" |
| #include "qemu/rcu.h" |
| #include "qemu/rcu_queue.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 = |
| * qatomic_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; |
| |
| /* Should be holding either ram_list.mutex, or the RCU lock. */ |
| #define INTERNAL_RAMBLOCK_FOREACH(block) \ |
| QLIST_FOREACH_RCU(block, &ram_list.blocks, next) |
| /* Never use the INTERNAL_ version except for defining other macros */ |
| #define RAMBLOCK_FOREACH(block) INTERNAL_RAMBLOCK_FOREACH(block) |
| |
| 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, |
| size_t max_size); |
| void (*ram_block_removed)(RAMBlockNotifier *n, void *host, size_t size, |
| size_t max_size); |
| void (*ram_block_resized)(RAMBlockNotifier *n, void *host, size_t old_size, |
| size_t new_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, size_t max_size); |
| void ram_block_notify_remove(void *host, size_t size, size_t max_size); |
| void ram_block_notify_resize(void *host, size_t old_size, size_t new_size); |
| |
| GString *ram_block_format(void); |
| |
| #endif /* RAMLIST_H */ |