qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 00/22] Migration next
@ 2012-07-13  7:23 Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 01/22] savevm: Use a struct to pass all handlers Juan Quintela
                   ` (21 more replies)
  0 siblings, 22 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel

Hi

This includes ram_save_live split + XBZRE changes.

Changes for the previous posts of this series:

ram_save_split:
- recalculate expected_time (Igor).
- Use PRIu64 consistently everywhere

XBZRE:
- rebased on top of previous series
- remove stage3 optimization, now XBZRE don't know about stages
  (code is much simpler)
- reintroduce the stage3(complete) optimization.  We now sent the page
  compressed if the page is on the cache, otherwise, we sent the page
  uncompressed without putting it on the cache.  This is better than
  the previous optimization.

Please review, Juan.

Juan Quintela (13):
  savevm: Use a struct to pass all handlers
  savevm: Live migration handlers register the struct directly
  savevm: remove SaveSetParamsHandler
  savevm: remove SaveLiveStateHandler
  savevm: Refactor cancel operation in its own operation
  savevm: introduce is_active method
  savevm: split save_live_setup from save_live_state
  savevm: split save_live into stage2 and stage3
  ram: save_live_setup() don't need to sent pages
  ram: save_live_complete() only do one loop
  ram: iterate phase
  ram: save_live_setup() we don't need to synchronize the dirty bitmap.
  ram: save live optimization

Orit Wasserman (9):
  Add migration capabilities
  Add XBZRLE documentation
  Add cache handling functions
  Add uleb encoding/decoding functions
  Change ram_save_block to return -1 if there are no more changes
  Add xbzrle_encode_buffer and xbzrle_decode_buffer functions
  Add XBZRLE to ram_save_block and ram_save_live
  Add migrate_set_cachesize command
  Add XBZRLE statistics

 Makefile.objs             |    1 +
 arch_init.c               |  380 +++++++++++++++++++++++++++++++++++++++------
 block-migration.c         |  153 +++++++++++-------
 cutils.c                  |   42 +++++
 docs/xbzrle.txt           |  136 ++++++++++++++++
 hmp-commands.hx           |   36 +++++
 hmp.c                     |   90 +++++++++++
 hmp.h                     |    3 +
 include/qemu/page_cache.h |   79 ++++++++++
 migration.c               |  159 ++++++++++++++++++-
 migration.h               |   25 ++-
 monitor.c                 |    7 +
 page_cache.c              |  216 ++++++++++++++++++++++++++
 qapi-schema.json          |  102 +++++++++++-
 qemu-common.h             |   21 +++
 qmp-commands.hx           |  127 ++++++++++++++-
 savevm.c                  |  236 +++++++++++++++++++++++-----
 vl.c                      |    3 +-
 vmstate.h                 |   18 ++-
 19 files changed, 1676 insertions(+), 158 deletions(-)
 create mode 100644 docs/xbzrle.txt
 create mode 100644 include/qemu/page_cache.h
 create mode 100644 page_cache.c

-- 
1.7.10.4

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 01/22] savevm: Use a struct to pass all handlers
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 02/22] savevm: Live migration handlers register the struct directly Juan Quintela
                   ` (20 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel

This would make easier to add more operations in the next patches.

Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 savevm.c  |   54 +++++++++++++++++++++++++-----------------------------
 vmstate.h |    7 +++++++
 2 files changed, 32 insertions(+), 29 deletions(-)

diff --git a/savevm.c b/savevm.c
index a15c163..73626d4 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1171,10 +1171,7 @@ typedef struct SaveStateEntry {
     int alias_id;
     int version_id;
     int section_id;
-    SaveSetParamsHandler *set_params;
-    SaveLiveStateHandler *save_live_state;
-    SaveStateHandler *save_state;
-    LoadStateHandler *load_state;
+    SaveVMHandlers *ops;
     const VMStateDescription *vmsd;
     void *opaque;
     CompatEntry *compat;
@@ -1237,10 +1234,11 @@ int register_savevm_live(DeviceState *dev,
     se = g_malloc0(sizeof(SaveStateEntry));
     se->version_id = version_id;
     se->section_id = global_section_id++;
-    se->set_params = set_params;
-    se->save_live_state = save_live_state;
-    se->save_state = save_state;
-    se->load_state = load_state;
+    se->ops = g_malloc0(sizeof(SaveVMHandlers));
+    se->ops->set_params = set_params;
+    se->ops->save_live_state = save_live_state;
+    se->ops->save_state = save_state;
+    se->ops->load_state = load_state;
     se->opaque = opaque;
     se->vmsd = NULL;
     se->no_migrate = 0;
@@ -1309,6 +1307,7 @@ void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque)
             if (se->compat) {
                 g_free(se->compat);
             }
+            g_free(se->ops);
             g_free(se);
         }
     }
@@ -1327,9 +1326,6 @@ int vmstate_register_with_alias_id(DeviceState *dev, int instance_id,
     se = g_malloc0(sizeof(SaveStateEntry));
     se->version_id = vmsd->version_id;
     se->section_id = global_section_id++;
-    se->save_live_state = NULL;
-    se->save_state = NULL;
-    se->load_state = NULL;
     se->opaque = opaque;
     se->vmsd = vmsd;
     se->alias_id = alias_id;
@@ -1524,7 +1520,7 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
 static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id)
 {
     if (!se->vmsd) {         /* Old style */
-        return se->load_state(f, se->opaque, version_id);
+        return se->ops->load_state(f, se->opaque, version_id);
     }
     return vmstate_load_state(f, se->vmsd, se->opaque, version_id);
 }
@@ -1532,7 +1528,7 @@ static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id)
 static void vmstate_save(QEMUFile *f, SaveStateEntry *se)
 {
     if (!se->vmsd) {         /* Old style */
-        se->save_state(f, se->opaque);
+        se->ops->save_state(f, se->opaque);
         return;
     }
     vmstate_save_state(f,se->vmsd, se->opaque);
@@ -1569,10 +1565,10 @@ int qemu_savevm_state_begin(QEMUFile *f,
     int ret;

     QTAILQ_FOREACH(se, &savevm_handlers, entry) {
-        if(se->set_params == NULL) {
+        if (!se->ops || !se->ops->set_params) {
             continue;
         }
-        se->set_params(params, se->opaque);
+        se->ops->set_params(params, se->opaque);
     }
     
     qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
@@ -1581,9 +1577,9 @@ int qemu_savevm_state_begin(QEMUFile *f,
     QTAILQ_FOREACH(se, &savevm_handlers, entry) {
         int len;

-        if (se->save_live_state == NULL)
+        if (!se->ops || !se->ops->save_live_state) {
             continue;
-
+        }
         /* Section type */
         qemu_put_byte(f, QEMU_VM_SECTION_START);
         qemu_put_be32(f, se->section_id);
@@ -1596,7 +1592,7 @@ int qemu_savevm_state_begin(QEMUFile *f,
         qemu_put_be32(f, se->instance_id);
         qemu_put_be32(f, se->version_id);

-        ret = se->save_live_state(f, QEMU_VM_SECTION_START, se->opaque);
+        ret = se->ops->save_live_state(f, QEMU_VM_SECTION_START, se->opaque);
         if (ret < 0) {
             qemu_savevm_state_cancel(f);
             return ret;
@@ -1623,9 +1619,9 @@ int qemu_savevm_state_iterate(QEMUFile *f)
     int ret = 1;

     QTAILQ_FOREACH(se, &savevm_handlers, entry) {
-        if (se->save_live_state == NULL)
+        if (!se->ops || !se->ops->save_live_state) {
             continue;
-
+        }
         if (qemu_file_rate_limit(f)) {
             return 0;
         }
@@ -1634,7 +1630,7 @@ int qemu_savevm_state_iterate(QEMUFile *f)
         qemu_put_byte(f, QEMU_VM_SECTION_PART);
         qemu_put_be32(f, se->section_id);

-        ret = se->save_live_state(f, QEMU_VM_SECTION_PART, se->opaque);
+        ret = se->ops->save_live_state(f, QEMU_VM_SECTION_PART, se->opaque);
         trace_savevm_section_end(se->section_id);

         if (ret <= 0) {
@@ -1663,15 +1659,15 @@ int qemu_savevm_state_complete(QEMUFile *f)
     cpu_synchronize_all_states();

     QTAILQ_FOREACH(se, &savevm_handlers, entry) {
-        if (se->save_live_state == NULL)
+        if (!se->ops || !se->ops->save_live_state) {
             continue;
-
+        }
         trace_savevm_section_start();
         /* Section type */
         qemu_put_byte(f, QEMU_VM_SECTION_END);
         qemu_put_be32(f, se->section_id);

-        ret = se->save_live_state(f, QEMU_VM_SECTION_END, se->opaque);
+        ret = se->ops->save_live_state(f, QEMU_VM_SECTION_END, se->opaque);
         trace_savevm_section_end(se->section_id);
         if (ret < 0) {
             return ret;
@@ -1681,9 +1677,9 @@ int qemu_savevm_state_complete(QEMUFile *f)
     QTAILQ_FOREACH(se, &savevm_handlers, entry) {
         int len;

-	if (se->save_state == NULL && se->vmsd == NULL)
+        if ((!se->ops || !se->ops->save_state) && !se->vmsd) {
 	    continue;
-
+        }
         trace_savevm_section_start();
         /* Section type */
         qemu_put_byte(f, QEMU_VM_SECTION_FULL);
@@ -1711,8 +1707,8 @@ void qemu_savevm_state_cancel(QEMUFile *f)
     SaveStateEntry *se;

     QTAILQ_FOREACH(se, &savevm_handlers, entry) {
-        if (se->save_live_state) {
-            se->save_live_state(f, -1, se->opaque);
+        if (se->ops && se->ops->save_live_state) {
+            se->ops->save_live_state(f, -1, se->opaque);
         }
     }
 }
@@ -1765,7 +1761,7 @@ static int qemu_save_device_state(QEMUFile *f)
         if (se->is_ram) {
             continue;
         }
-        if (se->save_state == NULL && se->vmsd == NULL) {
+        if ((!se->ops || !se->ops->save_state) && !se->vmsd) {
             continue;
         }

diff --git a/vmstate.h b/vmstate.h
index 5af45e0..909af69 100644
--- a/vmstate.h
+++ b/vmstate.h
@@ -31,6 +31,13 @@ typedef void SaveStateHandler(QEMUFile *f, void *opaque);
 typedef int SaveLiveStateHandler(QEMUFile *f, int stage, void *opaque);
 typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);

+typedef struct SaveVMHandlers {
+    SaveSetParamsHandler *set_params;
+    SaveStateHandler *save_state;
+    SaveLiveStateHandler *save_live_state;
+    LoadStateHandler *load_state;
+} SaveVMHandlers;
+
 int register_savevm(DeviceState *dev,
                     const char *idstr,
                     int instance_id,
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 02/22] savevm: Live migration handlers register the struct directly
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 01/22] savevm: Use a struct to pass all handlers Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 03/22] savevm: remove SaveSetParamsHandler Juan Quintela
                   ` (19 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel

Notice that the live migration users never unregister, so no problem
about freeing the ops structure.

Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 arch_init.c       |    9 +++++++--
 block-migration.c |   10 ++++++++--
 migration.h       |    4 ++--
 savevm.c          |   18 +++++++-----------
 vl.c              |    3 +--
 vmstate.h         |    5 +----
 6 files changed, 26 insertions(+), 23 deletions(-)

diff --git a/arch_init.c b/arch_init.c
index 5b0f562..606b848 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -298,7 +298,7 @@ static void migration_end(void)

 #define MAX_WAIT 50 /* ms, half buffered_file limit */

-int ram_save_live(QEMUFile *f, int stage, void *opaque)
+static int ram_save_live(QEMUFile *f, int stage, void *opaque)
 {
     ram_addr_t addr;
     uint64_t bytes_transferred_last;
@@ -437,7 +437,7 @@ static inline void *host_from_stream_offset(QEMUFile *f,
     return NULL;
 }

-int ram_load(QEMUFile *f, void *opaque, int version_id)
+static int ram_load(QEMUFile *f, void *opaque, int version_id)
 {
     ram_addr_t addr;
     int flags, ret = 0;
@@ -534,6 +534,11 @@ done:
     return ret;
 }

+SaveVMHandlers savevm_ram_handlers = {
+    .save_live_state = ram_save_live,
+    .load_state = ram_load,
+};
+
 #ifdef HAS_AUDIO
 struct soundhw {
     const char *name;
diff --git a/block-migration.c b/block-migration.c
index b95b4e1..00151a0 100644
--- a/block-migration.c
+++ b/block-migration.c
@@ -709,11 +709,17 @@ static void block_set_params(const MigrationParams *params, void *opaque)
     block_mig_state.blk_enable |= params->shared;
 }

+SaveVMHandlers savevm_block_handlers = {
+    .set_params = block_set_params,
+    .save_live_state = block_save_live,
+    .load_state = block_load,
+};
+
 void blk_mig_init(void)
 {
     QSIMPLEQ_INIT(&block_mig_state.bmds_list);
     QSIMPLEQ_INIT(&block_mig_state.blk_list);

-    register_savevm_live(NULL, "block", 0, 1, block_set_params,
-                         block_save_live, NULL, block_load, &block_mig_state);
+    register_savevm_live(NULL, "block", 0, 1, &savevm_block_handlers,
+                         &block_mig_state);
 }
diff --git a/migration.h b/migration.h
index de13004..57572a6 100644
--- a/migration.h
+++ b/migration.h
@@ -18,6 +18,7 @@
 #include "qemu-common.h"
 #include "notify.h"
 #include "error.h"
+#include "vmstate.h"

 struct MigrationParams {
     bool blk;
@@ -81,8 +82,7 @@ uint64_t ram_bytes_remaining(void);
 uint64_t ram_bytes_transferred(void);
 uint64_t ram_bytes_total(void);

-int ram_save_live(QEMUFile *f, int stage, void *opaque);
-int ram_load(QEMUFile *f, void *opaque, int version_id);
+extern SaveVMHandlers savevm_ram_handlers;

 /**
  * @migrate_add_blocker - prevent migration from proceeding
diff --git a/savevm.c b/savevm.c
index 73626d4..a451be2 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1223,10 +1223,7 @@ int register_savevm_live(DeviceState *dev,
                          const char *idstr,
                          int instance_id,
                          int version_id,
-                         SaveSetParamsHandler *set_params,
-                         SaveLiveStateHandler *save_live_state,
-                         SaveStateHandler *save_state,
-                         LoadStateHandler *load_state,
+                         SaveVMHandlers *ops,
                          void *opaque)
 {
     SaveStateEntry *se;
@@ -1234,16 +1231,12 @@ int register_savevm_live(DeviceState *dev,
     se = g_malloc0(sizeof(SaveStateEntry));
     se->version_id = version_id;
     se->section_id = global_section_id++;
-    se->ops = g_malloc0(sizeof(SaveVMHandlers));
-    se->ops->set_params = set_params;
-    se->ops->save_live_state = save_live_state;
-    se->ops->save_state = save_state;
-    se->ops->load_state = load_state;
+    se->ops = ops;
     se->opaque = opaque;
     se->vmsd = NULL;
     se->no_migrate = 0;
     /* if this is a live_savem then set is_ram */
-    if (save_live_state != NULL) {
+    if (ops->save_live_state != NULL) {
         se->is_ram = 1;
     }

@@ -1282,8 +1275,11 @@ int register_savevm(DeviceState *dev,
                     LoadStateHandler *load_state,
                     void *opaque)
 {
+    SaveVMHandlers *ops = g_malloc0(sizeof(SaveVMHandlers));
+    ops->save_state = save_state;
+    ops->load_state = load_state;
     return register_savevm_live(dev, idstr, instance_id, version_id,
-                                NULL, NULL, save_state, load_state, opaque);
+                                ops, opaque);
 }

 void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque)
diff --git a/vl.c b/vl.c
index 2e140f5..61fe7df 100644
--- a/vl.c
+++ b/vl.c
@@ -3437,8 +3437,7 @@ int main(int argc, char **argv, char **envp)
     default_drive(default_sdcard, snapshot, machine->use_scsi,
                   IF_SD, 0, SD_OPTS);

-    register_savevm_live(NULL, "ram", 0, 4, NULL, ram_save_live, NULL,
-                         ram_load, NULL);
+    register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, NULL);

     if (nb_numa_nodes > 0) {
         int i;
diff --git a/vmstate.h b/vmstate.h
index 909af69..4bce53b 100644
--- a/vmstate.h
+++ b/vmstate.h
@@ -50,10 +50,7 @@ int register_savevm_live(DeviceState *dev,
                          const char *idstr,
                          int instance_id,
                          int version_id,
-                         SaveSetParamsHandler *set_params,
-                         SaveLiveStateHandler *save_live_state,
-                         SaveStateHandler *save_state,
-                         LoadStateHandler *load_state,
+                         SaveVMHandlers *ops,
                          void *opaque);

 void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque);
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 03/22] savevm: remove SaveSetParamsHandler
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 01/22] savevm: Use a struct to pass all handlers Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 02/22] savevm: Live migration handlers register the struct directly Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 04/22] savevm: remove SaveLiveStateHandler Juan Quintela
                   ` (18 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel

It was used only once, just unfold.

Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 vmstate.h |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/vmstate.h b/vmstate.h
index 4bce53b..5e1a7cc 100644
--- a/vmstate.h
+++ b/vmstate.h
@@ -26,13 +26,12 @@
 #ifndef QEMU_VMSTATE_H
 #define QEMU_VMSTATE_H 1

-typedef void SaveSetParamsHandler(const MigrationParams *params, void * opaque);
 typedef void SaveStateHandler(QEMUFile *f, void *opaque);
 typedef int SaveLiveStateHandler(QEMUFile *f, int stage, void *opaque);
 typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);

 typedef struct SaveVMHandlers {
-    SaveSetParamsHandler *set_params;
+    void (*set_params)(const MigrationParams *params, void * opaque);
     SaveStateHandler *save_state;
     SaveLiveStateHandler *save_live_state;
     LoadStateHandler *load_state;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 04/22] savevm: remove SaveLiveStateHandler
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (2 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 03/22] savevm: remove SaveSetParamsHandler Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 05/22] savevm: Refactor cancel operation in its own operation Juan Quintela
                   ` (17 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel

It was used only once, just unfold.

Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 vmstate.h |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/vmstate.h b/vmstate.h
index 5e1a7cc..0e24834 100644
--- a/vmstate.h
+++ b/vmstate.h
@@ -27,13 +27,12 @@
 #define QEMU_VMSTATE_H 1

 typedef void SaveStateHandler(QEMUFile *f, void *opaque);
-typedef int SaveLiveStateHandler(QEMUFile *f, int stage, void *opaque);
 typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);

 typedef struct SaveVMHandlers {
     void (*set_params)(const MigrationParams *params, void * opaque);
     SaveStateHandler *save_state;
-    SaveLiveStateHandler *save_live_state;
+    int (*save_live_state)(QEMUFile *f, int stage, void *opaque);
     LoadStateHandler *load_state;
 } SaveVMHandlers;

-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 05/22] savevm: Refactor cancel operation in its own operation
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (3 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 04/22] savevm: remove SaveLiveStateHandler Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 06/22] savevm: introduce is_active method Juan Quintela
                   ` (16 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel

Intead of abusing stage with value -1.

Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 arch_init.c       |   11 ++++++-----
 block-migration.c |   10 ++++++----
 savevm.c          |    4 ++--
 vmstate.h         |    1 +
 4 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/arch_init.c b/arch_init.c
index 606b848..f6bc3fe 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -296,6 +296,11 @@ static void migration_end(void)
     memory_global_dirty_log_stop();
 }

+static void ram_migration_cancel(void *opaque)
+{
+    migration_end();
+}
+
 #define MAX_WAIT 50 /* ms, half buffered_file limit */

 static int ram_save_live(QEMUFile *f, int stage, void *opaque)
@@ -306,11 +311,6 @@ static int ram_save_live(QEMUFile *f, int stage, void *opaque)
     int ret;
     int i;

-    if (stage < 0) {
-        migration_end();
-        return 0;
-    }
-
     memory_global_sync_dirty_bitmap(get_system_memory());

     if (stage == 1) {
@@ -537,6 +537,7 @@ done:
 SaveVMHandlers savevm_ram_handlers = {
     .save_live_state = ram_save_live,
     .load_state = ram_load,
+    .cancel = ram_migration_cancel,
 };

 #ifdef HAS_AUDIO
diff --git a/block-migration.c b/block-migration.c
index 00151a0..cd8a8dd 100644
--- a/block-migration.c
+++ b/block-migration.c
@@ -536,6 +536,11 @@ static void blk_mig_cleanup(void)
     }
 }

+static void block_migration_cancel(void *opaque)
+{
+    blk_mig_cleanup();
+}
+
 static int block_save_live(QEMUFile *f, int stage, void *opaque)
 {
     int ret;
@@ -543,10 +548,6 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
     DPRINTF("Enter save live stage %d submitted %d transferred %d\n",
             stage, block_mig_state.submitted, block_mig_state.transferred);

-    if (stage < 0) {
-        blk_mig_cleanup();
-        return 0;
-    }

     if (block_mig_state.blk_enable != 1) {
         /* no need to migrate storage */
@@ -713,6 +714,7 @@ SaveVMHandlers savevm_block_handlers = {
     .set_params = block_set_params,
     .save_live_state = block_save_live,
     .load_state = block_load,
+    .cancel = block_migration_cancel,
 };

 void blk_mig_init(void)
diff --git a/savevm.c b/savevm.c
index a451be2..888c5a2 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1703,8 +1703,8 @@ void qemu_savevm_state_cancel(QEMUFile *f)
     SaveStateEntry *se;

     QTAILQ_FOREACH(se, &savevm_handlers, entry) {
-        if (se->ops && se->ops->save_live_state) {
-            se->ops->save_live_state(f, -1, se->opaque);
+        if (se->ops && se->ops->cancel) {
+            se->ops->cancel(se->opaque);
         }
     }
 }
diff --git a/vmstate.h b/vmstate.h
index 0e24834..1dd42f5 100644
--- a/vmstate.h
+++ b/vmstate.h
@@ -33,6 +33,7 @@ typedef struct SaveVMHandlers {
     void (*set_params)(const MigrationParams *params, void * opaque);
     SaveStateHandler *save_state;
     int (*save_live_state)(QEMUFile *f, int stage, void *opaque);
+    void (*cancel)(void *opaque);
     LoadStateHandler *load_state;
 } SaveVMHandlers;

-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 06/22] savevm: introduce is_active method
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (4 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 05/22] savevm: Refactor cancel operation in its own operation Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 07/22] savevm: split save_live_setup from save_live_state Juan Quintela
                   ` (15 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel

Enable the creation of a method to tell migration if that section is
active and should be migrate.  We use it for blk-migration, that is
normally not active.  We don't create the method for RAM, as setups
without RAM are very strange O:-)

Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 block-migration.c |   13 ++++++-------
 savevm.c          |   15 +++++++++++++++
 vmstate.h         |    1 +
 3 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/block-migration.c b/block-migration.c
index cd8a8dd..6d37dc1 100644
--- a/block-migration.c
+++ b/block-migration.c
@@ -548,13 +548,6 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
     DPRINTF("Enter save live stage %d submitted %d transferred %d\n",
             stage, block_mig_state.submitted, block_mig_state.transferred);

-
-    if (block_mig_state.blk_enable != 1) {
-        /* no need to migrate storage */
-        qemu_put_be64(f, BLK_MIG_FLAG_EOS);
-        return 1;
-    }
-
     if (stage == 1) {
         init_blk_migration(f);

@@ -710,11 +703,17 @@ static void block_set_params(const MigrationParams *params, void *opaque)
     block_mig_state.blk_enable |= params->shared;
 }

+static bool block_is_active(void *opaque)
+{
+    return block_mig_state.blk_enable == 1;
+}
+
 SaveVMHandlers savevm_block_handlers = {
     .set_params = block_set_params,
     .save_live_state = block_save_live,
     .load_state = block_load,
     .cancel = block_migration_cancel,
+    .is_active = block_is_active,
 };

 void blk_mig_init(void)
diff --git a/savevm.c b/savevm.c
index 888c5a2..afa0c9e 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1576,6 +1576,11 @@ int qemu_savevm_state_begin(QEMUFile *f,
         if (!se->ops || !se->ops->save_live_state) {
             continue;
         }
+        if (se->ops && se->ops->is_active) {
+            if (!se->ops->is_active(se->opaque)) {
+                continue;
+            }
+        }
         /* Section type */
         qemu_put_byte(f, QEMU_VM_SECTION_START);
         qemu_put_be32(f, se->section_id);
@@ -1618,6 +1623,11 @@ int qemu_savevm_state_iterate(QEMUFile *f)
         if (!se->ops || !se->ops->save_live_state) {
             continue;
         }
+        if (se->ops && se->ops->is_active) {
+            if (!se->ops->is_active(se->opaque)) {
+                continue;
+            }
+        }
         if (qemu_file_rate_limit(f)) {
             return 0;
         }
@@ -1658,6 +1668,11 @@ int qemu_savevm_state_complete(QEMUFile *f)
         if (!se->ops || !se->ops->save_live_state) {
             continue;
         }
+        if (se->ops && se->ops->is_active) {
+            if (!se->ops->is_active(se->opaque)) {
+                continue;
+            }
+        }
         trace_savevm_section_start();
         /* Section type */
         qemu_put_byte(f, QEMU_VM_SECTION_END);
diff --git a/vmstate.h b/vmstate.h
index 1dd42f5..96651a5 100644
--- a/vmstate.h
+++ b/vmstate.h
@@ -35,6 +35,7 @@ typedef struct SaveVMHandlers {
     int (*save_live_state)(QEMUFile *f, int stage, void *opaque);
     void (*cancel)(void *opaque);
     LoadStateHandler *load_state;
+    bool (*is_active)(void *opaque);
 } SaveVMHandlers;

 int register_savevm(DeviceState *dev,
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 07/22] savevm: split save_live_setup from save_live_state
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (5 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 06/22] savevm: introduce is_active method Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 08/22] savevm: split save_live into stage2 and stage3 Juan Quintela
                   ` (14 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel

This patch splits stage 1 to its own function for both save_live
users, ram and block.  It is just a copy of the function, removing the
parts of the other stages.  Optimizations would came later.

Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 arch_init.c       |   87 +++++++++++++++++++++++++++++++++++++++--------------
 block-migration.c |   35 ++++++++++++++++-----
 savevm.c          |    4 +--
 vmstate.h         |    1 +
 4 files changed, 96 insertions(+), 31 deletions(-)

diff --git a/arch_init.c b/arch_init.c
index f6bc3fe..2a4903c 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -303,44 +303,86 @@ static void ram_migration_cancel(void *opaque)

 #define MAX_WAIT 50 /* ms, half buffered_file limit */

-static int ram_save_live(QEMUFile *f, int stage, void *opaque)
+static int ram_save_setup(QEMUFile *f, void *opaque)
 {
     ram_addr_t addr;
-    uint64_t bytes_transferred_last;
+    RAMBlock *block;
     double bwidth = 0;
     int ret;
     int i;

     memory_global_sync_dirty_bitmap(get_system_memory());

-    if (stage == 1) {
-        RAMBlock *block;
-        bytes_transferred = 0;
-        last_block = NULL;
-        last_offset = 0;
-        sort_ram_list();
-
-        /* Make sure all dirty bits are set */
-        QLIST_FOREACH(block, &ram_list.blocks, next) {
-            for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) {
-                if (!memory_region_get_dirty(block->mr, addr, TARGET_PAGE_SIZE,
-                                             DIRTY_MEMORY_MIGRATION)) {
-                    memory_region_set_dirty(block->mr, addr, TARGET_PAGE_SIZE);
-                }
+    bytes_transferred = 0;
+    last_block = NULL;
+    last_offset = 0;
+    sort_ram_list();
+
+    /* Make sure all dirty bits are set */
+    QLIST_FOREACH(block, &ram_list.blocks, next) {
+        for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) {
+            if (!memory_region_get_dirty(block->mr, addr, TARGET_PAGE_SIZE,
+                                         DIRTY_MEMORY_MIGRATION)) {
+                memory_region_set_dirty(block->mr, addr, TARGET_PAGE_SIZE);
             }
         }
+    }

-        memory_global_dirty_log_start();
+    memory_global_dirty_log_start();
+
+    qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
+
+    QLIST_FOREACH(block, &ram_list.blocks, next) {
+        qemu_put_byte(f, strlen(block->idstr));
+        qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr));
+        qemu_put_be64(f, block->length);
+    }
+
+    bwidth = qemu_get_clock_ns(rt_clock);

-        qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
+    i = 0;
+    while ((ret = qemu_file_rate_limit(f)) == 0) {
+        int bytes_sent;

-        QLIST_FOREACH(block, &ram_list.blocks, next) {
-            qemu_put_byte(f, strlen(block->idstr));
-            qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr));
-            qemu_put_be64(f, block->length);
+        bytes_sent = ram_save_block(f);
+        bytes_transferred += bytes_sent;
+        if (bytes_sent == 0) { /* no more blocks */
+            break;
+        }
+        /* we want to check in the 1st loop, just in case it was the 1st time
+           and we had to sync the dirty bitmap.
+           qemu_get_clock_ns() is a bit expensive, so we only check each some
+           iterations
+        */
+        if ((i & 63) == 0) {
+            uint64_t t1 = (qemu_get_clock_ns(rt_clock) - bwidth) / 1000000;
+            if (t1 > MAX_WAIT) {
+                DPRINTF("big wait: " PRIu64 " milliseconds, %d iterations\n",
+                        t1, i);
+                break;
+            }
         }
+        i++;
+    }
+
+    if (ret < 0) {
+        return ret;
     }

+    qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
+
+    return 0;
+}
+
+static int ram_save_live(QEMUFile *f, int stage, void *opaque)
+{
+    uint64_t bytes_transferred_last;
+    double bwidth = 0;
+    int ret;
+    int i;
+
+    memory_global_sync_dirty_bitmap(get_system_memory());
+
     bytes_transferred_last = bytes_transferred;
     bwidth = qemu_get_clock_ns(rt_clock);

@@ -535,6 +577,7 @@ done:
 }

 SaveVMHandlers savevm_ram_handlers = {
+    .save_live_setup = ram_save_setup,
     .save_live_state = ram_save_live,
     .load_state = ram_load,
     .cancel = ram_migration_cancel,
diff --git a/block-migration.c b/block-migration.c
index 6d37dc1..fc3d1f4 100644
--- a/block-migration.c
+++ b/block-migration.c
@@ -541,20 +541,40 @@ static void block_migration_cancel(void *opaque)
     blk_mig_cleanup();
 }

-static int block_save_live(QEMUFile *f, int stage, void *opaque)
+static int block_save_setup(QEMUFile *f, void *opaque)
 {
     int ret;

-    DPRINTF("Enter save live stage %d submitted %d transferred %d\n",
-            stage, block_mig_state.submitted, block_mig_state.transferred);
+    DPRINTF("Enter save live setup submitted %d transferred %d\n",
+            block_mig_state.submitted, block_mig_state.transferred);

-    if (stage == 1) {
-        init_blk_migration(f);
+    init_blk_migration(f);
+
+    /* start track dirty blocks */
+    set_dirty_tracking(1);
+
+    flush_blks(f);

-        /* start track dirty blocks */
-        set_dirty_tracking(1);
+    ret = qemu_file_get_error(f);
+    if (ret) {
+        blk_mig_cleanup();
+        return ret;
     }

+    blk_mig_reset_dirty_cursor();
+
+    qemu_put_be64(f, BLK_MIG_FLAG_EOS);
+
+    return 0;
+}
+
+static int block_save_live(QEMUFile *f, int stage, void *opaque)
+{
+    int ret;
+
+    DPRINTF("Enter save live stage %d submitted %d transferred %d\n",
+            stage, block_mig_state.submitted, block_mig_state.transferred);
+
     flush_blks(f);

     ret = qemu_file_get_error(f);
@@ -710,6 +730,7 @@ static bool block_is_active(void *opaque)

 SaveVMHandlers savevm_block_handlers = {
     .set_params = block_set_params,
+    .save_live_setup = block_save_setup,
     .save_live_state = block_save_live,
     .load_state = block_load,
     .cancel = block_migration_cancel,
diff --git a/savevm.c b/savevm.c
index afa0c9e..0b80a94 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1573,7 +1573,7 @@ int qemu_savevm_state_begin(QEMUFile *f,
     QTAILQ_FOREACH(se, &savevm_handlers, entry) {
         int len;

-        if (!se->ops || !se->ops->save_live_state) {
+        if (!se->ops || !se->ops->save_live_setup) {
             continue;
         }
         if (se->ops && se->ops->is_active) {
@@ -1593,7 +1593,7 @@ int qemu_savevm_state_begin(QEMUFile *f,
         qemu_put_be32(f, se->instance_id);
         qemu_put_be32(f, se->version_id);

-        ret = se->ops->save_live_state(f, QEMU_VM_SECTION_START, se->opaque);
+        ret = se->ops->save_live_setup(f, se->opaque);
         if (ret < 0) {
             qemu_savevm_state_cancel(f);
             return ret;
diff --git a/vmstate.h b/vmstate.h
index 96651a5..049f2b7 100644
--- a/vmstate.h
+++ b/vmstate.h
@@ -32,6 +32,7 @@ typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);
 typedef struct SaveVMHandlers {
     void (*set_params)(const MigrationParams *params, void * opaque);
     SaveStateHandler *save_state;
+    int (*save_live_setup)(QEMUFile *f, void *opaque);
     int (*save_live_state)(QEMUFile *f, int stage, void *opaque);
     void (*cancel)(void *opaque);
     LoadStateHandler *load_state;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 08/22] savevm: split save_live into stage2 and stage3
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (6 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 07/22] savevm: split save_live_setup from save_live_state Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 09/22] ram: save_live_setup() don't need to sent pages Juan Quintela
                   ` (13 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel

We split it into 2 functions, foo_live_iterate, and foo_live_complete.
At this point, we only remove the bits that are for the other stage,
functionally this is equivalent to previous code.

Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 arch_init.c       |   72 +++++++++++++++++++++++++++++---------
 block-migration.c |   99 ++++++++++++++++++++++++++++++++---------------------
 savevm.c          |   10 +++---
 vmstate.h         |    3 +-
 4 files changed, 122 insertions(+), 62 deletions(-)

diff --git a/arch_init.c b/arch_init.c
index 2a4903c..7e04ea5 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -374,12 +374,13 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
     return 0;
 }

-static int ram_save_live(QEMUFile *f, int stage, void *opaque)
+static int ram_save_iterate(QEMUFile *f, void *opaque)
 {
     uint64_t bytes_transferred_last;
     double bwidth = 0;
     int ret;
     int i;
+    uint64_t expected_time;

     memory_global_sync_dirty_bitmap(get_system_memory());

@@ -424,28 +425,64 @@ static int ram_save_live(QEMUFile *f, int stage, void *opaque)
         bwidth = 0.000001;
     }

-    /* try transferring iterative blocks of memory */
-    if (stage == 3) {
-        int bytes_sent;
+    qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
+
+    expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
+
+    DPRINTF("ram_save_live: expected(" PRIu64 ") <= max(" PRIu64 ")?\n",
+            expected_time, migrate_max_downtime());
+
+    return expected_time <= migrate_max_downtime();
+}
+
+static int ram_save_complete(QEMUFile *f, void *opaque)
+{
+    double bwidth = 0;
+    int ret;
+    int i;
+    int bytes_sent;

-        /* flush all remaining blocks regardless of rate limiting */
-        while ((bytes_sent = ram_save_block(f)) != 0) {
-            bytes_transferred += bytes_sent;
+    memory_global_sync_dirty_bitmap(get_system_memory());
+
+    bwidth = qemu_get_clock_ns(rt_clock);
+
+    i = 0;
+    while ((ret = qemu_file_rate_limit(f)) == 0) {
+        bytes_sent = ram_save_block(f);
+        bytes_transferred += bytes_sent;
+        if (bytes_sent == 0) { /* no more blocks */
+            break;
         }
-        memory_global_dirty_log_stop();
+        /* we want to check in the 1st loop, just in case it was the 1st time
+           and we had to sync the dirty bitmap.
+           qemu_get_clock_ns() is a bit expensive, so we only check each some
+           iterations
+        */
+        if ((i & 63) == 0) {
+            uint64_t t1 = (qemu_get_clock_ns(rt_clock) - bwidth) / 1000000;
+            if (t1 > MAX_WAIT) {
+                DPRINTF("big wait: " PRIu64 " milliseconds, %d iterations\n",
+                        t1, i);
+                break;
+            }
+        }
+        i++;
     }

-    qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
-
-    if (stage == 2) {
-        uint64_t expected_time;
-        expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
+    if (ret < 0) {
+        return ret;
+    }

-        DPRINTF("ram_save_live: expected(" PRIu64 ") <= max(" PRIu64 ")?\n",
-                expected_time, migrate_max_downtime());
+    /* try transferring iterative blocks of memory */

-        return expected_time <= migrate_max_downtime();
+    /* flush all remaining blocks regardless of rate limiting */
+    while ((bytes_sent = ram_save_block(f)) != 0) {
+        bytes_transferred += bytes_sent;
     }
+    memory_global_dirty_log_stop();
+
+    qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
+
     return 0;
 }

@@ -578,7 +615,8 @@ done:

 SaveVMHandlers savevm_ram_handlers = {
     .save_live_setup = ram_save_setup,
-    .save_live_state = ram_save_live,
+    .save_live_iterate = ram_save_iterate,
+    .save_live_complete = ram_save_complete,
     .load_state = ram_load,
     .cancel = ram_migration_cancel,
 };
diff --git a/block-migration.c b/block-migration.c
index fc3d1f4..7def8ab 100644
--- a/block-migration.c
+++ b/block-migration.c
@@ -568,12 +568,12 @@ static int block_save_setup(QEMUFile *f, void *opaque)
     return 0;
 }

-static int block_save_live(QEMUFile *f, int stage, void *opaque)
+static int block_save_iterate(QEMUFile *f, void *opaque)
 {
     int ret;

-    DPRINTF("Enter save live stage %d submitted %d transferred %d\n",
-            stage, block_mig_state.submitted, block_mig_state.transferred);
+    DPRINTF("Enter save live iterate submitted %d transferred %d\n",
+            block_mig_state.submitted, block_mig_state.transferred);

     flush_blks(f);

@@ -585,56 +585,76 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)

     blk_mig_reset_dirty_cursor();

-    if (stage == 2) {
-        /* control the rate of transfer */
-        while ((block_mig_state.submitted +
-                block_mig_state.read_done) * BLOCK_SIZE <
-               qemu_file_get_rate_limit(f)) {
-            if (block_mig_state.bulk_completed == 0) {
-                /* first finish the bulk phase */
-                if (blk_mig_save_bulked_block(f) == 0) {
-                    /* finished saving bulk on all devices */
-                    block_mig_state.bulk_completed = 1;
-                }
-            } else {
-                if (blk_mig_save_dirty_block(f, 1) == 0) {
-                    /* no more dirty blocks */
-                    break;
-                }
+    /* control the rate of transfer */
+    while ((block_mig_state.submitted +
+            block_mig_state.read_done) * BLOCK_SIZE <
+           qemu_file_get_rate_limit(f)) {
+        if (block_mig_state.bulk_completed == 0) {
+            /* first finish the bulk phase */
+            if (blk_mig_save_bulked_block(f) == 0) {
+                /* finished saving bulk on all devices */
+                block_mig_state.bulk_completed = 1;
+            }
+        } else {
+            if (blk_mig_save_dirty_block(f, 1) == 0) {
+                /* no more dirty blocks */
+                break;
             }
         }
+    }

-        flush_blks(f);
+    flush_blks(f);

-        ret = qemu_file_get_error(f);
-        if (ret) {
-            blk_mig_cleanup();
-            return ret;
-        }
+    ret = qemu_file_get_error(f);
+    if (ret) {
+        blk_mig_cleanup();
+        return ret;
     }

-    if (stage == 3) {
-        /* we know for sure that save bulk is completed and
-           all async read completed */
-        assert(block_mig_state.submitted == 0);
+    qemu_put_be64(f, BLK_MIG_FLAG_EOS);
+
+    return is_stage2_completed();
+}
+
+static int block_save_complete(QEMUFile *f, void *opaque)
+{
+    int ret;
+
+    DPRINTF("Enter save live complete submitted %d transferred %d\n",
+            block_mig_state.submitted, block_mig_state.transferred);
+
+    flush_blks(f);

-        while (blk_mig_save_dirty_block(f, 0) != 0);
+    ret = qemu_file_get_error(f);
+    if (ret) {
         blk_mig_cleanup();
+        return ret;
+    }

-        /* report completion */
-        qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS);
+    blk_mig_reset_dirty_cursor();

-        ret = qemu_file_get_error(f);
-        if (ret) {
-            return ret;
-        }
+    /* we know for sure that save bulk is completed and
+       all async read completed */
+    assert(block_mig_state.submitted == 0);
+
+    while (blk_mig_save_dirty_block(f, 0) != 0) {
+        /* Do nothing */
+    }
+    blk_mig_cleanup();
+
+    /* report completion */
+    qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS);

-        DPRINTF("Block migration completed\n");
+    ret = qemu_file_get_error(f);
+    if (ret) {
+        return ret;
     }

+    DPRINTF("Block migration completed\n");
+
     qemu_put_be64(f, BLK_MIG_FLAG_EOS);

-    return ((stage == 2) && is_stage2_completed());
+    return 0;
 }

 static int block_load(QEMUFile *f, void *opaque, int version_id)
@@ -731,7 +751,8 @@ static bool block_is_active(void *opaque)
 SaveVMHandlers savevm_block_handlers = {
     .set_params = block_set_params,
     .save_live_setup = block_save_setup,
-    .save_live_state = block_save_live,
+    .save_live_iterate = block_save_iterate,
+    .save_live_complete = block_save_complete,
     .load_state = block_load,
     .cancel = block_migration_cancel,
     .is_active = block_is_active,
diff --git a/savevm.c b/savevm.c
index 0b80a94..6e82b2d 100644
--- a/savevm.c
+++ b/savevm.c
@@ -1236,7 +1236,7 @@ int register_savevm_live(DeviceState *dev,
     se->vmsd = NULL;
     se->no_migrate = 0;
     /* if this is a live_savem then set is_ram */
-    if (ops->save_live_state != NULL) {
+    if (ops->save_live_setup != NULL) {
         se->is_ram = 1;
     }

@@ -1620,7 +1620,7 @@ int qemu_savevm_state_iterate(QEMUFile *f)
     int ret = 1;

     QTAILQ_FOREACH(se, &savevm_handlers, entry) {
-        if (!se->ops || !se->ops->save_live_state) {
+        if (!se->ops || !se->ops->save_live_iterate) {
             continue;
         }
         if (se->ops && se->ops->is_active) {
@@ -1636,7 +1636,7 @@ int qemu_savevm_state_iterate(QEMUFile *f)
         qemu_put_byte(f, QEMU_VM_SECTION_PART);
         qemu_put_be32(f, se->section_id);

-        ret = se->ops->save_live_state(f, QEMU_VM_SECTION_PART, se->opaque);
+        ret = se->ops->save_live_iterate(f, se->opaque);
         trace_savevm_section_end(se->section_id);

         if (ret <= 0) {
@@ -1665,7 +1665,7 @@ int qemu_savevm_state_complete(QEMUFile *f)
     cpu_synchronize_all_states();

     QTAILQ_FOREACH(se, &savevm_handlers, entry) {
-        if (!se->ops || !se->ops->save_live_state) {
+        if (!se->ops || !se->ops->save_live_complete) {
             continue;
         }
         if (se->ops && se->ops->is_active) {
@@ -1678,7 +1678,7 @@ int qemu_savevm_state_complete(QEMUFile *f)
         qemu_put_byte(f, QEMU_VM_SECTION_END);
         qemu_put_be32(f, se->section_id);

-        ret = se->ops->save_live_state(f, QEMU_VM_SECTION_END, se->opaque);
+        ret = se->ops->save_live_complete(f, se->opaque);
         trace_savevm_section_end(se->section_id);
         if (ret < 0) {
             return ret;
diff --git a/vmstate.h b/vmstate.h
index 049f2b7..5bd2b76 100644
--- a/vmstate.h
+++ b/vmstate.h
@@ -33,7 +33,8 @@ typedef struct SaveVMHandlers {
     void (*set_params)(const MigrationParams *params, void * opaque);
     SaveStateHandler *save_state;
     int (*save_live_setup)(QEMUFile *f, void *opaque);
-    int (*save_live_state)(QEMUFile *f, int stage, void *opaque);
+    int (*save_live_iterate)(QEMUFile *f, void *opaque);
+    int (*save_live_complete)(QEMUFile *f, void *opaque);
     void (*cancel)(void *opaque);
     LoadStateHandler *load_state;
     bool (*is_active)(void *opaque);
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 09/22] ram: save_live_setup() don't need to sent pages
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (7 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 08/22] savevm: split save_live into stage2 and stage3 Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 10/22] ram: save_live_complete() only do one loop Juan Quintela
                   ` (12 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel

We should send pages on interate phase, not in setup one.  This was a
"bug".  Just removing the loop does what we want.  Tested that it
works with current ram_load().

Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 arch_init.c |   34 ----------------------------------
 1 file changed, 34 deletions(-)

diff --git a/arch_init.c b/arch_init.c
index 7e04ea5..2a6fc77 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -307,9 +307,6 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
 {
     ram_addr_t addr;
     RAMBlock *block;
-    double bwidth = 0;
-    int ret;
-    int i;

     memory_global_sync_dirty_bitmap(get_system_memory());

@@ -338,37 +335,6 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
         qemu_put_be64(f, block->length);
     }

-    bwidth = qemu_get_clock_ns(rt_clock);
-
-    i = 0;
-    while ((ret = qemu_file_rate_limit(f)) == 0) {
-        int bytes_sent;
-
-        bytes_sent = ram_save_block(f);
-        bytes_transferred += bytes_sent;
-        if (bytes_sent == 0) { /* no more blocks */
-            break;
-        }
-        /* we want to check in the 1st loop, just in case it was the 1st time
-           and we had to sync the dirty bitmap.
-           qemu_get_clock_ns() is a bit expensive, so we only check each some
-           iterations
-        */
-        if ((i & 63) == 0) {
-            uint64_t t1 = (qemu_get_clock_ns(rt_clock) - bwidth) / 1000000;
-            if (t1 > MAX_WAIT) {
-                DPRINTF("big wait: " PRIu64 " milliseconds, %d iterations\n",
-                        t1, i);
-                break;
-            }
-        }
-        i++;
-    }
-
-    if (ret < 0) {
-        return ret;
-    }
-
     qemu_put_be64(f, RAM_SAVE_FLAG_EOS);

     return 0;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 10/22] ram: save_live_complete() only do one loop
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (8 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 09/22] ram: save_live_setup() don't need to sent pages Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 11/22] ram: iterate phase Juan Quintela
                   ` (11 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel

We were doing the same loop that stage2, and a new one for stage3.  We
only need the one for stage3.

Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 arch_init.c |   32 --------------------------------
 1 file changed, 32 deletions(-)

diff --git a/arch_init.c b/arch_init.c
index 2a6fc77..3c38a52 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -403,42 +403,10 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)

 static int ram_save_complete(QEMUFile *f, void *opaque)
 {
-    double bwidth = 0;
-    int ret;
-    int i;
     int bytes_sent;

     memory_global_sync_dirty_bitmap(get_system_memory());

-    bwidth = qemu_get_clock_ns(rt_clock);
-
-    i = 0;
-    while ((ret = qemu_file_rate_limit(f)) == 0) {
-        bytes_sent = ram_save_block(f);
-        bytes_transferred += bytes_sent;
-        if (bytes_sent == 0) { /* no more blocks */
-            break;
-        }
-        /* we want to check in the 1st loop, just in case it was the 1st time
-           and we had to sync the dirty bitmap.
-           qemu_get_clock_ns() is a bit expensive, so we only check each some
-           iterations
-        */
-        if ((i & 63) == 0) {
-            uint64_t t1 = (qemu_get_clock_ns(rt_clock) - bwidth) / 1000000;
-            if (t1 > MAX_WAIT) {
-                DPRINTF("big wait: " PRIu64 " milliseconds, %d iterations\n",
-                        t1, i);
-                break;
-            }
-        }
-        i++;
-    }
-
-    if (ret < 0) {
-        return ret;
-    }
-
     /* try transferring iterative blocks of memory */

     /* flush all remaining blocks regardless of rate limiting */
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 11/22] ram: iterate phase
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (9 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 10/22] ram: save_live_complete() only do one loop Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 12/22] ram: save_live_setup() we don't need to synchronize the dirty bitmap Juan Quintela
                   ` (10 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel

We only need to synchronize the bitmap when the number of dirty pages is low.
Not every time that we call the function.

Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 arch_init.c |   10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/arch_init.c b/arch_init.c
index 3c38a52..b08c615 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -348,8 +348,6 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
     int i;
     uint64_t expected_time;

-    memory_global_sync_dirty_bitmap(get_system_memory());
-
     bytes_transferred_last = bytes_transferred;
     bwidth = qemu_get_clock_ns(rt_clock);

@@ -398,7 +396,13 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
     DPRINTF("ram_save_live: expected(" PRIu64 ") <= max(" PRIu64 ")?\n",
             expected_time, migrate_max_downtime());

-    return expected_time <= migrate_max_downtime();
+    if (expected_time <= migrate_max_downtime()) {
+        memory_global_sync_dirty_bitmap(get_system_memory());
+        expected_time = ram_save_remaining() * TARGET_PAGE_SIZE / bwidth;
+
+        return expected_time <= migrate_max_downtime();
+    }
+    return 0;
 }

 static int ram_save_complete(QEMUFile *f, void *opaque)
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 12/22] ram: save_live_setup() we don't need to synchronize the dirty bitmap.
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (10 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 11/22] ram: iterate phase Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 13/22] Add migration capabilities Juan Quintela
                   ` (9 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel

1st: we were synchonizing the dirty bitmap before calling
      memory_global_dirty_log_start().

2nd: We are marking all pages as dirty anywhere, no reason to go
     through all the bitmap to "mark" dirty same pages twice.

So, call removed.

Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 arch_init.c |    2 --
 1 file changed, 2 deletions(-)

diff --git a/arch_init.c b/arch_init.c
index b08c615..8cbf8f4 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -308,8 +308,6 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
     ram_addr_t addr;
     RAMBlock *block;

-    memory_global_sync_dirty_bitmap(get_system_memory());
-
     bytes_transferred = 0;
     last_block = NULL;
     last_offset = 0;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 13/22] Add migration capabilities
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (11 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 12/22] ram: save_live_setup() we don't need to synchronize the dirty bitmap Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-23 18:23   ` Luiz Capitulino
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 14/22] Add XBZRLE documentation Juan Quintela
                   ` (8 subsequent siblings)
  21 siblings, 1 reply; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: Orit Wasserman

From: Orit Wasserman <owasserm@redhat.com>

Add migration capabilities that can be queried by the management.
The management can query the source QEMU and the destination QEMU in order to
verify both support some migration capability (currently only XBZRLE).
The management can enable a capability for the next migration by using
migrate_set_parameter command.

Signed-off-by: Orit Wasserman <owasserm@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 hmp-commands.hx  |   16 ++++++++++++
 hmp.c            |   64 ++++++++++++++++++++++++++++++++++++++++++++++++
 hmp.h            |    2 ++
 migration.c      |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 migration.h      |    2 ++
 monitor.c        |    7 ++++++
 qapi-schema.json |   53 ++++++++++++++++++++++++++++++++++++++--
 qmp-commands.hx  |   71 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 8 files changed, 280 insertions(+), 7 deletions(-)

diff --git a/hmp-commands.hx b/hmp-commands.hx
index f5d9d91..9245bef 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -861,6 +861,20 @@ Set maximum tolerated downtime (in seconds) for migration.
 ETEXI

     {
+        .name       = "migrate_set_parameter",
+        .args_type  = "capability:s,state:b",
+        .params     = "",
+        .help       = "Enable/Disable the usage of a capability for migration",
+        .mhandler.cmd = hmp_migrate_set_parameter,
+    },
+
+STEXI
+@item migrate_set_parameter @var{capability} @var{state}
+@findex migrate_set_parameter
+Enable/Disable the usage of a capability @var{capability} for migration.
+ETEXI
+
+    {
         .name       = "client_migrate_info",
         .args_type  = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
         .params     = "protocol hostname port tls-port cert-subject",
@@ -1419,6 +1433,8 @@ show CPU statistics
 show user network stack connection states
 @item info migrate
 show migration status
+@item info migration_capabilities
+show migration capabilities
 @item info balloon
 show balloon information
 @item info qtree
diff --git a/hmp.c b/hmp.c
index 4c6d4ae..b0440e6 100644
--- a/hmp.c
+++ b/hmp.c
@@ -131,9 +131,19 @@ void hmp_info_mice(Monitor *mon)
 void hmp_info_migrate(Monitor *mon)
 {
     MigrationInfo *info;
+    MigrationCapabilityInfoList *cap;

     info = qmp_query_migrate(NULL);

+    if (info->has_capabilities && info->capabilities) {
+        monitor_printf(mon, "capabilities: ");
+        for (cap = info->capabilities; cap; cap = cap->next) {
+            monitor_printf(mon, "%s: %s ",
+                           MigrationCapability_lookup[cap->value->capability],
+                           cap->value->state ? "on" : "off");
+        }
+        monitor_printf(mon, "\n");
+    }
     if (info->has_status) {
         monitor_printf(mon, "Migration status: %s\n", info->status);
     }
@@ -161,6 +171,25 @@ void hmp_info_migrate(Monitor *mon)
     qapi_free_MigrationInfo(info);
 }

+void hmp_info_migration_capabilities(Monitor *mon)
+{
+    MigrationCapabilityInfoList *caps_list, *cap;
+
+    caps_list = qmp_query_migration_capabilities(NULL);
+    if (!caps_list) {
+        monitor_printf(mon, "No migration capabilities found\n");
+        return;
+    }
+
+    for (cap = caps_list; cap; cap = cap->next) {
+        monitor_printf(mon, "%s: %s ",
+                       MigrationCapability_lookup[cap->value->capability],
+                       cap->value->state ? "on" : "off");
+    }
+
+    qapi_free_MigrationCapabilityInfoList(caps_list);
+}
+
 void hmp_info_cpus(Monitor *mon)
 {
     CpuInfoList *cpu_list, *cpu;
@@ -735,6 +764,41 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
     qmp_migrate_set_speed(value, NULL);
 }

+void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
+{
+    const char *cap = qdict_get_str(qdict, "capability");
+    bool state = qdict_get_bool(qdict, "state");
+    Error *err = NULL;
+    MigrationCapabilityInfoList *params = NULL;
+    int i;
+
+    for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
+        if (strcmp(cap, MigrationCapability_lookup[i]) == 0) {
+            if (!params) {
+                params = g_malloc0(sizeof(*params));
+            }
+            params->value = g_malloc0(sizeof(*params->value));
+            params->value->capability = i;
+            params->value->state = state;
+            params->next = NULL;
+            qmp_migrate_set_parameters(params, &err);
+            break;
+        }
+    }
+
+    if (i == MIGRATION_CAPABILITY_MAX) {
+        error_set(&err, QERR_INVALID_PARAMETER, cap);
+    }
+
+    qapi_free_MigrationCapabilityInfoList(params);
+
+    if (err) {
+        monitor_printf(mon, "migrate_set_parameter: %s\n",
+                       error_get_pretty(err));
+        error_free(err);
+    }
+}
+
 void hmp_set_password(Monitor *mon, const QDict *qdict)
 {
     const char *protocol  = qdict_get_str(qdict, "protocol");
diff --git a/hmp.h b/hmp.h
index 79d138d..09ba198 100644
--- a/hmp.h
+++ b/hmp.h
@@ -25,6 +25,7 @@ void hmp_info_uuid(Monitor *mon);
 void hmp_info_chardev(Monitor *mon);
 void hmp_info_mice(Monitor *mon);
 void hmp_info_migrate(Monitor *mon);
+void hmp_info_migration_capabilities(Monitor *mon);
 void hmp_info_cpus(Monitor *mon);
 void hmp_info_block(Monitor *mon);
 void hmp_info_blockstats(Monitor *mon);
@@ -51,6 +52,7 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
 void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
 void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
 void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
+void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict);
 void hmp_set_password(Monitor *mon, const QDict *qdict);
 void hmp_expire_password(Monitor *mon, const QDict *qdict);
 void hmp_eject(Monitor *mon, const QDict *qdict);
diff --git a/migration.c b/migration.c
index 8db1b43..fd004d7 100644
--- a/migration.c
+++ b/migration.c
@@ -117,12 +117,36 @@ MigrationInfo *qmp_query_migrate(Error **errp)
 {
     MigrationInfo *info = g_malloc0(sizeof(*info));
     MigrationState *s = migrate_get_current();
+    int i;

     switch (s->state) {
     case MIG_STATE_SETUP:
-        /* no migration has happened ever */
+        /* no migration has ever happened; show enabled capabilities */
+        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
+            if (!info->has_capabilities) {
+                info->capabilities = g_malloc0(sizeof(*info->capabilities));
+                info->has_capabilities = true;
+            }
+            info->capabilities->value =
+                g_malloc(sizeof(*info->capabilities->value));
+            info->capabilities->value->capability = i;
+            info->capabilities->value->state = s->enabled_capabilities[i];
+            info->capabilities->next = NULL;
+        }
         break;
     case MIG_STATE_ACTIVE:
+        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
+            if (!info->has_capabilities) {
+                info->capabilities = g_malloc0(sizeof(*info->capabilities));
+                info->has_capabilities = true;
+            }
+            info->capabilities->value =
+                g_malloc(sizeof(*info->capabilities->value));
+            info->capabilities->value->capability = i;
+            info->capabilities->value->state = s->enabled_capabilities[i];
+            info->capabilities->next = NULL;
+        }
+
         info->has_status = true;
         info->status = g_strdup("active");

@@ -143,6 +167,18 @@ MigrationInfo *qmp_query_migrate(Error **errp)
         }
         break;
     case MIG_STATE_COMPLETED:
+        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
+            if (!info->has_capabilities) {
+                info->capabilities = g_malloc0(sizeof(*info->capabilities));
+                info->has_capabilities = true;
+            }
+            info->capabilities->value =
+                g_malloc(sizeof(*info->capabilities->value));
+            info->capabilities->value->capability = i;
+            info->capabilities->value->state = s->enabled_capabilities[i];
+            info->capabilities->next = NULL;
+        }
+
         info->has_status = true;
         info->status = g_strdup("completed");

@@ -166,6 +202,33 @@ MigrationInfo *qmp_query_migrate(Error **errp)
     return info;
 }

+MigrationCapabilityInfoList *qmp_query_migration_capabilities(Error **errp)
+{
+    MigrationCapabilityInfoList *caps_list = g_malloc0(sizeof(*caps_list));
+
+    caps_list->value = g_malloc(sizeof(*caps_list->value));
+    caps_list->value->capability = MIGRATION_CAPABILITY_XBZRLE;
+    caps_list->next = NULL;
+
+    return caps_list;
+}
+
+void qmp_migrate_set_parameters(MigrationCapabilityInfoList *params,
+                                Error **errp)
+{
+    MigrationState *s = migrate_get_current();
+    MigrationCapabilityInfoList *cap;
+
+    if (s->state == MIG_STATE_ACTIVE) {
+        error_set(errp, QERR_MIGRATION_ACTIVE);
+        return;
+    }
+
+    for (cap = params; cap; cap = cap->next) {
+        s->enabled_capabilities[cap->value->capability] = cap->value->state;
+    }
+}
+
 /* shared migration helpers */

 static int migrate_fd_cleanup(MigrationState *s)
@@ -375,12 +438,17 @@ static MigrationState *migrate_init(const MigrationParams *params)
 {
     MigrationState *s = migrate_get_current();
     int64_t bandwidth_limit = s->bandwidth_limit;
+    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
+
+    memcpy(enabled_capabilities, s->enabled_capabilities,
+           sizeof(enabled_capabilities));

     memset(s, 0, sizeof(*s));
     s->bandwidth_limit = bandwidth_limit;
     s->params = *params;
+    memcpy(s->enabled_capabilities, enabled_capabilities,
+           sizeof(enabled_capabilities));

-    s->bandwidth_limit = bandwidth_limit;
     s->state = MIG_STATE_SETUP;
     s->total_time = qemu_get_clock_ms(rt_clock);

diff --git a/migration.h b/migration.h
index 57572a6..713aae0 100644
--- a/migration.h
+++ b/migration.h
@@ -19,6 +19,7 @@
 #include "notify.h"
 #include "error.h"
 #include "vmstate.h"
+#include "qapi-types.h"

 struct MigrationParams {
     bool blk;
@@ -39,6 +40,7 @@ struct MigrationState
     void *opaque;
     MigrationParams params;
     int64_t total_time;
+    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
 };

 void process_incoming_migration(QEMUFile *f);
diff --git a/monitor.c b/monitor.c
index f6107ba..e2be6cd 100644
--- a/monitor.c
+++ b/monitor.c
@@ -2687,6 +2687,13 @@ static mon_cmd_t info_cmds[] = {
         .mhandler.info = hmp_info_migrate,
     },
     {
+        .name       = "migration_capabilities",
+        .args_type  = "",
+        .params     = "",
+        .help       = "show migration capabilities",
+        .mhandler.info = hmp_info_migration_capabilities,
+    },
+    {
         .name       = "balloon",
         .args_type  = "",
         .params     = "",
diff --git a/qapi-schema.json b/qapi-schema.json
index 1ab5dbd..a8408fd 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -288,11 +288,15 @@
 #        status, only returned if status is 'active' and it is a block
 #        migration
 #
-# Since: 0.14.0
+# @capabilities: #optional a list describing all the migration capabilities
+#                state
+#
+# Since: 0.14.0, 'capabilities' since 1.2
 ##
 { 'type': 'MigrationInfo',
   'data': {'*status': 'str', '*ram': 'MigrationStats',
-           '*disk': 'MigrationStats'} }
+           '*disk': 'MigrationStats',
+           '*capabilities': ['MigrationCapabilityInfo']} }

 ##
 # @query-migrate
@@ -306,6 +310,51 @@
 { 'command': 'query-migrate', 'returns': 'MigrationInfo' }

 ##
+# @MigrationCapability
+#
+# Migration capabilities enumeration
+#
+# @xbzrle: current migration supports xbzrle
+#
+# Since: 1.2
+##
+{ 'enum': 'MigrationCapability',
+  'data': ['xbzrle'] }
+
+##
+# @MigrationCapabilityInfo
+#
+# Migration capability information
+#
+# @capability: capability enum
+#
+# Since: 1.2
+##
+{ 'type': 'MigrationCapabilityInfo',
+  'data': { 'capability' : 'MigrationCapability', 'state' : 'bool' } }
+
+##
+# @query-migration-capabilities
+#
+# Returns information about current migration process capabilties.
+#
+# Returns: @MigrationCapabilityInfo list
+#
+# Since: 1.2
+##
+{ 'command': 'query-migration-capabilities', 'returns': ['MigrationCapabilityInfo'] }
+
+##
+# @migrate_set_parameters
+#
+# Enable/Disable the following migration capabilities (like xbzrle)
+#
+# Since: 1.2
+##
+{ 'command': 'migrate-set-parameters',
+  'data': { 'capabilities': ['MigrationCapabilityInfo'] } }
+
+##
 # @MouseInfo:
 #
 # Information about a mouse device.
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 2e1a38e..3ee6e00 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2075,18 +2075,31 @@ The main json-object contains the following:
          - "transferred": amount transferred (json-int)
          - "remaining": amount remaining (json-int)
          - "total": total (json-int)
-
+- "capabilities": migration capabilities state
+         - "xbzrle" : XBZRLE state (json-bool)
 Examples:

 1. Before the first migration

 -> { "execute": "query-migrate" }
-<- { "return": {} }
+<- { "return": {
+        "capabilities" :  [ { "capability" : "xbzrle", "state" : false } ]
+     }
+   }

 2. Migration is done and has succeeded

 -> { "execute": "query-migrate" }
-<- { "return": { "status": "completed" } }
+<- { "return": {
+        "status": "completed",
+        "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
+        "ram":{
+          "transferred":123,
+          "remaining":123,
+          "total":246
+        }
+     }
+   }

 3. Migration is done and has failed

@@ -2099,6 +2112,7 @@ Examples:
 <- {
       "return":{
          "status":"active",
+         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
          "ram":{
             "transferred":123,
             "remaining":123,
@@ -2113,6 +2127,7 @@ Examples:
 <- {
       "return":{
          "status":"active",
+         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
          "ram":{
             "total":1057024,
             "remaining":1053304,
@@ -2135,6 +2150,56 @@ EQMP
     },

 SQMP
+query-migration-capabilities
+-------
+
+Query migration capabilities
+
+- "xbzrle": xbzrle support
+
+Arguments:
+
+Example:
+
+-> { "execute": "query-migration-capabilities"}
+<- { "return": [ { "capability": "xbzrle", "state": true },
+                 { "capability": "foobar", "state": false } ] }
+
+EQMP
+
+    {
+        .name       = "query-migration-capabilities",
+        .args_type  = "",
+	.mhandler.cmd_new = qmp_marshal_input_query_migration_capabilities,
+    },
+
+SQMP
+migrate_set_parameters
+-------
+
+Enable/Disable migration capabilities
+
+- "xbzrle": xbzrle support
+
+Arguments:
+
+Example:
+
+-> { "execute": "migrate_set_parameters" , "arguments":
+     { "parameters": [ { "capability": "xbzrle", "state": true } ] } }
+
+EQMP
+
+    {
+        .name       = "migrate_set_parameters",
+        .args_type  = "parameters:O",
+	.params     = "capability:s,state:b",
+	.mhandler.cmd_new = qmp_marshal_input_migrate_set_parameters,
+    },
+
+
+
+SQMP
 query-balloon
 -------------

-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 14/22] Add XBZRLE documentation
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (12 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 13/22] Add migration capabilities Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 15/22] Add cache handling functions Juan Quintela
                   ` (7 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: Orit Wasserman

From: Orit Wasserman <owasserm@redhat.com>

Signed-off-by: Orit Wasserman <owasserm@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 docs/xbzrle.txt |  136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 136 insertions(+)
 create mode 100644 docs/xbzrle.txt

diff --git a/docs/xbzrle.txt b/docs/xbzrle.txt
new file mode 100644
index 0000000..f70e851
--- /dev/null
+++ b/docs/xbzrle.txt
@@ -0,0 +1,136 @@
+XBZRLE (Xor Based Zero Run Length Encoding)
+===========================================
+
+Using XBZRLE (Xor Based Zero Run Length Encoding) allows for the reduction
+of VM downtime and the total live-migration time of Virtual machines.
+It is particularly useful for virtual machines running memory write intensive
+workloads that are typical of large enterprise applications such as SAP ERP
+Systems, and generally speaking for any application that uses a sparse memory
+update pattern.
+
+Instead of sending the changed guest memory page this solution will send a
+compressed version of the updates, thus reducing the amount of data sent during
+live migration.
+In order to be able to calculate the update, the previous memory pages need to
+be stored on the source. Those pages are stored in a dedicated cache
+(hash table) and are
+accessed by their address.
+The larger the cache size the better the chances are that the page has already
+been stored in the cache.
+A small cache size will result in high cache miss rate.
+Cache size can be changed before and during migration.
+
+Format
+=======
+
+The compression format performs a XOR between the previous and current content
+of the page, where zero represents an unchanged value.
+The page data delta is represented by zero and non zero runs.
+A zero run is represented by its length (in bytes).
+A non zero run is represented by its length (in bytes) and the new data.
+The run length is encoded using ULEB128 (http://en.wikipedia.org/wiki/LEB128)
+
+There can be more than one valid encoding, the sender may send a longer encoding
+for the benefit of reducing computation cost.
+
+page = zrun nzrun
+       | zrun nzrun page
+
+zrun = length
+
+nzrun = length byte...
+
+length = uleb128 encoded integer
+
+On the sender side XBZRLE is used as a compact delta encoding of page updates,
+retrieving the old page content from the cache (default size of 512 MB). The
+receiving side uses the existing page's content and XBZRLE to decode the new
+page's content.
+
+This work was originally based on research results published
+VEE 2011: Evaluation of Delta Compression Techniques for Efficient Live
+Migration of Large Virtual Machines by Benoit, Svard, Tordsson and Elmroth.
+Additionally the delta encoder XBRLE was improved further using the XBZRLE
+instead.
+
+XBZRLE has a sustained bandwidth of 2-2.5 GB/s for typical workloads making it
+ideal for in-line, real-time encoding such as is needed for live-migration.
+
+Example
+old buffer:
+1001 zeros
+05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 68 00 00 6b 00 6d
+3074 zeros
+
+new buffer:
+1001 zeros
+01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 68 00 00 67 00 69
+3074 zeros
+
+encoded buffer:
+
+encoded length 24
+e9 07 0f 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 03 01 67 01 01 69
+
+Migration Capabilities
+======================
+In order to use XBZRLE the destination QEMU version should be able to
+decode the new format.
+Adding a new migration capabilities command that will allow external management
+to query for it support.
+A typical use for the destination
+    {qemu} info migrate_capabilities
+    {qemu} xbzrle, ...
+
+In order to enable capabilities for future live migration,
+a new command migrate_set_parameter is introduced:
+    {qemu} migrate_set_parameter xbzrle
+
+Usage
+======
+
+1. Activate xbzrle
+2. Set the XBZRLE cache size - the cache size is in MBytes and should be a
+power of 2. The cache default value is 64MBytes.
+3. start outgoing migration
+
+A typical usage scenario:
+On the incoming QEMU:
+    {qemu} migrate_set_parameter xbzrle on
+On the outgoing QEMU:
+    {qemu} migrate_set_parameter xbzrle on
+    {qemu} migrate_set_cachesize 256m
+    {qemu} migrate -d tcp:destination.host:4444
+    {qemu} info migrate
+    ...
+    cache size: 67108864 bytes
+    transferred ram-duplicate: A kbytes
+    transferred ram-normal: B kbytes
+    transferred ram-xbrle: C kbytes
+    overflow ram-xbrle: D pages
+    cache-miss ram-xbrle: E pages
+
+cache-miss: the number of cache misses to date - high cache-miss rate
+indicates that the cache size is set too low.
+overflow: the number of overflows in the decoding which where the delta could
+not be compressed. This can happen if the changes in the pages are too large
+or there are many short changes; for example, changing every second byte (half a
+page).
+
+Testing: Testing indicated that live migration with XBZRLE was completed in 110
+seconds, whereas without it would not be able to complete.
+
+A simple synthetic memory r/w load generator:
+..    include <stdlib.h>
+..    include <stdio.h>
+..    int main()
+..    {
+..        char *buf = (char *) calloc(4096, 4096);
+..        while (1) {
+..            int i;
+..            for (i = 0; i < 4096 * 4; i++) {
+..                buf[i * 4096 / 4]++;
+..            }
+..            printf(".");
+..        }
+..    }
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 15/22] Add cache handling functions
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (13 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 14/22] Add XBZRLE documentation Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 16/22] Add uleb encoding/decoding functions Juan Quintela
                   ` (6 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: Orit Wasserman, Benoit Hudzia, Aidan Shribman, Petter Svard

From: Orit Wasserman <owasserm@redhat.com>

Add LRU page cache mechanism.
The page are accessed by their address.

Signed-off-by: Benoit Hudzia <benoit.hudzia@sap.com>
Signed-off-by: Petter Svard <petters@cs.umu.se>
Signed-off-by: Aidan Shribman <aidan.shribman@sap.com>
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 Makefile.objs             |    1 +
 cutils.c                  |    9 ++
 include/qemu/page_cache.h |   79 +++++++++++++++++
 page_cache.c              |  216 +++++++++++++++++++++++++++++++++++++++++++++
 qemu-common.h             |   13 +++
 5 files changed, 318 insertions(+)
 create mode 100644 include/qemu/page_cache.h
 create mode 100644 page_cache.c

diff --git a/Makefile.objs b/Makefile.objs
index 5ebbcfa..e0fb69b 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -77,6 +77,7 @@ common-obj-y += qemu-char.o #aio.o
 common-obj-y += block-migration.o iohandler.o
 common-obj-y += pflib.o
 common-obj-y += bitmap.o bitops.o
+common-obj-y += page_cache.o

 common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
 common-obj-$(CONFIG_WIN32) += version.o
diff --git a/cutils.c b/cutils.c
index e2bc1b8..b0bdd4b 100644
--- a/cutils.c
+++ b/cutils.c
@@ -375,3 +375,12 @@ int qemu_parse_fd(const char *param)
     }
     return fd;
 }
+
+/* round down to the nearest power of 2*/
+int64_t pow2floor(int64_t value)
+{
+    if (!is_power_of_2(value)) {
+        value = 0x8000000000000000ULL >> clz64(value);
+    }
+    return value;
+}
diff --git a/include/qemu/page_cache.h b/include/qemu/page_cache.h
new file mode 100644
index 0000000..3839ac7
--- /dev/null
+++ b/include/qemu/page_cache.h
@@ -0,0 +1,79 @@
+/*
+ * Page cache for QEMU
+ * The cache is base on a hash of the page address
+ *
+ * Copyright 2012 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ *  Orit Wasserman  <owasserm@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef PAGE_CACHE_H
+#define PAGE_CACHE_H
+
+/* Page cache for storing guest pages */
+typedef struct PageCache PageCache;
+
+/**
+ * cache_init: Initialize the page cache
+ *
+ *
+ * Returns new allocated cache or NULL on error
+ *
+ * @cache pointer to the PageCache struct
+ * @num_pages: cache maximal number of cached pages
+ * @page_size: cache page size
+ */
+PageCache *cache_init(int64_t num_pages, unsigned int page_size);
+
+/**
+ * cache_fini: free all cache resources
+ * @cache pointer to the PageCache struct
+ */
+void cache_fini(PageCache *cache);
+
+/**
+ * cache_is_cached: Checks to see if the page is cached
+ *
+ * Returns %true if page is cached
+ *
+ * @cache pointer to the PageCache struct
+ * @addr: page addr
+ */
+bool cache_is_cached(const PageCache *cache, uint64_t addr);
+
+/**
+ * get_cached_data: Get the data cached for an addr
+ *
+ * Returns pointer to the data cached or NULL if not cached
+ *
+ * @cache pointer to the PageCache struct
+ * @addr: page addr
+ */
+uint8_t *get_cached_data(const PageCache *cache, uint64_t addr);
+
+/**
+ * cache_insert: insert the page into the cache. the previous value will be overwritten
+ *
+ * @cache pointer to the PageCache struct
+ * @addr: page address
+ * @pdata: pointer to the page
+ */
+void cache_insert(PageCache *cache, uint64_t addr, uint8_t *pdata);
+
+/**
+ * cache_resize: resize the page cache. In case of size reduction the extra
+ * pages will be freed
+ *
+ * Returns -1 on error new cache size on success
+ *
+ * @cache pointer to the PageCache struct
+ * @num_pages: new page cache size (in pages)
+ */
+int64_t cache_resize(PageCache *cache, int64_t num_pages);
+
+#endif
diff --git a/page_cache.c b/page_cache.c
new file mode 100644
index 0000000..8110273
--- /dev/null
+++ b/page_cache.c
@@ -0,0 +1,216 @@
+/*
+ * Page cache for QEMU
+ * The cache is base on a hash of the page address
+ *
+ * Copyright 2012 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ *  Orit Wasserman  <owasserm@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <glib.h>
+#include <strings.h>
+
+#include "qemu-common.h"
+#include "qemu/page_cache.h"
+
+#ifdef DEBUG_CACHE
+#define DPRINTF(fmt, ...) \
+    do { fprintf(stdout, "cache: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) \
+    do { } while (0)
+#endif
+
+typedef struct CacheItem CacheItem;
+
+struct CacheItem {
+    uint64_t it_addr;
+    uint64_t it_age;
+    uint8_t *it_data;
+};
+
+struct PageCache {
+    CacheItem *page_cache;
+    unsigned int page_size;
+    int64_t max_num_items;
+    uint64_t max_item_age;
+    int64_t num_items;
+};
+
+PageCache *cache_init(int64_t num_pages, unsigned int page_size)
+{
+    int64_t i;
+
+    PageCache *cache = g_malloc(sizeof(*cache));
+
+    if (num_pages <= 0) {
+        DPRINTF("invalid number of pages\n");
+        return NULL;
+    }
+
+    /* round down to the nearest power of 2 */
+    if (!is_power_of_2(num_pages)) {
+        num_pages = pow2floor(num_pages);
+        DPRINTF("rounding down to %" PRId64 "\n", num_pages);
+    }
+    cache->page_size = page_size;
+    cache->num_items = 0;
+    cache->max_item_age = 0;
+    cache->max_num_items = num_pages;
+
+    DPRINTF("Setting cache buckets to %" PRId64 "\n", cache->max_num_items);
+
+    cache->page_cache = g_malloc((cache->max_num_items) *
+                                 sizeof(*cache->page_cache));
+
+    for (i = 0; i < cache->max_num_items; i++) {
+        cache->page_cache[i].it_data = NULL;
+        cache->page_cache[i].it_age = 0;
+        cache->page_cache[i].it_addr = -1;
+    }
+
+    return cache;
+}
+
+void cache_fini(PageCache *cache)
+{
+    int64_t i;
+
+    g_assert(cache);
+    g_assert(cache->page_cache);
+
+    for (i = 0; i < cache->max_num_items; i++) {
+        g_free(cache->page_cache[i].it_data);
+    }
+
+    g_free(cache->page_cache);
+    cache->page_cache = NULL;
+}
+
+static unsigned long cache_get_cache_pos(const PageCache *cache,
+                                         uint64_t address)
+{
+    unsigned long pos;
+
+    g_assert(cache->max_num_items);
+    pos = (address / cache->page_size) & (cache->max_num_items - 1);
+    return pos;
+}
+
+bool cache_is_cached(const PageCache *cache, uint64_t addr)
+{
+    unsigned long pos;
+
+    g_assert(cache);
+    g_assert(cache->page_cache);
+
+    pos = cache_get_cache_pos(cache, addr);
+
+    return (cache->page_cache[pos].it_addr == addr);
+}
+
+static CacheItem *cache_get_by_addr(const PageCache *cache, uint64_t addr)
+{
+    unsigned long pos;
+
+    g_assert(cache);
+    g_assert(cache->page_cache);
+
+    pos = cache_get_cache_pos(cache, addr);
+
+    return &cache->page_cache[pos];
+}
+
+uint8_t *get_cached_data(const PageCache *cache, uint64_t addr)
+{
+    return cache_get_by_addr(cache, addr)->it_data;
+}
+
+void cache_insert(PageCache *cache, uint64_t addr, uint8_t *pdata)
+{
+
+    CacheItem *it = NULL;
+
+    g_assert(cache);
+    g_assert(cache->page_cache);
+
+    /* actual update of entry */
+    it = cache_get_by_addr(cache, addr);
+
+    if (!it->it_data) {
+        cache->num_items++;
+    }
+
+    it->it_data = pdata;
+    it->it_age = ++cache->max_item_age;
+    it->it_addr = addr;
+}
+
+int64_t cache_resize(PageCache *cache, int64_t new_num_pages)
+{
+    PageCache *new_cache;
+    int64_t i;
+
+    CacheItem *old_it, *new_it;
+
+    g_assert(cache);
+
+    /* cache was not inited */
+    if (cache->page_cache == NULL) {
+        return -1;
+    }
+
+    /* same size */
+    if (pow2floor(new_num_pages) == cache->max_num_items) {
+        return cache->max_num_items;
+    }
+
+    new_cache = cache_init(new_num_pages, cache->page_size);
+    if (!(new_cache)) {
+        DPRINTF("Error creating new cache\n");
+        return -1;
+    }
+
+    /* move all data from old cache */
+    for (i = 0; i < cache->max_num_items; i++) {
+        old_it = &cache->page_cache[i];
+        if (old_it->it_addr != -1) {
+            /* check for collision , if there is, keep the first value */
+            new_it = cache_get_by_addr(new_cache, old_it->it_addr);
+            if (new_it->it_data) {
+                /* keep the oldest page */
+                if (new_it->it_age >= old_it->it_age) {
+                    g_free(old_it->it_data);
+                } else {
+                    g_free(new_it->it_data);
+                    new_it->it_data = old_it->it_data;
+                    new_it->it_age = old_it->it_age;
+                    new_it->it_addr = old_it->it_addr;
+                }
+            } else {
+                cache_insert(new_cache, old_it->it_addr, old_it->it_data);
+            }
+        }
+    }
+
+    cache->page_cache = new_cache->page_cache;
+    cache->max_num_items = new_cache->max_num_items;
+    cache->num_items = new_cache->num_items;
+
+    g_free(new_cache);
+
+    return cache->max_num_items;
+}
diff --git a/qemu-common.h b/qemu-common.h
index 09676f5..195bab5 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -1,3 +1,4 @@
+
 /* Common header file that is included by all of qemu.  */
 #ifndef QEMU_COMMON_H
 #define QEMU_COMMON_H
@@ -411,6 +412,18 @@ static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
 /* Round number up to multiple */
 #define QEMU_ALIGN_UP(n, m) QEMU_ALIGN_DOWN((n) + (m) - 1, (m))

+static inline bool is_power_of_2(int64_t value)
+{
+    if (!value) {
+        return 0;
+    }
+
+    return !(value & (value - 1));
+}
+
+/* round down to the nearest power of 2*/
+int64_t pow2floor(int64_t value);
+
 #include "module.h"

 #endif
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 16/22] Add uleb encoding/decoding functions
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (14 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 15/22] Add cache handling functions Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 17/22] Change ram_save_block to return -1 if there are no more changes Juan Quintela
                   ` (5 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: Orit Wasserman

From: Orit Wasserman <owasserm@redhat.com>

Implement Unsigned Little Endian Base 128.

Signed-off-by: Orit Wasserman <owasserm@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 cutils.c      |   33 +++++++++++++++++++++++++++++++++
 qemu-common.h |    8 ++++++++
 2 files changed, 41 insertions(+)

diff --git a/cutils.c b/cutils.c
index b0bdd4b..700f943 100644
--- a/cutils.c
+++ b/cutils.c
@@ -384,3 +384,36 @@ int64_t pow2floor(int64_t value)
     }
     return value;
 }
+
+/*
+ * Implementation of  ULEB128 (http://en.wikipedia.org/wiki/LEB128)
+ * Input is limited to 14-bit numbers
+ */
+int uleb128_encode_small(uint8_t *out, uint32_t n)
+{
+    g_assert(n <= 0x3fff);
+    if (n < 0x80) {
+        *out++ = n;
+        return 1;
+    } else {
+        *out++ = (n & 0x7f) | 0x80;
+        *out++ = n >> 7;
+        return 2;
+    }
+}
+
+int uleb128_decode_small(const uint8_t *in, uint32_t *n)
+{
+    if (!(*in & 0x80)) {
+        *n = *in++;
+        return 1;
+    } else {
+        *n = *in++ & 0x7f;
+        /* we exceed 14 bit number */
+        if (*in & 0x80) {
+            return -1;
+        }
+        *n |= *in++ << 7;
+        return 2;
+    }
+}
diff --git a/qemu-common.h b/qemu-common.h
index 195bab5..3188bdd 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -426,4 +426,12 @@ int64_t pow2floor(int64_t value);

 #include "module.h"

+/*
+ * Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128)
+ * Input is limited to 14-bit numbers
+ */
+
+int uleb128_encode_small(uint8_t *out, uint32_t n);
+int uleb128_decode_small(const uint8_t *in, uint32_t *n);
+
 #endif
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 17/22] Change ram_save_block to return -1 if there are no more changes
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (15 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 16/22] Add uleb encoding/decoding functions Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 18/22] Add xbzrle_encode_buffer and xbzrle_decode_buffer functions Juan Quintela
                   ` (4 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: Orit Wasserman

From: Orit Wasserman <owasserm@redhat.com>

It will return 0 if the page is unmodifed.

Signed-off-by: Orit Wasserman <owasserm@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 arch_init.c |   26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/arch_init.c b/arch_init.c
index 8cbf8f4..78cdf50 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -184,11 +184,19 @@ static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
 static RAMBlock *last_block;
 static ram_addr_t last_offset;

+/*
+ * ram_save_block: Writes a page of memory to the stream f
+ *
+ * Returns:  0: if the page hasn't changed
+ *          -1: if there are no more dirty pages
+ *           n: the amount of bytes written in other case
+ */
+
 static int ram_save_block(QEMUFile *f)
 {
     RAMBlock *block = last_block;
     ram_addr_t offset = last_offset;
-    int bytes_sent = 0;
+    int bytes_sent = -1;
     MemoryRegion *mr;

     if (!block)
@@ -354,10 +362,11 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
         int bytes_sent;

         bytes_sent = ram_save_block(f);
-        bytes_transferred += bytes_sent;
-        if (bytes_sent == 0) { /* no more blocks */
+        /* no more blocks to sent */
+        if (bytes_sent < 0) {
             break;
         }
+        bytes_transferred += bytes_sent;
         /* we want to check in the 1st loop, just in case it was the 1st time
            and we had to sync the dirty bitmap.
            qemu_get_clock_ns() is a bit expensive, so we only check each some
@@ -405,14 +414,19 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)

 static int ram_save_complete(QEMUFile *f, void *opaque)
 {
-    int bytes_sent;
-
     memory_global_sync_dirty_bitmap(get_system_memory());

     /* try transferring iterative blocks of memory */

     /* flush all remaining blocks regardless of rate limiting */
-    while ((bytes_sent = ram_save_block(f)) != 0) {
+    while (true) {
+        int bytes_sent;
+
+        bytes_sent = ram_save_block(f);
+        /* no more blocks to sent */
+        if (bytes_sent < 0) {
+            break;
+        }
         bytes_transferred += bytes_sent;
     }
     memory_global_dirty_log_stop();
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 18/22] Add xbzrle_encode_buffer and xbzrle_decode_buffer functions
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (16 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 17/22] Change ram_save_block to return -1 if there are no more changes Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 19/22] Add XBZRLE to ram_save_block and ram_save_live Juan Quintela
                   ` (3 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel
  Cc: Orit Wasserman, Benoit Hudzia, Eric Blake, Aidan Shribman,
	Petter Svard

From: Orit Wasserman <owasserm@redhat.com>

For performance we are encoding long word at a time.
For nzrun we use long-word-at-a-time NULL-detection tricks from strcmp():
using ((lword - 0x0101010101010101) & (~lword) & 0x8080808080808080) test
to find out if any byte in the long word is zero.

Signed-off-by: Benoit Hudzia <benoit.hudzia@sap.com>
Signed-off-by: Petter Svard <petters@cs.umu.se>
Signed-off-by: Aidan Shribman <aidan.shribman@sap.com>
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 migration.h |    4 ++
 savevm.c    |  159 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 163 insertions(+)

diff --git a/migration.h b/migration.h
index 713aae0..743c366 100644
--- a/migration.h
+++ b/migration.h
@@ -100,4 +100,8 @@ void migrate_add_blocker(Error *reason);
  */
 void migrate_del_blocker(Error *reason);

+int xbzrle_encode_buffer(uint8_t *old_buf, uint8_t *new_buf, int slen,
+                         uint8_t *dst, int dlen);
+int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen);
+
 #endif
diff --git a/savevm.c b/savevm.c
index 6e82b2d..c5fd13f 100644
--- a/savevm.c
+++ b/savevm.c
@@ -2392,3 +2392,162 @@ void vmstate_register_ram_global(MemoryRegion *mr)
 {
     vmstate_register_ram(mr, NULL);
 }
+
+/*
+  page = zrun nzrun
+       | zrun nzrun page
+
+  zrun = length
+
+  nzrun = length byte...
+
+  length = uleb128 encoded integer
+ */
+int xbzrle_encode_buffer(uint8_t *old_buf, uint8_t *new_buf, int slen,
+                         uint8_t *dst, int dlen)
+{
+    uint32_t zrun_len = 0, nzrun_len = 0;
+    int d = 0, i = 0;
+    long res, xor;
+    uint8_t *nzrun_start = NULL;
+
+    g_assert(!(((uintptr_t)old_buf | (uintptr_t)new_buf | slen) %
+               sizeof(long)));
+
+    while (i < slen) {
+        /* overflow */
+        if (d + 2 > dlen) {
+            return -1;
+        }
+
+        /* not aligned to sizeof(long) */
+        res = (slen - i) % sizeof(long);
+        while (res && old_buf[i] == new_buf[i]) {
+            zrun_len++;
+            i++;
+            res--;
+        }
+
+        /* word at a time for speed */
+        if (!res) {
+            while (i < slen &&
+                   (*(long *)(old_buf + i)) == (*(long *)(new_buf + i))) {
+                i += sizeof(long);
+                zrun_len += sizeof(long);
+            }
+
+            /* go over the rest */
+            while (i < slen && old_buf[i] == new_buf[i]) {
+                zrun_len++;
+                i++;
+            }
+        }
+
+        /* buffer unchanged */
+        if (zrun_len == slen) {
+            return 0;
+        }
+
+        /* skip last zero run */
+        if (i == slen) {
+            return d;
+        }
+
+        d += uleb128_encode_small(dst + d, zrun_len);
+
+        zrun_len = 0;
+        nzrun_start = new_buf + i;
+
+        /* overflow */
+        if (d + 2 > dlen) {
+            return -1;
+        }
+        /* not aligned to sizeof(long) */
+        res = (slen - i) % sizeof(long);
+        while (res && old_buf[i] != new_buf[i]) {
+            i++;
+            nzrun_len++;
+            res--;
+        }
+
+        /* word at a time for speed, use of 32-bit long okay */
+        if (!res) {
+            /* truncation to 32-bit long okay */
+            long mask = 0x0101010101010101ULL;
+            while (i < slen) {
+                xor = *(long *)(old_buf + i) ^ *(long *)(new_buf + i);
+                if ((xor - mask) & ~xor & (mask << 7)) {
+                    /* found the end of an nzrun within the current long */
+                    while (old_buf[i] != new_buf[i]) {
+                        nzrun_len++;
+                        i++;
+                    }
+                    break;
+                } else {
+                    i += sizeof(long);
+                    nzrun_len += sizeof(long);
+                }
+            }
+        }
+
+        d += uleb128_encode_small(dst + d, nzrun_len);
+        /* overflow */
+        if (d + nzrun_len > dlen) {
+            return -1;
+        }
+        memcpy(dst + d, nzrun_start, nzrun_len);
+        d += nzrun_len;
+        nzrun_len = 0;
+    }
+
+    return d;
+}
+
+int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen)
+{
+    int i = 0, d = 0;
+    int ret;
+    uint32_t count = 0;
+
+    while (i < slen) {
+
+        /* zrun */
+        if ((slen - i) < 2) {
+            return -1;
+        }
+
+        ret = uleb128_decode_small(src + i, &count);
+        if (ret < 0 || (i && !count)) {
+            return -1;
+        }
+        i += ret;
+        d += count;
+
+        /* overflow */
+        if (d > dlen) {
+            return -1;
+        }
+
+        /* nzrun */
+        if ((slen - i) < 2) {
+            return -1;
+        }
+
+        ret = uleb128_decode_small(src + i, &count);
+        if (ret < 0 || !count) {
+            return -1;
+        }
+        i += ret;
+
+        /* overflow */
+        if (d + count > dlen || i + count > slen) {
+            return -1;
+        }
+
+        memcpy(dst + d , src + i, count);
+        d += count;
+        i += count;
+    }
+
+    return d;
+}
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 19/22] Add XBZRLE to ram_save_block and ram_save_live
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (17 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 18/22] Add xbzrle_encode_buffer and xbzrle_decode_buffer functions Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 20/22] Add migrate_set_cachesize command Juan Quintela
                   ` (2 subsequent siblings)
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: Orit Wasserman, Benoit Hudzia, Aidan Shribman, Petter Svard

From: Orit Wasserman <owasserm@redhat.com>

In the outgoing migration check to see if the page is cached and
changed than send compressed page by using save_xbrle_page function.
In the incoming migration check to see if RAM_SAVE_FLAG_XBZRLE is set
and decompress the page (by using load_xbrle function).

Signed-off-by: Benoit Hudzia <benoit.hudzia@sap.com>
Signed-off-by: Petter Svard <petters@cs.umu.se>
Signed-off-by: Aidan Shribman <aidan.shribman@sap.com>
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 arch_init.c |  169 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 migration.c |   24 +++++++++
 migration.h |    4 ++
 3 files changed, 194 insertions(+), 3 deletions(-)

diff --git a/arch_init.c b/arch_init.c
index 78cdf50..a3a4707 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -43,6 +43,7 @@
 #include "hw/smbios.h"
 #include "exec-memory.h"
 #include "hw/pcspk.h"
+#include "qemu/page_cache.h"

 #ifdef DEBUG_ARCH_INIT
 #define DPRINTF(fmt, ...) \
@@ -102,6 +103,7 @@ const uint32_t arch_type = QEMU_ARCH;
 #define RAM_SAVE_FLAG_PAGE     0x08
 #define RAM_SAVE_FLAG_EOS      0x10
 #define RAM_SAVE_FLAG_CONTINUE 0x20
+#define RAM_SAVE_FLAG_XBZRLE   0x40

 #ifdef __ALTIVEC__
 #include <altivec.h>
@@ -169,6 +171,27 @@ static int is_dup_page(uint8_t *page)
     return 1;
 }

+/* XBZRLE (Xor Based Zero Length Encoding */
+typedef struct XBZRLEHeader {
+    uint16_t xh_len;
+    uint8_t xh_flags;
+} XBZRLEHeader;
+
+/* struct contains XBZRLE cache and a static page
+   used by the compression */
+static struct {
+    /* buffer used for XBZRLE encoding */
+    uint8_t *encoded_buf;
+    /* buffer used for XBZRLE decoding */
+    uint8_t *decoded_buf;
+    /* Cache for XBZRLE */
+    PageCache *cache;
+} XBZRLE = {
+    .encoded_buf = NULL,
+    .decoded_buf = NULL,
+    .cache = NULL,
+};
+
 static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
         int cont, int flag)
 {
@@ -181,6 +204,60 @@ static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,

 }

+#define ENCODING_FLAG_XBZRLE 0x1
+
+static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
+                            ram_addr_t current_addr, RAMBlock *block,
+                            ram_addr_t offset, int cont)
+{
+    int encoded_len = 0, bytes_sent = -1, ret = -1;
+    XBZRLEHeader hdr = {
+        .xh_len = 0,
+        .xh_flags = 0,
+    };
+    uint8_t *prev_cached_page;
+
+    if (!cache_is_cached(XBZRLE.cache, current_addr)) {
+        cache_insert(XBZRLE.cache, current_addr,
+                     g_memdup(current_data, TARGET_PAGE_SIZE));
+        return -1;
+    }
+
+    prev_cached_page = get_cached_data(XBZRLE.cache, current_addr);
+
+    /* XBZRLE encoding (if there is no overflow) */
+    encoded_len = xbzrle_encode_buffer(prev_cached_page, current_data,
+                                       TARGET_PAGE_SIZE, XBZRLE.encoded_buf,
+                                       TARGET_PAGE_SIZE);
+    if (encoded_len == 0) {
+        DPRINTF("Skipping unmodified page\n");
+        return 0;
+    } else if (encoded_len == -1) {
+        DPRINTF("Overflow\n");
+        /* update data in the cache */
+        memcpy(prev_cached_page, current_data, TARGET_PAGE_SIZE);
+        return -1;
+    }
+
+    /* we need to update the data in the cache, in order to get the same data
+       we cached we decode the encoded page on the cached data */
+    ret = xbzrle_decode_buffer(XBZRLE.encoded_buf, encoded_len,
+                               prev_cached_page, TARGET_PAGE_SIZE);
+    g_assert(ret != -1);
+
+    hdr.xh_len = encoded_len;
+    hdr.xh_flags |= ENCODING_FLAG_XBZRLE;
+
+    /* Send XBZRLE based compressed page */
+    save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_XBZRLE);
+    qemu_put_byte(f, hdr.xh_flags);
+    qemu_put_be16(f, hdr.xh_len);
+    qemu_put_buffer(f, XBZRLE.encoded_buf, encoded_len);
+    bytes_sent = encoded_len + sizeof(hdr);
+
+    return bytes_sent;
+}
+
 static RAMBlock *last_block;
 static ram_addr_t last_offset;

@@ -198,6 +275,7 @@ static int ram_save_block(QEMUFile *f)
     ram_addr_t offset = last_offset;
     int bytes_sent = -1;
     MemoryRegion *mr;
+    ram_addr_t current_addr;

     if (!block)
         block = QLIST_FIRST(&ram_list.blocks);
@@ -218,13 +296,24 @@ static int ram_save_block(QEMUFile *f)
                 save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_COMPRESS);
                 qemu_put_byte(f, *p);
                 bytes_sent = 1;
-            } else {
+            } else if (migrate_use_xbzrle()) {
+                current_addr = block->offset + offset;
+                bytes_sent = save_xbzrle_page(f, p, current_addr, block,
+                                              offset, cont);
+                p = get_cached_data(XBZRLE.cache, current_addr);
+            }
+
+            /* either we didn't send yet (we may have had XBZRLE overflow) */
+            if (bytes_sent == -1) {
                 save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
                 qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
                 bytes_sent = TARGET_PAGE_SIZE;
             }

-            break;
+            /* if page is unmodified, continue to the next */
+            if (bytes_sent != 0) {
+                break;
+            }
         }

         offset += TARGET_PAGE_SIZE;
@@ -302,6 +391,14 @@ static void sort_ram_list(void)
 static void migration_end(void)
 {
     memory_global_dirty_log_stop();
+
+    if (migrate_use_xbzrle()) {
+        cache_fini(XBZRLE.cache);
+        g_free(XBZRLE.cache);
+        g_free(XBZRLE.encoded_buf);
+        g_free(XBZRLE.decoded_buf);
+        XBZRLE.cache = NULL;
+    }
 }

 static void ram_migration_cancel(void *opaque)
@@ -321,7 +418,17 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
     last_offset = 0;
     sort_ram_list();

-    /* Make sure all dirty bits are set */
+    if (migrate_use_xbzrle()) {
+        XBZRLE.cache = cache_init(migrate_xbzrle_cache_size() /
+                                  TARGET_PAGE_SIZE,
+                                  TARGET_PAGE_SIZE);
+        if (!XBZRLE.cache) {
+            DPRINTF("Error creating cache\n");
+            return -1;
+        }
+        XBZRLE.encoded_buf = g_malloc0(TARGET_PAGE_SIZE);
+    }
+
     QLIST_FOREACH(block, &ram_list.blocks, next) {
         for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) {
             if (!memory_region_get_dirty(block->mr, addr, TARGET_PAGE_SIZE,
@@ -436,6 +543,49 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
     return 0;
 }

+static int load_xbzrle(QEMUFile *f, ram_addr_t addr, void *host)
+{
+    int ret, rc = 0;
+    XBZRLEHeader hdr = {
+        .xh_len = 0,
+        .xh_flags = 0,
+    };
+
+    if (!XBZRLE.decoded_buf) {
+        XBZRLE.decoded_buf = g_malloc(TARGET_PAGE_SIZE);
+    }
+
+    /* extract RLE header */
+    hdr.xh_flags = qemu_get_byte(f);
+    hdr.xh_len = qemu_get_be16(f);
+
+    if (!(hdr.xh_flags & ENCODING_FLAG_XBZRLE)) {
+        fprintf(stderr, "Failed to load XBZRLE page - wrong compression!\n");
+        return -1;
+    }
+
+    if (hdr.xh_len > TARGET_PAGE_SIZE) {
+        fprintf(stderr, "Failed to load XBZRLE page - len overflow!\n");
+        return -1;
+    }
+    /* load data and decode */
+    qemu_get_buffer(f, XBZRLE.decoded_buf, hdr.xh_len);
+
+    /* decode RLE */
+    ret = xbzrle_decode_buffer(XBZRLE.decoded_buf, hdr.xh_len, host,
+                               TARGET_PAGE_SIZE);
+    if (ret == -1) {
+        fprintf(stderr, "Failed to load XBZRLE page - decode error!\n");
+        rc = -1;
+    } else  if (ret > TARGET_PAGE_SIZE) {
+        fprintf(stderr, "Failed to load XBZRLE page - size %d exceeds %d!\n",
+                ret, TARGET_PAGE_SIZE);
+        rc = -1;
+    }
+
+    return rc;
+}
+
 static inline void *host_from_stream_offset(QEMUFile *f,
                                             ram_addr_t offset,
                                             int flags)
@@ -549,6 +699,19 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
             }

             qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
+        } else if (flags & RAM_SAVE_FLAG_XBZRLE) {
+            if (!migrate_use_xbzrle()) {
+                return -EINVAL;
+            }
+            void *host = host_from_stream_offset(f, addr, flags);
+            if (!host) {
+                return -EINVAL;
+            }
+
+            if (load_xbzrle(f, addr, host) < 0) {
+                ret = -EINVAL;
+                goto done;
+            }
         }
         error = qemu_file_get_error(f);
         if (error) {
diff --git a/migration.c b/migration.c
index fd004d7..1a264a9 100644
--- a/migration.c
+++ b/migration.c
@@ -43,6 +43,9 @@ enum {

 #define MAX_THROTTLE  (32 << 20)      /* Migration speed throttling */

+/* Migration XBZRLE default cache size */
+#define DEFAULT_MIGRATE_CACHE_SIZE (64 * 1024 * 1024)
+
 static NotifierList migration_state_notifiers =
     NOTIFIER_LIST_INITIALIZER(migration_state_notifiers);

@@ -55,6 +58,7 @@ static MigrationState *migrate_get_current(void)
     static MigrationState current_migration = {
         .state = MIG_STATE_SETUP,
         .bandwidth_limit = MAX_THROTTLE,
+        .xbzrle_cache_size = DEFAULT_MIGRATE_CACHE_SIZE,
     };

     return &current_migration;
@@ -439,6 +443,7 @@ static MigrationState *migrate_init(const MigrationParams *params)
     MigrationState *s = migrate_get_current();
     int64_t bandwidth_limit = s->bandwidth_limit;
     bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
+    int64_t xbzrle_cache_size = s->xbzrle_cache_size;

     memcpy(enabled_capabilities, s->enabled_capabilities,
            sizeof(enabled_capabilities));
@@ -448,6 +453,7 @@ static MigrationState *migrate_init(const MigrationParams *params)
     s->params = *params;
     memcpy(s->enabled_capabilities, enabled_capabilities,
            sizeof(enabled_capabilities));
+    s->xbzrle_cache_size = xbzrle_cache_size;

     s->state = MIG_STATE_SETUP;
     s->total_time = qemu_get_clock_ms(rt_clock);
@@ -546,3 +552,21 @@ void qmp_migrate_set_downtime(double value, Error **errp)
     value = MAX(0, MIN(UINT64_MAX, value));
     max_downtime = (uint64_t)value;
 }
+
+int migrate_use_xbzrle(void)
+{
+    MigrationState *s;
+
+    s = migrate_get_current();
+
+    return s->enabled_capabilities[MIGRATION_CAPABILITY_XBZRLE];
+}
+
+int64_t migrate_xbzrle_cache_size(void)
+{
+    MigrationState *s;
+
+    s = migrate_get_current();
+
+    return s->xbzrle_cache_size;
+}
diff --git a/migration.h b/migration.h
index 743c366..cdf6787 100644
--- a/migration.h
+++ b/migration.h
@@ -41,6 +41,7 @@ struct MigrationState
     MigrationParams params;
     int64_t total_time;
     bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
+    int64_t xbzrle_cache_size;
 };

 void process_incoming_migration(QEMUFile *f);
@@ -104,4 +105,7 @@ int xbzrle_encode_buffer(uint8_t *old_buf, uint8_t *new_buf, int slen,
                          uint8_t *dst, int dlen);
 int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen);

+int migrate_use_xbzrle(void);
+int64_t migrate_xbzrle_cache_size(void);
+
 #endif
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 20/22] Add migrate_set_cachesize command
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (18 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 19/22] Add XBZRLE to ram_save_block and ram_save_live Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-23 19:21   ` Luiz Capitulino
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 21/22] Add XBZRLE statistics Juan Quintela
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 22/22] ram: save live optimization Juan Quintela
  21 siblings, 1 reply; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: Orit Wasserman, Benoit Hudzia, Aidan Shribman, Petter Svard

From: Orit Wasserman <owasserm@redhat.com>

Change XBZRLE cache size in bytes (the size should be a power of 2, it will be
rounded down to the nearest power of 2).
If XBZRLE cache size is too small there will be many cache miss.

Signed-off-by: Benoit Hudzia <benoit.hudzia@sap.com>
Signed-off-by: Petter Svard <petters@cs.umu.se>
Signed-off-by: Aidan Shribman <aidan.shribman@sap.com>
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 arch_init.c      |   10 ++++++++++
 hmp-commands.hx  |   20 ++++++++++++++++++++
 hmp.c            |   13 +++++++++++++
 hmp.h            |    1 +
 migration.c      |   14 ++++++++++++++
 migration.h      |    2 ++
 qapi-schema.json |   16 ++++++++++++++++
 qmp-commands.hx  |   23 +++++++++++++++++++++++
 8 files changed, 99 insertions(+)

diff --git a/arch_init.c b/arch_init.c
index a3a4707..d972d84 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -192,6 +192,16 @@ static struct {
     .cache = NULL,
 };

+
+int64_t xbzrle_cache_resize(int64_t new_size)
+{
+    if (XBZRLE.cache != NULL) {
+        return cache_resize(XBZRLE.cache, new_size / TARGET_PAGE_SIZE) *
+            TARGET_PAGE_SIZE;
+    }
+    return pow2floor(new_size);
+}
+
 static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
         int cont, int flag)
 {
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 9245bef..052a0a3 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -829,6 +829,26 @@ STEXI
 @item migrate_cancel
 @findex migrate_cancel
 Cancel the current VM migration.
+
+ETEXI
+
+    {
+        .name       = "migrate_set_cachesize",
+        .args_type  = "value:o",
+        .params     = "value",
+        .help       = "set cache size (in bytes) for XBZRLE migrations,"
+                      "the cache size will be rounded down to the nearest "
+                      "power of 2.\n"
+                      "The cache size effects the number of cache misses."
+                      "In case of a high cache miss ratio you need to increase"
+                      " the cache size",
+        .mhandler.cmd = hmp_migrate_set_cachesize,
+    },
+
+STEXI
+@item migrate_set_cachesize @var{value}
+@findex migrate_set_cachesize
+Set cache size to @var{value} (in bytes) for xbzrle migrations.
 ETEXI

     {
diff --git a/hmp.c b/hmp.c
index b0440e6..99ad00a 100644
--- a/hmp.c
+++ b/hmp.c
@@ -758,6 +758,19 @@ void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict)
     qmp_migrate_set_downtime(value, NULL);
 }

+void hmp_migrate_set_cachesize(Monitor *mon, const QDict *qdict)
+{
+    int64_t value = qdict_get_int(qdict, "value");
+    Error *err = NULL;
+
+    qmp_migrate_set_cache_size(value, &err);
+    if (err) {
+        monitor_printf(mon, "%s\n", error_get_pretty(err));
+        error_free(err);
+        return;
+    }
+}
+
 void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
 {
     int64_t value = qdict_get_int(qdict, "value");
diff --git a/hmp.h b/hmp.h
index 09ba198..7c5117d 100644
--- a/hmp.h
+++ b/hmp.h
@@ -53,6 +53,7 @@ void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
 void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
 void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
 void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict);
+void hmp_migrate_set_cachesize(Monitor *mon, const QDict *qdict);
 void hmp_set_password(Monitor *mon, const QDict *qdict);
 void hmp_expire_password(Monitor *mon, const QDict *qdict);
 void hmp_eject(Monitor *mon, const QDict *qdict);
diff --git a/migration.c b/migration.c
index 1a264a9..d134bf6 100644
--- a/migration.c
+++ b/migration.c
@@ -533,6 +533,20 @@ void qmp_migrate_cancel(Error **errp)
     migrate_fd_cancel(migrate_get_current());
 }

+void qmp_migrate_set_cache_size(int64_t value, Error **errp)
+{
+    MigrationState *s = migrate_get_current();
+
+    /* Check for truncation */
+    if (value != (size_t)value) {
+        error_set(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
+                  "exceeding address space");
+        return;
+    }
+
+    s->xbzrle_cache_size = xbzrle_cache_resize(value);
+}
+
 void qmp_migrate_set_speed(int64_t value, Error **errp)
 {
     MigrationState *s;
diff --git a/migration.h b/migration.h
index cdf6787..337e225 100644
--- a/migration.h
+++ b/migration.h
@@ -108,4 +108,6 @@ int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen);
 int migrate_use_xbzrle(void);
 int64_t migrate_xbzrle_cache_size(void);

+int64_t xbzrle_cache_resize(int64_t new_size);
+
 #endif
diff --git a/qapi-schema.json b/qapi-schema.json
index a8408fd..a0f0f95 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1390,6 +1390,22 @@
 { 'command': 'migrate_set_speed', 'data': {'value': 'int'} }

 ##
+# @migrate-set-cache-size
+#
+# Set XBZRLE cache size
+#
+# @value: cache size in bytes
+#
+# The size will be rounded down to the nearest power of 2.
+# The cache size can be modified before and during ongoing migration
+#
+# Returns: nothing on success
+#
+# Since: 1.2
+##
+{ 'command': 'migrate-set-cache-size', 'data': {'value': 'int'} }
+
+##
 # @ObjectPropertyInfo:
 #
 # @name: the name of the property
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 3ee6e00..a3d57ce 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -520,6 +520,29 @@ Example:
 <- { "return": {} }

 EQMP
+{
+        .name       = "migrate-set-cache-size",
+        .args_type  = "value:o",
+        .mhandler.cmd_new = qmp_marshal_input_migrate_set_cache_size,
+    },
+
+SQMP
+migrate-set-cache-size
+---------------------
+
+Set cache size to be used by XBZRLE migration, the cache size will be rounded
+down to the nearest power of 2
+
+Arguments:
+
+- "value": cache size in bytes (json-int)
+
+Example:
+
+-> { "execute": "migrate-set-cache-size", "arguments": { "value": 536870912 } }
+<- { "return": {} }
+
+EQMP

     {
         .name       = "migrate_set_speed",
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 21/22] Add XBZRLE statistics
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (19 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 20/22] Add migrate_set_cachesize command Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  2012-07-23 19:33   ` Luiz Capitulino
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 22/22] ram: save live optimization Juan Quintela
  21 siblings, 1 reply; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel; +Cc: Orit Wasserman, Benoit Hudzia, Aidan Shribman, Petter Svard

From: Orit Wasserman <owasserm@redhat.com>

Signed-off-by: Benoit Hudzia <benoit.hudzia@sap.com>
Signed-off-by: Petter Svard <petters@cs.umu.se>
Signed-off-by: Aidan Shribman <aidan.shribman@sap.com>
Signed-off-by: Orit Wasserman <owasserm@redhat.com>
Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 arch_init.c      |   66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hmp.c            |   13 +++++++++++
 migration.c      |   49 ++++++++++++++++++++++++++++++++++++++++
 migration.h      |    9 ++++++++
 qapi-schema.json |   37 +++++++++++++++++++++++++-----
 qmp-commands.hx  |   35 ++++++++++++++++++++++++++++-
 6 files changed, 203 insertions(+), 6 deletions(-)

diff --git a/arch_init.c b/arch_init.c
index d972d84..ab3fb2c 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -202,6 +202,64 @@ int64_t xbzrle_cache_resize(int64_t new_size)
     return pow2floor(new_size);
 }

+/* accounting for migration statistics */
+typedef struct AccountingInfo {
+    uint64_t dup_pages;
+    uint64_t norm_pages;
+    uint64_t xbzrle_bytes;
+    uint64_t xbzrle_pages;
+    uint64_t xbzrle_cache_miss;
+    uint64_t iterations;
+    uint64_t xbzrle_overflows;
+} AccountingInfo;
+
+static AccountingInfo acct_info;
+
+static void acct_clear(void)
+{
+    memset(&acct_info, 0, sizeof(acct_info));
+}
+
+uint64_t dup_mig_bytes_transferred(void)
+{
+    return acct_info.dup_pages * TARGET_PAGE_SIZE;
+}
+
+uint64_t dup_mig_pages_transferred(void)
+{
+    return acct_info.dup_pages;
+}
+
+uint64_t norm_mig_bytes_transferred(void)
+{
+    return acct_info.norm_pages * TARGET_PAGE_SIZE;
+}
+
+uint64_t norm_mig_pages_transferred(void)
+{
+    return acct_info.norm_pages;
+}
+
+uint64_t xbzrle_mig_bytes_transferred(void)
+{
+    return acct_info.xbzrle_bytes;
+}
+
+uint64_t xbzrle_mig_pages_transferred(void)
+{
+    return acct_info.xbzrle_pages;
+}
+
+uint64_t xbzrle_mig_pages_cache_miss(void)
+{
+    return acct_info.xbzrle_cache_miss;
+}
+
+uint64_t xbzrle_mig_pages_overflow(void)
+{
+    return acct_info.xbzrle_overflows;
+}
+
 static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
         int cont, int flag)
 {
@@ -230,6 +288,7 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
     if (!cache_is_cached(XBZRLE.cache, current_addr)) {
         cache_insert(XBZRLE.cache, current_addr,
                      g_memdup(current_data, TARGET_PAGE_SIZE));
+        acct_info.xbzrle_cache_miss++;
         return -1;
     }

@@ -244,6 +303,7 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
         return 0;
     } else if (encoded_len == -1) {
         DPRINTF("Overflow\n");
+        acct_info.xbzrle_overflows++;
         /* update data in the cache */
         memcpy(prev_cached_page, current_data, TARGET_PAGE_SIZE);
         return -1;
@@ -263,7 +323,9 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
     qemu_put_byte(f, hdr.xh_flags);
     qemu_put_be16(f, hdr.xh_len);
     qemu_put_buffer(f, XBZRLE.encoded_buf, encoded_len);
+    acct_info.xbzrle_pages++;
     bytes_sent = encoded_len + sizeof(hdr);
+    acct_info.xbzrle_bytes += bytes_sent;

     return bytes_sent;
 }
@@ -303,6 +365,7 @@ static int ram_save_block(QEMUFile *f)
             p = memory_region_get_ram_ptr(mr) + offset;

             if (is_dup_page(p)) {
+                acct_info.dup_pages++;
                 save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_COMPRESS);
                 qemu_put_byte(f, *p);
                 bytes_sent = 1;
@@ -318,6 +381,7 @@ static int ram_save_block(QEMUFile *f)
                 save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
                 qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
                 bytes_sent = TARGET_PAGE_SIZE;
+                acct_info.norm_pages++;
             }

             /* if page is unmodified, continue to the next */
@@ -437,6 +501,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
             return -1;
         }
         XBZRLE.encoded_buf = g_malloc0(TARGET_PAGE_SIZE);
+        acct_clear();
     }

     QLIST_FOREACH(block, &ram_list.blocks, next) {
@@ -484,6 +549,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
             break;
         }
         bytes_transferred += bytes_sent;
+        acct_info.iterations++;
         /* we want to check in the 1st loop, just in case it was the 1st time
            and we had to sync the dirty bitmap.
            qemu_get_clock_ns() is a bit expensive, so we only check each some
diff --git a/hmp.c b/hmp.c
index 99ad00a..0d7333b 100644
--- a/hmp.c
+++ b/hmp.c
@@ -168,6 +168,19 @@ void hmp_info_migrate(Monitor *mon)
                        info->disk->total >> 10);
     }

+    if (info->has_cache) {
+        monitor_printf(mon, "cache size: %" PRIu64 " bytes\n",
+                       info->cache->cache_size);
+        monitor_printf(mon, "xbzrle transferred: %" PRIu64 " kbytes\n",
+                       info->cache->xbzrle_bytes >> 10);
+        monitor_printf(mon, "xbzrle pages: %" PRIu64 " pages\n",
+                       info->cache->xbzrle_pages);
+        monitor_printf(mon, "xbzrle cache miss: %" PRIu64 "\n",
+                       info->cache->xbzrle_cache_miss);
+        monitor_printf(mon, "xbzrle overflow : %" PRIu64 "\n",
+                       info->cache->xbzrle_overflow);
+    }
+
     qapi_free_MigrationInfo(info);
 }

diff --git a/migration.c b/migration.c
index d134bf6..e11f451 100644
--- a/migration.c
+++ b/migration.c
@@ -137,6 +137,17 @@ MigrationInfo *qmp_query_migrate(Error **errp)
             info->capabilities->value->state = s->enabled_capabilities[i];
             info->capabilities->next = NULL;
         }
+
+        /* display xbzrle cache size */
+        if (migrate_use_xbzrle()) {
+            info->has_cache = true;
+            info->cache = g_malloc0(sizeof(*info->cache));
+            info->cache->cache_size = migrate_xbzrle_cache_size();
+            info->cache->xbzrle_bytes = xbzrle_mig_bytes_transferred();
+            info->cache->xbzrle_pages = xbzrle_mig_pages_transferred();
+            info->cache->xbzrle_cache_miss = xbzrle_mig_pages_cache_miss();
+            info->cache->xbzrle_overflow = xbzrle_mig_pages_overflow();
+        }
         break;
     case MIG_STATE_ACTIVE:
         for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
@@ -161,6 +172,8 @@ MigrationInfo *qmp_query_migrate(Error **errp)
         info->ram->total = ram_bytes_total();
         info->ram->total_time = qemu_get_clock_ms(rt_clock)
             - s->total_time;
+        info->ram->duplicate = dup_mig_pages_transferred();
+        info->ram->normal = norm_mig_pages_transferred();

         if (blk_mig_active()) {
             info->has_disk = true;
@@ -169,6 +182,16 @@ MigrationInfo *qmp_query_migrate(Error **errp)
             info->disk->remaining = blk_mig_bytes_remaining();
             info->disk->total = blk_mig_bytes_total();
         }
+
+        if (migrate_use_xbzrle()) {
+            info->has_cache = true;
+            info->cache = g_malloc0(sizeof(*info->cache));
+            info->cache->cache_size = migrate_xbzrle_cache_size();
+            info->cache->xbzrle_bytes = xbzrle_mig_bytes_transferred();
+            info->cache->xbzrle_pages = xbzrle_mig_pages_transferred();
+            info->cache->xbzrle_cache_miss = xbzrle_mig_pages_cache_miss();
+            info->cache->xbzrle_overflow = xbzrle_mig_pages_overflow();
+        }
         break;
     case MIG_STATE_COMPLETED:
         for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
@@ -183,6 +206,32 @@ MigrationInfo *qmp_query_migrate(Error **errp)
             info->capabilities->next = NULL;
         }

+        info->has_ram = true;
+        info->ram = g_malloc0(sizeof(*info->ram));
+        info->ram->transferred = ram_bytes_transferred();
+        info->ram->remaining = ram_bytes_remaining();
+        info->ram->total = ram_bytes_total();
+        info->ram->duplicate = dup_mig_pages_transferred();
+        info->ram->normal = norm_mig_pages_transferred();
+
+        if (blk_mig_active()) {
+            info->has_disk = true;
+            info->disk = g_malloc0(sizeof(*info->disk));
+            info->disk->transferred = blk_mig_bytes_transferred();
+            info->disk->remaining = blk_mig_bytes_remaining();
+            info->disk->total = blk_mig_bytes_total();
+        }
+
+        if (migrate_use_xbzrle()) {
+            info->has_cache = true;
+            info->cache = g_malloc0(sizeof(*info->cache));
+            info->cache->cache_size = migrate_xbzrle_cache_size();
+            info->cache->xbzrle_bytes = xbzrle_mig_bytes_transferred();
+            info->cache->xbzrle_pages = xbzrle_mig_pages_transferred();
+            info->cache->xbzrle_cache_miss = xbzrle_mig_pages_cache_miss();
+            info->cache->xbzrle_overflow = xbzrle_mig_pages_overflow();
+        }
+
         info->has_status = true;
         info->status = g_strdup("completed");

diff --git a/migration.h b/migration.h
index 337e225..a9852fc 100644
--- a/migration.h
+++ b/migration.h
@@ -87,6 +87,15 @@ uint64_t ram_bytes_total(void);

 extern SaveVMHandlers savevm_ram_handlers;

+uint64_t dup_mig_bytes_transferred(void);
+uint64_t dup_mig_pages_transferred(void);
+uint64_t norm_mig_bytes_transferred(void);
+uint64_t norm_mig_pages_transferred(void);
+uint64_t xbzrle_mig_bytes_transferred(void);
+uint64_t xbzrle_mig_pages_transferred(void);
+uint64_t xbzrle_mig_pages_overflow(void);
+uint64_t xbzrle_mig_pages_cache_miss(void);
+
 /**
  * @migrate_add_blocker - prevent migration from proceeding
  *
diff --git a/qapi-schema.json b/qapi-schema.json
index a0f0f95..961810b 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -264,11 +264,35 @@
 #        migration has ended, it returns the total migration
 #        time. (since 1.2)
 #
-# Since: 0.14.0.
+# @duplicate: #optional, number of duplicate pages
+#
+# @normal : #optional, number of normal pages
+#
+# Since: 0.14.0, 'duplicate' and 'normal' since 1.2
 ##
 { 'type': 'MigrationStats',
   'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' ,
-           'total_time': 'int' } }
+           'total_time': 'int', '*duplicate': 'int', '*normal': 'int' } }
+##
+# @CacheStats
+#
+# Detailed XBZRLE migration cache statistics
+#
+# @cache_size: XBZRLE cache size
+#
+# @xbzrle_bytes: amount of bytes already transferred to the target VM
+#
+# @xbzrle_pages: amount of pages transferred to the target VM
+#
+# @xbzrle_cache_miss: number of cache miss
+#
+# @xbzrle_overflow: number of overflows
+#
+# Since: 1.2
+##
+{ 'type': 'CacheStats',
+  'data': {'cache-size': 'int', 'xbzrle-bytes': 'int', 'xbzrle-pages': 'int',
+           'xbzrle-cache-miss': 'int', 'xbzrle-overflow': 'int' } }

 ##
 # @MigrationInfo
@@ -291,13 +315,16 @@
 # @capabilities: #optional a list describing all the migration capabilities
 #                state
 #
-# Since: 0.14.0, 'capabilities' since 1.2
+# @cache: #optional @MigrationStats containing detailed XBZRLE migration
+#         statistics
+#
+# Since: 0.14.0, 'capabilities' and 'cache' since 1.2
 ##
 { 'type': 'MigrationInfo',
   'data': {'*status': 'str', '*ram': 'MigrationStats',
            '*disk': 'MigrationStats',
-           '*capabilities': ['MigrationCapabilityInfo']} }
-
+           '*capabilities': ['MigrationCapabilityInfo'],
+           '*cache': 'CacheStats'} }
 ##
 # @query-migrate
 #
diff --git a/qmp-commands.hx b/qmp-commands.hx
index a3d57ce..34d4675 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2093,6 +2093,8 @@ The main json-object contains the following:
          - "transferred": amount transferred (json-int)
          - "remaining": amount remaining (json-int)
          - "total": total (json-int)
+	 - "duplicate": number of duplicated pages (json-int)
+	 - "normal" : number of normal pages transferred (json-int)
 - "disk": only present if "status" is "active" and it is a block migration,
   it is a json-object with the following disk information (in bytes):
          - "transferred": amount transferred (json-int)
@@ -2100,10 +2102,17 @@ The main json-object contains the following:
          - "total": total (json-int)
 - "capabilities": migration capabilities state
          - "xbzrle" : XBZRLE state (json-bool)
+- "cache": only present if "status" and XBZRLE is active.
+  It is a json-object with the following XBZRLE information:
+         - "cache-size": XBZRLE cache size
+         - "xbzrle-bytes": total XBZRLE bytes transferred
+         - "xbzrle-pages": number of XBZRLE compressed pages
+         - "cache-miss": number of cache misses
+         - "overflow": number of XBZRLE overflows
+
 Examples:

 1. Before the first migration
-
 -> { "execute": "query-migrate" }
 <- { "return": {
         "capabilities" :  [ { "capability" : "xbzrle", "state" : false } ]
@@ -2164,6 +2173,30 @@ Examples:
       }
    }

+6. Migration is being performed and XBZRLE is active:
+
+-> { "execute": "query-migrate" }
+<- {
+      "return":{
+         "status":"active",
+         "capabilities" : [ { "capability": "xbzrle", "state" : true } ],
+         "ram":{
+            "total":1057024,
+            "remaining":1053304,
+            "transferred":3720,
+            "duplicate": 10,
+            "normal" : 3333
+         },
+         "cache":{
+            "cache-size": 1024
+            "xbzrle-transferred":20971520,
+            "xbzrle-pages":2444343,
+            "xbzrle-cache-miss":2244,
+            "xbzrle-overflow":34434
+         }
+      }
+   }
+
 EQMP

     {
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [Qemu-devel] [PATCH 22/22] ram: save live optimization
  2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
                   ` (20 preceding siblings ...)
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 21/22] Add XBZRLE statistics Juan Quintela
@ 2012-07-13  7:23 ` Juan Quintela
  21 siblings, 0 replies; 35+ messages in thread
From: Juan Quintela @ 2012-07-13  7:23 UTC (permalink / raw)
  To: qemu-devel

Optimize stage3 (complete), to use the page cached if it is there, but
not update the cache with new pages (they will never be reused).

Signed-off-by: Juan Quintela <quintela@redhat.com>
---
 arch_init.c |   14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/arch_init.c b/arch_init.c
index ab3fb2c..de2d801 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -276,7 +276,7 @@ static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,

 static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
                             ram_addr_t current_addr, RAMBlock *block,
-                            ram_addr_t offset, int cont)
+                            ram_addr_t offset, int cont, bool last_stage)
 {
     int encoded_len = 0, bytes_sent = -1, ret = -1;
     XBZRLEHeader hdr = {
@@ -341,7 +341,7 @@ static ram_addr_t last_offset;
  *           n: the amount of bytes written in other case
  */

-static int ram_save_block(QEMUFile *f)
+static int ram_save_block(QEMUFile *f, bool last_stage)
 {
     RAMBlock *block = last_block;
     ram_addr_t offset = last_offset;
@@ -372,8 +372,10 @@ static int ram_save_block(QEMUFile *f)
             } else if (migrate_use_xbzrle()) {
                 current_addr = block->offset + offset;
                 bytes_sent = save_xbzrle_page(f, p, current_addr, block,
-                                              offset, cont);
-                p = get_cached_data(XBZRLE.cache, current_addr);
+                                              offset, cont, last_stage);
+                if ((bytes_sent != -1) || !last_stage) {
+                    p = get_cached_data(XBZRLE.cache, current_addr);
+                }
             }

             /* either we didn't send yet (we may have had XBZRLE overflow) */
@@ -543,7 +545,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
     while ((ret = qemu_file_rate_limit(f)) == 0) {
         int bytes_sent;

-        bytes_sent = ram_save_block(f);
+        bytes_sent = ram_save_block(f, false);
         /* no more blocks to sent */
         if (bytes_sent < 0) {
             break;
@@ -605,7 +607,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque)
     while (true) {
         int bytes_sent;

-        bytes_sent = ram_save_block(f);
+        bytes_sent = ram_save_block(f, true);
         /* no more blocks to sent */
         if (bytes_sent < 0) {
             break;
-- 
1.7.10.4

^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH 13/22] Add migration capabilities
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 13/22] Add migration capabilities Juan Quintela
@ 2012-07-23 18:23   ` Luiz Capitulino
  2012-07-23 19:30     ` Eric Blake
  2012-07-24  6:25     ` Orit Wasserman
  0 siblings, 2 replies; 35+ messages in thread
From: Luiz Capitulino @ 2012-07-23 18:23 UTC (permalink / raw)
  To: Juan Quintela; +Cc: Orit Wasserman, Eric Blake, qemu-devel

On Fri, 13 Jul 2012 09:23:35 +0200
Juan Quintela <quintela@redhat.com> wrote:

> From: Orit Wasserman <owasserm@redhat.com>
> 
> Add migration capabilities that can be queried by the management.
> The management can query the source QEMU and the destination QEMU in order to
> verify both support some migration capability (currently only XBZRLE).
> The management can enable a capability for the next migration by using
> migrate_set_parameter command.

Please, split this into one command per-patch. Otherwise it's difficult to
review.

Have libvirt folks acked this approach btw? It looks fine to me, but we need
their ack too.

More comments below.

> 
> Signed-off-by: Orit Wasserman <owasserm@redhat.com>
> Signed-off-by: Juan Quintela <quintela@redhat.com>
> ---
>  hmp-commands.hx  |   16 ++++++++++++
>  hmp.c            |   64 ++++++++++++++++++++++++++++++++++++++++++++++++
>  hmp.h            |    2 ++
>  migration.c      |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
>  migration.h      |    2 ++
>  monitor.c        |    7 ++++++
>  qapi-schema.json |   53 ++++++++++++++++++++++++++++++++++++++--
>  qmp-commands.hx  |   71 ++++++++++++++++++++++++++++++++++++++++++++++++++---
>  8 files changed, 280 insertions(+), 7 deletions(-)
> 
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index f5d9d91..9245bef 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -861,6 +861,20 @@ Set maximum tolerated downtime (in seconds) for migration.
>  ETEXI
> 
>      {
> +        .name       = "migrate_set_parameter",
> +        .args_type  = "capability:s,state:b",
> +        .params     = "",

Please, fill in params.

> +        .help       = "Enable/Disable the usage of a capability for migration",
> +        .mhandler.cmd = hmp_migrate_set_parameter,
> +    },
> +
> +STEXI
> +@item migrate_set_parameter @var{capability} @var{state}
> +@findex migrate_set_parameter
> +Enable/Disable the usage of a capability @var{capability} for migration.
> +ETEXI
> +
> +    {
>          .name       = "client_migrate_info",
>          .args_type  = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
>          .params     = "protocol hostname port tls-port cert-subject",
> @@ -1419,6 +1433,8 @@ show CPU statistics
>  show user network stack connection states
>  @item info migrate
>  show migration status
> +@item info migration_capabilities
> +show migration capabilities
>  @item info balloon
>  show balloon information
>  @item info qtree
> diff --git a/hmp.c b/hmp.c
> index 4c6d4ae..b0440e6 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -131,9 +131,19 @@ void hmp_info_mice(Monitor *mon)
>  void hmp_info_migrate(Monitor *mon)
>  {
>      MigrationInfo *info;
> +    MigrationCapabilityInfoList *cap;
> 
>      info = qmp_query_migrate(NULL);
> 
> +    if (info->has_capabilities && info->capabilities) {
> +        monitor_printf(mon, "capabilities: ");
> +        for (cap = info->capabilities; cap; cap = cap->next) {
> +            monitor_printf(mon, "%s: %s ",
> +                           MigrationCapability_lookup[cap->value->capability],
> +                           cap->value->state ? "on" : "off");
> +        }
> +        monitor_printf(mon, "\n");

Why is this is needed? Isn't info migration-capabilities good enough?
Besides, info migrate should only contain information about current migration
process...

> +    }
>      if (info->has_status) {
>          monitor_printf(mon, "Migration status: %s\n", info->status);
>      }
> @@ -161,6 +171,25 @@ void hmp_info_migrate(Monitor *mon)
>      qapi_free_MigrationInfo(info);
>  }
> 
> +void hmp_info_migration_capabilities(Monitor *mon)
> +{
> +    MigrationCapabilityInfoList *caps_list, *cap;
> +
> +    caps_list = qmp_query_migration_capabilities(NULL);
> +    if (!caps_list) {
> +        monitor_printf(mon, "No migration capabilities found\n");
> +        return;
> +    }
> +
> +    for (cap = caps_list; cap; cap = cap->next) {
> +        monitor_printf(mon, "%s: %s ",
> +                       MigrationCapability_lookup[cap->value->capability],
> +                       cap->value->state ? "on" : "off");
> +    }
> +
> +    qapi_free_MigrationCapabilityInfoList(caps_list);
> +}
> +
>  void hmp_info_cpus(Monitor *mon)
>  {
>      CpuInfoList *cpu_list, *cpu;
> @@ -735,6 +764,41 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
>      qmp_migrate_set_speed(value, NULL);
>  }
> 
> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
> +{
> +    const char *cap = qdict_get_str(qdict, "capability");
> +    bool state = qdict_get_bool(qdict, "state");
> +    Error *err = NULL;
> +    MigrationCapabilityInfoList *params = NULL;
> +    int i;
> +
> +    for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> +        if (strcmp(cap, MigrationCapability_lookup[i]) == 0) {
> +            if (!params) {
> +                params = g_malloc0(sizeof(*params));
> +            }
> +            params->value = g_malloc0(sizeof(*params->value));
> +            params->value->capability = i;
> +            params->value->state = state;
> +            params->next = NULL;
> +            qmp_migrate_set_parameters(params, &err);
> +            break;
> +        }
> +    }
> +
> +    if (i == MIGRATION_CAPABILITY_MAX) {
> +        error_set(&err, QERR_INVALID_PARAMETER, cap);
> +    }
> +
> +    qapi_free_MigrationCapabilityInfoList(params);
> +
> +    if (err) {
> +        monitor_printf(mon, "migrate_set_parameter: %s\n",
> +                       error_get_pretty(err));
> +        error_free(err);
> +    }
> +}
> +
>  void hmp_set_password(Monitor *mon, const QDict *qdict)
>  {
>      const char *protocol  = qdict_get_str(qdict, "protocol");
> diff --git a/hmp.h b/hmp.h
> index 79d138d..09ba198 100644
> --- a/hmp.h
> +++ b/hmp.h
> @@ -25,6 +25,7 @@ void hmp_info_uuid(Monitor *mon);
>  void hmp_info_chardev(Monitor *mon);
>  void hmp_info_mice(Monitor *mon);
>  void hmp_info_migrate(Monitor *mon);
> +void hmp_info_migration_capabilities(Monitor *mon);
>  void hmp_info_cpus(Monitor *mon);
>  void hmp_info_block(Monitor *mon);
>  void hmp_info_blockstats(Monitor *mon);
> @@ -51,6 +52,7 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
>  void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
>  void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
>  void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict);
>  void hmp_set_password(Monitor *mon, const QDict *qdict);
>  void hmp_expire_password(Monitor *mon, const QDict *qdict);
>  void hmp_eject(Monitor *mon, const QDict *qdict);
> diff --git a/migration.c b/migration.c
> index 8db1b43..fd004d7 100644
> --- a/migration.c
> +++ b/migration.c
> @@ -117,12 +117,36 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>  {
>      MigrationInfo *info = g_malloc0(sizeof(*info));
>      MigrationState *s = migrate_get_current();
> +    int i;
> 
>      switch (s->state) {
>      case MIG_STATE_SETUP:
> -        /* no migration has happened ever */
> +        /* no migration has ever happened; show enabled capabilities */
> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> +            if (!info->has_capabilities) {
> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
> +                info->has_capabilities = true;
> +            }
> +            info->capabilities->value =
> +                g_malloc(sizeof(*info->capabilities->value));
> +            info->capabilities->value->capability = i;
> +            info->capabilities->value->state = s->enabled_capabilities[i];
> +            info->capabilities->next = NULL;
> +        }
>          break;
>      case MIG_STATE_ACTIVE:
> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> +            if (!info->has_capabilities) {
> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
> +                info->has_capabilities = true;
> +            }
> +            info->capabilities->value =
> +                g_malloc(sizeof(*info->capabilities->value));
> +            info->capabilities->value->capability = i;
> +            info->capabilities->value->state = s->enabled_capabilities[i];
> +            info->capabilities->next = NULL;
> +        }
> +
>          info->has_status = true;
>          info->status = g_strdup("active");
> 
> @@ -143,6 +167,18 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>          }
>          break;
>      case MIG_STATE_COMPLETED:
> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> +            if (!info->has_capabilities) {
> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
> +                info->has_capabilities = true;
> +            }
> +            info->capabilities->value =
> +                g_malloc(sizeof(*info->capabilities->value));
> +            info->capabilities->value->capability = i;
> +            info->capabilities->value->state = s->enabled_capabilities[i];
> +            info->capabilities->next = NULL;
> +        }

Code triplication :)

Why is this is needed? Isn't query-migration-capabilities good enough?
Besides, query-migrate should only contain information about current migration
process...

> +
>          info->has_status = true;
>          info->status = g_strdup("completed");
> 
> @@ -166,6 +202,33 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>      return info;
>  }
> 
> +MigrationCapabilityInfoList *qmp_query_migration_capabilities(Error **errp)
> +{
> +    MigrationCapabilityInfoList *caps_list = g_malloc0(sizeof(*caps_list));
> +
> +    caps_list->value = g_malloc(sizeof(*caps_list->value));
> +    caps_list->value->capability = MIGRATION_CAPABILITY_XBZRLE;
> +    caps_list->next = NULL;

Shouldn't this get the capabilities array from migrate_get_current()?

I mean, this makes query-migration-capabilities always return true for
xbzrle, even if we set it to off.

> +
> +    return caps_list;
> +}
> +
> +void qmp_migrate_set_parameters(MigrationCapabilityInfoList *params,
> +                                Error **errp)
> +{
> +    MigrationState *s = migrate_get_current();
> +    MigrationCapabilityInfoList *cap;
> +
> +    if (s->state == MIG_STATE_ACTIVE) {
> +        error_set(errp, QERR_MIGRATION_ACTIVE);
> +        return;
> +    }
> +
> +    for (cap = params; cap; cap = cap->next) {
> +        s->enabled_capabilities[cap->value->capability] = cap->value->state;
> +    }
> +}
> +
>  /* shared migration helpers */
> 
>  static int migrate_fd_cleanup(MigrationState *s)
> @@ -375,12 +438,17 @@ static MigrationState *migrate_init(const MigrationParams *params)
>  {
>      MigrationState *s = migrate_get_current();
>      int64_t bandwidth_limit = s->bandwidth_limit;
> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
> +
> +    memcpy(enabled_capabilities, s->enabled_capabilities,
> +           sizeof(enabled_capabilities));
> 
>      memset(s, 0, sizeof(*s));
>      s->bandwidth_limit = bandwidth_limit;
>      s->params = *params;
> +    memcpy(s->enabled_capabilities, enabled_capabilities,
> +           sizeof(enabled_capabilities));
> 
> -    s->bandwidth_limit = bandwidth_limit;
>      s->state = MIG_STATE_SETUP;
>      s->total_time = qemu_get_clock_ms(rt_clock);
> 
> diff --git a/migration.h b/migration.h
> index 57572a6..713aae0 100644
> --- a/migration.h
> +++ b/migration.h
> @@ -19,6 +19,7 @@
>  #include "notify.h"
>  #include "error.h"
>  #include "vmstate.h"
> +#include "qapi-types.h"
> 
>  struct MigrationParams {
>      bool blk;
> @@ -39,6 +40,7 @@ struct MigrationState
>      void *opaque;
>      MigrationParams params;
>      int64_t total_time;
> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
>  };
> 
>  void process_incoming_migration(QEMUFile *f);
> diff --git a/monitor.c b/monitor.c
> index f6107ba..e2be6cd 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -2687,6 +2687,13 @@ static mon_cmd_t info_cmds[] = {
>          .mhandler.info = hmp_info_migrate,
>      },
>      {
> +        .name       = "migration_capabilities",

migration-capabilities is better.

> +        .args_type  = "",
> +        .params     = "",
> +        .help       = "show migration capabilities",
> +        .mhandler.info = hmp_info_migration_capabilities,
> +    },
> +    {
>          .name       = "balloon",
>          .args_type  = "",
>          .params     = "",
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 1ab5dbd..a8408fd 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -288,11 +288,15 @@
>  #        status, only returned if status is 'active' and it is a block
>  #        migration
>  #
> -# Since: 0.14.0
> +# @capabilities: #optional a list describing all the migration capabilities
> +#                state

I don't think this is needed, as I've said above.

> +#
> +# Since: 0.14.0, 'capabilities' since 1.2
>  ##
>  { 'type': 'MigrationInfo',
>    'data': {'*status': 'str', '*ram': 'MigrationStats',
> -           '*disk': 'MigrationStats'} }
> +           '*disk': 'MigrationStats',
> +           '*capabilities': ['MigrationCapabilityInfo']} }
> 
>  ##
>  # @query-migrate
> @@ -306,6 +310,51 @@
>  { 'command': 'query-migrate', 'returns': 'MigrationInfo' }
> 
>  ##
> +# @MigrationCapability
> +#
> +# Migration capabilities enumeration
> +#
> +# @xbzrle: current migration supports xbzrle

You should explain what xbzrle is.

> +#
> +# Since: 1.2
> +##
> +{ 'enum': 'MigrationCapability',
> +  'data': ['xbzrle'] }
> +
> +##
> +# @MigrationCapabilityInfo

MigrationCapabilityStatus?

> +#
> +# Migration capability information
> +#
> +# @capability: capability enum

Please, document state too.

> +#
> +# Since: 1.2
> +##
> +{ 'type': 'MigrationCapabilityInfo',
> +  'data': { 'capability' : 'MigrationCapability', 'state' : 'bool' } }
> +
> +##
> +# @query-migration-capabilities
> +#
> +# Returns information about current migration process capabilties.
> +#
> +# Returns: @MigrationCapabilityInfo list
> +#
> +# Since: 1.2
> +##
> +{ 'command': 'query-migration-capabilities', 'returns': ['MigrationCapabilityInfo'] }
> +
> +##
> +# @migrate_set_parameters
> +#
> +# Enable/Disable the following migration capabilities (like xbzrle)
> +#
> +# Since: 1.2
> +##
> +{ 'command': 'migrate-set-parameters',
> +  'data': { 'capabilities': ['MigrationCapabilityInfo'] } }
> +
> +##
>  # @MouseInfo:
>  #
>  # Information about a mouse device.
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 2e1a38e..3ee6e00 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -2075,18 +2075,31 @@ The main json-object contains the following:
>           - "transferred": amount transferred (json-int)
>           - "remaining": amount remaining (json-int)
>           - "total": total (json-int)
> -
> +- "capabilities": migration capabilities state
> +         - "xbzrle" : XBZRLE state (json-bool)
>  Examples:
> 
>  1. Before the first migration
> 
>  -> { "execute": "query-migrate" }
> -<- { "return": {} }
> +<- { "return": {
> +        "capabilities" :  [ { "capability" : "xbzrle", "state" : false } ]
> +     }
> +   }
> 
>  2. Migration is done and has succeeded
> 
>  -> { "execute": "query-migrate" }
> -<- { "return": { "status": "completed" } }
> +<- { "return": {
> +        "status": "completed",
> +        "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
> +        "ram":{
> +          "transferred":123,
> +          "remaining":123,
> +          "total":246
> +        }
> +     }
> +   }
> 
>  3. Migration is done and has failed
> 
> @@ -2099,6 +2112,7 @@ Examples:
>  <- {
>        "return":{
>           "status":"active",
> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
>           "ram":{
>              "transferred":123,
>              "remaining":123,
> @@ -2113,6 +2127,7 @@ Examples:
>  <- {
>        "return":{
>           "status":"active",
> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
>           "ram":{
>              "total":1057024,
>              "remaining":1053304,
> @@ -2135,6 +2150,56 @@ EQMP
>      },
> 
>  SQMP
> +query-migration-capabilities
> +-------
> +
> +Query migration capabilities
> +
> +- "xbzrle": xbzrle support
> +
> +Arguments:
> +
> +Example:
> +
> +-> { "execute": "query-migration-capabilities"}
> +<- { "return": [ { "capability": "xbzrle", "state": true },
> +                 { "capability": "foobar", "state": false } ] }
> +
> +EQMP
> +
> +    {
> +        .name       = "query-migration-capabilities",
> +        .args_type  = "",
> +	.mhandler.cmd_new = qmp_marshal_input_query_migration_capabilities,
> +    },
> +
> +SQMP
> +migrate_set_parameters
> +-------
> +
> +Enable/Disable migration capabilities
> +
> +- "xbzrle": xbzrle support
> +
> +Arguments:
> +
> +Example:
> +
> +-> { "execute": "migrate_set_parameters" , "arguments":
> +     { "parameters": [ { "capability": "xbzrle", "state": true } ] } }
> +
> +EQMP
> +
> +    {
> +        .name       = "migrate_set_parameters",
> +        .args_type  = "parameters:O",
> +	.params     = "capability:s,state:b",
> +	.mhandler.cmd_new = qmp_marshal_input_migrate_set_parameters,
> +    },
> +
> +
> +
> +SQMP
>  query-balloon
>  -------------
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH 20/22] Add migrate_set_cachesize command
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 20/22] Add migrate_set_cachesize command Juan Quintela
@ 2012-07-23 19:21   ` Luiz Capitulino
  0 siblings, 0 replies; 35+ messages in thread
From: Luiz Capitulino @ 2012-07-23 19:21 UTC (permalink / raw)
  To: Juan Quintela
  Cc: Orit Wasserman, Benoit Hudzia, qemu-devel, Aidan Shribman,
	Petter Svard

On Fri, 13 Jul 2012 09:23:42 +0200
Juan Quintela <quintela@redhat.com> wrote:

> From: Orit Wasserman <owasserm@redhat.com>
> 
> Change XBZRLE cache size in bytes (the size should be a power of 2, it will be
> rounded down to the nearest power of 2).
> If XBZRLE cache size is too small there will be many cache miss.

As far as QMP is concerned:

 Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>

> 
> Signed-off-by: Benoit Hudzia <benoit.hudzia@sap.com>
> Signed-off-by: Petter Svard <petters@cs.umu.se>
> Signed-off-by: Aidan Shribman <aidan.shribman@sap.com>
> Signed-off-by: Orit Wasserman <owasserm@redhat.com>
> Signed-off-by: Juan Quintela <quintela@redhat.com>
> ---
>  arch_init.c      |   10 ++++++++++
>  hmp-commands.hx  |   20 ++++++++++++++++++++
>  hmp.c            |   13 +++++++++++++
>  hmp.h            |    1 +
>  migration.c      |   14 ++++++++++++++
>  migration.h      |    2 ++
>  qapi-schema.json |   16 ++++++++++++++++
>  qmp-commands.hx  |   23 +++++++++++++++++++++++
>  8 files changed, 99 insertions(+)
> 
> diff --git a/arch_init.c b/arch_init.c
> index a3a4707..d972d84 100644
> --- a/arch_init.c
> +++ b/arch_init.c
> @@ -192,6 +192,16 @@ static struct {
>      .cache = NULL,
>  };
> 
> +
> +int64_t xbzrle_cache_resize(int64_t new_size)
> +{
> +    if (XBZRLE.cache != NULL) {
> +        return cache_resize(XBZRLE.cache, new_size / TARGET_PAGE_SIZE) *
> +            TARGET_PAGE_SIZE;
> +    }
> +    return pow2floor(new_size);
> +}
> +
>  static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
>          int cont, int flag)
>  {
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index 9245bef..052a0a3 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -829,6 +829,26 @@ STEXI
>  @item migrate_cancel
>  @findex migrate_cancel
>  Cancel the current VM migration.
> +
> +ETEXI
> +
> +    {
> +        .name       = "migrate_set_cachesize",
> +        .args_type  = "value:o",
> +        .params     = "value",
> +        .help       = "set cache size (in bytes) for XBZRLE migrations,"
> +                      "the cache size will be rounded down to the nearest "
> +                      "power of 2.\n"
> +                      "The cache size effects the number of cache misses."
> +                      "In case of a high cache miss ratio you need to increase"
> +                      " the cache size",
> +        .mhandler.cmd = hmp_migrate_set_cachesize,
> +    },
> +
> +STEXI
> +@item migrate_set_cachesize @var{value}
> +@findex migrate_set_cachesize
> +Set cache size to @var{value} (in bytes) for xbzrle migrations.
>  ETEXI
> 
>      {
> diff --git a/hmp.c b/hmp.c
> index b0440e6..99ad00a 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -758,6 +758,19 @@ void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict)
>      qmp_migrate_set_downtime(value, NULL);
>  }
> 
> +void hmp_migrate_set_cachesize(Monitor *mon, const QDict *qdict)
> +{
> +    int64_t value = qdict_get_int(qdict, "value");
> +    Error *err = NULL;
> +
> +    qmp_migrate_set_cache_size(value, &err);
> +    if (err) {
> +        monitor_printf(mon, "%s\n", error_get_pretty(err));
> +        error_free(err);
> +        return;
> +    }
> +}
> +
>  void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
>  {
>      int64_t value = qdict_get_int(qdict, "value");
> diff --git a/hmp.h b/hmp.h
> index 09ba198..7c5117d 100644
> --- a/hmp.h
> +++ b/hmp.h
> @@ -53,6 +53,7 @@ void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
>  void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
>  void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
>  void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict);
> +void hmp_migrate_set_cachesize(Monitor *mon, const QDict *qdict);
>  void hmp_set_password(Monitor *mon, const QDict *qdict);
>  void hmp_expire_password(Monitor *mon, const QDict *qdict);
>  void hmp_eject(Monitor *mon, const QDict *qdict);
> diff --git a/migration.c b/migration.c
> index 1a264a9..d134bf6 100644
> --- a/migration.c
> +++ b/migration.c
> @@ -533,6 +533,20 @@ void qmp_migrate_cancel(Error **errp)
>      migrate_fd_cancel(migrate_get_current());
>  }
> 
> +void qmp_migrate_set_cache_size(int64_t value, Error **errp)
> +{
> +    MigrationState *s = migrate_get_current();
> +
> +    /* Check for truncation */
> +    if (value != (size_t)value) {
> +        error_set(errp, QERR_INVALID_PARAMETER_VALUE, "cache size",
> +                  "exceeding address space");
> +        return;
> +    }
> +
> +    s->xbzrle_cache_size = xbzrle_cache_resize(value);
> +}
> +
>  void qmp_migrate_set_speed(int64_t value, Error **errp)
>  {
>      MigrationState *s;
> diff --git a/migration.h b/migration.h
> index cdf6787..337e225 100644
> --- a/migration.h
> +++ b/migration.h
> @@ -108,4 +108,6 @@ int xbzrle_decode_buffer(uint8_t *src, int slen, uint8_t *dst, int dlen);
>  int migrate_use_xbzrle(void);
>  int64_t migrate_xbzrle_cache_size(void);
> 
> +int64_t xbzrle_cache_resize(int64_t new_size);
> +
>  #endif
> diff --git a/qapi-schema.json b/qapi-schema.json
> index a8408fd..a0f0f95 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -1390,6 +1390,22 @@
>  { 'command': 'migrate_set_speed', 'data': {'value': 'int'} }
> 
>  ##
> +# @migrate-set-cache-size
> +#
> +# Set XBZRLE cache size
> +#
> +# @value: cache size in bytes
> +#
> +# The size will be rounded down to the nearest power of 2.
> +# The cache size can be modified before and during ongoing migration
> +#
> +# Returns: nothing on success
> +#
> +# Since: 1.2
> +##
> +{ 'command': 'migrate-set-cache-size', 'data': {'value': 'int'} }
> +
> +##
>  # @ObjectPropertyInfo:
>  #
>  # @name: the name of the property
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 3ee6e00..a3d57ce 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -520,6 +520,29 @@ Example:
>  <- { "return": {} }
> 
>  EQMP
> +{
> +        .name       = "migrate-set-cache-size",
> +        .args_type  = "value:o",
> +        .mhandler.cmd_new = qmp_marshal_input_migrate_set_cache_size,
> +    },
> +
> +SQMP
> +migrate-set-cache-size
> +---------------------
> +
> +Set cache size to be used by XBZRLE migration, the cache size will be rounded
> +down to the nearest power of 2
> +
> +Arguments:
> +
> +- "value": cache size in bytes (json-int)
> +
> +Example:
> +
> +-> { "execute": "migrate-set-cache-size", "arguments": { "value": 536870912 } }
> +<- { "return": {} }
> +
> +EQMP
> 
>      {
>          .name       = "migrate_set_speed",

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH 13/22] Add migration capabilities
  2012-07-23 18:23   ` Luiz Capitulino
@ 2012-07-23 19:30     ` Eric Blake
  2012-07-24  6:25     ` Orit Wasserman
  1 sibling, 0 replies; 35+ messages in thread
From: Eric Blake @ 2012-07-23 19:30 UTC (permalink / raw)
  To: Luiz Capitulino; +Cc: Orit Wasserman, qemu-devel, Juan Quintela

[-- Attachment #1: Type: text/plain, Size: 971 bytes --]

On 07/23/2012 12:23 PM, Luiz Capitulino wrote:
> On Fri, 13 Jul 2012 09:23:35 +0200
> Juan Quintela <quintela@redhat.com> wrote:
> 
>> From: Orit Wasserman <owasserm@redhat.com>
>>
>> Add migration capabilities that can be queried by the management.
>> The management can query the source QEMU and the destination QEMU in order to
>> verify both support some migration capability (currently only XBZRLE).
>> The management can enable a capability for the next migration by using
>> migrate_set_parameter command.
> 
> Please, split this into one command per-patch. Otherwise it's difficult to
> review.
> 
> Have libvirt folks acked this approach btw? It looks fine to me, but we need
> their ack too.

Yes, I've been reviewing versions of this series, and am okay with the
libvirt impact with the current proposed set of new QMP commands.

-- 
Eric Blake   eblake@redhat.com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 620 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH 21/22] Add XBZRLE statistics
  2012-07-13  7:23 ` [Qemu-devel] [PATCH 21/22] Add XBZRLE statistics Juan Quintela
@ 2012-07-23 19:33   ` Luiz Capitulino
  2012-07-24  6:32     ` Orit Wasserman
  0 siblings, 1 reply; 35+ messages in thread
From: Luiz Capitulino @ 2012-07-23 19:33 UTC (permalink / raw)
  To: Juan Quintela
  Cc: Orit Wasserman, Benoit Hudzia, qemu-devel, Aidan Shribman,
	Petter Svard

On Fri, 13 Jul 2012 09:23:43 +0200
Juan Quintela <quintela@redhat.com> wrote:

> From: Orit Wasserman <owasserm@redhat.com>
> 
> Signed-off-by: Benoit Hudzia <benoit.hudzia@sap.com>
> Signed-off-by: Petter Svard <petters@cs.umu.se>
> Signed-off-by: Aidan Shribman <aidan.shribman@sap.com>
> Signed-off-by: Orit Wasserman <owasserm@redhat.com>
> Signed-off-by: Juan Quintela <quintela@redhat.com>
> ---
>  arch_init.c      |   66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  hmp.c            |   13 +++++++++++
>  migration.c      |   49 ++++++++++++++++++++++++++++++++++++++++
>  migration.h      |    9 ++++++++
>  qapi-schema.json |   37 +++++++++++++++++++++++++-----
>  qmp-commands.hx  |   35 ++++++++++++++++++++++++++++-
>  6 files changed, 203 insertions(+), 6 deletions(-)
> 
> diff --git a/arch_init.c b/arch_init.c
> index d972d84..ab3fb2c 100644
> --- a/arch_init.c
> +++ b/arch_init.c
> @@ -202,6 +202,64 @@ int64_t xbzrle_cache_resize(int64_t new_size)
>      return pow2floor(new_size);
>  }
> 
> +/* accounting for migration statistics */
> +typedef struct AccountingInfo {
> +    uint64_t dup_pages;
> +    uint64_t norm_pages;
> +    uint64_t xbzrle_bytes;
> +    uint64_t xbzrle_pages;
> +    uint64_t xbzrle_cache_miss;
> +    uint64_t iterations;
> +    uint64_t xbzrle_overflows;
> +} AccountingInfo;
> +
> +static AccountingInfo acct_info;
> +
> +static void acct_clear(void)
> +{
> +    memset(&acct_info, 0, sizeof(acct_info));
> +}
> +
> +uint64_t dup_mig_bytes_transferred(void)
> +{
> +    return acct_info.dup_pages * TARGET_PAGE_SIZE;
> +}
> +
> +uint64_t dup_mig_pages_transferred(void)
> +{
> +    return acct_info.dup_pages;
> +}
> +
> +uint64_t norm_mig_bytes_transferred(void)
> +{
> +    return acct_info.norm_pages * TARGET_PAGE_SIZE;
> +}
> +
> +uint64_t norm_mig_pages_transferred(void)
> +{
> +    return acct_info.norm_pages;
> +}
> +
> +uint64_t xbzrle_mig_bytes_transferred(void)
> +{
> +    return acct_info.xbzrle_bytes;
> +}
> +
> +uint64_t xbzrle_mig_pages_transferred(void)
> +{
> +    return acct_info.xbzrle_pages;
> +}
> +
> +uint64_t xbzrle_mig_pages_cache_miss(void)
> +{
> +    return acct_info.xbzrle_cache_miss;
> +}
> +
> +uint64_t xbzrle_mig_pages_overflow(void)
> +{
> +    return acct_info.xbzrle_overflows;
> +}
> +
>  static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
>          int cont, int flag)
>  {
> @@ -230,6 +288,7 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
>      if (!cache_is_cached(XBZRLE.cache, current_addr)) {
>          cache_insert(XBZRLE.cache, current_addr,
>                       g_memdup(current_data, TARGET_PAGE_SIZE));
> +        acct_info.xbzrle_cache_miss++;
>          return -1;
>      }
> 
> @@ -244,6 +303,7 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
>          return 0;
>      } else if (encoded_len == -1) {
>          DPRINTF("Overflow\n");
> +        acct_info.xbzrle_overflows++;
>          /* update data in the cache */
>          memcpy(prev_cached_page, current_data, TARGET_PAGE_SIZE);
>          return -1;
> @@ -263,7 +323,9 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
>      qemu_put_byte(f, hdr.xh_flags);
>      qemu_put_be16(f, hdr.xh_len);
>      qemu_put_buffer(f, XBZRLE.encoded_buf, encoded_len);
> +    acct_info.xbzrle_pages++;
>      bytes_sent = encoded_len + sizeof(hdr);
> +    acct_info.xbzrle_bytes += bytes_sent;
> 
>      return bytes_sent;
>  }
> @@ -303,6 +365,7 @@ static int ram_save_block(QEMUFile *f)
>              p = memory_region_get_ram_ptr(mr) + offset;
> 
>              if (is_dup_page(p)) {
> +                acct_info.dup_pages++;
>                  save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_COMPRESS);
>                  qemu_put_byte(f, *p);
>                  bytes_sent = 1;
> @@ -318,6 +381,7 @@ static int ram_save_block(QEMUFile *f)
>                  save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
>                  qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
>                  bytes_sent = TARGET_PAGE_SIZE;
> +                acct_info.norm_pages++;
>              }
> 
>              /* if page is unmodified, continue to the next */
> @@ -437,6 +501,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
>              return -1;
>          }
>          XBZRLE.encoded_buf = g_malloc0(TARGET_PAGE_SIZE);
> +        acct_clear();
>      }
> 
>      QLIST_FOREACH(block, &ram_list.blocks, next) {
> @@ -484,6 +549,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
>              break;
>          }
>          bytes_transferred += bytes_sent;
> +        acct_info.iterations++;
>          /* we want to check in the 1st loop, just in case it was the 1st time
>             and we had to sync the dirty bitmap.
>             qemu_get_clock_ns() is a bit expensive, so we only check each some
> diff --git a/hmp.c b/hmp.c
> index 99ad00a..0d7333b 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -168,6 +168,19 @@ void hmp_info_migrate(Monitor *mon)
>                         info->disk->total >> 10);
>      }
> 
> +    if (info->has_cache) {
> +        monitor_printf(mon, "cache size: %" PRIu64 " bytes\n",
> +                       info->cache->cache_size);
> +        monitor_printf(mon, "xbzrle transferred: %" PRIu64 " kbytes\n",
> +                       info->cache->xbzrle_bytes >> 10);
> +        monitor_printf(mon, "xbzrle pages: %" PRIu64 " pages\n",
> +                       info->cache->xbzrle_pages);
> +        monitor_printf(mon, "xbzrle cache miss: %" PRIu64 "\n",
> +                       info->cache->xbzrle_cache_miss);
> +        monitor_printf(mon, "xbzrle overflow : %" PRIu64 "\n",
> +                       info->cache->xbzrle_overflow);
> +    }
> +
>      qapi_free_MigrationInfo(info);
>  }
> 
> diff --git a/migration.c b/migration.c
> index d134bf6..e11f451 100644
> --- a/migration.c
> +++ b/migration.c
> @@ -137,6 +137,17 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>              info->capabilities->value->state = s->enabled_capabilities[i];
>              info->capabilities->next = NULL;
>          }
> +
> +        /* display xbzrle cache size */
> +        if (migrate_use_xbzrle()) {
> +            info->has_cache = true;
> +            info->cache = g_malloc0(sizeof(*info->cache));
> +            info->cache->cache_size = migrate_xbzrle_cache_size();
> +            info->cache->xbzrle_bytes = xbzrle_mig_bytes_transferred();
> +            info->cache->xbzrle_pages = xbzrle_mig_pages_transferred();
> +            info->cache->xbzrle_cache_miss = xbzrle_mig_pages_cache_miss();
> +            info->cache->xbzrle_overflow = xbzrle_mig_pages_overflow();
> +        }

Why do you need this in MIG_STATE_SETUP? I think having it only in
MIG_STATE_ACTIVE is enough (and probably that's what we want).

>          break;
>      case MIG_STATE_ACTIVE:
>          for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> @@ -161,6 +172,8 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>          info->ram->total = ram_bytes_total();
>          info->ram->total_time = qemu_get_clock_ms(rt_clock)
>              - s->total_time;
> +        info->ram->duplicate = dup_mig_pages_transferred();
> +        info->ram->normal = norm_mig_pages_transferred();

Can we do this extension in a different patch?

> 
>          if (blk_mig_active()) {
>              info->has_disk = true;
> @@ -169,6 +182,16 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>              info->disk->remaining = blk_mig_bytes_remaining();
>              info->disk->total = blk_mig_bytes_total();
>          }
> +
> +        if (migrate_use_xbzrle()) {
> +            info->has_cache = true;
> +            info->cache = g_malloc0(sizeof(*info->cache));
> +            info->cache->cache_size = migrate_xbzrle_cache_size();
> +            info->cache->xbzrle_bytes = xbzrle_mig_bytes_transferred();
> +            info->cache->xbzrle_pages = xbzrle_mig_pages_transferred();
> +            info->cache->xbzrle_cache_miss = xbzrle_mig_pages_cache_miss();
> +            info->cache->xbzrle_overflow = xbzrle_mig_pages_overflow();
> +        }
>          break;
>      case MIG_STATE_COMPLETED:
>          for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> @@ -183,6 +206,32 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>              info->capabilities->next = NULL;
>          }
> 
> +        info->has_ram = true;
> +        info->ram = g_malloc0(sizeof(*info->ram));
> +        info->ram->transferred = ram_bytes_transferred();
> +        info->ram->remaining = ram_bytes_remaining();
> +        info->ram->total = ram_bytes_total();
> +        info->ram->duplicate = dup_mig_pages_transferred();
> +        info->ram->normal = norm_mig_pages_transferred();

This hunk is probably bad copy & paste, as this code already exists.

> +
> +        if (blk_mig_active()) {
> +            info->has_disk = true;
> +            info->disk = g_malloc0(sizeof(*info->disk));
> +            info->disk->transferred = blk_mig_bytes_transferred();
> +            info->disk->remaining = blk_mig_bytes_remaining();
> +            info->disk->total = blk_mig_bytes_total();
> +        }

Why are you doing this, bad copy & paste too?

> +
> +        if (migrate_use_xbzrle()) {
> +            info->has_cache = true;
> +            info->cache = g_malloc0(sizeof(*info->cache));
> +            info->cache->cache_size = migrate_xbzrle_cache_size();
> +            info->cache->xbzrle_bytes = xbzrle_mig_bytes_transferred();
> +            info->cache->xbzrle_pages = xbzrle_mig_pages_transferred();
> +            info->cache->xbzrle_cache_miss = xbzrle_mig_pages_cache_miss();
> +            info->cache->xbzrle_overflow = xbzrle_mig_pages_overflow();
> +        }
> +
>          info->has_status = true;
>          info->status = g_strdup("completed");
> 
> diff --git a/migration.h b/migration.h
> index 337e225..a9852fc 100644
> --- a/migration.h
> +++ b/migration.h
> @@ -87,6 +87,15 @@ uint64_t ram_bytes_total(void);
> 
>  extern SaveVMHandlers savevm_ram_handlers;
> 
> +uint64_t dup_mig_bytes_transferred(void);
> +uint64_t dup_mig_pages_transferred(void);
> +uint64_t norm_mig_bytes_transferred(void);
> +uint64_t norm_mig_pages_transferred(void);
> +uint64_t xbzrle_mig_bytes_transferred(void);
> +uint64_t xbzrle_mig_pages_transferred(void);
> +uint64_t xbzrle_mig_pages_overflow(void);
> +uint64_t xbzrle_mig_pages_cache_miss(void);
> +
>  /**
>   * @migrate_add_blocker - prevent migration from proceeding
>   *
> diff --git a/qapi-schema.json b/qapi-schema.json
> index a0f0f95..961810b 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -264,11 +264,35 @@
>  #        migration has ended, it returns the total migration
>  #        time. (since 1.2)
>  #
> -# Since: 0.14.0.
> +# @duplicate: #optional, number of duplicate pages
> +#
> +# @normal : #optional, number of normal pages
> +#
> +# Since: 0.14.0, 'duplicate' and 'normal' since 1.2

Please, put 'since' in the same line of the field, like this:

 @normal : #optional, number of normal pages (since 1.2)

>  ##
>  { 'type': 'MigrationStats',
>    'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' ,
> -           'total_time': 'int' } }
> +           'total_time': 'int', '*duplicate': 'int', '*normal': 'int' } }
> +##
> +# @CacheStats

This is specific to xbzrle, right? So this should be called XBZRLECacheStats,
or something.

> +#
> +# Detailed XBZRLE migration cache statistics
> +#
> +# @cache_size: XBZRLE cache size
> +#
> +# @xbzrle_bytes: amount of bytes already transferred to the target VM
> +#
> +# @xbzrle_pages: amount of pages transferred to the target VM
> +#
> +# @xbzrle_cache_miss: number of cache miss
> +#
> +# @xbzrle_overflow: number of overflows
> +#
> +# Since: 1.2
> +##
> +{ 'type': 'CacheStats',
> +  'data': {'cache-size': 'int', 'xbzrle-bytes': 'int', 'xbzrle-pages': 'int',
> +           'xbzrle-cache-miss': 'int', 'xbzrle-overflow': 'int' } }
> 
>  ##
>  # @MigrationInfo
> @@ -291,13 +315,16 @@
>  # @capabilities: #optional a list describing all the migration capabilities
>  #                state
>  #
> -# Since: 0.14.0, 'capabilities' since 1.2
> +# @cache: #optional @MigrationStats containing detailed XBZRLE migration
> +#         statistics

xbzrle-cache.

> +#
> +# Since: 0.14.0, 'capabilities' and 'cache' since 1.2
>  ##
>  { 'type': 'MigrationInfo',
>    'data': {'*status': 'str', '*ram': 'MigrationStats',
>             '*disk': 'MigrationStats',
> -           '*capabilities': ['MigrationCapabilityInfo']} }
> -
> +           '*capabilities': ['MigrationCapabilityInfo'],
> +           '*cache': 'CacheStats'} }
>  ##
>  # @query-migrate
>  #
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index a3d57ce..34d4675 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -2093,6 +2093,8 @@ The main json-object contains the following:
>           - "transferred": amount transferred (json-int)
>           - "remaining": amount remaining (json-int)
>           - "total": total (json-int)
> +	 - "duplicate": number of duplicated pages (json-int)
> +	 - "normal" : number of normal pages transferred (json-int)
>  - "disk": only present if "status" is "active" and it is a block migration,
>    it is a json-object with the following disk information (in bytes):
>           - "transferred": amount transferred (json-int)
> @@ -2100,10 +2102,17 @@ The main json-object contains the following:
>           - "total": total (json-int)
>  - "capabilities": migration capabilities state
>           - "xbzrle" : XBZRLE state (json-bool)
> +- "cache": only present if "status" and XBZRLE is active.
> +  It is a json-object with the following XBZRLE information:
> +         - "cache-size": XBZRLE cache size
> +         - "xbzrle-bytes": total XBZRLE bytes transferred
> +         - "xbzrle-pages": number of XBZRLE compressed pages
> +         - "cache-miss": number of cache misses
> +         - "overflow": number of XBZRLE overflows
> +
>  Examples:
> 
>  1. Before the first migration
> -
>  -> { "execute": "query-migrate" }
>  <- { "return": {
>          "capabilities" :  [ { "capability" : "xbzrle", "state" : false } ]
> @@ -2164,6 +2173,30 @@ Examples:
>        }
>     }
> 
> +6. Migration is being performed and XBZRLE is active:
> +
> +-> { "execute": "query-migrate" }
> +<- {
> +      "return":{
> +         "status":"active",
> +         "capabilities" : [ { "capability": "xbzrle", "state" : true } ],
> +         "ram":{
> +            "total":1057024,
> +            "remaining":1053304,
> +            "transferred":3720,
> +            "duplicate": 10,
> +            "normal" : 3333
> +         },
> +         "cache":{
> +            "cache-size": 1024
> +            "xbzrle-transferred":20971520,
> +            "xbzrle-pages":2444343,
> +            "xbzrle-cache-miss":2244,
> +            "xbzrle-overflow":34434
> +         }
> +      }
> +   }
> +
>  EQMP
> 
>      {

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH 13/22] Add migration capabilities
  2012-07-23 18:23   ` Luiz Capitulino
  2012-07-23 19:30     ` Eric Blake
@ 2012-07-24  6:25     ` Orit Wasserman
  2012-07-24 12:50       ` Luiz Capitulino
  1 sibling, 1 reply; 35+ messages in thread
From: Orit Wasserman @ 2012-07-24  6:25 UTC (permalink / raw)
  To: Luiz Capitulino; +Cc: Eric Blake, qemu-devel, Juan Quintela

On 07/23/2012 09:23 PM, Luiz Capitulino wrote:
> On Fri, 13 Jul 2012 09:23:35 +0200
> Juan Quintela <quintela@redhat.com> wrote:
> 
>> From: Orit Wasserman <owasserm@redhat.com>
>>
>> Add migration capabilities that can be queried by the management.
>> The management can query the source QEMU and the destination QEMU in order to
>> verify both support some migration capability (currently only XBZRLE).
>> The management can enable a capability for the next migration by using
>> migrate_set_parameter command.
> 
> Please, split this into one command per-patch. Otherwise it's difficult to
> review.
> 
Sure.
> Have libvirt folks acked this approach btw? It looks fine to me, but we need
> their ack too.
> 
> More comments below.
> 
>>
>> Signed-off-by: Orit Wasserman <owasserm@redhat.com>
>> Signed-off-by: Juan Quintela <quintela@redhat.com>
>> ---
>>  hmp-commands.hx  |   16 ++++++++++++
>>  hmp.c            |   64 ++++++++++++++++++++++++++++++++++++++++++++++++
>>  hmp.h            |    2 ++
>>  migration.c      |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
>>  migration.h      |    2 ++
>>  monitor.c        |    7 ++++++
>>  qapi-schema.json |   53 ++++++++++++++++++++++++++++++++++++++--
>>  qmp-commands.hx  |   71 ++++++++++++++++++++++++++++++++++++++++++++++++++---
>>  8 files changed, 280 insertions(+), 7 deletions(-)
>>
>> diff --git a/hmp-commands.hx b/hmp-commands.hx
>> index f5d9d91..9245bef 100644
>> --- a/hmp-commands.hx
>> +++ b/hmp-commands.hx
>> @@ -861,6 +861,20 @@ Set maximum tolerated downtime (in seconds) for migration.
>>  ETEXI
>>
>>      {
>> +        .name       = "migrate_set_parameter",
>> +        .args_type  = "capability:s,state:b",
>> +        .params     = "",
> 
> Please, fill in params.
ok
> 
>> +        .help       = "Enable/Disable the usage of a capability for migration",
>> +        .mhandler.cmd = hmp_migrate_set_parameter,
>> +    },
>> +
>> +STEXI
>> +@item migrate_set_parameter @var{capability} @var{state}
>> +@findex migrate_set_parameter
>> +Enable/Disable the usage of a capability @var{capability} for migration.
>> +ETEXI
>> +
>> +    {
>>          .name       = "client_migrate_info",
>>          .args_type  = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
>>          .params     = "protocol hostname port tls-port cert-subject",
>> @@ -1419,6 +1433,8 @@ show CPU statistics
>>  show user network stack connection states
>>  @item info migrate
>>  show migration status
>> +@item info migration_capabilities
>> +show migration capabilities
>>  @item info balloon
>>  show balloon information
>>  @item info qtree
>> diff --git a/hmp.c b/hmp.c
>> index 4c6d4ae..b0440e6 100644
>> --- a/hmp.c
>> +++ b/hmp.c
>> @@ -131,9 +131,19 @@ void hmp_info_mice(Monitor *mon)
>>  void hmp_info_migrate(Monitor *mon)
>>  {
>>      MigrationInfo *info;
>> +    MigrationCapabilityInfoList *cap;
>>
>>      info = qmp_query_migrate(NULL);
>>
>> +    if (info->has_capabilities && info->capabilities) {
>> +        monitor_printf(mon, "capabilities: ");
>> +        for (cap = info->capabilities; cap; cap = cap->next) {
>> +            monitor_printf(mon, "%s: %s ",
>> +                           MigrationCapability_lookup[cap->value->capability],
>> +                           cap->value->state ? "on" : "off");
>> +        }
>> +        monitor_printf(mon, "\n");
> 
> Why is this is needed? Isn't info migration-capabilities good enough?
> Besides, info migrate should only contain information about current migration
> process...
The reason we introduced capabilities is that xbzrle needs for both source and destination QEMU
to be able to handle it. Even if both side support xbzrle the user may decide not to use it.
User that wants to use xbzrle needs to check that both sides have support for it (using info capabilities) , than 
enable it in both sides (using migrate-set-parameter/s commands). This is a parameter for the current migration.
So the user needs to know if xbzrle was enabled or disabled for the current migration, this code displays it.

some day when there will be better migration protocol, feature negotiations can be part of it ...
> 
>> +    }
>>      if (info->has_status) {
>>          monitor_printf(mon, "Migration status: %s\n", info->status);
>>      }
>> @@ -161,6 +171,25 @@ void hmp_info_migrate(Monitor *mon)
>>      qapi_free_MigrationInfo(info);
>>  }
>>
>> +void hmp_info_migration_capabilities(Monitor *mon)
>> +{
>> +    MigrationCapabilityInfoList *caps_list, *cap;
>> +
>> +    caps_list = qmp_query_migration_capabilities(NULL);
>> +    if (!caps_list) {
>> +        monitor_printf(mon, "No migration capabilities found\n");
>> +        return;
>> +    }
>> +
>> +    for (cap = caps_list; cap; cap = cap->next) {
>> +        monitor_printf(mon, "%s: %s ",
>> +                       MigrationCapability_lookup[cap->value->capability],
>> +                       cap->value->state ? "on" : "off");
>> +    }
>> +
>> +    qapi_free_MigrationCapabilityInfoList(caps_list);
>> +}
>> +
>>  void hmp_info_cpus(Monitor *mon)
>>  {
>>      CpuInfoList *cpu_list, *cpu;
>> @@ -735,6 +764,41 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
>>      qmp_migrate_set_speed(value, NULL);
>>  }
>>
>> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
>> +{
>> +    const char *cap = qdict_get_str(qdict, "capability");
>> +    bool state = qdict_get_bool(qdict, "state");
>> +    Error *err = NULL;
>> +    MigrationCapabilityInfoList *params = NULL;
>> +    int i;
>> +
>> +    for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>> +        if (strcmp(cap, MigrationCapability_lookup[i]) == 0) {
>> +            if (!params) {
>> +                params = g_malloc0(sizeof(*params));
>> +            }
>> +            params->value = g_malloc0(sizeof(*params->value));
>> +            params->value->capability = i;
>> +            params->value->state = state;
>> +            params->next = NULL;
>> +            qmp_migrate_set_parameters(params, &err);
>> +            break;
>> +        }
>> +    }
>> +
>> +    if (i == MIGRATION_CAPABILITY_MAX) {
>> +        error_set(&err, QERR_INVALID_PARAMETER, cap);
>> +    }
>> +
>> +    qapi_free_MigrationCapabilityInfoList(params);
>> +
>> +    if (err) {
>> +        monitor_printf(mon, "migrate_set_parameter: %s\n",
>> +                       error_get_pretty(err));
>> +        error_free(err);
>> +    }
>> +}
>> +
>>  void hmp_set_password(Monitor *mon, const QDict *qdict)
>>  {
>>      const char *protocol  = qdict_get_str(qdict, "protocol");
>> diff --git a/hmp.h b/hmp.h
>> index 79d138d..09ba198 100644
>> --- a/hmp.h
>> +++ b/hmp.h
>> @@ -25,6 +25,7 @@ void hmp_info_uuid(Monitor *mon);
>>  void hmp_info_chardev(Monitor *mon);
>>  void hmp_info_mice(Monitor *mon);
>>  void hmp_info_migrate(Monitor *mon);
>> +void hmp_info_migration_capabilities(Monitor *mon);
>>  void hmp_info_cpus(Monitor *mon);
>>  void hmp_info_block(Monitor *mon);
>>  void hmp_info_blockstats(Monitor *mon);
>> @@ -51,6 +52,7 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
>>  void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
>>  void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
>>  void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
>> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict);
>>  void hmp_set_password(Monitor *mon, const QDict *qdict);
>>  void hmp_expire_password(Monitor *mon, const QDict *qdict);
>>  void hmp_eject(Monitor *mon, const QDict *qdict);
>> diff --git a/migration.c b/migration.c
>> index 8db1b43..fd004d7 100644
>> --- a/migration.c
>> +++ b/migration.c
>> @@ -117,12 +117,36 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>  {
>>      MigrationInfo *info = g_malloc0(sizeof(*info));
>>      MigrationState *s = migrate_get_current();
>> +    int i;
>>
>>      switch (s->state) {
>>      case MIG_STATE_SETUP:
>> -        /* no migration has happened ever */
>> +        /* no migration has ever happened; show enabled capabilities */
>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>> +            if (!info->has_capabilities) {
>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
>> +                info->has_capabilities = true;
>> +            }
>> +            info->capabilities->value =
>> +                g_malloc(sizeof(*info->capabilities->value));
>> +            info->capabilities->value->capability = i;
>> +            info->capabilities->value->state = s->enabled_capabilities[i];
>> +            info->capabilities->next = NULL;
>> +        }
>>          break;
>>      case MIG_STATE_ACTIVE:
>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>> +            if (!info->has_capabilities) {
>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
>> +                info->has_capabilities = true;
>> +            }
>> +            info->capabilities->value =
>> +                g_malloc(sizeof(*info->capabilities->value));
>> +            info->capabilities->value->capability = i;
>> +            info->capabilities->value->state = s->enabled_capabilities[i];
>> +            info->capabilities->next = NULL;
>> +        }
>> +
>>          info->has_status = true;
>>          info->status = g_strdup("active");
>>
>> @@ -143,6 +167,18 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>          }
>>          break;
>>      case MIG_STATE_COMPLETED:
>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>> +            if (!info->has_capabilities) {
>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
>> +                info->has_capabilities = true;
>> +            }
>> +            info->capabilities->value =
>> +                g_malloc(sizeof(*info->capabilities->value));
>> +            info->capabilities->value->capability = i;
>> +            info->capabilities->value->state = s->enabled_capabilities[i];
>> +            info->capabilities->next = NULL;
>> +        }
> 
> Code triplication :)
> 
> Why is this is needed? Isn't query-migration-capabilities good enough?
> Besides, query-migrate should only contain information about current migration
> process...
> 
see above
>> +
>>          info->has_status = true;
>>          info->status = g_strdup("completed");
>>
>> @@ -166,6 +202,33 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>      return info;
>>  }
>>
>> +MigrationCapabilityInfoList *qmp_query_migration_capabilities(Error **errp)
>> +{
>> +    MigrationCapabilityInfoList *caps_list = g_malloc0(sizeof(*caps_list));
>> +
>> +    caps_list->value = g_malloc(sizeof(*caps_list->value));
>> +    caps_list->value->capability = MIGRATION_CAPABILITY_XBZRLE;
>> +    caps_list->next = NULL;
> 
> Shouldn't this get the capabilities array from migrate_get_current()?
> 
> I mean, this makes query-migration-capabilities always return true for
> xbzrle, even if we set it to off.
Those are the general capabilities , if I want to use xbzrle ,does this QEMU support it ? 
this is required because we need both the destination and the source to be able to handle it.
If one QEMU doesn't (older version of qemu, maybe it will be disbaled for certain architectures) we can't use the feature.
This is for management and the user to check what migration capabilities this QEMU supports.

> 
>> +
>> +    return caps_list;
>> +}
>> +
>> +void qmp_migrate_set_parameters(MigrationCapabilityInfoList *params,
>> +                                Error **errp)
>> +{
>> +    MigrationState *s = migrate_get_current();
>> +    MigrationCapabilityInfoList *cap;
>> +
>> +    if (s->state == MIG_STATE_ACTIVE) {
>> +        error_set(errp, QERR_MIGRATION_ACTIVE);
>> +        return;
>> +    }
>> +
>> +    for (cap = params; cap; cap = cap->next) {
>> +        s->enabled_capabilities[cap->value->capability] = cap->value->state;
>> +    }
>> +}
>> +
>>  /* shared migration helpers */
>>
>>  static int migrate_fd_cleanup(MigrationState *s)
>> @@ -375,12 +438,17 @@ static MigrationState *migrate_init(const MigrationParams *params)
>>  {
>>      MigrationState *s = migrate_get_current();
>>      int64_t bandwidth_limit = s->bandwidth_limit;
>> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
>> +
>> +    memcpy(enabled_capabilities, s->enabled_capabilities,
>> +           sizeof(enabled_capabilities));
>>
>>      memset(s, 0, sizeof(*s));
>>      s->bandwidth_limit = bandwidth_limit;
>>      s->params = *params;
>> +    memcpy(s->enabled_capabilities, enabled_capabilities,
>> +           sizeof(enabled_capabilities));
>>
>> -    s->bandwidth_limit = bandwidth_limit;
>>      s->state = MIG_STATE_SETUP;
>>      s->total_time = qemu_get_clock_ms(rt_clock);
>>
>> diff --git a/migration.h b/migration.h
>> index 57572a6..713aae0 100644
>> --- a/migration.h
>> +++ b/migration.h
>> @@ -19,6 +19,7 @@
>>  #include "notify.h"
>>  #include "error.h"
>>  #include "vmstate.h"
>> +#include "qapi-types.h"
>>
>>  struct MigrationParams {
>>      bool blk;
>> @@ -39,6 +40,7 @@ struct MigrationState
>>      void *opaque;
>>      MigrationParams params;
>>      int64_t total_time;
>> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
>>  };
>>
>>  void process_incoming_migration(QEMUFile *f);
>> diff --git a/monitor.c b/monitor.c
>> index f6107ba..e2be6cd 100644
>> --- a/monitor.c
>> +++ b/monitor.c
>> @@ -2687,6 +2687,13 @@ static mon_cmd_t info_cmds[] = {
>>          .mhandler.info = hmp_info_migrate,
>>      },
>>      {
>> +        .name       = "migration_capabilities",
> 
> migration-capabilities is better.
ok
> 
>> +        .args_type  = "",
>> +        .params     = "",
>> +        .help       = "show migration capabilities",
>> +        .mhandler.info = hmp_info_migration_capabilities,
>> +    },
>> +    {
>>          .name       = "balloon",
>>          .args_type  = "",
>>          .params     = "",
>> diff --git a/qapi-schema.json b/qapi-schema.json
>> index 1ab5dbd..a8408fd 100644
>> --- a/qapi-schema.json
>> +++ b/qapi-schema.json
>> @@ -288,11 +288,15 @@
>>  #        status, only returned if status is 'active' and it is a block
>>  #        migration
>>  #
>> -# Since: 0.14.0
>> +# @capabilities: #optional a list describing all the migration capabilities
>> +#                state
> 
> I don't think this is needed, as I've said above.
see above
> 
>> +#
>> +# Since: 0.14.0, 'capabilities' since 1.2
>>  ##
>>  { 'type': 'MigrationInfo',
>>    'data': {'*status': 'str', '*ram': 'MigrationStats',
>> -           '*disk': 'MigrationStats'} }
>> +           '*disk': 'MigrationStats',
>> +           '*capabilities': ['MigrationCapabilityInfo']} }
>>
>>  ##
>>  # @query-migrate
>> @@ -306,6 +310,51 @@
>>  { 'command': 'query-migrate', 'returns': 'MigrationInfo' }
>>
>>  ##
>> +# @MigrationCapability
>> +#
>> +# Migration capabilities enumeration
>> +#
>> +# @xbzrle: current migration supports xbzrle
> 
> You should explain what xbzrle is.
ok
> 
>> +#
>> +# Since: 1.2
>> +##
>> +{ 'enum': 'MigrationCapability',
>> +  'data': ['xbzrle'] }
>> +
>> +##
>> +# @MigrationCapabilityInfo
> 
> MigrationCapabilityStatus?
> 
>> +#
>> +# Migration capability information
>> +#
>> +# @capability: capability enum
> 
> Please, document state too.
ok

Orit
> 
>> +#
>> +# Since: 1.2
>> +##
>> +{ 'type': 'MigrationCapabilityInfo',
>> +  'data': { 'capability' : 'MigrationCapability', 'state' : 'bool' } }
>> +
>> +##
>> +# @query-migration-capabilities
>> +#
>> +# Returns information about current migration process capabilties.
>> +#
>> +# Returns: @MigrationCapabilityInfo list
>> +#
>> +# Since: 1.2
>> +##
>> +{ 'command': 'query-migration-capabilities', 'returns': ['MigrationCapabilityInfo'] }
>> +
>> +##
>> +# @migrate_set_parameters
>> +#
>> +# Enable/Disable the following migration capabilities (like xbzrle)
>> +#
>> +# Since: 1.2
>> +##
>> +{ 'command': 'migrate-set-parameters',
>> +  'data': { 'capabilities': ['MigrationCapabilityInfo'] } }
>> +
>> +##
>>  # @MouseInfo:
>>  #
>>  # Information about a mouse device.
>> diff --git a/qmp-commands.hx b/qmp-commands.hx
>> index 2e1a38e..3ee6e00 100644
>> --- a/qmp-commands.hx
>> +++ b/qmp-commands.hx
>> @@ -2075,18 +2075,31 @@ The main json-object contains the following:
>>           - "transferred": amount transferred (json-int)
>>           - "remaining": amount remaining (json-int)
>>           - "total": total (json-int)
>> -
>> +- "capabilities": migration capabilities state
>> +         - "xbzrle" : XBZRLE state (json-bool)
>>  Examples:
>>
>>  1. Before the first migration
>>
>>  -> { "execute": "query-migrate" }
>> -<- { "return": {} }
>> +<- { "return": {
>> +        "capabilities" :  [ { "capability" : "xbzrle", "state" : false } ]
>> +     }
>> +   }
>>
>>  2. Migration is done and has succeeded
>>
>>  -> { "execute": "query-migrate" }
>> -<- { "return": { "status": "completed" } }
>> +<- { "return": {
>> +        "status": "completed",
>> +        "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
>> +        "ram":{
>> +          "transferred":123,
>> +          "remaining":123,
>> +          "total":246
>> +        }
>> +     }
>> +   }
>>
>>  3. Migration is done and has failed
>>
>> @@ -2099,6 +2112,7 @@ Examples:
>>  <- {
>>        "return":{
>>           "status":"active",
>> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
>>           "ram":{
>>              "transferred":123,
>>              "remaining":123,
>> @@ -2113,6 +2127,7 @@ Examples:
>>  <- {
>>        "return":{
>>           "status":"active",
>> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
>>           "ram":{
>>              "total":1057024,
>>              "remaining":1053304,
>> @@ -2135,6 +2150,56 @@ EQMP
>>      },
>>
>>  SQMP
>> +query-migration-capabilities
>> +-------
>> +
>> +Query migration capabilities
>> +
>> +- "xbzrle": xbzrle support
>> +
>> +Arguments:
>> +
>> +Example:
>> +
>> +-> { "execute": "query-migration-capabilities"}
>> +<- { "return": [ { "capability": "xbzrle", "state": true },
>> +                 { "capability": "foobar", "state": false } ] }
>> +
>> +EQMP
>> +
>> +    {
>> +        .name       = "query-migration-capabilities",
>> +        .args_type  = "",
>> +	.mhandler.cmd_new = qmp_marshal_input_query_migration_capabilities,
>> +    },
>> +
>> +SQMP
>> +migrate_set_parameters
>> +-------
>> +
>> +Enable/Disable migration capabilities
>> +
>> +- "xbzrle": xbzrle support
>> +
>> +Arguments:
>> +
>> +Example:
>> +
>> +-> { "execute": "migrate_set_parameters" , "arguments":
>> +     { "parameters": [ { "capability": "xbzrle", "state": true } ] } }
>> +
>> +EQMP
>> +
>> +    {
>> +        .name       = "migrate_set_parameters",
>> +        .args_type  = "parameters:O",
>> +	.params     = "capability:s,state:b",
>> +	.mhandler.cmd_new = qmp_marshal_input_migrate_set_parameters,
>> +    },
>> +
>> +
>> +
>> +SQMP
>>  query-balloon
>>  -------------
>>
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH 21/22] Add XBZRLE statistics
  2012-07-23 19:33   ` Luiz Capitulino
@ 2012-07-24  6:32     ` Orit Wasserman
  0 siblings, 0 replies; 35+ messages in thread
From: Orit Wasserman @ 2012-07-24  6:32 UTC (permalink / raw)
  To: Luiz Capitulino
  Cc: Petter Svard, Aidan Shribman, Benoit Hudzia, qemu-devel,
	Juan Quintela

On 07/23/2012 10:33 PM, Luiz Capitulino wrote:
> On Fri, 13 Jul 2012 09:23:43 +0200
> Juan Quintela <quintela@redhat.com> wrote:
> 
>> From: Orit Wasserman <owasserm@redhat.com>
>>
>> Signed-off-by: Benoit Hudzia <benoit.hudzia@sap.com>
>> Signed-off-by: Petter Svard <petters@cs.umu.se>
>> Signed-off-by: Aidan Shribman <aidan.shribman@sap.com>
>> Signed-off-by: Orit Wasserman <owasserm@redhat.com>
>> Signed-off-by: Juan Quintela <quintela@redhat.com>
>> ---
>>  arch_init.c      |   66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  hmp.c            |   13 +++++++++++
>>  migration.c      |   49 ++++++++++++++++++++++++++++++++++++++++
>>  migration.h      |    9 ++++++++
>>  qapi-schema.json |   37 +++++++++++++++++++++++++-----
>>  qmp-commands.hx  |   35 ++++++++++++++++++++++++++++-
>>  6 files changed, 203 insertions(+), 6 deletions(-)
>>
>> diff --git a/arch_init.c b/arch_init.c
>> index d972d84..ab3fb2c 100644
>> --- a/arch_init.c
>> +++ b/arch_init.c
>> @@ -202,6 +202,64 @@ int64_t xbzrle_cache_resize(int64_t new_size)
>>      return pow2floor(new_size);
>>  }
>>
>> +/* accounting for migration statistics */
>> +typedef struct AccountingInfo {
>> +    uint64_t dup_pages;
>> +    uint64_t norm_pages;
>> +    uint64_t xbzrle_bytes;
>> +    uint64_t xbzrle_pages;
>> +    uint64_t xbzrle_cache_miss;
>> +    uint64_t iterations;
>> +    uint64_t xbzrle_overflows;
>> +} AccountingInfo;
>> +
>> +static AccountingInfo acct_info;
>> +
>> +static void acct_clear(void)
>> +{
>> +    memset(&acct_info, 0, sizeof(acct_info));
>> +}
>> +
>> +uint64_t dup_mig_bytes_transferred(void)
>> +{
>> +    return acct_info.dup_pages * TARGET_PAGE_SIZE;
>> +}
>> +
>> +uint64_t dup_mig_pages_transferred(void)
>> +{
>> +    return acct_info.dup_pages;
>> +}
>> +
>> +uint64_t norm_mig_bytes_transferred(void)
>> +{
>> +    return acct_info.norm_pages * TARGET_PAGE_SIZE;
>> +}
>> +
>> +uint64_t norm_mig_pages_transferred(void)
>> +{
>> +    return acct_info.norm_pages;
>> +}
>> +
>> +uint64_t xbzrle_mig_bytes_transferred(void)
>> +{
>> +    return acct_info.xbzrle_bytes;
>> +}
>> +
>> +uint64_t xbzrle_mig_pages_transferred(void)
>> +{
>> +    return acct_info.xbzrle_pages;
>> +}
>> +
>> +uint64_t xbzrle_mig_pages_cache_miss(void)
>> +{
>> +    return acct_info.xbzrle_cache_miss;
>> +}
>> +
>> +uint64_t xbzrle_mig_pages_overflow(void)
>> +{
>> +    return acct_info.xbzrle_overflows;
>> +}
>> +
>>  static void save_block_hdr(QEMUFile *f, RAMBlock *block, ram_addr_t offset,
>>          int cont, int flag)
>>  {
>> @@ -230,6 +288,7 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
>>      if (!cache_is_cached(XBZRLE.cache, current_addr)) {
>>          cache_insert(XBZRLE.cache, current_addr,
>>                       g_memdup(current_data, TARGET_PAGE_SIZE));
>> +        acct_info.xbzrle_cache_miss++;
>>          return -1;
>>      }
>>
>> @@ -244,6 +303,7 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
>>          return 0;
>>      } else if (encoded_len == -1) {
>>          DPRINTF("Overflow\n");
>> +        acct_info.xbzrle_overflows++;
>>          /* update data in the cache */
>>          memcpy(prev_cached_page, current_data, TARGET_PAGE_SIZE);
>>          return -1;
>> @@ -263,7 +323,9 @@ static int save_xbzrle_page(QEMUFile *f, uint8_t *current_data,
>>      qemu_put_byte(f, hdr.xh_flags);
>>      qemu_put_be16(f, hdr.xh_len);
>>      qemu_put_buffer(f, XBZRLE.encoded_buf, encoded_len);
>> +    acct_info.xbzrle_pages++;
>>      bytes_sent = encoded_len + sizeof(hdr);
>> +    acct_info.xbzrle_bytes += bytes_sent;
>>
>>      return bytes_sent;
>>  }
>> @@ -303,6 +365,7 @@ static int ram_save_block(QEMUFile *f)
>>              p = memory_region_get_ram_ptr(mr) + offset;
>>
>>              if (is_dup_page(p)) {
>> +                acct_info.dup_pages++;
>>                  save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_COMPRESS);
>>                  qemu_put_byte(f, *p);
>>                  bytes_sent = 1;
>> @@ -318,6 +381,7 @@ static int ram_save_block(QEMUFile *f)
>>                  save_block_hdr(f, block, offset, cont, RAM_SAVE_FLAG_PAGE);
>>                  qemu_put_buffer(f, p, TARGET_PAGE_SIZE);
>>                  bytes_sent = TARGET_PAGE_SIZE;
>> +                acct_info.norm_pages++;
>>              }
>>
>>              /* if page is unmodified, continue to the next */
>> @@ -437,6 +501,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
>>              return -1;
>>          }
>>          XBZRLE.encoded_buf = g_malloc0(TARGET_PAGE_SIZE);
>> +        acct_clear();
>>      }
>>
>>      QLIST_FOREACH(block, &ram_list.blocks, next) {
>> @@ -484,6 +549,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque)
>>              break;
>>          }
>>          bytes_transferred += bytes_sent;
>> +        acct_info.iterations++;
>>          /* we want to check in the 1st loop, just in case it was the 1st time
>>             and we had to sync the dirty bitmap.
>>             qemu_get_clock_ns() is a bit expensive, so we only check each some
>> diff --git a/hmp.c b/hmp.c
>> index 99ad00a..0d7333b 100644
>> --- a/hmp.c
>> +++ b/hmp.c
>> @@ -168,6 +168,19 @@ void hmp_info_migrate(Monitor *mon)
>>                         info->disk->total >> 10);
>>      }
>>
>> +    if (info->has_cache) {
>> +        monitor_printf(mon, "cache size: %" PRIu64 " bytes\n",
>> +                       info->cache->cache_size);
>> +        monitor_printf(mon, "xbzrle transferred: %" PRIu64 " kbytes\n",
>> +                       info->cache->xbzrle_bytes >> 10);
>> +        monitor_printf(mon, "xbzrle pages: %" PRIu64 " pages\n",
>> +                       info->cache->xbzrle_pages);
>> +        monitor_printf(mon, "xbzrle cache miss: %" PRIu64 "\n",
>> +                       info->cache->xbzrle_cache_miss);
>> +        monitor_printf(mon, "xbzrle overflow : %" PRIu64 "\n",
>> +                       info->cache->xbzrle_overflow);
>> +    }
>> +
>>      qapi_free_MigrationInfo(info);
>>  }
>>
>> diff --git a/migration.c b/migration.c
>> index d134bf6..e11f451 100644
>> --- a/migration.c
>> +++ b/migration.c
>> @@ -137,6 +137,17 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>              info->capabilities->value->state = s->enabled_capabilities[i];
>>              info->capabilities->next = NULL;
>>          }
>> +
>> +        /* display xbzrle cache size */
>> +        if (migrate_use_xbzrle()) {
>> +            info->has_cache = true;
>> +            info->cache = g_malloc0(sizeof(*info->cache));
>> +            info->cache->cache_size = migrate_xbzrle_cache_size();
>> +            info->cache->xbzrle_bytes = xbzrle_mig_bytes_transferred();
>> +            info->cache->xbzrle_pages = xbzrle_mig_pages_transferred();
>> +            info->cache->xbzrle_cache_miss = xbzrle_mig_pages_cache_miss();
>> +            info->cache->xbzrle_overflow = xbzrle_mig_pages_overflow();
>> +        }
> 
> Why do you need this in MIG_STATE_SETUP? I think having it only in
> MIG_STATE_ACTIVE is enough (and probably that's what we want).
you are right we just need the cache size (which can be set before the migration).
> 
>>          break;
>>      case MIG_STATE_ACTIVE:
>>          for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>> @@ -161,6 +172,8 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>          info->ram->total = ram_bytes_total();
>>          info->ram->total_time = qemu_get_clock_ms(rt_clock)
>>              - s->total_time;
>> +        info->ram->duplicate = dup_mig_pages_transferred();
>> +        info->ram->normal = norm_mig_pages_transferred();
> 
> Can we do this extension in a different patch?
ok
> 
>>
>>          if (blk_mig_active()) {
>>              info->has_disk = true;
>> @@ -169,6 +182,16 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>              info->disk->remaining = blk_mig_bytes_remaining();
>>              info->disk->total = blk_mig_bytes_total();
>>          }
>> +
>> +        if (migrate_use_xbzrle()) {
>> +            info->has_cache = true;
>> +            info->cache = g_malloc0(sizeof(*info->cache));
>> +            info->cache->cache_size = migrate_xbzrle_cache_size();
>> +            info->cache->xbzrle_bytes = xbzrle_mig_bytes_transferred();
>> +            info->cache->xbzrle_pages = xbzrle_mig_pages_transferred();
>> +            info->cache->xbzrle_cache_miss = xbzrle_mig_pages_cache_miss();
>> +            info->cache->xbzrle_overflow = xbzrle_mig_pages_overflow();
>> +        }
>>          break;
>>      case MIG_STATE_COMPLETED:
>>          for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>> @@ -183,6 +206,32 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>              info->capabilities->next = NULL;
>>          }
>>
>> +        info->has_ram = true;
>> +        info->ram = g_malloc0(sizeof(*info->ram));
>> +        info->ram->transferred = ram_bytes_transferred();
>> +        info->ram->remaining = ram_bytes_remaining();
>> +        info->ram->total = ram_bytes_total();
>> +        info->ram->duplicate = dup_mig_pages_transferred();
>> +        info->ram->normal = norm_mig_pages_transferred();
> 
> This hunk is probably bad copy & paste, as this code already exists.
looks like a bad merge I will remove it
> 
>> +
>> +        if (blk_mig_active()) {
>> +            info->has_disk = true;
>> +            info->disk = g_malloc0(sizeof(*info->disk));
>> +            info->disk->transferred = blk_mig_bytes_transferred();
>> +            info->disk->remaining = blk_mig_bytes_remaining();
>> +            info->disk->total = blk_mig_bytes_total();
>> +        }
> 
> Why are you doing this, bad copy & paste too?
looks like a bad merge I will remove it
> 
>> +
>> +        if (migrate_use_xbzrle()) {
>> +            info->has_cache = true;
>> +            info->cache = g_malloc0(sizeof(*info->cache));
>> +            info->cache->cache_size = migrate_xbzrle_cache_size();
>> +            info->cache->xbzrle_bytes = xbzrle_mig_bytes_transferred();
>> +            info->cache->xbzrle_pages = xbzrle_mig_pages_transferred();
>> +            info->cache->xbzrle_cache_miss = xbzrle_mig_pages_cache_miss();
>> +            info->cache->xbzrle_overflow = xbzrle_mig_pages_overflow();
>> +        }
>> +
>>          info->has_status = true;
>>          info->status = g_strdup("completed");
>>
>> diff --git a/migration.h b/migration.h
>> index 337e225..a9852fc 100644
>> --- a/migration.h
>> +++ b/migration.h
>> @@ -87,6 +87,15 @@ uint64_t ram_bytes_total(void);
>>
>>  extern SaveVMHandlers savevm_ram_handlers;
>>
>> +uint64_t dup_mig_bytes_transferred(void);
>> +uint64_t dup_mig_pages_transferred(void);
>> +uint64_t norm_mig_bytes_transferred(void);
>> +uint64_t norm_mig_pages_transferred(void);
>> +uint64_t xbzrle_mig_bytes_transferred(void);
>> +uint64_t xbzrle_mig_pages_transferred(void);
>> +uint64_t xbzrle_mig_pages_overflow(void);
>> +uint64_t xbzrle_mig_pages_cache_miss(void);
>> +
>>  /**
>>   * @migrate_add_blocker - prevent migration from proceeding
>>   *
>> diff --git a/qapi-schema.json b/qapi-schema.json
>> index a0f0f95..961810b 100644
>> --- a/qapi-schema.json
>> +++ b/qapi-schema.json
>> @@ -264,11 +264,35 @@
>>  #        migration has ended, it returns the total migration
>>  #        time. (since 1.2)
>>  #
>> -# Since: 0.14.0.
>> +# @duplicate: #optional, number of duplicate pages
>> +#
>> +# @normal : #optional, number of normal pages
>> +#
>> +# Since: 0.14.0, 'duplicate' and 'normal' since 1.2
> 
> Please, put 'since' in the same line of the field, like this:
> 
>  @normal : #optional, number of normal pages (since 1.2)
ok
> 
>>  ##
>>  { 'type': 'MigrationStats',
>>    'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' ,
>> -           'total_time': 'int' } }
>> +           'total_time': 'int', '*duplicate': 'int', '*normal': 'int' } }
>> +##
>> +# @CacheStats
> 
> This is specific to xbzrle, right? So this should be called XBZRLECacheStats,
> or something.
sure
> 
>> +#
>> +# Detailed XBZRLE migration cache statistics
>> +#
>> +# @cache_size: XBZRLE cache size
>> +#
>> +# @xbzrle_bytes: amount of bytes already transferred to the target VM
>> +#
>> +# @xbzrle_pages: amount of pages transferred to the target VM
>> +#
>> +# @xbzrle_cache_miss: number of cache miss
>> +#
>> +# @xbzrle_overflow: number of overflows
>> +#
>> +# Since: 1.2
>> +##
>> +{ 'type': 'CacheStats',
>> +  'data': {'cache-size': 'int', 'xbzrle-bytes': 'int', 'xbzrle-pages': 'int',
>> +           'xbzrle-cache-miss': 'int', 'xbzrle-overflow': 'int' } }
>>
>>  ##
>>  # @MigrationInfo
>> @@ -291,13 +315,16 @@
>>  # @capabilities: #optional a list describing all the migration capabilities
>>  #                state
>>  #
>> -# Since: 0.14.0, 'capabilities' since 1.2
>> +# @cache: #optional @MigrationStats containing detailed XBZRLE migration
>> +#         statistics
> 
> xbzrle-cache.
ok

Thanks,
Orit
> 
>> +#
>> +# Since: 0.14.0, 'capabilities' and 'cache' since 1.2
>>  ##
>>  { 'type': 'MigrationInfo',
>>    'data': {'*status': 'str', '*ram': 'MigrationStats',
>>             '*disk': 'MigrationStats',
>> -           '*capabilities': ['MigrationCapabilityInfo']} }
>> -
>> +           '*capabilities': ['MigrationCapabilityInfo'],
>> +           '*cache': 'CacheStats'} }
>>  ##
>>  # @query-migrate
>>  #
>> diff --git a/qmp-commands.hx b/qmp-commands.hx
>> index a3d57ce..34d4675 100644
>> --- a/qmp-commands.hx
>> +++ b/qmp-commands.hx
>> @@ -2093,6 +2093,8 @@ The main json-object contains the following:
>>           - "transferred": amount transferred (json-int)
>>           - "remaining": amount remaining (json-int)
>>           - "total": total (json-int)
>> +	 - "duplicate": number of duplicated pages (json-int)
>> +	 - "normal" : number of normal pages transferred (json-int)
>>  - "disk": only present if "status" is "active" and it is a block migration,
>>    it is a json-object with the following disk information (in bytes):
>>           - "transferred": amount transferred (json-int)
>> @@ -2100,10 +2102,17 @@ The main json-object contains the following:
>>           - "total": total (json-int)
>>  - "capabilities": migration capabilities state
>>           - "xbzrle" : XBZRLE state (json-bool)
>> +- "cache": only present if "status" and XBZRLE is active.
>> +  It is a json-object with the following XBZRLE information:
>> +         - "cache-size": XBZRLE cache size
>> +         - "xbzrle-bytes": total XBZRLE bytes transferred
>> +         - "xbzrle-pages": number of XBZRLE compressed pages
>> +         - "cache-miss": number of cache misses
>> +         - "overflow": number of XBZRLE overflows
>> +
>>  Examples:
>>
>>  1. Before the first migration
>> -
>>  -> { "execute": "query-migrate" }
>>  <- { "return": {
>>          "capabilities" :  [ { "capability" : "xbzrle", "state" : false } ]
>> @@ -2164,6 +2173,30 @@ Examples:
>>        }
>>     }
>>
>> +6. Migration is being performed and XBZRLE is active:
>> +
>> +-> { "execute": "query-migrate" }
>> +<- {
>> +      "return":{
>> +         "status":"active",
>> +         "capabilities" : [ { "capability": "xbzrle", "state" : true } ],
>> +         "ram":{
>> +            "total":1057024,
>> +            "remaining":1053304,
>> +            "transferred":3720,
>> +            "duplicate": 10,
>> +            "normal" : 3333
>> +         },
>> +         "cache":{
>> +            "cache-size": 1024
>> +            "xbzrle-transferred":20971520,
>> +            "xbzrle-pages":2444343,
>> +            "xbzrle-cache-miss":2244,
>> +            "xbzrle-overflow":34434
>> +         }
>> +      }
>> +   }
>> +
>>  EQMP
>>
>>      {
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH 13/22] Add migration capabilities
  2012-07-24  6:25     ` Orit Wasserman
@ 2012-07-24 12:50       ` Luiz Capitulino
  2012-07-24 17:06         ` Orit Wasserman
  0 siblings, 1 reply; 35+ messages in thread
From: Luiz Capitulino @ 2012-07-24 12:50 UTC (permalink / raw)
  To: Orit Wasserman; +Cc: Eric Blake, qemu-devel, Juan Quintela

On Tue, 24 Jul 2012 09:25:11 +0300
Orit Wasserman <owasserm@redhat.com> wrote:

> On 07/23/2012 09:23 PM, Luiz Capitulino wrote:
> > On Fri, 13 Jul 2012 09:23:35 +0200
> > Juan Quintela <quintela@redhat.com> wrote:
> > 
> >> From: Orit Wasserman <owasserm@redhat.com>
> >>
> >> Add migration capabilities that can be queried by the management.
> >> The management can query the source QEMU and the destination QEMU in order to
> >> verify both support some migration capability (currently only XBZRLE).
> >> The management can enable a capability for the next migration by using
> >> migrate_set_parameter command.
> > 
> > Please, split this into one command per-patch. Otherwise it's difficult to
> > review.
> > 
> Sure.
> > Have libvirt folks acked this approach btw? It looks fine to me, but we need
> > their ack too.
> > 
> > More comments below.
> > 
> >>
> >> Signed-off-by: Orit Wasserman <owasserm@redhat.com>
> >> Signed-off-by: Juan Quintela <quintela@redhat.com>
> >> ---
> >>  hmp-commands.hx  |   16 ++++++++++++
> >>  hmp.c            |   64 ++++++++++++++++++++++++++++++++++++++++++++++++
> >>  hmp.h            |    2 ++
> >>  migration.c      |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
> >>  migration.h      |    2 ++
> >>  monitor.c        |    7 ++++++
> >>  qapi-schema.json |   53 ++++++++++++++++++++++++++++++++++++++--
> >>  qmp-commands.hx  |   71 ++++++++++++++++++++++++++++++++++++++++++++++++++---
> >>  8 files changed, 280 insertions(+), 7 deletions(-)
> >>
> >> diff --git a/hmp-commands.hx b/hmp-commands.hx
> >> index f5d9d91..9245bef 100644
> >> --- a/hmp-commands.hx
> >> +++ b/hmp-commands.hx
> >> @@ -861,6 +861,20 @@ Set maximum tolerated downtime (in seconds) for migration.
> >>  ETEXI
> >>
> >>      {
> >> +        .name       = "migrate_set_parameter",
> >> +        .args_type  = "capability:s,state:b",
> >> +        .params     = "",
> > 
> > Please, fill in params.
> ok
> > 
> >> +        .help       = "Enable/Disable the usage of a capability for migration",
> >> +        .mhandler.cmd = hmp_migrate_set_parameter,
> >> +    },
> >> +
> >> +STEXI
> >> +@item migrate_set_parameter @var{capability} @var{state}
> >> +@findex migrate_set_parameter
> >> +Enable/Disable the usage of a capability @var{capability} for migration.
> >> +ETEXI
> >> +
> >> +    {
> >>          .name       = "client_migrate_info",
> >>          .args_type  = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
> >>          .params     = "protocol hostname port tls-port cert-subject",
> >> @@ -1419,6 +1433,8 @@ show CPU statistics
> >>  show user network stack connection states
> >>  @item info migrate
> >>  show migration status
> >> +@item info migration_capabilities
> >> +show migration capabilities
> >>  @item info balloon
> >>  show balloon information
> >>  @item info qtree
> >> diff --git a/hmp.c b/hmp.c
> >> index 4c6d4ae..b0440e6 100644
> >> --- a/hmp.c
> >> +++ b/hmp.c
> >> @@ -131,9 +131,19 @@ void hmp_info_mice(Monitor *mon)
> >>  void hmp_info_migrate(Monitor *mon)
> >>  {
> >>      MigrationInfo *info;
> >> +    MigrationCapabilityInfoList *cap;
> >>
> >>      info = qmp_query_migrate(NULL);
> >>
> >> +    if (info->has_capabilities && info->capabilities) {
> >> +        monitor_printf(mon, "capabilities: ");
> >> +        for (cap = info->capabilities; cap; cap = cap->next) {
> >> +            monitor_printf(mon, "%s: %s ",
> >> +                           MigrationCapability_lookup[cap->value->capability],
> >> +                           cap->value->state ? "on" : "off");
> >> +        }
> >> +        monitor_printf(mon, "\n");
> > 
> > Why is this is needed? Isn't info migration-capabilities good enough?
> > Besides, info migrate should only contain information about current migration
> > process...
> The reason we introduced capabilities is that xbzrle needs for both source and destination QEMU
> to be able to handle it. Even if both side support xbzrle the user may decide not to use it.
> User that wants to use xbzrle needs to check that both sides have support for it (using info capabilities) , than 
> enable it in both sides (using migrate-set-parameter/s commands). This is a parameter for the current migration.
> So the user needs to know if xbzrle was enabled or disabled for the current migration, this code displays it.

But this is being returned even when there is no migration taking place. I'm
answering this here in the 'info migrate' hunk, but my main concern is
query-migrate.

query-migrate shouldn't return this info in MIG_STATE_SETUP, and for
MIG_STATE_COMPLETED it should only be returned if there was a migration.

> some day when there will be better migration protocol, feature negotiations can be part of it ...
> > 
> >> +    }
> >>      if (info->has_status) {
> >>          monitor_printf(mon, "Migration status: %s\n", info->status);
> >>      }
> >> @@ -161,6 +171,25 @@ void hmp_info_migrate(Monitor *mon)
> >>      qapi_free_MigrationInfo(info);
> >>  }
> >>
> >> +void hmp_info_migration_capabilities(Monitor *mon)
> >> +{
> >> +    MigrationCapabilityInfoList *caps_list, *cap;
> >> +
> >> +    caps_list = qmp_query_migration_capabilities(NULL);
> >> +    if (!caps_list) {
> >> +        monitor_printf(mon, "No migration capabilities found\n");
> >> +        return;
> >> +    }
> >> +
> >> +    for (cap = caps_list; cap; cap = cap->next) {
> >> +        monitor_printf(mon, "%s: %s ",
> >> +                       MigrationCapability_lookup[cap->value->capability],
> >> +                       cap->value->state ? "on" : "off");
> >> +    }
> >> +
> >> +    qapi_free_MigrationCapabilityInfoList(caps_list);
> >> +}
> >> +
> >>  void hmp_info_cpus(Monitor *mon)
> >>  {
> >>      CpuInfoList *cpu_list, *cpu;
> >> @@ -735,6 +764,41 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
> >>      qmp_migrate_set_speed(value, NULL);
> >>  }
> >>
> >> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
> >> +{
> >> +    const char *cap = qdict_get_str(qdict, "capability");
> >> +    bool state = qdict_get_bool(qdict, "state");
> >> +    Error *err = NULL;
> >> +    MigrationCapabilityInfoList *params = NULL;
> >> +    int i;
> >> +
> >> +    for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> >> +        if (strcmp(cap, MigrationCapability_lookup[i]) == 0) {
> >> +            if (!params) {
> >> +                params = g_malloc0(sizeof(*params));
> >> +            }
> >> +            params->value = g_malloc0(sizeof(*params->value));
> >> +            params->value->capability = i;
> >> +            params->value->state = state;
> >> +            params->next = NULL;
> >> +            qmp_migrate_set_parameters(params, &err);
> >> +            break;
> >> +        }
> >> +    }
> >> +
> >> +    if (i == MIGRATION_CAPABILITY_MAX) {
> >> +        error_set(&err, QERR_INVALID_PARAMETER, cap);
> >> +    }
> >> +
> >> +    qapi_free_MigrationCapabilityInfoList(params);
> >> +
> >> +    if (err) {
> >> +        monitor_printf(mon, "migrate_set_parameter: %s\n",
> >> +                       error_get_pretty(err));
> >> +        error_free(err);
> >> +    }
> >> +}
> >> +
> >>  void hmp_set_password(Monitor *mon, const QDict *qdict)
> >>  {
> >>      const char *protocol  = qdict_get_str(qdict, "protocol");
> >> diff --git a/hmp.h b/hmp.h
> >> index 79d138d..09ba198 100644
> >> --- a/hmp.h
> >> +++ b/hmp.h
> >> @@ -25,6 +25,7 @@ void hmp_info_uuid(Monitor *mon);
> >>  void hmp_info_chardev(Monitor *mon);
> >>  void hmp_info_mice(Monitor *mon);
> >>  void hmp_info_migrate(Monitor *mon);
> >> +void hmp_info_migration_capabilities(Monitor *mon);
> >>  void hmp_info_cpus(Monitor *mon);
> >>  void hmp_info_block(Monitor *mon);
> >>  void hmp_info_blockstats(Monitor *mon);
> >> @@ -51,6 +52,7 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
> >>  void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
> >>  void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
> >>  void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
> >> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict);
> >>  void hmp_set_password(Monitor *mon, const QDict *qdict);
> >>  void hmp_expire_password(Monitor *mon, const QDict *qdict);
> >>  void hmp_eject(Monitor *mon, const QDict *qdict);
> >> diff --git a/migration.c b/migration.c
> >> index 8db1b43..fd004d7 100644
> >> --- a/migration.c
> >> +++ b/migration.c
> >> @@ -117,12 +117,36 @@ MigrationInfo *qmp_query_migrate(Error **errp)
> >>  {
> >>      MigrationInfo *info = g_malloc0(sizeof(*info));
> >>      MigrationState *s = migrate_get_current();
> >> +    int i;
> >>
> >>      switch (s->state) {
> >>      case MIG_STATE_SETUP:
> >> -        /* no migration has happened ever */
> >> +        /* no migration has ever happened; show enabled capabilities */
> >> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> >> +            if (!info->has_capabilities) {
> >> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
> >> +                info->has_capabilities = true;
> >> +            }
> >> +            info->capabilities->value =
> >> +                g_malloc(sizeof(*info->capabilities->value));
> >> +            info->capabilities->value->capability = i;
> >> +            info->capabilities->value->state = s->enabled_capabilities[i];
> >> +            info->capabilities->next = NULL;
> >> +        }
> >>          break;
> >>      case MIG_STATE_ACTIVE:
> >> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> >> +            if (!info->has_capabilities) {
> >> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
> >> +                info->has_capabilities = true;
> >> +            }
> >> +            info->capabilities->value =
> >> +                g_malloc(sizeof(*info->capabilities->value));
> >> +            info->capabilities->value->capability = i;
> >> +            info->capabilities->value->state = s->enabled_capabilities[i];
> >> +            info->capabilities->next = NULL;
> >> +        }
> >> +
> >>          info->has_status = true;
> >>          info->status = g_strdup("active");
> >>
> >> @@ -143,6 +167,18 @@ MigrationInfo *qmp_query_migrate(Error **errp)
> >>          }
> >>          break;
> >>      case MIG_STATE_COMPLETED:
> >> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> >> +            if (!info->has_capabilities) {
> >> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
> >> +                info->has_capabilities = true;
> >> +            }
> >> +            info->capabilities->value =
> >> +                g_malloc(sizeof(*info->capabilities->value));
> >> +            info->capabilities->value->capability = i;
> >> +            info->capabilities->value->state = s->enabled_capabilities[i];
> >> +            info->capabilities->next = NULL;
> >> +        }
> > 
> > Code triplication :)
> > 
> > Why is this is needed? Isn't query-migration-capabilities good enough?
> > Besides, query-migrate should only contain information about current migration
> > process...
> > 
> see above
> >> +
> >>          info->has_status = true;
> >>          info->status = g_strdup("completed");
> >>
> >> @@ -166,6 +202,33 @@ MigrationInfo *qmp_query_migrate(Error **errp)
> >>      return info;
> >>  }
> >>
> >> +MigrationCapabilityInfoList *qmp_query_migration_capabilities(Error **errp)
> >> +{
> >> +    MigrationCapabilityInfoList *caps_list = g_malloc0(sizeof(*caps_list));
> >> +
> >> +    caps_list->value = g_malloc(sizeof(*caps_list->value));
> >> +    caps_list->value->capability = MIGRATION_CAPABILITY_XBZRLE;
> >> +    caps_list->next = NULL;
> > 
> > Shouldn't this get the capabilities array from migrate_get_current()?
> > 
> > I mean, this makes query-migration-capabilities always return true for
> > xbzrle, even if we set it to off.
> Those are the general capabilities , if I want to use xbzrle ,does this QEMU support it ? 
> this is required because we need both the destination and the source to be able to handle it.
> If one QEMU doesn't (older version of qemu, maybe it will be disbaled for certain architectures) we can't use the feature.
> This is for management and the user to check what migration capabilities this QEMU supports.

My point was that query-migration-capabilities will always return state=true
for all existing capabilities. Don't we want users/mngt app to know which
states have been turned off?

> 
> > 
> >> +
> >> +    return caps_list;
> >> +}
> >> +
> >> +void qmp_migrate_set_parameters(MigrationCapabilityInfoList *params,
> >> +                                Error **errp)
> >> +{
> >> +    MigrationState *s = migrate_get_current();
> >> +    MigrationCapabilityInfoList *cap;
> >> +
> >> +    if (s->state == MIG_STATE_ACTIVE) {
> >> +        error_set(errp, QERR_MIGRATION_ACTIVE);
> >> +        return;
> >> +    }
> >> +
> >> +    for (cap = params; cap; cap = cap->next) {
> >> +        s->enabled_capabilities[cap->value->capability] = cap->value->state;
> >> +    }
> >> +}
> >> +
> >>  /* shared migration helpers */
> >>
> >>  static int migrate_fd_cleanup(MigrationState *s)
> >> @@ -375,12 +438,17 @@ static MigrationState *migrate_init(const MigrationParams *params)
> >>  {
> >>      MigrationState *s = migrate_get_current();
> >>      int64_t bandwidth_limit = s->bandwidth_limit;
> >> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
> >> +
> >> +    memcpy(enabled_capabilities, s->enabled_capabilities,
> >> +           sizeof(enabled_capabilities));
> >>
> >>      memset(s, 0, sizeof(*s));
> >>      s->bandwidth_limit = bandwidth_limit;
> >>      s->params = *params;
> >> +    memcpy(s->enabled_capabilities, enabled_capabilities,
> >> +           sizeof(enabled_capabilities));
> >>
> >> -    s->bandwidth_limit = bandwidth_limit;
> >>      s->state = MIG_STATE_SETUP;
> >>      s->total_time = qemu_get_clock_ms(rt_clock);
> >>
> >> diff --git a/migration.h b/migration.h
> >> index 57572a6..713aae0 100644
> >> --- a/migration.h
> >> +++ b/migration.h
> >> @@ -19,6 +19,7 @@
> >>  #include "notify.h"
> >>  #include "error.h"
> >>  #include "vmstate.h"
> >> +#include "qapi-types.h"
> >>
> >>  struct MigrationParams {
> >>      bool blk;
> >> @@ -39,6 +40,7 @@ struct MigrationState
> >>      void *opaque;
> >>      MigrationParams params;
> >>      int64_t total_time;
> >> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
> >>  };
> >>
> >>  void process_incoming_migration(QEMUFile *f);
> >> diff --git a/monitor.c b/monitor.c
> >> index f6107ba..e2be6cd 100644
> >> --- a/monitor.c
> >> +++ b/monitor.c
> >> @@ -2687,6 +2687,13 @@ static mon_cmd_t info_cmds[] = {
> >>          .mhandler.info = hmp_info_migrate,
> >>      },
> >>      {
> >> +        .name       = "migration_capabilities",
> > 
> > migration-capabilities is better.
> ok
> > 
> >> +        .args_type  = "",
> >> +        .params     = "",
> >> +        .help       = "show migration capabilities",
> >> +        .mhandler.info = hmp_info_migration_capabilities,
> >> +    },
> >> +    {
> >>          .name       = "balloon",
> >>          .args_type  = "",
> >>          .params     = "",
> >> diff --git a/qapi-schema.json b/qapi-schema.json
> >> index 1ab5dbd..a8408fd 100644
> >> --- a/qapi-schema.json
> >> +++ b/qapi-schema.json
> >> @@ -288,11 +288,15 @@
> >>  #        status, only returned if status is 'active' and it is a block
> >>  #        migration
> >>  #
> >> -# Since: 0.14.0
> >> +# @capabilities: #optional a list describing all the migration capabilities
> >> +#                state
> > 
> > I don't think this is needed, as I've said above.
> see above
> > 
> >> +#
> >> +# Since: 0.14.0, 'capabilities' since 1.2
> >>  ##
> >>  { 'type': 'MigrationInfo',
> >>    'data': {'*status': 'str', '*ram': 'MigrationStats',
> >> -           '*disk': 'MigrationStats'} }
> >> +           '*disk': 'MigrationStats',
> >> +           '*capabilities': ['MigrationCapabilityInfo']} }
> >>
> >>  ##
> >>  # @query-migrate
> >> @@ -306,6 +310,51 @@
> >>  { 'command': 'query-migrate', 'returns': 'MigrationInfo' }
> >>
> >>  ##
> >> +# @MigrationCapability
> >> +#
> >> +# Migration capabilities enumeration
> >> +#
> >> +# @xbzrle: current migration supports xbzrle
> > 
> > You should explain what xbzrle is.
> ok
> > 
> >> +#
> >> +# Since: 1.2
> >> +##
> >> +{ 'enum': 'MigrationCapability',
> >> +  'data': ['xbzrle'] }
> >> +
> >> +##
> >> +# @MigrationCapabilityInfo
> > 
> > MigrationCapabilityStatus?
> > 
> >> +#
> >> +# Migration capability information
> >> +#
> >> +# @capability: capability enum
> > 
> > Please, document state too.
> ok
> 
> Orit
> > 
> >> +#
> >> +# Since: 1.2
> >> +##
> >> +{ 'type': 'MigrationCapabilityInfo',
> >> +  'data': { 'capability' : 'MigrationCapability', 'state' : 'bool' } }
> >> +
> >> +##
> >> +# @query-migration-capabilities
> >> +#
> >> +# Returns information about current migration process capabilties.
> >> +#
> >> +# Returns: @MigrationCapabilityInfo list
> >> +#
> >> +# Since: 1.2
> >> +##
> >> +{ 'command': 'query-migration-capabilities', 'returns': ['MigrationCapabilityInfo'] }
> >> +
> >> +##
> >> +# @migrate_set_parameters
> >> +#
> >> +# Enable/Disable the following migration capabilities (like xbzrle)
> >> +#
> >> +# Since: 1.2
> >> +##
> >> +{ 'command': 'migrate-set-parameters',
> >> +  'data': { 'capabilities': ['MigrationCapabilityInfo'] } }
> >> +
> >> +##
> >>  # @MouseInfo:
> >>  #
> >>  # Information about a mouse device.
> >> diff --git a/qmp-commands.hx b/qmp-commands.hx
> >> index 2e1a38e..3ee6e00 100644
> >> --- a/qmp-commands.hx
> >> +++ b/qmp-commands.hx
> >> @@ -2075,18 +2075,31 @@ The main json-object contains the following:
> >>           - "transferred": amount transferred (json-int)
> >>           - "remaining": amount remaining (json-int)
> >>           - "total": total (json-int)
> >> -
> >> +- "capabilities": migration capabilities state
> >> +         - "xbzrle" : XBZRLE state (json-bool)
> >>  Examples:
> >>
> >>  1. Before the first migration
> >>
> >>  -> { "execute": "query-migrate" }
> >> -<- { "return": {} }
> >> +<- { "return": {
> >> +        "capabilities" :  [ { "capability" : "xbzrle", "state" : false } ]
> >> +     }
> >> +   }
> >>
> >>  2. Migration is done and has succeeded
> >>
> >>  -> { "execute": "query-migrate" }
> >> -<- { "return": { "status": "completed" } }
> >> +<- { "return": {
> >> +        "status": "completed",
> >> +        "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
> >> +        "ram":{
> >> +          "transferred":123,
> >> +          "remaining":123,
> >> +          "total":246
> >> +        }
> >> +     }
> >> +   }
> >>
> >>  3. Migration is done and has failed
> >>
> >> @@ -2099,6 +2112,7 @@ Examples:
> >>  <- {
> >>        "return":{
> >>           "status":"active",
> >> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
> >>           "ram":{
> >>              "transferred":123,
> >>              "remaining":123,
> >> @@ -2113,6 +2127,7 @@ Examples:
> >>  <- {
> >>        "return":{
> >>           "status":"active",
> >> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
> >>           "ram":{
> >>              "total":1057024,
> >>              "remaining":1053304,
> >> @@ -2135,6 +2150,56 @@ EQMP
> >>      },
> >>
> >>  SQMP
> >> +query-migration-capabilities
> >> +-------
> >> +
> >> +Query migration capabilities
> >> +
> >> +- "xbzrle": xbzrle support
> >> +
> >> +Arguments:
> >> +
> >> +Example:
> >> +
> >> +-> { "execute": "query-migration-capabilities"}
> >> +<- { "return": [ { "capability": "xbzrle", "state": true },
> >> +                 { "capability": "foobar", "state": false } ] }
> >> +
> >> +EQMP
> >> +
> >> +    {
> >> +        .name       = "query-migration-capabilities",
> >> +        .args_type  = "",
> >> +	.mhandler.cmd_new = qmp_marshal_input_query_migration_capabilities,
> >> +    },
> >> +
> >> +SQMP
> >> +migrate_set_parameters
> >> +-------
> >> +
> >> +Enable/Disable migration capabilities
> >> +
> >> +- "xbzrle": xbzrle support
> >> +
> >> +Arguments:
> >> +
> >> +Example:
> >> +
> >> +-> { "execute": "migrate_set_parameters" , "arguments":
> >> +     { "parameters": [ { "capability": "xbzrle", "state": true } ] } }
> >> +
> >> +EQMP
> >> +
> >> +    {
> >> +        .name       = "migrate_set_parameters",
> >> +        .args_type  = "parameters:O",
> >> +	.params     = "capability:s,state:b",
> >> +	.mhandler.cmd_new = qmp_marshal_input_migrate_set_parameters,
> >> +    },
> >> +
> >> +
> >> +
> >> +SQMP
> >>  query-balloon
> >>  -------------
> >>
> > 
> 
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH 13/22] Add migration capabilities
  2012-07-24 12:50       ` Luiz Capitulino
@ 2012-07-24 17:06         ` Orit Wasserman
  2012-07-24 18:17           ` Luiz Capitulino
  0 siblings, 1 reply; 35+ messages in thread
From: Orit Wasserman @ 2012-07-24 17:06 UTC (permalink / raw)
  To: Luiz Capitulino; +Cc: Eric Blake, qemu-devel, Juan Quintela

On 07/24/2012 03:50 PM, Luiz Capitulino wrote:
> On Tue, 24 Jul 2012 09:25:11 +0300
> Orit Wasserman <owasserm@redhat.com> wrote:
> 
>> On 07/23/2012 09:23 PM, Luiz Capitulino wrote:
>>> On Fri, 13 Jul 2012 09:23:35 +0200
>>> Juan Quintela <quintela@redhat.com> wrote:
>>>
>>>> From: Orit Wasserman <owasserm@redhat.com>
>>>>
>>>> Add migration capabilities that can be queried by the management.
>>>> The management can query the source QEMU and the destination QEMU in order to
>>>> verify both support some migration capability (currently only XBZRLE).
>>>> The management can enable a capability for the next migration by using
>>>> migrate_set_parameter command.
>>>
>>> Please, split this into one command per-patch. Otherwise it's difficult to
>>> review.
>>>
>> Sure.
>>> Have libvirt folks acked this approach btw? It looks fine to me, but we need
>>> their ack too.
>>>
>>> More comments below.
>>>
>>>>
>>>> Signed-off-by: Orit Wasserman <owasserm@redhat.com>
>>>> Signed-off-by: Juan Quintela <quintela@redhat.com>
>>>> ---
>>>>  hmp-commands.hx  |   16 ++++++++++++
>>>>  hmp.c            |   64 ++++++++++++++++++++++++++++++++++++++++++++++++
>>>>  hmp.h            |    2 ++
>>>>  migration.c      |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
>>>>  migration.h      |    2 ++
>>>>  monitor.c        |    7 ++++++
>>>>  qapi-schema.json |   53 ++++++++++++++++++++++++++++++++++++++--
>>>>  qmp-commands.hx  |   71 ++++++++++++++++++++++++++++++++++++++++++++++++++---
>>>>  8 files changed, 280 insertions(+), 7 deletions(-)
>>>>
>>>> diff --git a/hmp-commands.hx b/hmp-commands.hx
>>>> index f5d9d91..9245bef 100644
>>>> --- a/hmp-commands.hx
>>>> +++ b/hmp-commands.hx
>>>> @@ -861,6 +861,20 @@ Set maximum tolerated downtime (in seconds) for migration.
>>>>  ETEXI
>>>>
>>>>      {
>>>> +        .name       = "migrate_set_parameter",
>>>> +        .args_type  = "capability:s,state:b",
>>>> +        .params     = "",
>>>
>>> Please, fill in params.
>> ok
>>>
>>>> +        .help       = "Enable/Disable the usage of a capability for migration",
>>>> +        .mhandler.cmd = hmp_migrate_set_parameter,
>>>> +    },
>>>> +
>>>> +STEXI
>>>> +@item migrate_set_parameter @var{capability} @var{state}
>>>> +@findex migrate_set_parameter
>>>> +Enable/Disable the usage of a capability @var{capability} for migration.
>>>> +ETEXI
>>>> +
>>>> +    {
>>>>          .name       = "client_migrate_info",
>>>>          .args_type  = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
>>>>          .params     = "protocol hostname port tls-port cert-subject",
>>>> @@ -1419,6 +1433,8 @@ show CPU statistics
>>>>  show user network stack connection states
>>>>  @item info migrate
>>>>  show migration status
>>>> +@item info migration_capabilities
>>>> +show migration capabilities
>>>>  @item info balloon
>>>>  show balloon information
>>>>  @item info qtree
>>>> diff --git a/hmp.c b/hmp.c
>>>> index 4c6d4ae..b0440e6 100644
>>>> --- a/hmp.c
>>>> +++ b/hmp.c
>>>> @@ -131,9 +131,19 @@ void hmp_info_mice(Monitor *mon)
>>>>  void hmp_info_migrate(Monitor *mon)
>>>>  {
>>>>      MigrationInfo *info;
>>>> +    MigrationCapabilityInfoList *cap;
>>>>
>>>>      info = qmp_query_migrate(NULL);
>>>>
>>>> +    if (info->has_capabilities && info->capabilities) {
>>>> +        monitor_printf(mon, "capabilities: ");
>>>> +        for (cap = info->capabilities; cap; cap = cap->next) {
>>>> +            monitor_printf(mon, "%s: %s ",
>>>> +                           MigrationCapability_lookup[cap->value->capability],
>>>> +                           cap->value->state ? "on" : "off");
>>>> +        }
>>>> +        monitor_printf(mon, "\n");
>>>
>>> Why is this is needed? Isn't info migration-capabilities good enough?
>>> Besides, info migrate should only contain information about current migration
>>> process...
>> The reason we introduced capabilities is that xbzrle needs for both source and destination QEMU
>> to be able to handle it. Even if both side support xbzrle the user may decide not to use it.
>> User that wants to use xbzrle needs to check that both sides have support for it (using info capabilities) , than 
>> enable it in both sides (using migrate-set-parameter/s commands). This is a parameter for the current migration.
>> So the user needs to know if xbzrle was enabled or disabled for the current migration, this code displays it.
> 
> But this is being returned even when there is no migration taking place. I'm
> answering this here in the 'info migrate' hunk, but my main concern is
> query-migrate.
I think migration configuration is part of migration information and should be available to the user in 'info migration'. Especially before he starts running it to see if he need to change the configuration. Also it is very helpful for the user during the migration and also after it's completes.
There is no way today to query  the downtime and migration speed at all . you can always set them again
but there is no way to know how it is configured.
I was actually thinking to add them to info migration (also for SETUP).
> 
> query-migrate shouldn't return this info in MIG_STATE_SETUP, and for
> MIG_STATE_COMPLETED it should only be returned if there was a migration.
I think is case of MIG_SETUP we should return the configuration and params for the next migration. 
MIG_STATE_COMPLETED happens only if there was a successful migration other wise there will be another state.
> 
>> some day when there will be better migration protocol, feature negotiations can be part of it ...
>>>
>>>> +    }
>>>>      if (info->has_status) {
>>>>          monitor_printf(mon, "Migration status: %s\n", info->status);
>>>>      }
>>>> @@ -161,6 +171,25 @@ void hmp_info_migrate(Monitor *mon)
>>>>      qapi_free_MigrationInfo(info);
>>>>  }
>>>>
>>>> +void hmp_info_migration_capabilities(Monitor *mon)
>>>> +{
>>>> +    MigrationCapabilityInfoList *caps_list, *cap;
>>>> +
>>>> +    caps_list = qmp_query_migration_capabilities(NULL);
>>>> +    if (!caps_list) {
>>>> +        monitor_printf(mon, "No migration capabilities found\n");
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    for (cap = caps_list; cap; cap = cap->next) {
>>>> +        monitor_printf(mon, "%s: %s ",
>>>> +                       MigrationCapability_lookup[cap->value->capability],
>>>> +                       cap->value->state ? "on" : "off");
>>>> +    }
>>>> +
>>>> +    qapi_free_MigrationCapabilityInfoList(caps_list);
>>>> +}
>>>> +
>>>>  void hmp_info_cpus(Monitor *mon)
>>>>  {
>>>>      CpuInfoList *cpu_list, *cpu;
>>>> @@ -735,6 +764,41 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
>>>>      qmp_migrate_set_speed(value, NULL);
>>>>  }
>>>>
>>>> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
>>>> +{
>>>> +    const char *cap = qdict_get_str(qdict, "capability");
>>>> +    bool state = qdict_get_bool(qdict, "state");
>>>> +    Error *err = NULL;
>>>> +    MigrationCapabilityInfoList *params = NULL;
>>>> +    int i;
>>>> +
>>>> +    for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>>>> +        if (strcmp(cap, MigrationCapability_lookup[i]) == 0) {
>>>> +            if (!params) {
>>>> +                params = g_malloc0(sizeof(*params));
>>>> +            }
>>>> +            params->value = g_malloc0(sizeof(*params->value));
>>>> +            params->value->capability = i;
>>>> +            params->value->state = state;
>>>> +            params->next = NULL;
>>>> +            qmp_migrate_set_parameters(params, &err);
>>>> +            break;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    if (i == MIGRATION_CAPABILITY_MAX) {
>>>> +        error_set(&err, QERR_INVALID_PARAMETER, cap);
>>>> +    }
>>>> +
>>>> +    qapi_free_MigrationCapabilityInfoList(params);
>>>> +
>>>> +    if (err) {
>>>> +        monitor_printf(mon, "migrate_set_parameter: %s\n",
>>>> +                       error_get_pretty(err));
>>>> +        error_free(err);
>>>> +    }
>>>> +}
>>>> +
>>>>  void hmp_set_password(Monitor *mon, const QDict *qdict)
>>>>  {
>>>>      const char *protocol  = qdict_get_str(qdict, "protocol");
>>>> diff --git a/hmp.h b/hmp.h
>>>> index 79d138d..09ba198 100644
>>>> --- a/hmp.h
>>>> +++ b/hmp.h
>>>> @@ -25,6 +25,7 @@ void hmp_info_uuid(Monitor *mon);
>>>>  void hmp_info_chardev(Monitor *mon);
>>>>  void hmp_info_mice(Monitor *mon);
>>>>  void hmp_info_migrate(Monitor *mon);
>>>> +void hmp_info_migration_capabilities(Monitor *mon);
>>>>  void hmp_info_cpus(Monitor *mon);
>>>>  void hmp_info_block(Monitor *mon);
>>>>  void hmp_info_blockstats(Monitor *mon);
>>>> @@ -51,6 +52,7 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
>>>>  void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
>>>>  void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
>>>>  void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
>>>> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict);
>>>>  void hmp_set_password(Monitor *mon, const QDict *qdict);
>>>>  void hmp_expire_password(Monitor *mon, const QDict *qdict);
>>>>  void hmp_eject(Monitor *mon, const QDict *qdict);
>>>> diff --git a/migration.c b/migration.c
>>>> index 8db1b43..fd004d7 100644
>>>> --- a/migration.c
>>>> +++ b/migration.c
>>>> @@ -117,12 +117,36 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>>>  {
>>>>      MigrationInfo *info = g_malloc0(sizeof(*info));
>>>>      MigrationState *s = migrate_get_current();
>>>> +    int i;
>>>>
>>>>      switch (s->state) {
>>>>      case MIG_STATE_SETUP:
>>>> -        /* no migration has happened ever */
>>>> +        /* no migration has ever happened; show enabled capabilities */
>>>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>>>> +            if (!info->has_capabilities) {
>>>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
>>>> +                info->has_capabilities = true;
>>>> +            }
>>>> +            info->capabilities->value =
>>>> +                g_malloc(sizeof(*info->capabilities->value));
>>>> +            info->capabilities->value->capability = i;
>>>> +            info->capabilities->value->state = s->enabled_capabilities[i];
>>>> +            info->capabilities->next = NULL;
>>>> +        }
>>>>          break;
>>>>      case MIG_STATE_ACTIVE:
>>>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>>>> +            if (!info->has_capabilities) {
>>>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
>>>> +                info->has_capabilities = true;
>>>> +            }
>>>> +            info->capabilities->value =
>>>> +                g_malloc(sizeof(*info->capabilities->value));
>>>> +            info->capabilities->value->capability = i;
>>>> +            info->capabilities->value->state = s->enabled_capabilities[i];
>>>> +            info->capabilities->next = NULL;
>>>> +        }
>>>> +
>>>>          info->has_status = true;
>>>>          info->status = g_strdup("active");
>>>>
>>>> @@ -143,6 +167,18 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>>>          }
>>>>          break;
>>>>      case MIG_STATE_COMPLETED:
>>>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>>>> +            if (!info->has_capabilities) {
>>>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
>>>> +                info->has_capabilities = true;
>>>> +            }
>>>> +            info->capabilities->value =
>>>> +                g_malloc(sizeof(*info->capabilities->value));
>>>> +            info->capabilities->value->capability = i;
>>>> +            info->capabilities->value->state = s->enabled_capabilities[i];
>>>> +            info->capabilities->next = NULL;
>>>> +        }
>>>
>>> Code triplication :)
>>>
>>> Why is this is needed? Isn't query-migration-capabilities good enough?
>>> Besides, query-migrate should only contain information about current migration
>>> process...
>>>
>> see above
>>>> +
>>>>          info->has_status = true;
>>>>          info->status = g_strdup("completed");
>>>>
>>>> @@ -166,6 +202,33 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>>>      return info;
>>>>  }
>>>>
>>>> +MigrationCapabilityInfoList *qmp_query_migration_capabilities(Error **errp)
>>>> +{
>>>> +    MigrationCapabilityInfoList *caps_list = g_malloc0(sizeof(*caps_list));
>>>> +
>>>> +    caps_list->value = g_malloc(sizeof(*caps_list->value));
>>>> +    caps_list->value->capability = MIGRATION_CAPABILITY_XBZRLE;
>>>> +    caps_list->next = NULL;
>>>
>>> Shouldn't this get the capabilities array from migrate_get_current()?
>>>
>>> I mean, this makes query-migration-capabilities always return true for
>>> xbzrle, even if we set it to off.
>> Those are the general capabilities , if I want to use xbzrle ,does this QEMU support it ? 
>> this is required because we need both the destination and the source to be able to handle it.
>> If one QEMU doesn't (older version of qemu, maybe it will be disbaled for certain architectures) we can't use the feature.
>> This is for management and the user to check what migration capabilities this QEMU supports.
> 
> My point was that query-migration-capabilities will always return state=true
> for all existing capabilities. Don't we want users/mngt app to know which
> states have been turned off?
You need to separate the ability to use a capability and actually using it for migration. Also I don't want the user to be able to set some capability on, it depends on the code not user configuration.
And for xbzrle we always return true for some version on QEMU , let say we decide we want to remove the code that implement it for some reason in some future version than it will return false.
There is a reason the command that enables a capability for a migration was called 'migrate-set-parameter' and not set-capability to distinguish between them one is per QEMU code base and one is per migration.

Regards,
Orit
> 
>>
>>>
>>>> +
>>>> +    return caps_list;
>>>> +}
>>>> +
>>>> +void qmp_migrate_set_parameters(MigrationCapabilityInfoList *params,
>>>> +                                Error **errp)
>>>> +{
>>>> +    MigrationState *s = migrate_get_current();
>>>> +    MigrationCapabilityInfoList *cap;
>>>> +
>>>> +    if (s->state == MIG_STATE_ACTIVE) {
>>>> +        error_set(errp, QERR_MIGRATION_ACTIVE);
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    for (cap = params; cap; cap = cap->next) {
>>>> +        s->enabled_capabilities[cap->value->capability] = cap->value->state;
>>>> +    }
>>>> +}
>>>> +
>>>>  /* shared migration helpers */
>>>>
>>>>  static int migrate_fd_cleanup(MigrationState *s)
>>>> @@ -375,12 +438,17 @@ static MigrationState *migrate_init(const MigrationParams *params)
>>>>  {
>>>>      MigrationState *s = migrate_get_current();
>>>>      int64_t bandwidth_limit = s->bandwidth_limit;
>>>> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
>>>> +
>>>> +    memcpy(enabled_capabilities, s->enabled_capabilities,
>>>> +           sizeof(enabled_capabilities));
>>>>
>>>>      memset(s, 0, sizeof(*s));
>>>>      s->bandwidth_limit = bandwidth_limit;
>>>>      s->params = *params;
>>>> +    memcpy(s->enabled_capabilities, enabled_capabilities,
>>>> +           sizeof(enabled_capabilities));
>>>>
>>>> -    s->bandwidth_limit = bandwidth_limit;
>>>>      s->state = MIG_STATE_SETUP;
>>>>      s->total_time = qemu_get_clock_ms(rt_clock);
>>>>
>>>> diff --git a/migration.h b/migration.h
>>>> index 57572a6..713aae0 100644
>>>> --- a/migration.h
>>>> +++ b/migration.h
>>>> @@ -19,6 +19,7 @@
>>>>  #include "notify.h"
>>>>  #include "error.h"
>>>>  #include "vmstate.h"
>>>> +#include "qapi-types.h"
>>>>
>>>>  struct MigrationParams {
>>>>      bool blk;
>>>> @@ -39,6 +40,7 @@ struct MigrationState
>>>>      void *opaque;
>>>>      MigrationParams params;
>>>>      int64_t total_time;
>>>> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
>>>>  };
>>>>
>>>>  void process_incoming_migration(QEMUFile *f);
>>>> diff --git a/monitor.c b/monitor.c
>>>> index f6107ba..e2be6cd 100644
>>>> --- a/monitor.c
>>>> +++ b/monitor.c
>>>> @@ -2687,6 +2687,13 @@ static mon_cmd_t info_cmds[] = {
>>>>          .mhandler.info = hmp_info_migrate,
>>>>      },
>>>>      {
>>>> +        .name       = "migration_capabilities",
>>>
>>> migration-capabilities is better.
>> ok
>>>
>>>> +        .args_type  = "",
>>>> +        .params     = "",
>>>> +        .help       = "show migration capabilities",
>>>> +        .mhandler.info = hmp_info_migration_capabilities,
>>>> +    },
>>>> +    {
>>>>          .name       = "balloon",
>>>>          .args_type  = "",
>>>>          .params     = "",
>>>> diff --git a/qapi-schema.json b/qapi-schema.json
>>>> index 1ab5dbd..a8408fd 100644
>>>> --- a/qapi-schema.json
>>>> +++ b/qapi-schema.json
>>>> @@ -288,11 +288,15 @@
>>>>  #        status, only returned if status is 'active' and it is a block
>>>>  #        migration
>>>>  #
>>>> -# Since: 0.14.0
>>>> +# @capabilities: #optional a list describing all the migration capabilities
>>>> +#                state
>>>
>>> I don't think this is needed, as I've said above.
>> see above
>>>
>>>> +#
>>>> +# Since: 0.14.0, 'capabilities' since 1.2
>>>>  ##
>>>>  { 'type': 'MigrationInfo',
>>>>    'data': {'*status': 'str', '*ram': 'MigrationStats',
>>>> -           '*disk': 'MigrationStats'} }
>>>> +           '*disk': 'MigrationStats',
>>>> +           '*capabilities': ['MigrationCapabilityInfo']} }
>>>>
>>>>  ##
>>>>  # @query-migrate
>>>> @@ -306,6 +310,51 @@
>>>>  { 'command': 'query-migrate', 'returns': 'MigrationInfo' }
>>>>
>>>>  ##
>>>> +# @MigrationCapability
>>>> +#
>>>> +# Migration capabilities enumeration
>>>> +#
>>>> +# @xbzrle: current migration supports xbzrle
>>>
>>> You should explain what xbzrle is.
>> ok
>>>
>>>> +#
>>>> +# Since: 1.2
>>>> +##
>>>> +{ 'enum': 'MigrationCapability',
>>>> +  'data': ['xbzrle'] }
>>>> +
>>>> +##
>>>> +# @MigrationCapabilityInfo
>>>
>>> MigrationCapabilityStatus?
>>>
>>>> +#
>>>> +# Migration capability information
>>>> +#
>>>> +# @capability: capability enum
>>>
>>> Please, document state too.
>> ok
>>
>> Orit
>>>
>>>> +#
>>>> +# Since: 1.2
>>>> +##
>>>> +{ 'type': 'MigrationCapabilityInfo',
>>>> +  'data': { 'capability' : 'MigrationCapability', 'state' : 'bool' } }
>>>> +
>>>> +##
>>>> +# @query-migration-capabilities
>>>> +#
>>>> +# Returns information about current migration process capabilties.
>>>> +#
>>>> +# Returns: @MigrationCapabilityInfo list
>>>> +#
>>>> +# Since: 1.2
>>>> +##
>>>> +{ 'command': 'query-migration-capabilities', 'returns': ['MigrationCapabilityInfo'] }
>>>> +
>>>> +##
>>>> +# @migrate_set_parameters
>>>> +#
>>>> +# Enable/Disable the following migration capabilities (like xbzrle)
>>>> +#
>>>> +# Since: 1.2
>>>> +##
>>>> +{ 'command': 'migrate-set-parameters',
>>>> +  'data': { 'capabilities': ['MigrationCapabilityInfo'] } }
>>>> +
>>>> +##
>>>>  # @MouseInfo:
>>>>  #
>>>>  # Information about a mouse device.
>>>> diff --git a/qmp-commands.hx b/qmp-commands.hx
>>>> index 2e1a38e..3ee6e00 100644
>>>> --- a/qmp-commands.hx
>>>> +++ b/qmp-commands.hx
>>>> @@ -2075,18 +2075,31 @@ The main json-object contains the following:
>>>>           - "transferred": amount transferred (json-int)
>>>>           - "remaining": amount remaining (json-int)
>>>>           - "total": total (json-int)
>>>> -
>>>> +- "capabilities": migration capabilities state
>>>> +         - "xbzrle" : XBZRLE state (json-bool)
>>>>  Examples:
>>>>
>>>>  1. Before the first migration
>>>>
>>>>  -> { "execute": "query-migrate" }
>>>> -<- { "return": {} }
>>>> +<- { "return": {
>>>> +        "capabilities" :  [ { "capability" : "xbzrle", "state" : false } ]
>>>> +     }
>>>> +   }
>>>>
>>>>  2. Migration is done and has succeeded
>>>>
>>>>  -> { "execute": "query-migrate" }
>>>> -<- { "return": { "status": "completed" } }
>>>> +<- { "return": {
>>>> +        "status": "completed",
>>>> +        "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
>>>> +        "ram":{
>>>> +          "transferred":123,
>>>> +          "remaining":123,
>>>> +          "total":246
>>>> +        }
>>>> +     }
>>>> +   }
>>>>
>>>>  3. Migration is done and has failed
>>>>
>>>> @@ -2099,6 +2112,7 @@ Examples:
>>>>  <- {
>>>>        "return":{
>>>>           "status":"active",
>>>> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
>>>>           "ram":{
>>>>              "transferred":123,
>>>>              "remaining":123,
>>>> @@ -2113,6 +2127,7 @@ Examples:
>>>>  <- {
>>>>        "return":{
>>>>           "status":"active",
>>>> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
>>>>           "ram":{
>>>>              "total":1057024,
>>>>              "remaining":1053304,
>>>> @@ -2135,6 +2150,56 @@ EQMP
>>>>      },
>>>>
>>>>  SQMP
>>>> +query-migration-capabilities
>>>> +-------
>>>> +
>>>> +Query migration capabilities
>>>> +
>>>> +- "xbzrle": xbzrle support
>>>> +
>>>> +Arguments:
>>>> +
>>>> +Example:
>>>> +
>>>> +-> { "execute": "query-migration-capabilities"}
>>>> +<- { "return": [ { "capability": "xbzrle", "state": true },
>>>> +                 { "capability": "foobar", "state": false } ] }
>>>> +
>>>> +EQMP
>>>> +
>>>> +    {
>>>> +        .name       = "query-migration-capabilities",
>>>> +        .args_type  = "",
>>>> +	.mhandler.cmd_new = qmp_marshal_input_query_migration_capabilities,
>>>> +    },
>>>> +
>>>> +SQMP
>>>> +migrate_set_parameters
>>>> +-------
>>>> +
>>>> +Enable/Disable migration capabilities
>>>> +
>>>> +- "xbzrle": xbzrle support
>>>> +
>>>> +Arguments:
>>>> +
>>>> +Example:
>>>> +
>>>> +-> { "execute": "migrate_set_parameters" , "arguments":
>>>> +     { "parameters": [ { "capability": "xbzrle", "state": true } ] } }
>>>> +
>>>> +EQMP
>>>> +
>>>> +    {
>>>> +        .name       = "migrate_set_parameters",
>>>> +        .args_type  = "parameters:O",
>>>> +	.params     = "capability:s,state:b",
>>>> +	.mhandler.cmd_new = qmp_marshal_input_migrate_set_parameters,
>>>> +    },
>>>> +
>>>> +
>>>> +
>>>> +SQMP
>>>>  query-balloon
>>>>  -------------
>>>>
>>>
>>
>>
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH 13/22] Add migration capabilities
  2012-07-24 17:06         ` Orit Wasserman
@ 2012-07-24 18:17           ` Luiz Capitulino
  2012-07-25 13:05             ` Orit Wasserman
  0 siblings, 1 reply; 35+ messages in thread
From: Luiz Capitulino @ 2012-07-24 18:17 UTC (permalink / raw)
  To: Orit Wasserman; +Cc: Eric Blake, qemu-devel, Juan Quintela

On Tue, 24 Jul 2012 20:06:06 +0300
Orit Wasserman <owasserm@redhat.com> wrote:

> On 07/24/2012 03:50 PM, Luiz Capitulino wrote:
> > On Tue, 24 Jul 2012 09:25:11 +0300
> > Orit Wasserman <owasserm@redhat.com> wrote:
> > 
> >> On 07/23/2012 09:23 PM, Luiz Capitulino wrote:
> >>> On Fri, 13 Jul 2012 09:23:35 +0200
> >>> Juan Quintela <quintela@redhat.com> wrote:
> >>>
> >>>> From: Orit Wasserman <owasserm@redhat.com>
> >>>>
> >>>> Add migration capabilities that can be queried by the management.
> >>>> The management can query the source QEMU and the destination QEMU in order to
> >>>> verify both support some migration capability (currently only XBZRLE).
> >>>> The management can enable a capability for the next migration by using
> >>>> migrate_set_parameter command.
> >>>
> >>> Please, split this into one command per-patch. Otherwise it's difficult to
> >>> review.
> >>>
> >> Sure.
> >>> Have libvirt folks acked this approach btw? It looks fine to me, but we need
> >>> their ack too.
> >>>
> >>> More comments below.
> >>>
> >>>>
> >>>> Signed-off-by: Orit Wasserman <owasserm@redhat.com>
> >>>> Signed-off-by: Juan Quintela <quintela@redhat.com>
> >>>> ---
> >>>>  hmp-commands.hx  |   16 ++++++++++++
> >>>>  hmp.c            |   64 ++++++++++++++++++++++++++++++++++++++++++++++++
> >>>>  hmp.h            |    2 ++
> >>>>  migration.c      |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
> >>>>  migration.h      |    2 ++
> >>>>  monitor.c        |    7 ++++++
> >>>>  qapi-schema.json |   53 ++++++++++++++++++++++++++++++++++++++--
> >>>>  qmp-commands.hx  |   71 ++++++++++++++++++++++++++++++++++++++++++++++++++---
> >>>>  8 files changed, 280 insertions(+), 7 deletions(-)
> >>>>
> >>>> diff --git a/hmp-commands.hx b/hmp-commands.hx
> >>>> index f5d9d91..9245bef 100644
> >>>> --- a/hmp-commands.hx
> >>>> +++ b/hmp-commands.hx
> >>>> @@ -861,6 +861,20 @@ Set maximum tolerated downtime (in seconds) for migration.
> >>>>  ETEXI
> >>>>
> >>>>      {
> >>>> +        .name       = "migrate_set_parameter",
> >>>> +        .args_type  = "capability:s,state:b",
> >>>> +        .params     = "",
> >>>
> >>> Please, fill in params.
> >> ok
> >>>
> >>>> +        .help       = "Enable/Disable the usage of a capability for migration",
> >>>> +        .mhandler.cmd = hmp_migrate_set_parameter,
> >>>> +    },
> >>>> +
> >>>> +STEXI
> >>>> +@item migrate_set_parameter @var{capability} @var{state}
> >>>> +@findex migrate_set_parameter
> >>>> +Enable/Disable the usage of a capability @var{capability} for migration.
> >>>> +ETEXI
> >>>> +
> >>>> +    {
> >>>>          .name       = "client_migrate_info",
> >>>>          .args_type  = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
> >>>>          .params     = "protocol hostname port tls-port cert-subject",
> >>>> @@ -1419,6 +1433,8 @@ show CPU statistics
> >>>>  show user network stack connection states
> >>>>  @item info migrate
> >>>>  show migration status
> >>>> +@item info migration_capabilities
> >>>> +show migration capabilities
> >>>>  @item info balloon
> >>>>  show balloon information
> >>>>  @item info qtree
> >>>> diff --git a/hmp.c b/hmp.c
> >>>> index 4c6d4ae..b0440e6 100644
> >>>> --- a/hmp.c
> >>>> +++ b/hmp.c
> >>>> @@ -131,9 +131,19 @@ void hmp_info_mice(Monitor *mon)
> >>>>  void hmp_info_migrate(Monitor *mon)
> >>>>  {
> >>>>      MigrationInfo *info;
> >>>> +    MigrationCapabilityInfoList *cap;
> >>>>
> >>>>      info = qmp_query_migrate(NULL);
> >>>>
> >>>> +    if (info->has_capabilities && info->capabilities) {
> >>>> +        monitor_printf(mon, "capabilities: ");
> >>>> +        for (cap = info->capabilities; cap; cap = cap->next) {
> >>>> +            monitor_printf(mon, "%s: %s ",
> >>>> +                           MigrationCapability_lookup[cap->value->capability],
> >>>> +                           cap->value->state ? "on" : "off");
> >>>> +        }
> >>>> +        monitor_printf(mon, "\n");
> >>>
> >>> Why is this is needed? Isn't info migration-capabilities good enough?
> >>> Besides, info migrate should only contain information about current migration
> >>> process...
> >> The reason we introduced capabilities is that xbzrle needs for both source and destination QEMU
> >> to be able to handle it. Even if both side support xbzrle the user may decide not to use it.
> >> User that wants to use xbzrle needs to check that both sides have support for it (using info capabilities) , than 
> >> enable it in both sides (using migrate-set-parameter/s commands). This is a parameter for the current migration.
> >> So the user needs to know if xbzrle was enabled or disabled for the current migration, this code displays it.
> > 
> > But this is being returned even when there is no migration taking place. I'm
> > answering this here in the 'info migrate' hunk, but my main concern is
> > query-migrate.
> I think migration configuration is part of migration information and should be available to the user in 'info migration'. Especially before he starts running it to see if he need to change the configuration. Also it is very helpful for the user during the migration and also after it's completes.

I don't mind having this in 'info migrate' because we can change it later case
we regret, so it's fine with me having that info there.

But query-migrate is different, once we add anything there it's there forever,
so we have to be careful.

> There is no way today to query  the downtime and migration speed at all . you can always set them again
> but there is no way to know how it is configured.

Good point, but I think it's better to concentrate on the needs of xbzrle
for now.

> I was actually thinking to add them to info migration (also for SETUP).

But we're in SETUP state only once, what happens if the settings are changed
after a migration completed? How is mngt going to be able to query it?

My advice to not delay the inclusion of this series is to drop the
information from SETUP state and rely on query-migration-capabilities in
'info migrate'. We can extend later if needed (by means of new commands or
adding info to query-migrate).

> > 
> > query-migrate shouldn't return this info in MIG_STATE_SETUP, and for
> > MIG_STATE_COMPLETED it should only be returned if there was a migration.
> I think is case of MIG_SETUP we should return the configuration and params for the next migration. 
> MIG_STATE_COMPLETED happens only if there was a successful migration other wise there will be another state.
> > 
> >> some day when there will be better migration protocol, feature negotiations can be part of it ...
> >>>
> >>>> +    }
> >>>>      if (info->has_status) {
> >>>>          monitor_printf(mon, "Migration status: %s\n", info->status);
> >>>>      }
> >>>> @@ -161,6 +171,25 @@ void hmp_info_migrate(Monitor *mon)
> >>>>      qapi_free_MigrationInfo(info);
> >>>>  }
> >>>>
> >>>> +void hmp_info_migration_capabilities(Monitor *mon)
> >>>> +{
> >>>> +    MigrationCapabilityInfoList *caps_list, *cap;
> >>>> +
> >>>> +    caps_list = qmp_query_migration_capabilities(NULL);
> >>>> +    if (!caps_list) {
> >>>> +        monitor_printf(mon, "No migration capabilities found\n");
> >>>> +        return;
> >>>> +    }
> >>>> +
> >>>> +    for (cap = caps_list; cap; cap = cap->next) {
> >>>> +        monitor_printf(mon, "%s: %s ",
> >>>> +                       MigrationCapability_lookup[cap->value->capability],
> >>>> +                       cap->value->state ? "on" : "off");
> >>>> +    }
> >>>> +
> >>>> +    qapi_free_MigrationCapabilityInfoList(caps_list);
> >>>> +}
> >>>> +
> >>>>  void hmp_info_cpus(Monitor *mon)
> >>>>  {
> >>>>      CpuInfoList *cpu_list, *cpu;
> >>>> @@ -735,6 +764,41 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
> >>>>      qmp_migrate_set_speed(value, NULL);
> >>>>  }
> >>>>
> >>>> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
> >>>> +{
> >>>> +    const char *cap = qdict_get_str(qdict, "capability");
> >>>> +    bool state = qdict_get_bool(qdict, "state");
> >>>> +    Error *err = NULL;
> >>>> +    MigrationCapabilityInfoList *params = NULL;
> >>>> +    int i;
> >>>> +
> >>>> +    for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> >>>> +        if (strcmp(cap, MigrationCapability_lookup[i]) == 0) {
> >>>> +            if (!params) {
> >>>> +                params = g_malloc0(sizeof(*params));
> >>>> +            }
> >>>> +            params->value = g_malloc0(sizeof(*params->value));
> >>>> +            params->value->capability = i;
> >>>> +            params->value->state = state;
> >>>> +            params->next = NULL;
> >>>> +            qmp_migrate_set_parameters(params, &err);
> >>>> +            break;
> >>>> +        }
> >>>> +    }
> >>>> +
> >>>> +    if (i == MIGRATION_CAPABILITY_MAX) {
> >>>> +        error_set(&err, QERR_INVALID_PARAMETER, cap);
> >>>> +    }
> >>>> +
> >>>> +    qapi_free_MigrationCapabilityInfoList(params);
> >>>> +
> >>>> +    if (err) {
> >>>> +        monitor_printf(mon, "migrate_set_parameter: %s\n",
> >>>> +                       error_get_pretty(err));
> >>>> +        error_free(err);
> >>>> +    }
> >>>> +}
> >>>> +
> >>>>  void hmp_set_password(Monitor *mon, const QDict *qdict)
> >>>>  {
> >>>>      const char *protocol  = qdict_get_str(qdict, "protocol");
> >>>> diff --git a/hmp.h b/hmp.h
> >>>> index 79d138d..09ba198 100644
> >>>> --- a/hmp.h
> >>>> +++ b/hmp.h
> >>>> @@ -25,6 +25,7 @@ void hmp_info_uuid(Monitor *mon);
> >>>>  void hmp_info_chardev(Monitor *mon);
> >>>>  void hmp_info_mice(Monitor *mon);
> >>>>  void hmp_info_migrate(Monitor *mon);
> >>>> +void hmp_info_migration_capabilities(Monitor *mon);
> >>>>  void hmp_info_cpus(Monitor *mon);
> >>>>  void hmp_info_block(Monitor *mon);
> >>>>  void hmp_info_blockstats(Monitor *mon);
> >>>> @@ -51,6 +52,7 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
> >>>>  void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
> >>>>  void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
> >>>>  void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
> >>>> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict);
> >>>>  void hmp_set_password(Monitor *mon, const QDict *qdict);
> >>>>  void hmp_expire_password(Monitor *mon, const QDict *qdict);
> >>>>  void hmp_eject(Monitor *mon, const QDict *qdict);
> >>>> diff --git a/migration.c b/migration.c
> >>>> index 8db1b43..fd004d7 100644
> >>>> --- a/migration.c
> >>>> +++ b/migration.c
> >>>> @@ -117,12 +117,36 @@ MigrationInfo *qmp_query_migrate(Error **errp)
> >>>>  {
> >>>>      MigrationInfo *info = g_malloc0(sizeof(*info));
> >>>>      MigrationState *s = migrate_get_current();
> >>>> +    int i;
> >>>>
> >>>>      switch (s->state) {
> >>>>      case MIG_STATE_SETUP:
> >>>> -        /* no migration has happened ever */
> >>>> +        /* no migration has ever happened; show enabled capabilities */
> >>>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> >>>> +            if (!info->has_capabilities) {
> >>>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
> >>>> +                info->has_capabilities = true;
> >>>> +            }
> >>>> +            info->capabilities->value =
> >>>> +                g_malloc(sizeof(*info->capabilities->value));
> >>>> +            info->capabilities->value->capability = i;
> >>>> +            info->capabilities->value->state = s->enabled_capabilities[i];
> >>>> +            info->capabilities->next = NULL;
> >>>> +        }
> >>>>          break;
> >>>>      case MIG_STATE_ACTIVE:
> >>>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> >>>> +            if (!info->has_capabilities) {
> >>>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
> >>>> +                info->has_capabilities = true;
> >>>> +            }
> >>>> +            info->capabilities->value =
> >>>> +                g_malloc(sizeof(*info->capabilities->value));
> >>>> +            info->capabilities->value->capability = i;
> >>>> +            info->capabilities->value->state = s->enabled_capabilities[i];
> >>>> +            info->capabilities->next = NULL;
> >>>> +        }
> >>>> +
> >>>>          info->has_status = true;
> >>>>          info->status = g_strdup("active");
> >>>>
> >>>> @@ -143,6 +167,18 @@ MigrationInfo *qmp_query_migrate(Error **errp)
> >>>>          }
> >>>>          break;
> >>>>      case MIG_STATE_COMPLETED:
> >>>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> >>>> +            if (!info->has_capabilities) {
> >>>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
> >>>> +                info->has_capabilities = true;
> >>>> +            }
> >>>> +            info->capabilities->value =
> >>>> +                g_malloc(sizeof(*info->capabilities->value));
> >>>> +            info->capabilities->value->capability = i;
> >>>> +            info->capabilities->value->state = s->enabled_capabilities[i];
> >>>> +            info->capabilities->next = NULL;
> >>>> +        }
> >>>
> >>> Code triplication :)
> >>>
> >>> Why is this is needed? Isn't query-migration-capabilities good enough?
> >>> Besides, query-migrate should only contain information about current migration
> >>> process...
> >>>
> >> see above
> >>>> +
> >>>>          info->has_status = true;
> >>>>          info->status = g_strdup("completed");
> >>>>
> >>>> @@ -166,6 +202,33 @@ MigrationInfo *qmp_query_migrate(Error **errp)
> >>>>      return info;
> >>>>  }
> >>>>
> >>>> +MigrationCapabilityInfoList *qmp_query_migration_capabilities(Error **errp)
> >>>> +{
> >>>> +    MigrationCapabilityInfoList *caps_list = g_malloc0(sizeof(*caps_list));
> >>>> +
> >>>> +    caps_list->value = g_malloc(sizeof(*caps_list->value));
> >>>> +    caps_list->value->capability = MIGRATION_CAPABILITY_XBZRLE;
> >>>> +    caps_list->next = NULL;
> >>>
> >>> Shouldn't this get the capabilities array from migrate_get_current()?
> >>>
> >>> I mean, this makes query-migration-capabilities always return true for
> >>> xbzrle, even if we set it to off.
> >> Those are the general capabilities , if I want to use xbzrle ,does this QEMU support it ? 
> >> this is required because we need both the destination and the source to be able to handle it.
> >> If one QEMU doesn't (older version of qemu, maybe it will be disbaled for certain architectures) we can't use the feature.
> >> This is for management and the user to check what migration capabilities this QEMU supports.
> > 
> > My point was that query-migration-capabilities will always return state=true
> > for all existing capabilities. Don't we want users/mngt app to know which
> > states have been turned off?
> You need to separate the ability to use a capability and actually using it for migration. Also I don't want the user to be able to set some capability on, it depends on the code not user configuration.

Not sure I can follow, a user can set xbzrle capability to off, no?

> And for xbzrle we always return true for some version on QEMU , let say we decide we want to remove the code that implement it for some reason in some future version than it will return false.

That's fine (I mean, I'm not sure we will ever do it, but shouldn't be a problem
if we do).

> There is a reason the command that enables a capability for a migration was called 'migrate-set-parameter' and not set-capability to distinguish between them one is per QEMU code base and one is per migration.
> 
> Regards,
> Orit
> > 
> >>
> >>>
> >>>> +
> >>>> +    return caps_list;
> >>>> +}
> >>>> +
> >>>> +void qmp_migrate_set_parameters(MigrationCapabilityInfoList *params,
> >>>> +                                Error **errp)
> >>>> +{
> >>>> +    MigrationState *s = migrate_get_current();
> >>>> +    MigrationCapabilityInfoList *cap;
> >>>> +
> >>>> +    if (s->state == MIG_STATE_ACTIVE) {
> >>>> +        error_set(errp, QERR_MIGRATION_ACTIVE);
> >>>> +        return;
> >>>> +    }
> >>>> +
> >>>> +    for (cap = params; cap; cap = cap->next) {
> >>>> +        s->enabled_capabilities[cap->value->capability] = cap->value->state;
> >>>> +    }
> >>>> +}
> >>>> +
> >>>>  /* shared migration helpers */
> >>>>
> >>>>  static int migrate_fd_cleanup(MigrationState *s)
> >>>> @@ -375,12 +438,17 @@ static MigrationState *migrate_init(const MigrationParams *params)
> >>>>  {
> >>>>      MigrationState *s = migrate_get_current();
> >>>>      int64_t bandwidth_limit = s->bandwidth_limit;
> >>>> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
> >>>> +
> >>>> +    memcpy(enabled_capabilities, s->enabled_capabilities,
> >>>> +           sizeof(enabled_capabilities));
> >>>>
> >>>>      memset(s, 0, sizeof(*s));
> >>>>      s->bandwidth_limit = bandwidth_limit;
> >>>>      s->params = *params;
> >>>> +    memcpy(s->enabled_capabilities, enabled_capabilities,
> >>>> +           sizeof(enabled_capabilities));
> >>>>
> >>>> -    s->bandwidth_limit = bandwidth_limit;
> >>>>      s->state = MIG_STATE_SETUP;
> >>>>      s->total_time = qemu_get_clock_ms(rt_clock);
> >>>>
> >>>> diff --git a/migration.h b/migration.h
> >>>> index 57572a6..713aae0 100644
> >>>> --- a/migration.h
> >>>> +++ b/migration.h
> >>>> @@ -19,6 +19,7 @@
> >>>>  #include "notify.h"
> >>>>  #include "error.h"
> >>>>  #include "vmstate.h"
> >>>> +#include "qapi-types.h"
> >>>>
> >>>>  struct MigrationParams {
> >>>>      bool blk;
> >>>> @@ -39,6 +40,7 @@ struct MigrationState
> >>>>      void *opaque;
> >>>>      MigrationParams params;
> >>>>      int64_t total_time;
> >>>> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
> >>>>  };
> >>>>
> >>>>  void process_incoming_migration(QEMUFile *f);
> >>>> diff --git a/monitor.c b/monitor.c
> >>>> index f6107ba..e2be6cd 100644
> >>>> --- a/monitor.c
> >>>> +++ b/monitor.c
> >>>> @@ -2687,6 +2687,13 @@ static mon_cmd_t info_cmds[] = {
> >>>>          .mhandler.info = hmp_info_migrate,
> >>>>      },
> >>>>      {
> >>>> +        .name       = "migration_capabilities",
> >>>
> >>> migration-capabilities is better.
> >> ok
> >>>
> >>>> +        .args_type  = "",
> >>>> +        .params     = "",
> >>>> +        .help       = "show migration capabilities",
> >>>> +        .mhandler.info = hmp_info_migration_capabilities,
> >>>> +    },
> >>>> +    {
> >>>>          .name       = "balloon",
> >>>>          .args_type  = "",
> >>>>          .params     = "",
> >>>> diff --git a/qapi-schema.json b/qapi-schema.json
> >>>> index 1ab5dbd..a8408fd 100644
> >>>> --- a/qapi-schema.json
> >>>> +++ b/qapi-schema.json
> >>>> @@ -288,11 +288,15 @@
> >>>>  #        status, only returned if status is 'active' and it is a block
> >>>>  #        migration
> >>>>  #
> >>>> -# Since: 0.14.0
> >>>> +# @capabilities: #optional a list describing all the migration capabilities
> >>>> +#                state
> >>>
> >>> I don't think this is needed, as I've said above.
> >> see above
> >>>
> >>>> +#
> >>>> +# Since: 0.14.0, 'capabilities' since 1.2
> >>>>  ##
> >>>>  { 'type': 'MigrationInfo',
> >>>>    'data': {'*status': 'str', '*ram': 'MigrationStats',
> >>>> -           '*disk': 'MigrationStats'} }
> >>>> +           '*disk': 'MigrationStats',
> >>>> +           '*capabilities': ['MigrationCapabilityInfo']} }
> >>>>
> >>>>  ##
> >>>>  # @query-migrate
> >>>> @@ -306,6 +310,51 @@
> >>>>  { 'command': 'query-migrate', 'returns': 'MigrationInfo' }
> >>>>
> >>>>  ##
> >>>> +# @MigrationCapability
> >>>> +#
> >>>> +# Migration capabilities enumeration
> >>>> +#
> >>>> +# @xbzrle: current migration supports xbzrle
> >>>
> >>> You should explain what xbzrle is.
> >> ok
> >>>
> >>>> +#
> >>>> +# Since: 1.2
> >>>> +##
> >>>> +{ 'enum': 'MigrationCapability',
> >>>> +  'data': ['xbzrle'] }
> >>>> +
> >>>> +##
> >>>> +# @MigrationCapabilityInfo
> >>>
> >>> MigrationCapabilityStatus?
> >>>
> >>>> +#
> >>>> +# Migration capability information
> >>>> +#
> >>>> +# @capability: capability enum
> >>>
> >>> Please, document state too.
> >> ok
> >>
> >> Orit
> >>>
> >>>> +#
> >>>> +# Since: 1.2
> >>>> +##
> >>>> +{ 'type': 'MigrationCapabilityInfo',
> >>>> +  'data': { 'capability' : 'MigrationCapability', 'state' : 'bool' } }
> >>>> +
> >>>> +##
> >>>> +# @query-migration-capabilities
> >>>> +#
> >>>> +# Returns information about current migration process capabilties.
> >>>> +#
> >>>> +# Returns: @MigrationCapabilityInfo list
> >>>> +#
> >>>> +# Since: 1.2
> >>>> +##
> >>>> +{ 'command': 'query-migration-capabilities', 'returns': ['MigrationCapabilityInfo'] }
> >>>> +
> >>>> +##
> >>>> +# @migrate_set_parameters
> >>>> +#
> >>>> +# Enable/Disable the following migration capabilities (like xbzrle)
> >>>> +#
> >>>> +# Since: 1.2
> >>>> +##
> >>>> +{ 'command': 'migrate-set-parameters',
> >>>> +  'data': { 'capabilities': ['MigrationCapabilityInfo'] } }
> >>>> +
> >>>> +##
> >>>>  # @MouseInfo:
> >>>>  #
> >>>>  # Information about a mouse device.
> >>>> diff --git a/qmp-commands.hx b/qmp-commands.hx
> >>>> index 2e1a38e..3ee6e00 100644
> >>>> --- a/qmp-commands.hx
> >>>> +++ b/qmp-commands.hx
> >>>> @@ -2075,18 +2075,31 @@ The main json-object contains the following:
> >>>>           - "transferred": amount transferred (json-int)
> >>>>           - "remaining": amount remaining (json-int)
> >>>>           - "total": total (json-int)
> >>>> -
> >>>> +- "capabilities": migration capabilities state
> >>>> +         - "xbzrle" : XBZRLE state (json-bool)
> >>>>  Examples:
> >>>>
> >>>>  1. Before the first migration
> >>>>
> >>>>  -> { "execute": "query-migrate" }
> >>>> -<- { "return": {} }
> >>>> +<- { "return": {
> >>>> +        "capabilities" :  [ { "capability" : "xbzrle", "state" : false } ]
> >>>> +     }
> >>>> +   }
> >>>>
> >>>>  2. Migration is done and has succeeded
> >>>>
> >>>>  -> { "execute": "query-migrate" }
> >>>> -<- { "return": { "status": "completed" } }
> >>>> +<- { "return": {
> >>>> +        "status": "completed",
> >>>> +        "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
> >>>> +        "ram":{
> >>>> +          "transferred":123,
> >>>> +          "remaining":123,
> >>>> +          "total":246
> >>>> +        }
> >>>> +     }
> >>>> +   }
> >>>>
> >>>>  3. Migration is done and has failed
> >>>>
> >>>> @@ -2099,6 +2112,7 @@ Examples:
> >>>>  <- {
> >>>>        "return":{
> >>>>           "status":"active",
> >>>> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
> >>>>           "ram":{
> >>>>              "transferred":123,
> >>>>              "remaining":123,
> >>>> @@ -2113,6 +2127,7 @@ Examples:
> >>>>  <- {
> >>>>        "return":{
> >>>>           "status":"active",
> >>>> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
> >>>>           "ram":{
> >>>>              "total":1057024,
> >>>>              "remaining":1053304,
> >>>> @@ -2135,6 +2150,56 @@ EQMP
> >>>>      },
> >>>>
> >>>>  SQMP
> >>>> +query-migration-capabilities
> >>>> +-------
> >>>> +
> >>>> +Query migration capabilities
> >>>> +
> >>>> +- "xbzrle": xbzrle support
> >>>> +
> >>>> +Arguments:
> >>>> +
> >>>> +Example:
> >>>> +
> >>>> +-> { "execute": "query-migration-capabilities"}
> >>>> +<- { "return": [ { "capability": "xbzrle", "state": true },
> >>>> +                 { "capability": "foobar", "state": false } ] }
> >>>> +
> >>>> +EQMP
> >>>> +
> >>>> +    {
> >>>> +        .name       = "query-migration-capabilities",
> >>>> +        .args_type  = "",
> >>>> +	.mhandler.cmd_new = qmp_marshal_input_query_migration_capabilities,
> >>>> +    },
> >>>> +
> >>>> +SQMP
> >>>> +migrate_set_parameters
> >>>> +-------
> >>>> +
> >>>> +Enable/Disable migration capabilities
> >>>> +
> >>>> +- "xbzrle": xbzrle support
> >>>> +
> >>>> +Arguments:
> >>>> +
> >>>> +Example:
> >>>> +
> >>>> +-> { "execute": "migrate_set_parameters" , "arguments":
> >>>> +     { "parameters": [ { "capability": "xbzrle", "state": true } ] } }
> >>>> +
> >>>> +EQMP
> >>>> +
> >>>> +    {
> >>>> +        .name       = "migrate_set_parameters",
> >>>> +        .args_type  = "parameters:O",
> >>>> +	.params     = "capability:s,state:b",
> >>>> +	.mhandler.cmd_new = qmp_marshal_input_migrate_set_parameters,
> >>>> +    },
> >>>> +
> >>>> +
> >>>> +
> >>>> +SQMP
> >>>>  query-balloon
> >>>>  -------------
> >>>>
> >>>
> >>
> >>
> > 
> 
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH 13/22] Add migration capabilities
  2012-07-24 18:17           ` Luiz Capitulino
@ 2012-07-25 13:05             ` Orit Wasserman
  2012-07-25 13:11               ` Luiz Capitulino
  0 siblings, 1 reply; 35+ messages in thread
From: Orit Wasserman @ 2012-07-25 13:05 UTC (permalink / raw)
  To: Luiz Capitulino; +Cc: Eric Blake, qemu-devel, Juan Quintela

On 07/24/2012 09:17 PM, Luiz Capitulino wrote:
> On Tue, 24 Jul 2012 20:06:06 +0300
> Orit Wasserman <owasserm@redhat.com> wrote:
> 
>> On 07/24/2012 03:50 PM, Luiz Capitulino wrote:
>>> On Tue, 24 Jul 2012 09:25:11 +0300
>>> Orit Wasserman <owasserm@redhat.com> wrote:
>>>
>>>> On 07/23/2012 09:23 PM, Luiz Capitulino wrote:
>>>>> On Fri, 13 Jul 2012 09:23:35 +0200
>>>>> Juan Quintela <quintela@redhat.com> wrote:
>>>>>
>>>>>> From: Orit Wasserman <owasserm@redhat.com>
>>>>>>
>>>>>> Add migration capabilities that can be queried by the management.
>>>>>> The management can query the source QEMU and the destination QEMU in order to
>>>>>> verify both support some migration capability (currently only XBZRLE).
>>>>>> The management can enable a capability for the next migration by using
>>>>>> migrate_set_parameter command.
>>>>>
>>>>> Please, split this into one command per-patch. Otherwise it's difficult to
>>>>> review.
>>>>>
>>>> Sure.
>>>>> Have libvirt folks acked this approach btw? It looks fine to me, but we need
>>>>> their ack too.
>>>>>
>>>>> More comments below.
>>>>>
>>>>>>
>>>>>> Signed-off-by: Orit Wasserman <owasserm@redhat.com>
>>>>>> Signed-off-by: Juan Quintela <quintela@redhat.com>
>>>>>> ---
>>>>>>  hmp-commands.hx  |   16 ++++++++++++
>>>>>>  hmp.c            |   64 ++++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>  hmp.h            |    2 ++
>>>>>>  migration.c      |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
>>>>>>  migration.h      |    2 ++
>>>>>>  monitor.c        |    7 ++++++
>>>>>>  qapi-schema.json |   53 ++++++++++++++++++++++++++++++++++++++--
>>>>>>  qmp-commands.hx  |   71 ++++++++++++++++++++++++++++++++++++++++++++++++++---
>>>>>>  8 files changed, 280 insertions(+), 7 deletions(-)
>>>>>>
>>>>>> diff --git a/hmp-commands.hx b/hmp-commands.hx
>>>>>> index f5d9d91..9245bef 100644
>>>>>> --- a/hmp-commands.hx
>>>>>> +++ b/hmp-commands.hx
>>>>>> @@ -861,6 +861,20 @@ Set maximum tolerated downtime (in seconds) for migration.
>>>>>>  ETEXI
>>>>>>
>>>>>>      {
>>>>>> +        .name       = "migrate_set_parameter",
>>>>>> +        .args_type  = "capability:s,state:b",
>>>>>> +        .params     = "",
>>>>>
>>>>> Please, fill in params.
>>>> ok
>>>>>
>>>>>> +        .help       = "Enable/Disable the usage of a capability for migration",
>>>>>> +        .mhandler.cmd = hmp_migrate_set_parameter,
>>>>>> +    },
>>>>>> +
>>>>>> +STEXI
>>>>>> +@item migrate_set_parameter @var{capability} @var{state}
>>>>>> +@findex migrate_set_parameter
>>>>>> +Enable/Disable the usage of a capability @var{capability} for migration.
>>>>>> +ETEXI
>>>>>> +
>>>>>> +    {
>>>>>>          .name       = "client_migrate_info",
>>>>>>          .args_type  = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
>>>>>>          .params     = "protocol hostname port tls-port cert-subject",
>>>>>> @@ -1419,6 +1433,8 @@ show CPU statistics
>>>>>>  show user network stack connection states
>>>>>>  @item info migrate
>>>>>>  show migration status
>>>>>> +@item info migration_capabilities
>>>>>> +show migration capabilities
>>>>>>  @item info balloon
>>>>>>  show balloon information
>>>>>>  @item info qtree
>>>>>> diff --git a/hmp.c b/hmp.c
>>>>>> index 4c6d4ae..b0440e6 100644
>>>>>> --- a/hmp.c
>>>>>> +++ b/hmp.c
>>>>>> @@ -131,9 +131,19 @@ void hmp_info_mice(Monitor *mon)
>>>>>>  void hmp_info_migrate(Monitor *mon)
>>>>>>  {
>>>>>>      MigrationInfo *info;
>>>>>> +    MigrationCapabilityInfoList *cap;
>>>>>>
>>>>>>      info = qmp_query_migrate(NULL);
>>>>>>
>>>>>> +    if (info->has_capabilities && info->capabilities) {
>>>>>> +        monitor_printf(mon, "capabilities: ");
>>>>>> +        for (cap = info->capabilities; cap; cap = cap->next) {
>>>>>> +            monitor_printf(mon, "%s: %s ",
>>>>>> +                           MigrationCapability_lookup[cap->value->capability],
>>>>>> +                           cap->value->state ? "on" : "off");
>>>>>> +        }
>>>>>> +        monitor_printf(mon, "\n");
>>>>>
>>>>> Why is this is needed? Isn't info migration-capabilities good enough?
>>>>> Besides, info migrate should only contain information about current migration
>>>>> process...
>>>> The reason we introduced capabilities is that xbzrle needs for both source and destination QEMU
>>>> to be able to handle it. Even if both side support xbzrle the user may decide not to use it.
>>>> User that wants to use xbzrle needs to check that both sides have support for it (using info capabilities) , than 
>>>> enable it in both sides (using migrate-set-parameter/s commands). This is a parameter for the current migration.
>>>> So the user needs to know if xbzrle was enabled or disabled for the current migration, this code displays it.
>>>
>>> But this is being returned even when there is no migration taking place. I'm
>>> answering this here in the 'info migrate' hunk, but my main concern is
>>> query-migrate.
>> I think migration configuration is part of migration information and should be available to the user in 'info migration'. Especially before he starts running it to see if he need to change the configuration. Also it is very helpful for the user during the migration and also after it's completes.
> 
> I don't mind having this in 'info migrate' because we can change it later case
> we regret, so it's fine with me having that info there.
> 
> But query-migrate is different, once we add anything there it's there forever,
> so we have to be careful.
ok I will remove them from query-migrate.
> 
>> There is no way today to query  the downtime and migration speed at all . you can always set them again
>> but there is no way to know how it is configured.
> 
> Good point, but I think it's better to concentrate on the needs of xbzrle
> for now.
> 
>> I was actually thinking to add them to info migration (also for SETUP).
> 
> But we're in SETUP state only once, what happens if the settings are changed
> after a migration completed? How is mngt going to be able to query it?
After migration completes you can't change those setting , you should get an error.

> 
> My advice to not delay the inclusion of this series is to drop the
> information from SETUP state and rely on query-migration-capabilities in
> 'info migrate'. We can extend later if needed (by means of new commands or
> adding info to query-migrate).
It needs to be another command, I can add query-migration-parameters.
You can look at this as 64bit support - you have 64bit support in the processor (this is what query-capabilities) but the OS can decide not to work in 64bit mode (do nothing) or using 64 bit (migrate-set-parameter).

Orit
> 
>>>
>>> query-migrate shouldn't return this info in MIG_STATE_SETUP, and for
>>> MIG_STATE_COMPLETED it should only be returned if there was a migration.
>> I think is case of MIG_SETUP we should return the configuration and params for the next migration. 
>> MIG_STATE_COMPLETED happens only if there was a successful migration other wise there will be another state.
>>>
>>>> some day when there will be better migration protocol, feature negotiations can be part of it ...
>>>>>
>>>>>> +    }
>>>>>>      if (info->has_status) {
>>>>>>          monitor_printf(mon, "Migration status: %s\n", info->status);
>>>>>>      }
>>>>>> @@ -161,6 +171,25 @@ void hmp_info_migrate(Monitor *mon)
>>>>>>      qapi_free_MigrationInfo(info);
>>>>>>  }
>>>>>>
>>>>>> +void hmp_info_migration_capabilities(Monitor *mon)
>>>>>> +{
>>>>>> +    MigrationCapabilityInfoList *caps_list, *cap;
>>>>>> +
>>>>>> +    caps_list = qmp_query_migration_capabilities(NULL);
>>>>>> +    if (!caps_list) {
>>>>>> +        monitor_printf(mon, "No migration capabilities found\n");
>>>>>> +        return;
>>>>>> +    }
>>>>>> +
>>>>>> +    for (cap = caps_list; cap; cap = cap->next) {
>>>>>> +        monitor_printf(mon, "%s: %s ",
>>>>>> +                       MigrationCapability_lookup[cap->value->capability],
>>>>>> +                       cap->value->state ? "on" : "off");
>>>>>> +    }
>>>>>> +
>>>>>> +    qapi_free_MigrationCapabilityInfoList(caps_list);
>>>>>> +}
>>>>>> +
>>>>>>  void hmp_info_cpus(Monitor *mon)
>>>>>>  {
>>>>>>      CpuInfoList *cpu_list, *cpu;
>>>>>> @@ -735,6 +764,41 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
>>>>>>      qmp_migrate_set_speed(value, NULL);
>>>>>>  }
>>>>>>
>>>>>> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
>>>>>> +{
>>>>>> +    const char *cap = qdict_get_str(qdict, "capability");
>>>>>> +    bool state = qdict_get_bool(qdict, "state");
>>>>>> +    Error *err = NULL;
>>>>>> +    MigrationCapabilityInfoList *params = NULL;
>>>>>> +    int i;
>>>>>> +
>>>>>> +    for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>>>>>> +        if (strcmp(cap, MigrationCapability_lookup[i]) == 0) {
>>>>>> +            if (!params) {
>>>>>> +                params = g_malloc0(sizeof(*params));
>>>>>> +            }
>>>>>> +            params->value = g_malloc0(sizeof(*params->value));
>>>>>> +            params->value->capability = i;
>>>>>> +            params->value->state = state;
>>>>>> +            params->next = NULL;
>>>>>> +            qmp_migrate_set_parameters(params, &err);
>>>>>> +            break;
>>>>>> +        }
>>>>>> +    }
>>>>>> +
>>>>>> +    if (i == MIGRATION_CAPABILITY_MAX) {
>>>>>> +        error_set(&err, QERR_INVALID_PARAMETER, cap);
>>>>>> +    }
>>>>>> +
>>>>>> +    qapi_free_MigrationCapabilityInfoList(params);
>>>>>> +
>>>>>> +    if (err) {
>>>>>> +        monitor_printf(mon, "migrate_set_parameter: %s\n",
>>>>>> +                       error_get_pretty(err));
>>>>>> +        error_free(err);
>>>>>> +    }
>>>>>> +}
>>>>>> +
>>>>>>  void hmp_set_password(Monitor *mon, const QDict *qdict)
>>>>>>  {
>>>>>>      const char *protocol  = qdict_get_str(qdict, "protocol");
>>>>>> diff --git a/hmp.h b/hmp.h
>>>>>> index 79d138d..09ba198 100644
>>>>>> --- a/hmp.h
>>>>>> +++ b/hmp.h
>>>>>> @@ -25,6 +25,7 @@ void hmp_info_uuid(Monitor *mon);
>>>>>>  void hmp_info_chardev(Monitor *mon);
>>>>>>  void hmp_info_mice(Monitor *mon);
>>>>>>  void hmp_info_migrate(Monitor *mon);
>>>>>> +void hmp_info_migration_capabilities(Monitor *mon);
>>>>>>  void hmp_info_cpus(Monitor *mon);
>>>>>>  void hmp_info_block(Monitor *mon);
>>>>>>  void hmp_info_blockstats(Monitor *mon);
>>>>>> @@ -51,6 +52,7 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
>>>>>>  void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
>>>>>>  void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
>>>>>>  void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
>>>>>> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict);
>>>>>>  void hmp_set_password(Monitor *mon, const QDict *qdict);
>>>>>>  void hmp_expire_password(Monitor *mon, const QDict *qdict);
>>>>>>  void hmp_eject(Monitor *mon, const QDict *qdict);
>>>>>> diff --git a/migration.c b/migration.c
>>>>>> index 8db1b43..fd004d7 100644
>>>>>> --- a/migration.c
>>>>>> +++ b/migration.c
>>>>>> @@ -117,12 +117,36 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>>>>>  {
>>>>>>      MigrationInfo *info = g_malloc0(sizeof(*info));
>>>>>>      MigrationState *s = migrate_get_current();
>>>>>> +    int i;
>>>>>>
>>>>>>      switch (s->state) {
>>>>>>      case MIG_STATE_SETUP:
>>>>>> -        /* no migration has happened ever */
>>>>>> +        /* no migration has ever happened; show enabled capabilities */
>>>>>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>>>>>> +            if (!info->has_capabilities) {
>>>>>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
>>>>>> +                info->has_capabilities = true;
>>>>>> +            }
>>>>>> +            info->capabilities->value =
>>>>>> +                g_malloc(sizeof(*info->capabilities->value));
>>>>>> +            info->capabilities->value->capability = i;
>>>>>> +            info->capabilities->value->state = s->enabled_capabilities[i];
>>>>>> +            info->capabilities->next = NULL;
>>>>>> +        }
>>>>>>          break;
>>>>>>      case MIG_STATE_ACTIVE:
>>>>>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>>>>>> +            if (!info->has_capabilities) {
>>>>>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
>>>>>> +                info->has_capabilities = true;
>>>>>> +            }
>>>>>> +            info->capabilities->value =
>>>>>> +                g_malloc(sizeof(*info->capabilities->value));
>>>>>> +            info->capabilities->value->capability = i;
>>>>>> +            info->capabilities->value->state = s->enabled_capabilities[i];
>>>>>> +            info->capabilities->next = NULL;
>>>>>> +        }
>>>>>> +
>>>>>>          info->has_status = true;
>>>>>>          info->status = g_strdup("active");
>>>>>>
>>>>>> @@ -143,6 +167,18 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>>>>>          }
>>>>>>          break;
>>>>>>      case MIG_STATE_COMPLETED:
>>>>>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>>>>>> +            if (!info->has_capabilities) {
>>>>>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
>>>>>> +                info->has_capabilities = true;
>>>>>> +            }
>>>>>> +            info->capabilities->value =
>>>>>> +                g_malloc(sizeof(*info->capabilities->value));
>>>>>> +            info->capabilities->value->capability = i;
>>>>>> +            info->capabilities->value->state = s->enabled_capabilities[i];
>>>>>> +            info->capabilities->next = NULL;
>>>>>> +        }
>>>>>
>>>>> Code triplication :)
>>>>>
>>>>> Why is this is needed? Isn't query-migration-capabilities good enough?
>>>>> Besides, query-migrate should only contain information about current migration
>>>>> process...
>>>>>
>>>> see above
>>>>>> +
>>>>>>          info->has_status = true;
>>>>>>          info->status = g_strdup("completed");
>>>>>>
>>>>>> @@ -166,6 +202,33 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>>>>>      return info;
>>>>>>  }
>>>>>>
>>>>>> +MigrationCapabilityInfoList *qmp_query_migration_capabilities(Error **errp)
>>>>>> +{
>>>>>> +    MigrationCapabilityInfoList *caps_list = g_malloc0(sizeof(*caps_list));
>>>>>> +
>>>>>> +    caps_list->value = g_malloc(sizeof(*caps_list->value));
>>>>>> +    caps_list->value->capability = MIGRATION_CAPABILITY_XBZRLE;
>>>>>> +    caps_list->next = NULL;
>>>>>
>>>>> Shouldn't this get the capabilities array from migrate_get_current()?
>>>>>
>>>>> I mean, this makes query-migration-capabilities always return true for
>>>>> xbzrle, even if we set it to off.
>>>> Those are the general capabilities , if I want to use xbzrle ,does this QEMU support it ? 
>>>> this is required because we need both the destination and the source to be able to handle it.
>>>> If one QEMU doesn't (older version of qemu, maybe it will be disbaled for certain architectures) we can't use the feature.
>>>> This is for management and the user to check what migration capabilities this QEMU supports.
>>>
>>> My point was that query-migration-capabilities will always return state=true
>>> for all existing capabilities. Don't we want users/mngt app to know which
>>> states have been turned off?
>> You need to separate the ability to use a capability and actually using it for migration. Also I don't want the user to be able to set some capability on, it depends on the code not user configuration.
> 
> Not sure I can follow, a user can set xbzrle capability to off, no?
> 
>> And for xbzrle we always return true for some version on QEMU , let say we decide we want to remove the code that implement it for some reason in some future version than it will return false.
> 
> That's fine (I mean, I'm not sure we will ever do it, but shouldn't be a problem
> if we do).
> 
>> There is a reason the command that enables a capability for a migration was called 'migrate-set-parameter' and not set-capability to distinguish between them one is per QEMU code base and one is per migration.
>>
>> Regards,
>> Orit
>>>
>>>>
>>>>>
>>>>>> +
>>>>>> +    return caps_list;
>>>>>> +}
>>>>>> +
>>>>>> +void qmp_migrate_set_parameters(MigrationCapabilityInfoList *params,
>>>>>> +                                Error **errp)
>>>>>> +{
>>>>>> +    MigrationState *s = migrate_get_current();
>>>>>> +    MigrationCapabilityInfoList *cap;
>>>>>> +
>>>>>> +    if (s->state == MIG_STATE_ACTIVE) {
>>>>>> +        error_set(errp, QERR_MIGRATION_ACTIVE);
>>>>>> +        return;
>>>>>> +    }
>>>>>> +
>>>>>> +    for (cap = params; cap; cap = cap->next) {
>>>>>> +        s->enabled_capabilities[cap->value->capability] = cap->value->state;
>>>>>> +    }
>>>>>> +}
>>>>>> +
>>>>>>  /* shared migration helpers */
>>>>>>
>>>>>>  static int migrate_fd_cleanup(MigrationState *s)
>>>>>> @@ -375,12 +438,17 @@ static MigrationState *migrate_init(const MigrationParams *params)
>>>>>>  {
>>>>>>      MigrationState *s = migrate_get_current();
>>>>>>      int64_t bandwidth_limit = s->bandwidth_limit;
>>>>>> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
>>>>>> +
>>>>>> +    memcpy(enabled_capabilities, s->enabled_capabilities,
>>>>>> +           sizeof(enabled_capabilities));
>>>>>>
>>>>>>      memset(s, 0, sizeof(*s));
>>>>>>      s->bandwidth_limit = bandwidth_limit;
>>>>>>      s->params = *params;
>>>>>> +    memcpy(s->enabled_capabilities, enabled_capabilities,
>>>>>> +           sizeof(enabled_capabilities));
>>>>>>
>>>>>> -    s->bandwidth_limit = bandwidth_limit;
>>>>>>      s->state = MIG_STATE_SETUP;
>>>>>>      s->total_time = qemu_get_clock_ms(rt_clock);
>>>>>>
>>>>>> diff --git a/migration.h b/migration.h
>>>>>> index 57572a6..713aae0 100644
>>>>>> --- a/migration.h
>>>>>> +++ b/migration.h
>>>>>> @@ -19,6 +19,7 @@
>>>>>>  #include "notify.h"
>>>>>>  #include "error.h"
>>>>>>  #include "vmstate.h"
>>>>>> +#include "qapi-types.h"
>>>>>>
>>>>>>  struct MigrationParams {
>>>>>>      bool blk;
>>>>>> @@ -39,6 +40,7 @@ struct MigrationState
>>>>>>      void *opaque;
>>>>>>      MigrationParams params;
>>>>>>      int64_t total_time;
>>>>>> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
>>>>>>  };
>>>>>>
>>>>>>  void process_incoming_migration(QEMUFile *f);
>>>>>> diff --git a/monitor.c b/monitor.c
>>>>>> index f6107ba..e2be6cd 100644
>>>>>> --- a/monitor.c
>>>>>> +++ b/monitor.c
>>>>>> @@ -2687,6 +2687,13 @@ static mon_cmd_t info_cmds[] = {
>>>>>>          .mhandler.info = hmp_info_migrate,
>>>>>>      },
>>>>>>      {
>>>>>> +        .name       = "migration_capabilities",
>>>>>
>>>>> migration-capabilities is better.
>>>> ok
>>>>>
>>>>>> +        .args_type  = "",
>>>>>> +        .params     = "",
>>>>>> +        .help       = "show migration capabilities",
>>>>>> +        .mhandler.info = hmp_info_migration_capabilities,
>>>>>> +    },
>>>>>> +    {
>>>>>>          .name       = "balloon",
>>>>>>          .args_type  = "",
>>>>>>          .params     = "",
>>>>>> diff --git a/qapi-schema.json b/qapi-schema.json
>>>>>> index 1ab5dbd..a8408fd 100644
>>>>>> --- a/qapi-schema.json
>>>>>> +++ b/qapi-schema.json
>>>>>> @@ -288,11 +288,15 @@
>>>>>>  #        status, only returned if status is 'active' and it is a block
>>>>>>  #        migration
>>>>>>  #
>>>>>> -# Since: 0.14.0
>>>>>> +# @capabilities: #optional a list describing all the migration capabilities
>>>>>> +#                state
>>>>>
>>>>> I don't think this is needed, as I've said above.
>>>> see above
>>>>>
>>>>>> +#
>>>>>> +# Since: 0.14.0, 'capabilities' since 1.2
>>>>>>  ##
>>>>>>  { 'type': 'MigrationInfo',
>>>>>>    'data': {'*status': 'str', '*ram': 'MigrationStats',
>>>>>> -           '*disk': 'MigrationStats'} }
>>>>>> +           '*disk': 'MigrationStats',
>>>>>> +           '*capabilities': ['MigrationCapabilityInfo']} }
>>>>>>
>>>>>>  ##
>>>>>>  # @query-migrate
>>>>>> @@ -306,6 +310,51 @@
>>>>>>  { 'command': 'query-migrate', 'returns': 'MigrationInfo' }
>>>>>>
>>>>>>  ##
>>>>>> +# @MigrationCapability
>>>>>> +#
>>>>>> +# Migration capabilities enumeration
>>>>>> +#
>>>>>> +# @xbzrle: current migration supports xbzrle
>>>>>
>>>>> You should explain what xbzrle is.
>>>> ok
>>>>>
>>>>>> +#
>>>>>> +# Since: 1.2
>>>>>> +##
>>>>>> +{ 'enum': 'MigrationCapability',
>>>>>> +  'data': ['xbzrle'] }
>>>>>> +
>>>>>> +##
>>>>>> +# @MigrationCapabilityInfo
>>>>>
>>>>> MigrationCapabilityStatus?
>>>>>
>>>>>> +#
>>>>>> +# Migration capability information
>>>>>> +#
>>>>>> +# @capability: capability enum
>>>>>
>>>>> Please, document state too.
>>>> ok
>>>>
>>>> Orit
>>>>>
>>>>>> +#
>>>>>> +# Since: 1.2
>>>>>> +##
>>>>>> +{ 'type': 'MigrationCapabilityInfo',
>>>>>> +  'data': { 'capability' : 'MigrationCapability', 'state' : 'bool' } }
>>>>>> +
>>>>>> +##
>>>>>> +# @query-migration-capabilities
>>>>>> +#
>>>>>> +# Returns information about current migration process capabilties.
>>>>>> +#
>>>>>> +# Returns: @MigrationCapabilityInfo list
>>>>>> +#
>>>>>> +# Since: 1.2
>>>>>> +##
>>>>>> +{ 'command': 'query-migration-capabilities', 'returns': ['MigrationCapabilityInfo'] }
>>>>>> +
>>>>>> +##
>>>>>> +# @migrate_set_parameters
>>>>>> +#
>>>>>> +# Enable/Disable the following migration capabilities (like xbzrle)
>>>>>> +#
>>>>>> +# Since: 1.2
>>>>>> +##
>>>>>> +{ 'command': 'migrate-set-parameters',
>>>>>> +  'data': { 'capabilities': ['MigrationCapabilityInfo'] } }
>>>>>> +
>>>>>> +##
>>>>>>  # @MouseInfo:
>>>>>>  #
>>>>>>  # Information about a mouse device.
>>>>>> diff --git a/qmp-commands.hx b/qmp-commands.hx
>>>>>> index 2e1a38e..3ee6e00 100644
>>>>>> --- a/qmp-commands.hx
>>>>>> +++ b/qmp-commands.hx
>>>>>> @@ -2075,18 +2075,31 @@ The main json-object contains the following:
>>>>>>           - "transferred": amount transferred (json-int)
>>>>>>           - "remaining": amount remaining (json-int)
>>>>>>           - "total": total (json-int)
>>>>>> -
>>>>>> +- "capabilities": migration capabilities state
>>>>>> +         - "xbzrle" : XBZRLE state (json-bool)
>>>>>>  Examples:
>>>>>>
>>>>>>  1. Before the first migration
>>>>>>
>>>>>>  -> { "execute": "query-migrate" }
>>>>>> -<- { "return": {} }
>>>>>> +<- { "return": {
>>>>>> +        "capabilities" :  [ { "capability" : "xbzrle", "state" : false } ]
>>>>>> +     }
>>>>>> +   }
>>>>>>
>>>>>>  2. Migration is done and has succeeded
>>>>>>
>>>>>>  -> { "execute": "query-migrate" }
>>>>>> -<- { "return": { "status": "completed" } }
>>>>>> +<- { "return": {
>>>>>> +        "status": "completed",
>>>>>> +        "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
>>>>>> +        "ram":{
>>>>>> +          "transferred":123,
>>>>>> +          "remaining":123,
>>>>>> +          "total":246
>>>>>> +        }
>>>>>> +     }
>>>>>> +   }
>>>>>>
>>>>>>  3. Migration is done and has failed
>>>>>>
>>>>>> @@ -2099,6 +2112,7 @@ Examples:
>>>>>>  <- {
>>>>>>        "return":{
>>>>>>           "status":"active",
>>>>>> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
>>>>>>           "ram":{
>>>>>>              "transferred":123,
>>>>>>              "remaining":123,
>>>>>> @@ -2113,6 +2127,7 @@ Examples:
>>>>>>  <- {
>>>>>>        "return":{
>>>>>>           "status":"active",
>>>>>> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
>>>>>>           "ram":{
>>>>>>              "total":1057024,
>>>>>>              "remaining":1053304,
>>>>>> @@ -2135,6 +2150,56 @@ EQMP
>>>>>>      },
>>>>>>
>>>>>>  SQMP
>>>>>> +query-migration-capabilities
>>>>>> +-------
>>>>>> +
>>>>>> +Query migration capabilities
>>>>>> +
>>>>>> +- "xbzrle": xbzrle support
>>>>>> +
>>>>>> +Arguments:
>>>>>> +
>>>>>> +Example:
>>>>>> +
>>>>>> +-> { "execute": "query-migration-capabilities"}
>>>>>> +<- { "return": [ { "capability": "xbzrle", "state": true },
>>>>>> +                 { "capability": "foobar", "state": false } ] }
>>>>>> +
>>>>>> +EQMP
>>>>>> +
>>>>>> +    {
>>>>>> +        .name       = "query-migration-capabilities",
>>>>>> +        .args_type  = "",
>>>>>> +	.mhandler.cmd_new = qmp_marshal_input_query_migration_capabilities,
>>>>>> +    },
>>>>>> +
>>>>>> +SQMP
>>>>>> +migrate_set_parameters
>>>>>> +-------
>>>>>> +
>>>>>> +Enable/Disable migration capabilities
>>>>>> +
>>>>>> +- "xbzrle": xbzrle support
>>>>>> +
>>>>>> +Arguments:
>>>>>> +
>>>>>> +Example:
>>>>>> +
>>>>>> +-> { "execute": "migrate_set_parameters" , "arguments":
>>>>>> +     { "parameters": [ { "capability": "xbzrle", "state": true } ] } }
>>>>>> +
>>>>>> +EQMP
>>>>>> +
>>>>>> +    {
>>>>>> +        .name       = "migrate_set_parameters",
>>>>>> +        .args_type  = "parameters:O",
>>>>>> +	.params     = "capability:s,state:b",
>>>>>> +	.mhandler.cmd_new = qmp_marshal_input_migrate_set_parameters,
>>>>>> +    },
>>>>>> +
>>>>>> +
>>>>>> +
>>>>>> +SQMP
>>>>>>  query-balloon
>>>>>>  -------------
>>>>>>
>>>>>
>>>>
>>>>
>>>
>>
>>
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH 13/22] Add migration capabilities
  2012-07-25 13:05             ` Orit Wasserman
@ 2012-07-25 13:11               ` Luiz Capitulino
  2012-07-25 13:33                 ` Orit Wasserman
  0 siblings, 1 reply; 35+ messages in thread
From: Luiz Capitulino @ 2012-07-25 13:11 UTC (permalink / raw)
  To: Orit Wasserman; +Cc: Eric Blake, qemu-devel, Juan Quintela

On Wed, 25 Jul 2012 16:05:10 +0300
Orit Wasserman <owasserm@redhat.com> wrote:

> On 07/24/2012 09:17 PM, Luiz Capitulino wrote:
> > On Tue, 24 Jul 2012 20:06:06 +0300
> > Orit Wasserman <owasserm@redhat.com> wrote:
> > 
> >> On 07/24/2012 03:50 PM, Luiz Capitulino wrote:
> >>> On Tue, 24 Jul 2012 09:25:11 +0300
> >>> Orit Wasserman <owasserm@redhat.com> wrote:
> >>>
> >>>> On 07/23/2012 09:23 PM, Luiz Capitulino wrote:
> >>>>> On Fri, 13 Jul 2012 09:23:35 +0200
> >>>>> Juan Quintela <quintela@redhat.com> wrote:
> >>>>>
> >>>>>> From: Orit Wasserman <owasserm@redhat.com>
> >>>>>>
> >>>>>> Add migration capabilities that can be queried by the management.
> >>>>>> The management can query the source QEMU and the destination QEMU in order to
> >>>>>> verify both support some migration capability (currently only XBZRLE).
> >>>>>> The management can enable a capability for the next migration by using
> >>>>>> migrate_set_parameter command.
> >>>>>
> >>>>> Please, split this into one command per-patch. Otherwise it's difficult to
> >>>>> review.
> >>>>>
> >>>> Sure.
> >>>>> Have libvirt folks acked this approach btw? It looks fine to me, but we need
> >>>>> their ack too.
> >>>>>
> >>>>> More comments below.
> >>>>>
> >>>>>>
> >>>>>> Signed-off-by: Orit Wasserman <owasserm@redhat.com>
> >>>>>> Signed-off-by: Juan Quintela <quintela@redhat.com>
> >>>>>> ---
> >>>>>>  hmp-commands.hx  |   16 ++++++++++++
> >>>>>>  hmp.c            |   64 ++++++++++++++++++++++++++++++++++++++++++++++++
> >>>>>>  hmp.h            |    2 ++
> >>>>>>  migration.c      |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
> >>>>>>  migration.h      |    2 ++
> >>>>>>  monitor.c        |    7 ++++++
> >>>>>>  qapi-schema.json |   53 ++++++++++++++++++++++++++++++++++++++--
> >>>>>>  qmp-commands.hx  |   71 ++++++++++++++++++++++++++++++++++++++++++++++++++---
> >>>>>>  8 files changed, 280 insertions(+), 7 deletions(-)
> >>>>>>
> >>>>>> diff --git a/hmp-commands.hx b/hmp-commands.hx
> >>>>>> index f5d9d91..9245bef 100644
> >>>>>> --- a/hmp-commands.hx
> >>>>>> +++ b/hmp-commands.hx
> >>>>>> @@ -861,6 +861,20 @@ Set maximum tolerated downtime (in seconds) for migration.
> >>>>>>  ETEXI
> >>>>>>
> >>>>>>      {
> >>>>>> +        .name       = "migrate_set_parameter",
> >>>>>> +        .args_type  = "capability:s,state:b",
> >>>>>> +        .params     = "",
> >>>>>
> >>>>> Please, fill in params.
> >>>> ok
> >>>>>
> >>>>>> +        .help       = "Enable/Disable the usage of a capability for migration",
> >>>>>> +        .mhandler.cmd = hmp_migrate_set_parameter,
> >>>>>> +    },
> >>>>>> +
> >>>>>> +STEXI
> >>>>>> +@item migrate_set_parameter @var{capability} @var{state}
> >>>>>> +@findex migrate_set_parameter
> >>>>>> +Enable/Disable the usage of a capability @var{capability} for migration.
> >>>>>> +ETEXI
> >>>>>> +
> >>>>>> +    {
> >>>>>>          .name       = "client_migrate_info",
> >>>>>>          .args_type  = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
> >>>>>>          .params     = "protocol hostname port tls-port cert-subject",
> >>>>>> @@ -1419,6 +1433,8 @@ show CPU statistics
> >>>>>>  show user network stack connection states
> >>>>>>  @item info migrate
> >>>>>>  show migration status
> >>>>>> +@item info migration_capabilities
> >>>>>> +show migration capabilities
> >>>>>>  @item info balloon
> >>>>>>  show balloon information
> >>>>>>  @item info qtree
> >>>>>> diff --git a/hmp.c b/hmp.c
> >>>>>> index 4c6d4ae..b0440e6 100644
> >>>>>> --- a/hmp.c
> >>>>>> +++ b/hmp.c
> >>>>>> @@ -131,9 +131,19 @@ void hmp_info_mice(Monitor *mon)
> >>>>>>  void hmp_info_migrate(Monitor *mon)
> >>>>>>  {
> >>>>>>      MigrationInfo *info;
> >>>>>> +    MigrationCapabilityInfoList *cap;
> >>>>>>
> >>>>>>      info = qmp_query_migrate(NULL);
> >>>>>>
> >>>>>> +    if (info->has_capabilities && info->capabilities) {
> >>>>>> +        monitor_printf(mon, "capabilities: ");
> >>>>>> +        for (cap = info->capabilities; cap; cap = cap->next) {
> >>>>>> +            monitor_printf(mon, "%s: %s ",
> >>>>>> +                           MigrationCapability_lookup[cap->value->capability],
> >>>>>> +                           cap->value->state ? "on" : "off");
> >>>>>> +        }
> >>>>>> +        monitor_printf(mon, "\n");
> >>>>>
> >>>>> Why is this is needed? Isn't info migration-capabilities good enough?
> >>>>> Besides, info migrate should only contain information about current migration
> >>>>> process...
> >>>> The reason we introduced capabilities is that xbzrle needs for both source and destination QEMU
> >>>> to be able to handle it. Even if both side support xbzrle the user may decide not to use it.
> >>>> User that wants to use xbzrle needs to check that both sides have support for it (using info capabilities) , than 
> >>>> enable it in both sides (using migrate-set-parameter/s commands). This is a parameter for the current migration.
> >>>> So the user needs to know if xbzrle was enabled or disabled for the current migration, this code displays it.
> >>>
> >>> But this is being returned even when there is no migration taking place. I'm
> >>> answering this here in the 'info migrate' hunk, but my main concern is
> >>> query-migrate.
> >> I think migration configuration is part of migration information and should be available to the user in 'info migration'. Especially before he starts running it to see if he need to change the configuration. Also it is very helpful for the user during the migration and also after it's completes.
> > 
> > I don't mind having this in 'info migrate' because we can change it later case
> > we regret, so it's fine with me having that info there.
> > 
> > But query-migrate is different, once we add anything there it's there forever,
> > so we have to be careful.
> ok I will remove them from query-migrate.

Note that I'm asking to remove it from MIG_SETUP, the others states are fine.

> > 
> >> There is no way today to query  the downtime and migration speed at all . you can always set them again
> >> but there is no way to know how it is configured.
> > 
> > Good point, but I think it's better to concentrate on the needs of xbzrle
> > for now.
> > 
> >> I was actually thinking to add them to info migration (also for SETUP).
> > 
> > But we're in SETUP state only once, what happens if the settings are changed
> > after a migration completed? How is mngt going to be able to query it?
> After migration completes you can't change those setting , you should get an error.

Really, why? I thought it would be fine to change them at any time
(except during migration, of course).

> 
> > 
> > My advice to not delay the inclusion of this series is to drop the
> > information from SETUP state and rely on query-migration-capabilities in
> > 'info migrate'. We can extend later if needed (by means of new commands or
> > adding info to query-migrate).
> It needs to be another command, I can add query-migration-parameters.
> You can look at this as 64bit support - you have 64bit support in the processor (this is what query-capabilities) but the OS can decide not to work in 64bit mode (do nothing) or using 64 bit (migrate-set-parameter).
> 
> Orit
> > 
> >>>
> >>> query-migrate shouldn't return this info in MIG_STATE_SETUP, and for
> >>> MIG_STATE_COMPLETED it should only be returned if there was a migration.
> >> I think is case of MIG_SETUP we should return the configuration and params for the next migration. 
> >> MIG_STATE_COMPLETED happens only if there was a successful migration other wise there will be another state.
> >>>
> >>>> some day when there will be better migration protocol, feature negotiations can be part of it ...
> >>>>>
> >>>>>> +    }
> >>>>>>      if (info->has_status) {
> >>>>>>          monitor_printf(mon, "Migration status: %s\n", info->status);
> >>>>>>      }
> >>>>>> @@ -161,6 +171,25 @@ void hmp_info_migrate(Monitor *mon)
> >>>>>>      qapi_free_MigrationInfo(info);
> >>>>>>  }
> >>>>>>
> >>>>>> +void hmp_info_migration_capabilities(Monitor *mon)
> >>>>>> +{
> >>>>>> +    MigrationCapabilityInfoList *caps_list, *cap;
> >>>>>> +
> >>>>>> +    caps_list = qmp_query_migration_capabilities(NULL);
> >>>>>> +    if (!caps_list) {
> >>>>>> +        monitor_printf(mon, "No migration capabilities found\n");
> >>>>>> +        return;
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    for (cap = caps_list; cap; cap = cap->next) {
> >>>>>> +        monitor_printf(mon, "%s: %s ",
> >>>>>> +                       MigrationCapability_lookup[cap->value->capability],
> >>>>>> +                       cap->value->state ? "on" : "off");
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    qapi_free_MigrationCapabilityInfoList(caps_list);
> >>>>>> +}
> >>>>>> +
> >>>>>>  void hmp_info_cpus(Monitor *mon)
> >>>>>>  {
> >>>>>>      CpuInfoList *cpu_list, *cpu;
> >>>>>> @@ -735,6 +764,41 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
> >>>>>>      qmp_migrate_set_speed(value, NULL);
> >>>>>>  }
> >>>>>>
> >>>>>> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
> >>>>>> +{
> >>>>>> +    const char *cap = qdict_get_str(qdict, "capability");
> >>>>>> +    bool state = qdict_get_bool(qdict, "state");
> >>>>>> +    Error *err = NULL;
> >>>>>> +    MigrationCapabilityInfoList *params = NULL;
> >>>>>> +    int i;
> >>>>>> +
> >>>>>> +    for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> >>>>>> +        if (strcmp(cap, MigrationCapability_lookup[i]) == 0) {
> >>>>>> +            if (!params) {
> >>>>>> +                params = g_malloc0(sizeof(*params));
> >>>>>> +            }
> >>>>>> +            params->value = g_malloc0(sizeof(*params->value));
> >>>>>> +            params->value->capability = i;
> >>>>>> +            params->value->state = state;
> >>>>>> +            params->next = NULL;
> >>>>>> +            qmp_migrate_set_parameters(params, &err);
> >>>>>> +            break;
> >>>>>> +        }
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    if (i == MIGRATION_CAPABILITY_MAX) {
> >>>>>> +        error_set(&err, QERR_INVALID_PARAMETER, cap);
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    qapi_free_MigrationCapabilityInfoList(params);
> >>>>>> +
> >>>>>> +    if (err) {
> >>>>>> +        monitor_printf(mon, "migrate_set_parameter: %s\n",
> >>>>>> +                       error_get_pretty(err));
> >>>>>> +        error_free(err);
> >>>>>> +    }
> >>>>>> +}
> >>>>>> +
> >>>>>>  void hmp_set_password(Monitor *mon, const QDict *qdict)
> >>>>>>  {
> >>>>>>      const char *protocol  = qdict_get_str(qdict, "protocol");
> >>>>>> diff --git a/hmp.h b/hmp.h
> >>>>>> index 79d138d..09ba198 100644
> >>>>>> --- a/hmp.h
> >>>>>> +++ b/hmp.h
> >>>>>> @@ -25,6 +25,7 @@ void hmp_info_uuid(Monitor *mon);
> >>>>>>  void hmp_info_chardev(Monitor *mon);
> >>>>>>  void hmp_info_mice(Monitor *mon);
> >>>>>>  void hmp_info_migrate(Monitor *mon);
> >>>>>> +void hmp_info_migration_capabilities(Monitor *mon);
> >>>>>>  void hmp_info_cpus(Monitor *mon);
> >>>>>>  void hmp_info_block(Monitor *mon);
> >>>>>>  void hmp_info_blockstats(Monitor *mon);
> >>>>>> @@ -51,6 +52,7 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
> >>>>>>  void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
> >>>>>>  void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
> >>>>>>  void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
> >>>>>> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict);
> >>>>>>  void hmp_set_password(Monitor *mon, const QDict *qdict);
> >>>>>>  void hmp_expire_password(Monitor *mon, const QDict *qdict);
> >>>>>>  void hmp_eject(Monitor *mon, const QDict *qdict);
> >>>>>> diff --git a/migration.c b/migration.c
> >>>>>> index 8db1b43..fd004d7 100644
> >>>>>> --- a/migration.c
> >>>>>> +++ b/migration.c
> >>>>>> @@ -117,12 +117,36 @@ MigrationInfo *qmp_query_migrate(Error **errp)
> >>>>>>  {
> >>>>>>      MigrationInfo *info = g_malloc0(sizeof(*info));
> >>>>>>      MigrationState *s = migrate_get_current();
> >>>>>> +    int i;
> >>>>>>
> >>>>>>      switch (s->state) {
> >>>>>>      case MIG_STATE_SETUP:
> >>>>>> -        /* no migration has happened ever */
> >>>>>> +        /* no migration has ever happened; show enabled capabilities */
> >>>>>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> >>>>>> +            if (!info->has_capabilities) {
> >>>>>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
> >>>>>> +                info->has_capabilities = true;
> >>>>>> +            }
> >>>>>> +            info->capabilities->value =
> >>>>>> +                g_malloc(sizeof(*info->capabilities->value));
> >>>>>> +            info->capabilities->value->capability = i;
> >>>>>> +            info->capabilities->value->state = s->enabled_capabilities[i];
> >>>>>> +            info->capabilities->next = NULL;
> >>>>>> +        }
> >>>>>>          break;
> >>>>>>      case MIG_STATE_ACTIVE:
> >>>>>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> >>>>>> +            if (!info->has_capabilities) {
> >>>>>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
> >>>>>> +                info->has_capabilities = true;
> >>>>>> +            }
> >>>>>> +            info->capabilities->value =
> >>>>>> +                g_malloc(sizeof(*info->capabilities->value));
> >>>>>> +            info->capabilities->value->capability = i;
> >>>>>> +            info->capabilities->value->state = s->enabled_capabilities[i];
> >>>>>> +            info->capabilities->next = NULL;
> >>>>>> +        }
> >>>>>> +
> >>>>>>          info->has_status = true;
> >>>>>>          info->status = g_strdup("active");
> >>>>>>
> >>>>>> @@ -143,6 +167,18 @@ MigrationInfo *qmp_query_migrate(Error **errp)
> >>>>>>          }
> >>>>>>          break;
> >>>>>>      case MIG_STATE_COMPLETED:
> >>>>>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
> >>>>>> +            if (!info->has_capabilities) {
> >>>>>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
> >>>>>> +                info->has_capabilities = true;
> >>>>>> +            }
> >>>>>> +            info->capabilities->value =
> >>>>>> +                g_malloc(sizeof(*info->capabilities->value));
> >>>>>> +            info->capabilities->value->capability = i;
> >>>>>> +            info->capabilities->value->state = s->enabled_capabilities[i];
> >>>>>> +            info->capabilities->next = NULL;
> >>>>>> +        }
> >>>>>
> >>>>> Code triplication :)
> >>>>>
> >>>>> Why is this is needed? Isn't query-migration-capabilities good enough?
> >>>>> Besides, query-migrate should only contain information about current migration
> >>>>> process...
> >>>>>
> >>>> see above
> >>>>>> +
> >>>>>>          info->has_status = true;
> >>>>>>          info->status = g_strdup("completed");
> >>>>>>
> >>>>>> @@ -166,6 +202,33 @@ MigrationInfo *qmp_query_migrate(Error **errp)
> >>>>>>      return info;
> >>>>>>  }
> >>>>>>
> >>>>>> +MigrationCapabilityInfoList *qmp_query_migration_capabilities(Error **errp)
> >>>>>> +{
> >>>>>> +    MigrationCapabilityInfoList *caps_list = g_malloc0(sizeof(*caps_list));
> >>>>>> +
> >>>>>> +    caps_list->value = g_malloc(sizeof(*caps_list->value));
> >>>>>> +    caps_list->value->capability = MIGRATION_CAPABILITY_XBZRLE;
> >>>>>> +    caps_list->next = NULL;
> >>>>>
> >>>>> Shouldn't this get the capabilities array from migrate_get_current()?
> >>>>>
> >>>>> I mean, this makes query-migration-capabilities always return true for
> >>>>> xbzrle, even if we set it to off.
> >>>> Those are the general capabilities , if I want to use xbzrle ,does this QEMU support it ? 
> >>>> this is required because we need both the destination and the source to be able to handle it.
> >>>> If one QEMU doesn't (older version of qemu, maybe it will be disbaled for certain architectures) we can't use the feature.
> >>>> This is for management and the user to check what migration capabilities this QEMU supports.
> >>>
> >>> My point was that query-migration-capabilities will always return state=true
> >>> for all existing capabilities. Don't we want users/mngt app to know which
> >>> states have been turned off?
> >> You need to separate the ability to use a capability and actually using it for migration. Also I don't want the user to be able to set some capability on, it depends on the code not user configuration.
> > 
> > Not sure I can follow, a user can set xbzrle capability to off, no?
> > 
> >> And for xbzrle we always return true for some version on QEMU , let say we decide we want to remove the code that implement it for some reason in some future version than it will return false.
> > 
> > That's fine (I mean, I'm not sure we will ever do it, but shouldn't be a problem
> > if we do).
> > 
> >> There is a reason the command that enables a capability for a migration was called 'migrate-set-parameter' and not set-capability to distinguish between them one is per QEMU code base and one is per migration.
> >>
> >> Regards,
> >> Orit
> >>>
> >>>>
> >>>>>
> >>>>>> +
> >>>>>> +    return caps_list;
> >>>>>> +}
> >>>>>> +
> >>>>>> +void qmp_migrate_set_parameters(MigrationCapabilityInfoList *params,
> >>>>>> +                                Error **errp)
> >>>>>> +{
> >>>>>> +    MigrationState *s = migrate_get_current();
> >>>>>> +    MigrationCapabilityInfoList *cap;
> >>>>>> +
> >>>>>> +    if (s->state == MIG_STATE_ACTIVE) {
> >>>>>> +        error_set(errp, QERR_MIGRATION_ACTIVE);
> >>>>>> +        return;
> >>>>>> +    }
> >>>>>> +
> >>>>>> +    for (cap = params; cap; cap = cap->next) {
> >>>>>> +        s->enabled_capabilities[cap->value->capability] = cap->value->state;
> >>>>>> +    }
> >>>>>> +}
> >>>>>> +
> >>>>>>  /* shared migration helpers */
> >>>>>>
> >>>>>>  static int migrate_fd_cleanup(MigrationState *s)
> >>>>>> @@ -375,12 +438,17 @@ static MigrationState *migrate_init(const MigrationParams *params)
> >>>>>>  {
> >>>>>>      MigrationState *s = migrate_get_current();
> >>>>>>      int64_t bandwidth_limit = s->bandwidth_limit;
> >>>>>> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
> >>>>>> +
> >>>>>> +    memcpy(enabled_capabilities, s->enabled_capabilities,
> >>>>>> +           sizeof(enabled_capabilities));
> >>>>>>
> >>>>>>      memset(s, 0, sizeof(*s));
> >>>>>>      s->bandwidth_limit = bandwidth_limit;
> >>>>>>      s->params = *params;
> >>>>>> +    memcpy(s->enabled_capabilities, enabled_capabilities,
> >>>>>> +           sizeof(enabled_capabilities));
> >>>>>>
> >>>>>> -    s->bandwidth_limit = bandwidth_limit;
> >>>>>>      s->state = MIG_STATE_SETUP;
> >>>>>>      s->total_time = qemu_get_clock_ms(rt_clock);
> >>>>>>
> >>>>>> diff --git a/migration.h b/migration.h
> >>>>>> index 57572a6..713aae0 100644
> >>>>>> --- a/migration.h
> >>>>>> +++ b/migration.h
> >>>>>> @@ -19,6 +19,7 @@
> >>>>>>  #include "notify.h"
> >>>>>>  #include "error.h"
> >>>>>>  #include "vmstate.h"
> >>>>>> +#include "qapi-types.h"
> >>>>>>
> >>>>>>  struct MigrationParams {
> >>>>>>      bool blk;
> >>>>>> @@ -39,6 +40,7 @@ struct MigrationState
> >>>>>>      void *opaque;
> >>>>>>      MigrationParams params;
> >>>>>>      int64_t total_time;
> >>>>>> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
> >>>>>>  };
> >>>>>>
> >>>>>>  void process_incoming_migration(QEMUFile *f);
> >>>>>> diff --git a/monitor.c b/monitor.c
> >>>>>> index f6107ba..e2be6cd 100644
> >>>>>> --- a/monitor.c
> >>>>>> +++ b/monitor.c
> >>>>>> @@ -2687,6 +2687,13 @@ static mon_cmd_t info_cmds[] = {
> >>>>>>          .mhandler.info = hmp_info_migrate,
> >>>>>>      },
> >>>>>>      {
> >>>>>> +        .name       = "migration_capabilities",
> >>>>>
> >>>>> migration-capabilities is better.
> >>>> ok
> >>>>>
> >>>>>> +        .args_type  = "",
> >>>>>> +        .params     = "",
> >>>>>> +        .help       = "show migration capabilities",
> >>>>>> +        .mhandler.info = hmp_info_migration_capabilities,
> >>>>>> +    },
> >>>>>> +    {
> >>>>>>          .name       = "balloon",
> >>>>>>          .args_type  = "",
> >>>>>>          .params     = "",
> >>>>>> diff --git a/qapi-schema.json b/qapi-schema.json
> >>>>>> index 1ab5dbd..a8408fd 100644
> >>>>>> --- a/qapi-schema.json
> >>>>>> +++ b/qapi-schema.json
> >>>>>> @@ -288,11 +288,15 @@
> >>>>>>  #        status, only returned if status is 'active' and it is a block
> >>>>>>  #        migration
> >>>>>>  #
> >>>>>> -# Since: 0.14.0
> >>>>>> +# @capabilities: #optional a list describing all the migration capabilities
> >>>>>> +#                state
> >>>>>
> >>>>> I don't think this is needed, as I've said above.
> >>>> see above
> >>>>>
> >>>>>> +#
> >>>>>> +# Since: 0.14.0, 'capabilities' since 1.2
> >>>>>>  ##
> >>>>>>  { 'type': 'MigrationInfo',
> >>>>>>    'data': {'*status': 'str', '*ram': 'MigrationStats',
> >>>>>> -           '*disk': 'MigrationStats'} }
> >>>>>> +           '*disk': 'MigrationStats',
> >>>>>> +           '*capabilities': ['MigrationCapabilityInfo']} }
> >>>>>>
> >>>>>>  ##
> >>>>>>  # @query-migrate
> >>>>>> @@ -306,6 +310,51 @@
> >>>>>>  { 'command': 'query-migrate', 'returns': 'MigrationInfo' }
> >>>>>>
> >>>>>>  ##
> >>>>>> +# @MigrationCapability
> >>>>>> +#
> >>>>>> +# Migration capabilities enumeration
> >>>>>> +#
> >>>>>> +# @xbzrle: current migration supports xbzrle
> >>>>>
> >>>>> You should explain what xbzrle is.
> >>>> ok
> >>>>>
> >>>>>> +#
> >>>>>> +# Since: 1.2
> >>>>>> +##
> >>>>>> +{ 'enum': 'MigrationCapability',
> >>>>>> +  'data': ['xbzrle'] }
> >>>>>> +
> >>>>>> +##
> >>>>>> +# @MigrationCapabilityInfo
> >>>>>
> >>>>> MigrationCapabilityStatus?
> >>>>>
> >>>>>> +#
> >>>>>> +# Migration capability information
> >>>>>> +#
> >>>>>> +# @capability: capability enum
> >>>>>
> >>>>> Please, document state too.
> >>>> ok
> >>>>
> >>>> Orit
> >>>>>
> >>>>>> +#
> >>>>>> +# Since: 1.2
> >>>>>> +##
> >>>>>> +{ 'type': 'MigrationCapabilityInfo',
> >>>>>> +  'data': { 'capability' : 'MigrationCapability', 'state' : 'bool' } }
> >>>>>> +
> >>>>>> +##
> >>>>>> +# @query-migration-capabilities
> >>>>>> +#
> >>>>>> +# Returns information about current migration process capabilties.
> >>>>>> +#
> >>>>>> +# Returns: @MigrationCapabilityInfo list
> >>>>>> +#
> >>>>>> +# Since: 1.2
> >>>>>> +##
> >>>>>> +{ 'command': 'query-migration-capabilities', 'returns': ['MigrationCapabilityInfo'] }
> >>>>>> +
> >>>>>> +##
> >>>>>> +# @migrate_set_parameters
> >>>>>> +#
> >>>>>> +# Enable/Disable the following migration capabilities (like xbzrle)
> >>>>>> +#
> >>>>>> +# Since: 1.2
> >>>>>> +##
> >>>>>> +{ 'command': 'migrate-set-parameters',
> >>>>>> +  'data': { 'capabilities': ['MigrationCapabilityInfo'] } }
> >>>>>> +
> >>>>>> +##
> >>>>>>  # @MouseInfo:
> >>>>>>  #
> >>>>>>  # Information about a mouse device.
> >>>>>> diff --git a/qmp-commands.hx b/qmp-commands.hx
> >>>>>> index 2e1a38e..3ee6e00 100644
> >>>>>> --- a/qmp-commands.hx
> >>>>>> +++ b/qmp-commands.hx
> >>>>>> @@ -2075,18 +2075,31 @@ The main json-object contains the following:
> >>>>>>           - "transferred": amount transferred (json-int)
> >>>>>>           - "remaining": amount remaining (json-int)
> >>>>>>           - "total": total (json-int)
> >>>>>> -
> >>>>>> +- "capabilities": migration capabilities state
> >>>>>> +         - "xbzrle" : XBZRLE state (json-bool)
> >>>>>>  Examples:
> >>>>>>
> >>>>>>  1. Before the first migration
> >>>>>>
> >>>>>>  -> { "execute": "query-migrate" }
> >>>>>> -<- { "return": {} }
> >>>>>> +<- { "return": {
> >>>>>> +        "capabilities" :  [ { "capability" : "xbzrle", "state" : false } ]
> >>>>>> +     }
> >>>>>> +   }
> >>>>>>
> >>>>>>  2. Migration is done and has succeeded
> >>>>>>
> >>>>>>  -> { "execute": "query-migrate" }
> >>>>>> -<- { "return": { "status": "completed" } }
> >>>>>> +<- { "return": {
> >>>>>> +        "status": "completed",
> >>>>>> +        "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
> >>>>>> +        "ram":{
> >>>>>> +          "transferred":123,
> >>>>>> +          "remaining":123,
> >>>>>> +          "total":246
> >>>>>> +        }
> >>>>>> +     }
> >>>>>> +   }
> >>>>>>
> >>>>>>  3. Migration is done and has failed
> >>>>>>
> >>>>>> @@ -2099,6 +2112,7 @@ Examples:
> >>>>>>  <- {
> >>>>>>        "return":{
> >>>>>>           "status":"active",
> >>>>>> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
> >>>>>>           "ram":{
> >>>>>>              "transferred":123,
> >>>>>>              "remaining":123,
> >>>>>> @@ -2113,6 +2127,7 @@ Examples:
> >>>>>>  <- {
> >>>>>>        "return":{
> >>>>>>           "status":"active",
> >>>>>> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
> >>>>>>           "ram":{
> >>>>>>              "total":1057024,
> >>>>>>              "remaining":1053304,
> >>>>>> @@ -2135,6 +2150,56 @@ EQMP
> >>>>>>      },
> >>>>>>
> >>>>>>  SQMP
> >>>>>> +query-migration-capabilities
> >>>>>> +-------
> >>>>>> +
> >>>>>> +Query migration capabilities
> >>>>>> +
> >>>>>> +- "xbzrle": xbzrle support
> >>>>>> +
> >>>>>> +Arguments:
> >>>>>> +
> >>>>>> +Example:
> >>>>>> +
> >>>>>> +-> { "execute": "query-migration-capabilities"}
> >>>>>> +<- { "return": [ { "capability": "xbzrle", "state": true },
> >>>>>> +                 { "capability": "foobar", "state": false } ] }
> >>>>>> +
> >>>>>> +EQMP
> >>>>>> +
> >>>>>> +    {
> >>>>>> +        .name       = "query-migration-capabilities",
> >>>>>> +        .args_type  = "",
> >>>>>> +	.mhandler.cmd_new = qmp_marshal_input_query_migration_capabilities,
> >>>>>> +    },
> >>>>>> +
> >>>>>> +SQMP
> >>>>>> +migrate_set_parameters
> >>>>>> +-------
> >>>>>> +
> >>>>>> +Enable/Disable migration capabilities
> >>>>>> +
> >>>>>> +- "xbzrle": xbzrle support
> >>>>>> +
> >>>>>> +Arguments:
> >>>>>> +
> >>>>>> +Example:
> >>>>>> +
> >>>>>> +-> { "execute": "migrate_set_parameters" , "arguments":
> >>>>>> +     { "parameters": [ { "capability": "xbzrle", "state": true } ] } }
> >>>>>> +
> >>>>>> +EQMP
> >>>>>> +
> >>>>>> +    {
> >>>>>> +        .name       = "migrate_set_parameters",
> >>>>>> +        .args_type  = "parameters:O",
> >>>>>> +	.params     = "capability:s,state:b",
> >>>>>> +	.mhandler.cmd_new = qmp_marshal_input_migrate_set_parameters,
> >>>>>> +    },
> >>>>>> +
> >>>>>> +
> >>>>>> +
> >>>>>> +SQMP
> >>>>>>  query-balloon
> >>>>>>  -------------
> >>>>>>
> >>>>>
> >>>>
> >>>>
> >>>
> >>
> >>
> > 
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [Qemu-devel] [PATCH 13/22] Add migration capabilities
  2012-07-25 13:11               ` Luiz Capitulino
@ 2012-07-25 13:33                 ` Orit Wasserman
  0 siblings, 0 replies; 35+ messages in thread
From: Orit Wasserman @ 2012-07-25 13:33 UTC (permalink / raw)
  To: Luiz Capitulino; +Cc: Eric Blake, qemu-devel, Juan Quintela

On 07/25/2012 04:11 PM, Luiz Capitulino wrote:
> On Wed, 25 Jul 2012 16:05:10 +0300
> Orit Wasserman <owasserm@redhat.com> wrote:
> 
>> On 07/24/2012 09:17 PM, Luiz Capitulino wrote:
>>> On Tue, 24 Jul 2012 20:06:06 +0300
>>> Orit Wasserman <owasserm@redhat.com> wrote:
>>>
>>>> On 07/24/2012 03:50 PM, Luiz Capitulino wrote:
>>>>> On Tue, 24 Jul 2012 09:25:11 +0300
>>>>> Orit Wasserman <owasserm@redhat.com> wrote:
>>>>>
>>>>>> On 07/23/2012 09:23 PM, Luiz Capitulino wrote:
>>>>>>> On Fri, 13 Jul 2012 09:23:35 +0200
>>>>>>> Juan Quintela <quintela@redhat.com> wrote:
>>>>>>>
>>>>>>>> From: Orit Wasserman <owasserm@redhat.com>
>>>>>>>>
>>>>>>>> Add migration capabilities that can be queried by the management.
>>>>>>>> The management can query the source QEMU and the destination QEMU in order to
>>>>>>>> verify both support some migration capability (currently only XBZRLE).
>>>>>>>> The management can enable a capability for the next migration by using
>>>>>>>> migrate_set_parameter command.
>>>>>>>
>>>>>>> Please, split this into one command per-patch. Otherwise it's difficult to
>>>>>>> review.
>>>>>>>
>>>>>> Sure.
>>>>>>> Have libvirt folks acked this approach btw? It looks fine to me, but we need
>>>>>>> their ack too.
>>>>>>>
>>>>>>> More comments below.
>>>>>>>
>>>>>>>>
>>>>>>>> Signed-off-by: Orit Wasserman <owasserm@redhat.com>
>>>>>>>> Signed-off-by: Juan Quintela <quintela@redhat.com>
>>>>>>>> ---
>>>>>>>>  hmp-commands.hx  |   16 ++++++++++++
>>>>>>>>  hmp.c            |   64 ++++++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>  hmp.h            |    2 ++
>>>>>>>>  migration.c      |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
>>>>>>>>  migration.h      |    2 ++
>>>>>>>>  monitor.c        |    7 ++++++
>>>>>>>>  qapi-schema.json |   53 ++++++++++++++++++++++++++++++++++++++--
>>>>>>>>  qmp-commands.hx  |   71 ++++++++++++++++++++++++++++++++++++++++++++++++++---
>>>>>>>>  8 files changed, 280 insertions(+), 7 deletions(-)
>>>>>>>>
>>>>>>>> diff --git a/hmp-commands.hx b/hmp-commands.hx
>>>>>>>> index f5d9d91..9245bef 100644
>>>>>>>> --- a/hmp-commands.hx
>>>>>>>> +++ b/hmp-commands.hx
>>>>>>>> @@ -861,6 +861,20 @@ Set maximum tolerated downtime (in seconds) for migration.
>>>>>>>>  ETEXI
>>>>>>>>
>>>>>>>>      {
>>>>>>>> +        .name       = "migrate_set_parameter",
>>>>>>>> +        .args_type  = "capability:s,state:b",
>>>>>>>> +        .params     = "",
>>>>>>>
>>>>>>> Please, fill in params.
>>>>>> ok
>>>>>>>
>>>>>>>> +        .help       = "Enable/Disable the usage of a capability for migration",
>>>>>>>> +        .mhandler.cmd = hmp_migrate_set_parameter,
>>>>>>>> +    },
>>>>>>>> +
>>>>>>>> +STEXI
>>>>>>>> +@item migrate_set_parameter @var{capability} @var{state}
>>>>>>>> +@findex migrate_set_parameter
>>>>>>>> +Enable/Disable the usage of a capability @var{capability} for migration.
>>>>>>>> +ETEXI
>>>>>>>> +
>>>>>>>> +    {
>>>>>>>>          .name       = "client_migrate_info",
>>>>>>>>          .args_type  = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
>>>>>>>>          .params     = "protocol hostname port tls-port cert-subject",
>>>>>>>> @@ -1419,6 +1433,8 @@ show CPU statistics
>>>>>>>>  show user network stack connection states
>>>>>>>>  @item info migrate
>>>>>>>>  show migration status
>>>>>>>> +@item info migration_capabilities
>>>>>>>> +show migration capabilities
>>>>>>>>  @item info balloon
>>>>>>>>  show balloon information
>>>>>>>>  @item info qtree
>>>>>>>> diff --git a/hmp.c b/hmp.c
>>>>>>>> index 4c6d4ae..b0440e6 100644
>>>>>>>> --- a/hmp.c
>>>>>>>> +++ b/hmp.c
>>>>>>>> @@ -131,9 +131,19 @@ void hmp_info_mice(Monitor *mon)
>>>>>>>>  void hmp_info_migrate(Monitor *mon)
>>>>>>>>  {
>>>>>>>>      MigrationInfo *info;
>>>>>>>> +    MigrationCapabilityInfoList *cap;
>>>>>>>>
>>>>>>>>      info = qmp_query_migrate(NULL);
>>>>>>>>
>>>>>>>> +    if (info->has_capabilities && info->capabilities) {
>>>>>>>> +        monitor_printf(mon, "capabilities: ");
>>>>>>>> +        for (cap = info->capabilities; cap; cap = cap->next) {
>>>>>>>> +            monitor_printf(mon, "%s: %s ",
>>>>>>>> +                           MigrationCapability_lookup[cap->value->capability],
>>>>>>>> +                           cap->value->state ? "on" : "off");
>>>>>>>> +        }
>>>>>>>> +        monitor_printf(mon, "\n");
>>>>>>>
>>>>>>> Why is this is needed? Isn't info migration-capabilities good enough?
>>>>>>> Besides, info migrate should only contain information about current migration
>>>>>>> process...
>>>>>> The reason we introduced capabilities is that xbzrle needs for both source and destination QEMU
>>>>>> to be able to handle it. Even if both side support xbzrle the user may decide not to use it.
>>>>>> User that wants to use xbzrle needs to check that both sides have support for it (using info capabilities) , than 
>>>>>> enable it in both sides (using migrate-set-parameter/s commands). This is a parameter for the current migration.
>>>>>> So the user needs to know if xbzrle was enabled or disabled for the current migration, this code displays it.
>>>>>
>>>>> But this is being returned even when there is no migration taking place. I'm
>>>>> answering this here in the 'info migrate' hunk, but my main concern is
>>>>> query-migrate.
>>>> I think migration configuration is part of migration information and should be available to the user in 'info migration'. Especially before he starts running it to see if he need to change the configuration. Also it is very helpful for the user during the migration and also after it's completes.
>>>
>>> I don't mind having this in 'info migrate' because we can change it later case
>>> we regret, so it's fine with me having that info there.
>>>
>>> But query-migrate is different, once we add anything there it's there forever,
>>> so we have to be careful.
>> ok I will remove them from query-migrate.
> 
> Note that I'm asking to remove it from MIG_SETUP, the others states are fine.
ok
> 
>>>
>>>> There is no way today to query  the downtime and migration speed at all . you can always set them again
>>>> but there is no way to know how it is configured.
>>>
>>> Good point, but I think it's better to concentrate on the needs of xbzrle
>>> for now.
>>>
>>>> I was actually thinking to add them to info migration (also for SETUP).
>>>
>>> But we're in SETUP state only once, what happens if the settings are changed
>>> after a migration completed? How is mngt going to be able to query it?
>> After migration completes you can't change those setting , you should get an error.
> 
> Really, why? I thought it would be fine to change them at any time
> (except during migration, of course).
you are right .
Orit
> 
>>
>>>
>>> My advice to not delay the inclusion of this series is to drop the
>>> information from SETUP state and rely on query-migration-capabilities in
>>> 'info migrate'. We can extend later if needed (by means of new commands or
>>> adding info to query-migrate).
>> It needs to be another command, I can add query-migration-parameters.
>> You can look at this as 64bit support - you have 64bit support in the processor (this is what query-capabilities) but the OS can decide not to work in 64bit mode (do nothing) or using 64 bit (migrate-set-parameter).
>>
>> Orit
>>>
>>>>>
>>>>> query-migrate shouldn't return this info in MIG_STATE_SETUP, and for
>>>>> MIG_STATE_COMPLETED it should only be returned if there was a migration.
>>>> I think is case of MIG_SETUP we should return the configuration and params for the next migration. 
>>>> MIG_STATE_COMPLETED happens only if there was a successful migration other wise there will be another state.
>>>>>
>>>>>> some day when there will be better migration protocol, feature negotiations can be part of it ...
>>>>>>>
>>>>>>>> +    }
>>>>>>>>      if (info->has_status) {
>>>>>>>>          monitor_printf(mon, "Migration status: %s\n", info->status);
>>>>>>>>      }
>>>>>>>> @@ -161,6 +171,25 @@ void hmp_info_migrate(Monitor *mon)
>>>>>>>>      qapi_free_MigrationInfo(info);
>>>>>>>>  }
>>>>>>>>
>>>>>>>> +void hmp_info_migration_capabilities(Monitor *mon)
>>>>>>>> +{
>>>>>>>> +    MigrationCapabilityInfoList *caps_list, *cap;
>>>>>>>> +
>>>>>>>> +    caps_list = qmp_query_migration_capabilities(NULL);
>>>>>>>> +    if (!caps_list) {
>>>>>>>> +        monitor_printf(mon, "No migration capabilities found\n");
>>>>>>>> +        return;
>>>>>>>> +    }
>>>>>>>> +
>>>>>>>> +    for (cap = caps_list; cap; cap = cap->next) {
>>>>>>>> +        monitor_printf(mon, "%s: %s ",
>>>>>>>> +                       MigrationCapability_lookup[cap->value->capability],
>>>>>>>> +                       cap->value->state ? "on" : "off");
>>>>>>>> +    }
>>>>>>>> +
>>>>>>>> +    qapi_free_MigrationCapabilityInfoList(caps_list);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>>  void hmp_info_cpus(Monitor *mon)
>>>>>>>>  {
>>>>>>>>      CpuInfoList *cpu_list, *cpu;
>>>>>>>> @@ -735,6 +764,41 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict)
>>>>>>>>      qmp_migrate_set_speed(value, NULL);
>>>>>>>>  }
>>>>>>>>
>>>>>>>> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
>>>>>>>> +{
>>>>>>>> +    const char *cap = qdict_get_str(qdict, "capability");
>>>>>>>> +    bool state = qdict_get_bool(qdict, "state");
>>>>>>>> +    Error *err = NULL;
>>>>>>>> +    MigrationCapabilityInfoList *params = NULL;
>>>>>>>> +    int i;
>>>>>>>> +
>>>>>>>> +    for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>>>>>>>> +        if (strcmp(cap, MigrationCapability_lookup[i]) == 0) {
>>>>>>>> +            if (!params) {
>>>>>>>> +                params = g_malloc0(sizeof(*params));
>>>>>>>> +            }
>>>>>>>> +            params->value = g_malloc0(sizeof(*params->value));
>>>>>>>> +            params->value->capability = i;
>>>>>>>> +            params->value->state = state;
>>>>>>>> +            params->next = NULL;
>>>>>>>> +            qmp_migrate_set_parameters(params, &err);
>>>>>>>> +            break;
>>>>>>>> +        }
>>>>>>>> +    }
>>>>>>>> +
>>>>>>>> +    if (i == MIGRATION_CAPABILITY_MAX) {
>>>>>>>> +        error_set(&err, QERR_INVALID_PARAMETER, cap);
>>>>>>>> +    }
>>>>>>>> +
>>>>>>>> +    qapi_free_MigrationCapabilityInfoList(params);
>>>>>>>> +
>>>>>>>> +    if (err) {
>>>>>>>> +        monitor_printf(mon, "migrate_set_parameter: %s\n",
>>>>>>>> +                       error_get_pretty(err));
>>>>>>>> +        error_free(err);
>>>>>>>> +    }
>>>>>>>> +}
>>>>>>>> +
>>>>>>>>  void hmp_set_password(Monitor *mon, const QDict *qdict)
>>>>>>>>  {
>>>>>>>>      const char *protocol  = qdict_get_str(qdict, "protocol");
>>>>>>>> diff --git a/hmp.h b/hmp.h
>>>>>>>> index 79d138d..09ba198 100644
>>>>>>>> --- a/hmp.h
>>>>>>>> +++ b/hmp.h
>>>>>>>> @@ -25,6 +25,7 @@ void hmp_info_uuid(Monitor *mon);
>>>>>>>>  void hmp_info_chardev(Monitor *mon);
>>>>>>>>  void hmp_info_mice(Monitor *mon);
>>>>>>>>  void hmp_info_migrate(Monitor *mon);
>>>>>>>> +void hmp_info_migration_capabilities(Monitor *mon);
>>>>>>>>  void hmp_info_cpus(Monitor *mon);
>>>>>>>>  void hmp_info_block(Monitor *mon);
>>>>>>>>  void hmp_info_blockstats(Monitor *mon);
>>>>>>>> @@ -51,6 +52,7 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
>>>>>>>>  void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
>>>>>>>>  void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict);
>>>>>>>>  void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
>>>>>>>> +void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict);
>>>>>>>>  void hmp_set_password(Monitor *mon, const QDict *qdict);
>>>>>>>>  void hmp_expire_password(Monitor *mon, const QDict *qdict);
>>>>>>>>  void hmp_eject(Monitor *mon, const QDict *qdict);
>>>>>>>> diff --git a/migration.c b/migration.c
>>>>>>>> index 8db1b43..fd004d7 100644
>>>>>>>> --- a/migration.c
>>>>>>>> +++ b/migration.c
>>>>>>>> @@ -117,12 +117,36 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>>>>>>>  {
>>>>>>>>      MigrationInfo *info = g_malloc0(sizeof(*info));
>>>>>>>>      MigrationState *s = migrate_get_current();
>>>>>>>> +    int i;
>>>>>>>>
>>>>>>>>      switch (s->state) {
>>>>>>>>      case MIG_STATE_SETUP:
>>>>>>>> -        /* no migration has happened ever */
>>>>>>>> +        /* no migration has ever happened; show enabled capabilities */
>>>>>>>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>>>>>>>> +            if (!info->has_capabilities) {
>>>>>>>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
>>>>>>>> +                info->has_capabilities = true;
>>>>>>>> +            }
>>>>>>>> +            info->capabilities->value =
>>>>>>>> +                g_malloc(sizeof(*info->capabilities->value));
>>>>>>>> +            info->capabilities->value->capability = i;
>>>>>>>> +            info->capabilities->value->state = s->enabled_capabilities[i];
>>>>>>>> +            info->capabilities->next = NULL;
>>>>>>>> +        }
>>>>>>>>          break;
>>>>>>>>      case MIG_STATE_ACTIVE:
>>>>>>>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>>>>>>>> +            if (!info->has_capabilities) {
>>>>>>>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
>>>>>>>> +                info->has_capabilities = true;
>>>>>>>> +            }
>>>>>>>> +            info->capabilities->value =
>>>>>>>> +                g_malloc(sizeof(*info->capabilities->value));
>>>>>>>> +            info->capabilities->value->capability = i;
>>>>>>>> +            info->capabilities->value->state = s->enabled_capabilities[i];
>>>>>>>> +            info->capabilities->next = NULL;
>>>>>>>> +        }
>>>>>>>> +
>>>>>>>>          info->has_status = true;
>>>>>>>>          info->status = g_strdup("active");
>>>>>>>>
>>>>>>>> @@ -143,6 +167,18 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>>>>>>>          }
>>>>>>>>          break;
>>>>>>>>      case MIG_STATE_COMPLETED:
>>>>>>>> +        for (i = 0; i < MIGRATION_CAPABILITY_MAX; i++) {
>>>>>>>> +            if (!info->has_capabilities) {
>>>>>>>> +                info->capabilities = g_malloc0(sizeof(*info->capabilities));
>>>>>>>> +                info->has_capabilities = true;
>>>>>>>> +            }
>>>>>>>> +            info->capabilities->value =
>>>>>>>> +                g_malloc(sizeof(*info->capabilities->value));
>>>>>>>> +            info->capabilities->value->capability = i;
>>>>>>>> +            info->capabilities->value->state = s->enabled_capabilities[i];
>>>>>>>> +            info->capabilities->next = NULL;
>>>>>>>> +        }
>>>>>>>
>>>>>>> Code triplication :)
>>>>>>>
>>>>>>> Why is this is needed? Isn't query-migration-capabilities good enough?
>>>>>>> Besides, query-migrate should only contain information about current migration
>>>>>>> process...
>>>>>>>
>>>>>> see above
>>>>>>>> +
>>>>>>>>          info->has_status = true;
>>>>>>>>          info->status = g_strdup("completed");
>>>>>>>>
>>>>>>>> @@ -166,6 +202,33 @@ MigrationInfo *qmp_query_migrate(Error **errp)
>>>>>>>>      return info;
>>>>>>>>  }
>>>>>>>>
>>>>>>>> +MigrationCapabilityInfoList *qmp_query_migration_capabilities(Error **errp)
>>>>>>>> +{
>>>>>>>> +    MigrationCapabilityInfoList *caps_list = g_malloc0(sizeof(*caps_list));
>>>>>>>> +
>>>>>>>> +    caps_list->value = g_malloc(sizeof(*caps_list->value));
>>>>>>>> +    caps_list->value->capability = MIGRATION_CAPABILITY_XBZRLE;
>>>>>>>> +    caps_list->next = NULL;
>>>>>>>
>>>>>>> Shouldn't this get the capabilities array from migrate_get_current()?
>>>>>>>
>>>>>>> I mean, this makes query-migration-capabilities always return true for
>>>>>>> xbzrle, even if we set it to off.
>>>>>> Those are the general capabilities , if I want to use xbzrle ,does this QEMU support it ? 
>>>>>> this is required because we need both the destination and the source to be able to handle it.
>>>>>> If one QEMU doesn't (older version of qemu, maybe it will be disbaled for certain architectures) we can't use the feature.
>>>>>> This is for management and the user to check what migration capabilities this QEMU supports.
>>>>>
>>>>> My point was that query-migration-capabilities will always return state=true
>>>>> for all existing capabilities. Don't we want users/mngt app to know which
>>>>> states have been turned off?
>>>> You need to separate the ability to use a capability and actually using it for migration. Also I don't want the user to be able to set some capability on, it depends on the code not user configuration.
>>>
>>> Not sure I can follow, a user can set xbzrle capability to off, no?
>>>
>>>> And for xbzrle we always return true for some version on QEMU , let say we decide we want to remove the code that implement it for some reason in some future version than it will return false.
>>>
>>> That's fine (I mean, I'm not sure we will ever do it, but shouldn't be a problem
>>> if we do).
>>>
>>>> There is a reason the command that enables a capability for a migration was called 'migrate-set-parameter' and not set-capability to distinguish between them one is per QEMU code base and one is per migration.
>>>>
>>>> Regards,
>>>> Orit
>>>>>
>>>>>>
>>>>>>>
>>>>>>>> +
>>>>>>>> +    return caps_list;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +void qmp_migrate_set_parameters(MigrationCapabilityInfoList *params,
>>>>>>>> +                                Error **errp)
>>>>>>>> +{
>>>>>>>> +    MigrationState *s = migrate_get_current();
>>>>>>>> +    MigrationCapabilityInfoList *cap;
>>>>>>>> +
>>>>>>>> +    if (s->state == MIG_STATE_ACTIVE) {
>>>>>>>> +        error_set(errp, QERR_MIGRATION_ACTIVE);
>>>>>>>> +        return;
>>>>>>>> +    }
>>>>>>>> +
>>>>>>>> +    for (cap = params; cap; cap = cap->next) {
>>>>>>>> +        s->enabled_capabilities[cap->value->capability] = cap->value->state;
>>>>>>>> +    }
>>>>>>>> +}
>>>>>>>> +
>>>>>>>>  /* shared migration helpers */
>>>>>>>>
>>>>>>>>  static int migrate_fd_cleanup(MigrationState *s)
>>>>>>>> @@ -375,12 +438,17 @@ static MigrationState *migrate_init(const MigrationParams *params)
>>>>>>>>  {
>>>>>>>>      MigrationState *s = migrate_get_current();
>>>>>>>>      int64_t bandwidth_limit = s->bandwidth_limit;
>>>>>>>> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
>>>>>>>> +
>>>>>>>> +    memcpy(enabled_capabilities, s->enabled_capabilities,
>>>>>>>> +           sizeof(enabled_capabilities));
>>>>>>>>
>>>>>>>>      memset(s, 0, sizeof(*s));
>>>>>>>>      s->bandwidth_limit = bandwidth_limit;
>>>>>>>>      s->params = *params;
>>>>>>>> +    memcpy(s->enabled_capabilities, enabled_capabilities,
>>>>>>>> +           sizeof(enabled_capabilities));
>>>>>>>>
>>>>>>>> -    s->bandwidth_limit = bandwidth_limit;
>>>>>>>>      s->state = MIG_STATE_SETUP;
>>>>>>>>      s->total_time = qemu_get_clock_ms(rt_clock);
>>>>>>>>
>>>>>>>> diff --git a/migration.h b/migration.h
>>>>>>>> index 57572a6..713aae0 100644
>>>>>>>> --- a/migration.h
>>>>>>>> +++ b/migration.h
>>>>>>>> @@ -19,6 +19,7 @@
>>>>>>>>  #include "notify.h"
>>>>>>>>  #include "error.h"
>>>>>>>>  #include "vmstate.h"
>>>>>>>> +#include "qapi-types.h"
>>>>>>>>
>>>>>>>>  struct MigrationParams {
>>>>>>>>      bool blk;
>>>>>>>> @@ -39,6 +40,7 @@ struct MigrationState
>>>>>>>>      void *opaque;
>>>>>>>>      MigrationParams params;
>>>>>>>>      int64_t total_time;
>>>>>>>> +    bool enabled_capabilities[MIGRATION_CAPABILITY_MAX];
>>>>>>>>  };
>>>>>>>>
>>>>>>>>  void process_incoming_migration(QEMUFile *f);
>>>>>>>> diff --git a/monitor.c b/monitor.c
>>>>>>>> index f6107ba..e2be6cd 100644
>>>>>>>> --- a/monitor.c
>>>>>>>> +++ b/monitor.c
>>>>>>>> @@ -2687,6 +2687,13 @@ static mon_cmd_t info_cmds[] = {
>>>>>>>>          .mhandler.info = hmp_info_migrate,
>>>>>>>>      },
>>>>>>>>      {
>>>>>>>> +        .name       = "migration_capabilities",
>>>>>>>
>>>>>>> migration-capabilities is better.
>>>>>> ok
>>>>>>>
>>>>>>>> +        .args_type  = "",
>>>>>>>> +        .params     = "",
>>>>>>>> +        .help       = "show migration capabilities",
>>>>>>>> +        .mhandler.info = hmp_info_migration_capabilities,
>>>>>>>> +    },
>>>>>>>> +    {
>>>>>>>>          .name       = "balloon",
>>>>>>>>          .args_type  = "",
>>>>>>>>          .params     = "",
>>>>>>>> diff --git a/qapi-schema.json b/qapi-schema.json
>>>>>>>> index 1ab5dbd..a8408fd 100644
>>>>>>>> --- a/qapi-schema.json
>>>>>>>> +++ b/qapi-schema.json
>>>>>>>> @@ -288,11 +288,15 @@
>>>>>>>>  #        status, only returned if status is 'active' and it is a block
>>>>>>>>  #        migration
>>>>>>>>  #
>>>>>>>> -# Since: 0.14.0
>>>>>>>> +# @capabilities: #optional a list describing all the migration capabilities
>>>>>>>> +#                state
>>>>>>>
>>>>>>> I don't think this is needed, as I've said above.
>>>>>> see above
>>>>>>>
>>>>>>>> +#
>>>>>>>> +# Since: 0.14.0, 'capabilities' since 1.2
>>>>>>>>  ##
>>>>>>>>  { 'type': 'MigrationInfo',
>>>>>>>>    'data': {'*status': 'str', '*ram': 'MigrationStats',
>>>>>>>> -           '*disk': 'MigrationStats'} }
>>>>>>>> +           '*disk': 'MigrationStats',
>>>>>>>> +           '*capabilities': ['MigrationCapabilityInfo']} }
>>>>>>>>
>>>>>>>>  ##
>>>>>>>>  # @query-migrate
>>>>>>>> @@ -306,6 +310,51 @@
>>>>>>>>  { 'command': 'query-migrate', 'returns': 'MigrationInfo' }
>>>>>>>>
>>>>>>>>  ##
>>>>>>>> +# @MigrationCapability
>>>>>>>> +#
>>>>>>>> +# Migration capabilities enumeration
>>>>>>>> +#
>>>>>>>> +# @xbzrle: current migration supports xbzrle
>>>>>>>
>>>>>>> You should explain what xbzrle is.
>>>>>> ok
>>>>>>>
>>>>>>>> +#
>>>>>>>> +# Since: 1.2
>>>>>>>> +##
>>>>>>>> +{ 'enum': 'MigrationCapability',
>>>>>>>> +  'data': ['xbzrle'] }
>>>>>>>> +
>>>>>>>> +##
>>>>>>>> +# @MigrationCapabilityInfo
>>>>>>>
>>>>>>> MigrationCapabilityStatus?
>>>>>>>
>>>>>>>> +#
>>>>>>>> +# Migration capability information
>>>>>>>> +#
>>>>>>>> +# @capability: capability enum
>>>>>>>
>>>>>>> Please, document state too.
>>>>>> ok
>>>>>>
>>>>>> Orit
>>>>>>>
>>>>>>>> +#
>>>>>>>> +# Since: 1.2
>>>>>>>> +##
>>>>>>>> +{ 'type': 'MigrationCapabilityInfo',
>>>>>>>> +  'data': { 'capability' : 'MigrationCapability', 'state' : 'bool' } }
>>>>>>>> +
>>>>>>>> +##
>>>>>>>> +# @query-migration-capabilities
>>>>>>>> +#
>>>>>>>> +# Returns information about current migration process capabilties.
>>>>>>>> +#
>>>>>>>> +# Returns: @MigrationCapabilityInfo list
>>>>>>>> +#
>>>>>>>> +# Since: 1.2
>>>>>>>> +##
>>>>>>>> +{ 'command': 'query-migration-capabilities', 'returns': ['MigrationCapabilityInfo'] }
>>>>>>>> +
>>>>>>>> +##
>>>>>>>> +# @migrate_set_parameters
>>>>>>>> +#
>>>>>>>> +# Enable/Disable the following migration capabilities (like xbzrle)
>>>>>>>> +#
>>>>>>>> +# Since: 1.2
>>>>>>>> +##
>>>>>>>> +{ 'command': 'migrate-set-parameters',
>>>>>>>> +  'data': { 'capabilities': ['MigrationCapabilityInfo'] } }
>>>>>>>> +
>>>>>>>> +##
>>>>>>>>  # @MouseInfo:
>>>>>>>>  #
>>>>>>>>  # Information about a mouse device.
>>>>>>>> diff --git a/qmp-commands.hx b/qmp-commands.hx
>>>>>>>> index 2e1a38e..3ee6e00 100644
>>>>>>>> --- a/qmp-commands.hx
>>>>>>>> +++ b/qmp-commands.hx
>>>>>>>> @@ -2075,18 +2075,31 @@ The main json-object contains the following:
>>>>>>>>           - "transferred": amount transferred (json-int)
>>>>>>>>           - "remaining": amount remaining (json-int)
>>>>>>>>           - "total": total (json-int)
>>>>>>>> -
>>>>>>>> +- "capabilities": migration capabilities state
>>>>>>>> +         - "xbzrle" : XBZRLE state (json-bool)
>>>>>>>>  Examples:
>>>>>>>>
>>>>>>>>  1. Before the first migration
>>>>>>>>
>>>>>>>>  -> { "execute": "query-migrate" }
>>>>>>>> -<- { "return": {} }
>>>>>>>> +<- { "return": {
>>>>>>>> +        "capabilities" :  [ { "capability" : "xbzrle", "state" : false } ]
>>>>>>>> +     }
>>>>>>>> +   }
>>>>>>>>
>>>>>>>>  2. Migration is done and has succeeded
>>>>>>>>
>>>>>>>>  -> { "execute": "query-migrate" }
>>>>>>>> -<- { "return": { "status": "completed" } }
>>>>>>>> +<- { "return": {
>>>>>>>> +        "status": "completed",
>>>>>>>> +        "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
>>>>>>>> +        "ram":{
>>>>>>>> +          "transferred":123,
>>>>>>>> +          "remaining":123,
>>>>>>>> +          "total":246
>>>>>>>> +        }
>>>>>>>> +     }
>>>>>>>> +   }
>>>>>>>>
>>>>>>>>  3. Migration is done and has failed
>>>>>>>>
>>>>>>>> @@ -2099,6 +2112,7 @@ Examples:
>>>>>>>>  <- {
>>>>>>>>        "return":{
>>>>>>>>           "status":"active",
>>>>>>>> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
>>>>>>>>           "ram":{
>>>>>>>>              "transferred":123,
>>>>>>>>              "remaining":123,
>>>>>>>> @@ -2113,6 +2127,7 @@ Examples:
>>>>>>>>  <- {
>>>>>>>>        "return":{
>>>>>>>>           "status":"active",
>>>>>>>> +         "capabilities":  [ { "capability" : "xbzrle", "state" : false } ],
>>>>>>>>           "ram":{
>>>>>>>>              "total":1057024,
>>>>>>>>              "remaining":1053304,
>>>>>>>> @@ -2135,6 +2150,56 @@ EQMP
>>>>>>>>      },
>>>>>>>>
>>>>>>>>  SQMP
>>>>>>>> +query-migration-capabilities
>>>>>>>> +-------
>>>>>>>> +
>>>>>>>> +Query migration capabilities
>>>>>>>> +
>>>>>>>> +- "xbzrle": xbzrle support
>>>>>>>> +
>>>>>>>> +Arguments:
>>>>>>>> +
>>>>>>>> +Example:
>>>>>>>> +
>>>>>>>> +-> { "execute": "query-migration-capabilities"}
>>>>>>>> +<- { "return": [ { "capability": "xbzrle", "state": true },
>>>>>>>> +                 { "capability": "foobar", "state": false } ] }
>>>>>>>> +
>>>>>>>> +EQMP
>>>>>>>> +
>>>>>>>> +    {
>>>>>>>> +        .name       = "query-migration-capabilities",
>>>>>>>> +        .args_type  = "",
>>>>>>>> +	.mhandler.cmd_new = qmp_marshal_input_query_migration_capabilities,
>>>>>>>> +    },
>>>>>>>> +
>>>>>>>> +SQMP
>>>>>>>> +migrate_set_parameters
>>>>>>>> +-------
>>>>>>>> +
>>>>>>>> +Enable/Disable migration capabilities
>>>>>>>> +
>>>>>>>> +- "xbzrle": xbzrle support
>>>>>>>> +
>>>>>>>> +Arguments:
>>>>>>>> +
>>>>>>>> +Example:
>>>>>>>> +
>>>>>>>> +-> { "execute": "migrate_set_parameters" , "arguments":
>>>>>>>> +     { "parameters": [ { "capability": "xbzrle", "state": true } ] } }
>>>>>>>> +
>>>>>>>> +EQMP
>>>>>>>> +
>>>>>>>> +    {
>>>>>>>> +        .name       = "migrate_set_parameters",
>>>>>>>> +        .args_type  = "parameters:O",
>>>>>>>> +	.params     = "capability:s,state:b",
>>>>>>>> +	.mhandler.cmd_new = qmp_marshal_input_migrate_set_parameters,
>>>>>>>> +    },
>>>>>>>> +
>>>>>>>> +
>>>>>>>> +
>>>>>>>> +SQMP
>>>>>>>>  query-balloon
>>>>>>>>  -------------
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>
>>>>
>>>
>>
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2012-07-25 13:33 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-07-13  7:23 [Qemu-devel] [PATCH 00/22] Migration next Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 01/22] savevm: Use a struct to pass all handlers Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 02/22] savevm: Live migration handlers register the struct directly Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 03/22] savevm: remove SaveSetParamsHandler Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 04/22] savevm: remove SaveLiveStateHandler Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 05/22] savevm: Refactor cancel operation in its own operation Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 06/22] savevm: introduce is_active method Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 07/22] savevm: split save_live_setup from save_live_state Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 08/22] savevm: split save_live into stage2 and stage3 Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 09/22] ram: save_live_setup() don't need to sent pages Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 10/22] ram: save_live_complete() only do one loop Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 11/22] ram: iterate phase Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 12/22] ram: save_live_setup() we don't need to synchronize the dirty bitmap Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 13/22] Add migration capabilities Juan Quintela
2012-07-23 18:23   ` Luiz Capitulino
2012-07-23 19:30     ` Eric Blake
2012-07-24  6:25     ` Orit Wasserman
2012-07-24 12:50       ` Luiz Capitulino
2012-07-24 17:06         ` Orit Wasserman
2012-07-24 18:17           ` Luiz Capitulino
2012-07-25 13:05             ` Orit Wasserman
2012-07-25 13:11               ` Luiz Capitulino
2012-07-25 13:33                 ` Orit Wasserman
2012-07-13  7:23 ` [Qemu-devel] [PATCH 14/22] Add XBZRLE documentation Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 15/22] Add cache handling functions Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 16/22] Add uleb encoding/decoding functions Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 17/22] Change ram_save_block to return -1 if there are no more changes Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 18/22] Add xbzrle_encode_buffer and xbzrle_decode_buffer functions Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 19/22] Add XBZRLE to ram_save_block and ram_save_live Juan Quintela
2012-07-13  7:23 ` [Qemu-devel] [PATCH 20/22] Add migrate_set_cachesize command Juan Quintela
2012-07-23 19:21   ` Luiz Capitulino
2012-07-13  7:23 ` [Qemu-devel] [PATCH 21/22] Add XBZRLE statistics Juan Quintela
2012-07-23 19:33   ` Luiz Capitulino
2012-07-24  6:32     ` Orit Wasserman
2012-07-13  7:23 ` [Qemu-devel] [PATCH 22/22] ram: save live optimization Juan Quintela

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).