@@ -1517,6 +1517,10 @@ static void kvm_log_sync(MemoryListener *listener,
{
KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener);
+ if (memory_global_dirty_devices()) {
+ return;
+ }
+
kvm_slots_lock();
kvm_physical_sync_dirty_bitmap(kml, section);
kvm_slots_unlock();
@@ -1529,6 +1533,10 @@ static void kvm_log_sync_global(MemoryListener *l)
KVMSlot *mem;
int i;
+ if (memory_global_dirty_devices()) {
+ return;
+ }
+
/* Flush all kernel dirty addresses into KVMSlot dirty bitmap */
kvm_dirty_ring_flush();
@@ -1558,6 +1566,10 @@ static void kvm_log_clear(MemoryListener *listener,
KVMMemoryListener *kml = container_of(listener, KVMMemoryListener, listener);
int r;
+ if (memory_global_dirty_devices()) {
+ return;
+ }
+
r = kvm_physical_log_clear(kml, section);
if (r < 0) {
error_report_once("%s: kvm log clear failed: mr=%s "
@@ -1739,10 +1739,11 @@ ERST
{
.name = "calc_dirty_rate",
- .args_type = "dirty_ring:-r,dirty_bitmap:-b,second:l,sample_pages_per_GB:l?",
- .params = "[-r] [-b] second [sample_pages_per_GB]",
+ .args_type = "dirty_devices:-d,dirty_ring:-r,dirty_bitmap:-b,second:l,sample_pages_per_GB:l?",
+ .params = "[-d] [-r] [-b] second [sample_pages_per_GB]",
.help = "start a round of guest dirty rate measurement (using -r to"
"\n\t\t\t specify dirty ring as the method of calculation and"
+ "\n\t\t\t specify devices as the only scope and"
"\n\t\t\t -b to specify dirty bitmap as method of calculation)",
.cmd = hmp_calc_dirty_rate,
},
@@ -84,6 +84,10 @@ static bool vfio_devices_all_dirty_tracking(VFIOContainer *bcontainer)
VFIODevice *vbasedev;
MigrationState *ms = migrate_get_current();
+ if (bcontainer->dirty_pages_supported) {
+ return true;
+ }
+
if (!migration_is_setup_or_active(ms->state)) {
return false;
}
@@ -311,6 +315,10 @@ static int vfio_get_dirty_bitmap(VFIOContainer *bcontainer, uint64_t iova,
uint64_t pages;
int ret;
+ if (!memory_global_dirty_devices()) {
+ return 0;
+ }
+
dbitmap = g_malloc0(sizeof(*dbitmap) + sizeof(*range));
dbitmap->argsz = sizeof(*dbitmap) + sizeof(*range);
@@ -150,6 +150,10 @@ static int iommufd_get_dirty_bitmap(VFIOContainer *bcontainer, uint64_t iova,
VFIOIOASHwpt *hwpt;
unsigned long *data, page_size, bitmap_size, pages;
+ if (!memory_global_dirty_devices()) {
+ return 0;
+ }
+
if (!bcontainer->dirty_pages_supported) {
return 0;
}
@@ -69,7 +69,10 @@ static inline void fuzz_dma_read_cb(size_t addr,
/* Dirty tracking enabled because measuring dirty rate */
#define GLOBAL_DIRTY_DIRTY_RATE (1U << 1)
-#define GLOBAL_DIRTY_MASK (0x3)
+/* Dirty tracking enabled because measuring devices dirty rate */
+#define GLOBAL_DIRTY_DIRTY_RATE_DEVICES (1U << 2)
+
+#define GLOBAL_DIRTY_MASK (0x7)
extern unsigned int global_dirty_tracking;
@@ -2433,6 +2436,11 @@ void memory_global_dirty_log_start(unsigned int flags);
*/
void memory_global_dirty_log_stop(unsigned int flags);
+/**
+ * memory_global_dirty_devices: check if the scope is just devices
+ */
+bool memory_global_dirty_devices(void);
+
void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled);
/**
@@ -45,6 +45,8 @@ static int CalculatingState = DIRTY_RATE_STATUS_UNSTARTED;
static struct DirtyRateStat DirtyStat;
static DirtyRateMeasureMode dirtyrate_mode =
DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
+static DirtyRateScope dirtyrate_scope =
+ DIRTY_RATE_SCOPE_ALL;
static int64_t set_sample_page_period(int64_t msec, int64_t initial_time)
{
@@ -99,6 +101,7 @@ static struct DirtyRateInfo *query_dirty_rate_info(void)
info->calc_time = DirtyStat.calc_time;
info->sample_pages = DirtyStat.sample_pages;
info->mode = dirtyrate_mode;
+ info->scope = dirtyrate_scope;
if (qatomic_read(&CalculatingState) == DIRTY_RATE_STATUS_MEASURED) {
info->has_dirty_rate = true;
@@ -406,32 +409,44 @@ static inline void record_dirtypages(DirtyPageRecord *dirty_pages,
}
}
-static void dirtyrate_global_dirty_log_start(void)
+static void dirtyrate_global_dirty_log_start(DirtyRateScope scope)
{
+ unsigned int flags = GLOBAL_DIRTY_DIRTY_RATE;
+
+ if (scope == DIRTY_RATE_SCOPE_DIRTY_DEVICES) {
+ flags |= GLOBAL_DIRTY_DIRTY_RATE_DEVICES;
+ }
+
qemu_mutex_lock_iothread();
- memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE);
+ memory_global_dirty_log_start(flags);
qemu_mutex_unlock_iothread();
}
-static void dirtyrate_global_dirty_log_stop(void)
+static void dirtyrate_global_dirty_log_stop(DirtyRateScope scope)
{
+ unsigned int flags = GLOBAL_DIRTY_DIRTY_RATE;
+
+ if (scope == DIRTY_RATE_SCOPE_DIRTY_DEVICES) {
+ flags |= GLOBAL_DIRTY_DIRTY_RATE_DEVICES;
+ }
+
qemu_mutex_lock_iothread();
memory_global_dirty_log_sync();
- memory_global_dirty_log_stop(GLOBAL_DIRTY_DIRTY_RATE);
+ memory_global_dirty_log_stop(flags);
qemu_mutex_unlock_iothread();
}
static int64_t do_calculate_dirtyrate_vcpu(DirtyPageRecord dirty_pages)
{
- uint64_t memory_size_MB;
+ uint64_t memory_size_KB;
int64_t time_s;
uint64_t increased_dirty_pages =
dirty_pages.end_pages - dirty_pages.start_pages;
- memory_size_MB = (increased_dirty_pages * TARGET_PAGE_SIZE) >> 20;
+ memory_size_KB = (increased_dirty_pages * TARGET_PAGE_SIZE) >> 10;
time_s = DirtyStat.calc_time;
- return memory_size_MB / time_s;
+ return memory_size_KB / time_s;
}
static inline void record_dirtypages_bitmap(DirtyPageRecord *dirty_pages,
@@ -466,9 +481,14 @@ static void calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config)
int64_t msec = 0;
int64_t start_time;
DirtyPageRecord dirty_pages;
+ unsigned int flags = GLOBAL_DIRTY_DIRTY_RATE;
+
+ if (config.scope == DIRTY_RATE_SCOPE_DIRTY_DEVICES) {
+ flags |= GLOBAL_DIRTY_DIRTY_RATE_DEVICES;
+ }
qemu_mutex_lock_iothread();
- memory_global_dirty_log_start(GLOBAL_DIRTY_DIRTY_RATE);
+ memory_global_dirty_log_start(flags);
/*
* 1'round of log sync may return all 1 bits with
@@ -500,7 +520,7 @@ static void calculate_dirtyrate_dirty_bitmap(struct DirtyRateConfig config)
* 1. fetch dirty bitmap from kvm
* 2. stop dirty tracking
*/
- dirtyrate_global_dirty_log_stop();
+ dirtyrate_global_dirty_log_stop(config.scope);
record_dirtypages_bitmap(&dirty_pages, false);
@@ -527,7 +547,7 @@ static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config)
DirtyStat.dirty_ring.nvcpu = nvcpu;
DirtyStat.dirty_ring.rates = malloc(sizeof(DirtyRateVcpu) * nvcpu);
- dirtyrate_global_dirty_log_start();
+ dirtyrate_global_dirty_log_start(config.scope);
CPU_FOREACH(cpu) {
record_dirtypages(dirty_pages, cpu, true);
@@ -540,7 +560,7 @@ static void calculate_dirtyrate_dirty_ring(struct DirtyRateConfig config)
msec = set_sample_page_period(msec, start_time);
DirtyStat.calc_time = msec / 1000;
- dirtyrate_global_dirty_log_stop();
+ dirtyrate_global_dirty_log_stop(config.scope);
CPU_FOREACH(cpu) {
record_dirtypages(dirty_pages, cpu, false);
@@ -631,6 +651,8 @@ void *get_dirtyrate_thread(void *arg)
void qmp_calc_dirty_rate(int64_t calc_time,
bool has_sample_pages,
int64_t sample_pages,
+ bool has_scope,
+ DirtyRateScope scope,
bool has_mode,
DirtyRateMeasureMode mode,
Error **errp)
@@ -701,6 +723,7 @@ void qmp_calc_dirty_rate(int64_t calc_time,
config.sample_period_seconds = calc_time;
config.sample_pages_per_gigabytes = sample_pages;
config.mode = mode;
+ config.scope = scope;
cleanup_dirtyrate_stat(config);
@@ -709,6 +732,7 @@ void qmp_calc_dirty_rate(int64_t calc_time,
* been used in last calculation
**/
dirtyrate_mode = mode;
+ dirtyrate_scope = scope;
start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000;
init_dirtyrate_stat(start_time, config);
@@ -736,9 +760,11 @@ void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict)
info->calc_time);
monitor_printf(mon, "Mode: %s\n",
DirtyRateMeasureMode_str(info->mode));
+ monitor_printf(mon, "Scope: %s\n",
+ DirtyRateScope_str(info->scope));
monitor_printf(mon, "Dirty rate: ");
if (info->has_dirty_rate) {
- monitor_printf(mon, "%"PRIi64" (MB/s)\n", info->dirty_rate);
+ monitor_printf(mon, "%"PRIi64" (KB/s)\n", info->dirty_rate);
if (info->has_vcpu_dirty_rate) {
DirtyRateVcpuList *rate, *head = info->vcpu_dirty_rate;
for (rate = head; rate != NULL; rate = rate->next) {
@@ -762,7 +788,9 @@ void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict)
bool has_sample_pages = (sample_pages != -1);
bool dirty_ring = qdict_get_try_bool(qdict, "dirty_ring", false);
bool dirty_bitmap = qdict_get_try_bool(qdict, "dirty_bitmap", false);
+ bool dirty_devices = qdict_get_try_bool(qdict, "dirty_devices", false);
DirtyRateMeasureMode mode = DIRTY_RATE_MEASURE_MODE_PAGE_SAMPLING;
+ DirtyRateScope scope = DIRTY_RATE_SCOPE_ALL;
Error *err = NULL;
if (!sec) {
@@ -781,9 +809,12 @@ void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict)
} else if (dirty_ring) {
mode = DIRTY_RATE_MEASURE_MODE_DIRTY_RING;
}
+ if (dirty_devices) {
+ scope = DIRTY_RATE_SCOPE_DIRTY_DEVICES;
+ }
- qmp_calc_dirty_rate(sec, has_sample_pages, sample_pages, true,
- mode, &err);
+ qmp_calc_dirty_rate(sec, has_sample_pages, sample_pages,
+ true, scope, true, mode, &err);
if (err) {
hmp_handle_error(mon, err);
return;
@@ -44,6 +44,7 @@ struct DirtyRateConfig {
uint64_t sample_pages_per_gigabytes; /* sample pages per GB */
int64_t sample_period_seconds; /* time duration between two sampling */
DirtyRateMeasureMode mode; /* mode of dirtyrate measurement */
+ DirtyRateScope scope; /* scope of dirtyrate measurement */
};
/*
@@ -1793,6 +1793,19 @@
{ 'enum': 'DirtyRateMeasureMode',
'data': ['page-sampling', 'dirty-ring', 'dirty-bitmap'] }
+##
+# @DirtyRateScope:
+#
+# An enumeration of scope of measuring dirtyrate.
+#
+# @dirty-devices: calculate dirtyrate by devices only.
+#
+# Since: 6.2
+#
+##
+{ 'enum': 'DirtyRateScope',
+ 'data': ['all', 'dirty-devices'] }
+
##
# @DirtyRateInfo:
#
@@ -1827,6 +1840,7 @@
'calc-time': 'int64',
'sample-pages': 'uint64',
'mode': 'DirtyRateMeasureMode',
+ 'scope': 'DirtyRateScope',
'*vcpu-dirty-rate': [ 'DirtyRateVcpu' ] } }
##
@@ -1851,6 +1865,7 @@
##
{ 'command': 'calc-dirty-rate', 'data': {'calc-time': 'int64',
'*sample-pages': 'int',
+ '*scope': 'DirtyRateScope',
'*mode': 'DirtyRateMeasureMode'} }
##
@@ -2826,6 +2826,11 @@ void memory_global_dirty_log_start(unsigned int flags)
}
}
+bool memory_global_dirty_devices(void)
+{
+ return !!(global_dirty_tracking & GLOBAL_DIRTY_DIRTY_RATE_DEVICES);
+}
+
static void memory_global_dirty_log_do_stop(unsigned int flags)
{
assert(flags && !(flags & (~GLOBAL_DIRTY_MASK)));
Expand dirtyrate measurer that is accessible via HMP calc_dirty_rate or QMP 'calc-dirty-rate' to receive a @scope argument. The scope then restricts the dirty tracking to be done at devices only, while neither enabling or using the KVM (CPU) dirty tracker. The default stays as is i.e. dirty-ring / dirty-bitmap from KVM. This is useful to test, exercise the IOMMU dirty tracker and observe how much a given device is dirtying memory. Signed-off-by: Joao Martins <joao.m.martins@oracle.com> --- accel/kvm/kvm-all.c | 12 +++++++++ hmp-commands.hx | 5 ++-- hw/vfio/container.c | 8 ++++++ hw/vfio/iommufd.c | 4 +++ include/exec/memory.h | 10 +++++++- migration/dirtyrate.c | 59 +++++++++++++++++++++++++++++++++---------- migration/dirtyrate.h | 1 + qapi/migration.json | 15 +++++++++++ softmmu/memory.c | 5 ++++ 9 files changed, 102 insertions(+), 17 deletions(-)