[PATCH RFC 09/10] migration/dirtyrate: Expand dirty_bitmap to be tracked separately for devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxx>
---
 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(-)

diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index 5f1377ca048c..b4bbe0d20f6e 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -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 "
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 8476277aa9c9..28122d268ea3 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -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,
     },
diff --git a/hw/vfio/container.c b/hw/vfio/container.c
index 6bc1b8763f75..fff8319c0036 100644
--- a/hw/vfio/container.c
+++ b/hw/vfio/container.c
@@ -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);
diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c
index d75ecbf2ae52..4686cc713aac 100644
--- a/hw/vfio/iommufd.c
+++ b/hw/vfio/iommufd.c
@@ -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;
     }
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 4d5997e6bbae..59c1d8bcc495 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -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);
 
 /**
diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c
index aace12a78764..8c00cb6a3702 100644
--- a/migration/dirtyrate.c
+++ b/migration/dirtyrate.c
@@ -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;
diff --git a/migration/dirtyrate.h b/migration/dirtyrate.h
index 69d4c5b8655f..4061edf9f4de 100644
--- a/migration/dirtyrate.h
+++ b/migration/dirtyrate.h
@@ -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 */
 };
 
 /*
diff --git a/qapi/migration.json b/qapi/migration.json
index 27d7b281581d..082830c6e771 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -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'} }
 
 ##
diff --git a/softmmu/memory.c b/softmmu/memory.c
index bfa5d5178c5b..120c41f3b303 100644
--- a/softmmu/memory.c
+++ b/softmmu/memory.c
@@ -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)));
-- 
2.17.2




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux