Paolo Bonzini | 0987d73 | 2016-12-21 00:31:36 +0800 | [diff] [blame] | 1 | #ifndef RAMLIST_H |
| 2 | #define RAMLIST_H |
| 3 | |
| 4 | #include "qemu/queue.h" |
| 5 | #include "qemu/thread.h" |
| 6 | #include "qemu/rcu.h" |
Peter Xu | 99e1558 | 2017-05-12 12:17:39 +0800 | [diff] [blame] | 7 | #include "qemu/rcu_queue.h" |
Paolo Bonzini | 0987d73 | 2016-12-21 00:31:36 +0800 | [diff] [blame] | 8 | |
| 9 | typedef struct RAMBlockNotifier RAMBlockNotifier; |
| 10 | |
| 11 | #define DIRTY_MEMORY_VGA 0 |
| 12 | #define DIRTY_MEMORY_CODE 1 |
| 13 | #define DIRTY_MEMORY_MIGRATION 2 |
| 14 | #define DIRTY_MEMORY_NUM 3 /* num of dirty bits */ |
| 15 | |
| 16 | /* The dirty memory bitmap is split into fixed-size blocks to allow growth |
| 17 | * under RCU. The bitmap for a block can be accessed as follows: |
| 18 | * |
| 19 | * rcu_read_lock(); |
| 20 | * |
| 21 | * DirtyMemoryBlocks *blocks = |
Stefan Hajnoczi | d73415a | 2020-09-23 11:56:46 +0100 | [diff] [blame] | 22 | * qatomic_rcu_read(&ram_list.dirty_memory[DIRTY_MEMORY_MIGRATION]); |
Paolo Bonzini | 0987d73 | 2016-12-21 00:31:36 +0800 | [diff] [blame] | 23 | * |
| 24 | * ram_addr_t idx = (addr >> TARGET_PAGE_BITS) / DIRTY_MEMORY_BLOCK_SIZE; |
| 25 | * unsigned long *block = blocks.blocks[idx]; |
| 26 | * ...access block bitmap... |
| 27 | * |
| 28 | * rcu_read_unlock(); |
| 29 | * |
| 30 | * Remember to check for the end of the block when accessing a range of |
| 31 | * addresses. Move on to the next block if you reach the end. |
| 32 | * |
| 33 | * Organization into blocks allows dirty memory to grow (but not shrink) under |
| 34 | * RCU. When adding new RAMBlocks requires the dirty memory to grow, a new |
| 35 | * DirtyMemoryBlocks array is allocated with pointers to existing blocks kept |
| 36 | * the same. Other threads can safely access existing blocks while dirty |
| 37 | * memory is being grown. When no threads are using the old DirtyMemoryBlocks |
| 38 | * anymore it is freed by RCU (but the underlying blocks stay because they are |
| 39 | * pointed to from the new DirtyMemoryBlocks). |
| 40 | */ |
| 41 | #define DIRTY_MEMORY_BLOCK_SIZE ((ram_addr_t)256 * 1024 * 8) |
| 42 | typedef struct { |
| 43 | struct rcu_head rcu; |
| 44 | unsigned long *blocks[]; |
| 45 | } DirtyMemoryBlocks; |
| 46 | |
| 47 | typedef struct RAMList { |
| 48 | QemuMutex mutex; |
| 49 | RAMBlock *mru_block; |
| 50 | /* RCU-enabled, writes protected by the ramlist lock. */ |
| 51 | QLIST_HEAD(, RAMBlock) blocks; |
| 52 | DirtyMemoryBlocks *dirty_memory[DIRTY_MEMORY_NUM]; |
| 53 | uint32_t version; |
| 54 | QLIST_HEAD(, RAMBlockNotifier) ramblock_notifiers; |
| 55 | } RAMList; |
| 56 | extern RAMList ram_list; |
| 57 | |
Peter Xu | 99e1558 | 2017-05-12 12:17:39 +0800 | [diff] [blame] | 58 | /* Should be holding either ram_list.mutex, or the RCU lock. */ |
Dr. David Alan Gilbert | 343f632 | 2018-06-05 17:25:45 +0100 | [diff] [blame] | 59 | #define INTERNAL_RAMBLOCK_FOREACH(block) \ |
Peter Xu | 99e1558 | 2017-05-12 12:17:39 +0800 | [diff] [blame] | 60 | QLIST_FOREACH_RCU(block, &ram_list.blocks, next) |
Dr. David Alan Gilbert | 343f632 | 2018-06-05 17:25:45 +0100 | [diff] [blame] | 61 | /* Never use the INTERNAL_ version except for defining other macros */ |
| 62 | #define RAMBLOCK_FOREACH(block) INTERNAL_RAMBLOCK_FOREACH(block) |
Peter Xu | 99e1558 | 2017-05-12 12:17:39 +0800 | [diff] [blame] | 63 | |
Paolo Bonzini | 0987d73 | 2016-12-21 00:31:36 +0800 | [diff] [blame] | 64 | void qemu_mutex_lock_ramlist(void); |
| 65 | void qemu_mutex_unlock_ramlist(void); |
| 66 | |
| 67 | struct RAMBlockNotifier { |
David Hildenbrand | 8f44304 | 2021-04-29 13:27:00 +0200 | [diff] [blame] | 68 | void (*ram_block_added)(RAMBlockNotifier *n, void *host, size_t size, |
| 69 | size_t max_size); |
| 70 | void (*ram_block_removed)(RAMBlockNotifier *n, void *host, size_t size, |
| 71 | size_t max_size); |
| 72 | void (*ram_block_resized)(RAMBlockNotifier *n, void *host, size_t old_size, |
| 73 | size_t new_size); |
Paolo Bonzini | 0987d73 | 2016-12-21 00:31:36 +0800 | [diff] [blame] | 74 | QLIST_ENTRY(RAMBlockNotifier) next; |
| 75 | }; |
| 76 | |
| 77 | void ram_block_notifier_add(RAMBlockNotifier *n); |
| 78 | void ram_block_notifier_remove(RAMBlockNotifier *n); |
David Hildenbrand | 8f44304 | 2021-04-29 13:27:00 +0200 | [diff] [blame] | 79 | void ram_block_notify_add(void *host, size_t size, size_t max_size); |
| 80 | void ram_block_notify_remove(void *host, size_t size, size_t max_size); |
| 81 | void ram_block_notify_resize(void *host, size_t old_size, size_t new_size); |
Paolo Bonzini | 0987d73 | 2016-12-21 00:31:36 +0800 | [diff] [blame] | 82 | |
Daniel P. Berrangé | ca411b7 | 2021-09-08 10:35:43 +0100 | [diff] [blame] | 83 | GString *ram_block_format(void); |
Paolo Bonzini | 0987d73 | 2016-12-21 00:31:36 +0800 | [diff] [blame] | 84 | |
| 85 | #endif /* RAMLIST_H */ |