ca411b7c8a
This is a counterpart to the HMP "info ramblock" command. It is being added with an "x-" prefix because this QMP command is intended as an adhoc debugging tool and will thus not be modelled in QAPI as fully structured data, nor will it have long term guaranteed stability. The existing HMP command is rewritten to call the QMP command. Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
86 lines
3.1 KiB
C
86 lines
3.1 KiB
C
#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 */
|