[libvirt] [PATCH v3 07/16] numatune: Encapsulate numatune configuration in order to unify results

Martin Kletzander mkletzan at redhat.com
Wed Jul 16 14:42:38 UTC 2014


There were numerous places where numatune configuration (and thus
domain config as well) was changed in different ways.  On some
places this even resulted in persistent domain definition not to be
stable (it would change with daemon's restart).

In order to uniformly change how numatune config is dealt with, all
the internals are now accessible directly only in numatune_conf.c and
outside this file accessors must be used.

Signed-off-by: Martin Kletzander <mkletzan at redhat.com>
---
 po/POTFILES.in                                     |   1 +
 src/conf/domain_conf.c                             | 159 ++---------
 src/conf/domain_conf.h                             |   8 +-
 src/conf/numatune_conf.c                           | 318 +++++++++++++++++++++
 src/conf/numatune_conf.h                           |  72 ++++-
 src/libvirt_private.syms                           |  11 +
 src/lxc/lxc_cgroup.c                               |  19 +-
 src/lxc/lxc_controller.c                           |   5 +-
 src/lxc/lxc_native.c                               |  15 +-
 src/parallels/parallels_driver.c                   |   7 +-
 src/qemu/qemu_cgroup.c                             |  23 +-
 src/qemu/qemu_driver.c                             |  84 +++---
 src/qemu/qemu_process.c                            |   8 +-
 src/util/virnuma.c                                 |  48 ++--
 src/util/virnuma.h                                 |   2 +-
 .../qemuxml2argv-numatune-auto-prefer.xml          |  29 ++
 .../qemuxml2xmlout-numatune-auto-prefer.xml        |  29 ++
 tests/qemuxml2xmltest.c                            |   2 +
 18 files changed, 555 insertions(+), 285 deletions(-)
 create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-numatune-auto-prefer.xml
 create mode 100644 tests/qemuxml2xmloutdata/qemuxml2xmlout-numatune-auto-prefer.xml

diff --git a/po/POTFILES.in b/po/POTFILES.in
index fd4b56e..d10e892 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -25,6 +25,7 @@ src/conf/netdev_vlan_conf.c
 src/conf/netdev_vport_profile_conf.c
 src/conf/network_conf.c
 src/conf/node_device_conf.c
+src/conf/numatune_conf.c
 src/conf/nwfilter_conf.c
 src/conf/nwfilter_params.c
 src/conf/object_event.c
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index f83050d..d34d94c 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2097,7 +2097,7 @@ void virDomainDefFree(virDomainDefPtr def)

     virDomainVcpuPinDefFree(def->cputune.emulatorpin);

-    virBitmapFree(def->numatune.memory.nodemask);
+    virDomainNumatuneFree(def->numatune);

     virSysinfoDefFree(def->sysinfo);

@@ -11256,7 +11256,6 @@ virDomainDefParseXML(xmlDocPtr xml,
     unsigned long count;
     bool uuid_generated = false;
     virHashTablePtr bootHash = NULL;
-    xmlNodePtr cur;
     bool usb_none = false;
     bool usb_other = false;
     bool usb_master = false;
@@ -11718,123 +11717,8 @@ virDomainDefParseXML(xmlDocPtr xml,
         }
     }

-    /* Extract numatune if exists. */
-    if ((n = virXPathNodeSet("./numatune", ctxt, &nodes)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       "%s", _("cannot extract numatune nodes"));
+    if (virDomainNumatuneParseXML(def, ctxt) < 0)
         goto error;
-    }
-
-    if (n > 1) {
-        virReportError(VIR_ERR_XML_ERROR, "%s",
-                       _("only one numatune is supported"));
-        VIR_FREE(nodes);
-        goto error;
-    }
-
-    if (n) {
-        cur = nodes[0]->children;
-        while (cur != NULL) {
-            if (cur->type == XML_ELEMENT_NODE) {
-                if (xmlStrEqual(cur->name, BAD_CAST "memory")) {
-                    char *mode = NULL;
-                    char *placement = NULL;
-                    char *nodeset = NULL;
-
-                    mode = virXMLPropString(cur, "mode");
-                    if (mode) {
-                        if ((def->numatune.memory.mode =
-                             virDomainNumatuneMemModeTypeFromString(mode)) < 0) {
-                            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                                           _("Unsupported NUMA memory "
-                                             "tuning mode '%s'"),
-                                           mode);
-                            VIR_FREE(mode);
-                            goto error;
-                        }
-                        VIR_FREE(mode);
-                    } else {
-                        def->numatune.memory.mode = VIR_DOMAIN_NUMATUNE_MEM_STRICT;
-                    }
-
-                    nodeset = virXMLPropString(cur, "nodeset");
-                    if (nodeset) {
-                        if (virBitmapParse(nodeset,
-                                           0,
-                                           &def->numatune.memory.nodemask,
-                                           VIR_DOMAIN_CPUMASK_LEN) < 0) {
-                            VIR_FREE(nodeset);
-                            goto error;
-                        }
-                        VIR_FREE(nodeset);
-                    }
-
-                    placement = virXMLPropString(cur, "placement");
-                    int placement_mode = 0;
-                    if (placement) {
-                        if ((placement_mode =
-                             virDomainNumatunePlacementTypeFromString(placement)) < 0) {
-                            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                                           _("Unsupported memory placement "
-                                             "mode '%s'"), placement);
-                            VIR_FREE(placement);
-                            goto error;
-                        }
-                        VIR_FREE(placement);
-                    } else if (def->numatune.memory.nodemask) {
-                        /* Defaults to "static" if nodeset is specified. */
-                        placement_mode = VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC;
-                    } else {
-                        /* Defaults to "placement" of <vcpu> if nodeset is
-                         * not specified.
-                         */
-                        if (def->placement_mode == VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC)
-                            placement_mode = VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC;
-                        else
-                            placement_mode = VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO;
-                    }
-
-                    if (placement_mode == VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC &&
-                        !def->numatune.memory.nodemask) {
-                        virReportError(VIR_ERR_XML_ERROR, "%s",
-                                       _("nodeset for NUMA memory tuning must be set "
-                                         "if 'placement' is 'static'"));
-                        goto error;
-                    }
-
-                    /* Ignore 'nodeset' if 'placement' is 'auto' finally */
-                    if (placement_mode == VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO) {
-                        virBitmapFree(def->numatune.memory.nodemask);
-                        def->numatune.memory.nodemask = NULL;
-                    }
-
-                    /* Copy 'placement' of <numatune> to <vcpu> if its 'placement'
-                     * is not specified and 'placement' of <numatune> is specified.
-                     */
-                    if (placement_mode == VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO &&
-                        !def->cpumask)
-                        def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO;
-
-                    def->numatune.memory.placement_mode = placement_mode;
-                } else {
-                    virReportError(VIR_ERR_XML_ERROR,
-                                   _("unsupported XML element %s"),
-                                   (const char *)cur->name);
-                    goto error;
-                }
-            }
-            cur = cur->next;
-        }
-    } else {
-        /* Defaults NUMA memory placement mode to 'auto' if no <numatune>
-         * and 'placement' of <vcpu> is 'auto'.
-         */
-        if (def->placement_mode == VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO) {
-            def->numatune.memory.placement_mode = VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO;
-            def->numatune.memory.mode = VIR_DOMAIN_NUMATUNE_MEM_STRICT;
-        }
-    }
-    VIR_FREE(nodes);

     if ((n = virXPathNodeSet("./resource", ctxt, &nodes)) < 0) {
         virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -17440,30 +17324,8 @@ virDomainDefFormatInternal(virDomainDefPtr def,
         def->cputune.emulator_period || def->cputune.emulator_quota)
         virBufferAddLit(buf, "</cputune>\n");

-    if (def->numatune.memory.nodemask ||
-        def->numatune.memory.placement_mode) {
-        const char *mode;
-        char *nodemask = NULL;
-        const char *placement;
-
-        virBufferAddLit(buf, "<numatune>\n");
-        virBufferAdjustIndent(buf, 2);
-        mode = virDomainNumatuneMemModeTypeToString(def->numatune.memory.mode);
-        virBufferAsprintf(buf, "<memory mode='%s' ", mode);
-
-        if (def->numatune.memory.placement_mode ==
-            VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC) {
-            if (!(nodemask = virBitmapFormat(def->numatune.memory.nodemask)))
-                goto error;
-            virBufferAsprintf(buf, "nodeset='%s'/>\n", nodemask);
-            VIR_FREE(nodemask);
-        } else if (def->numatune.memory.placement_mode) {
-            placement = virDomainNumatunePlacementTypeToString(def->numatune.memory.placement_mode);
-            virBufferAsprintf(buf, "placement='%s'/>\n", placement);
-        }
-        virBufferAdjustIndent(buf, -2);
-        virBufferAddLit(buf, "</numatune>\n");
-    }
+    if (virDomainNumatuneFormatXML(buf, def->numatune) < 0)
+        goto error;

     if (def->resource)
         virDomainResourceDefFormat(buf, def->resource);
@@ -19760,3 +19622,16 @@ virDomainObjSetMetadata(virDomainObjPtr vm,

     return 0;
 }
+
+
+bool
+virDomainDefNeedsPlacementAdvice(virDomainDefPtr def)
+{
+    if (def->placement_mode == VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO)
+        return true;
+
+    if (virDomainNumatuneHasPlacementAuto(def->numatune))
+        return true;
+
+    return false;
+}
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index b0ac6b4..9dc57d9 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -67,6 +67,9 @@ typedef virDomainFSDef *virDomainFSDefPtr;
 typedef struct _virDomainNetDef virDomainNetDef;
 typedef virDomainNetDef *virDomainNetDefPtr;

+typedef struct _virDomainNumatune virDomainNumatune;
+typedef virDomainNumatune *virDomainNumatunePtr;
+
 typedef struct _virDomainInputDef virDomainInputDef;
 typedef virDomainInputDef *virDomainInputDefPtr;

@@ -1891,7 +1894,7 @@ struct _virDomainDef {
         virDomainVcpuPinDefPtr emulatorpin;
     } cputune;

-    virDomainNumatune numatune;
+    virDomainNumatunePtr numatune;
     virDomainResourceDefPtr resource;
     virDomainIdMapDef idmap;

@@ -2711,4 +2714,7 @@ int virDomainObjSetMetadata(virDomainObjPtr vm,
                             const char *configDir,
                             unsigned int flags);

+bool virDomainDefNeedsPlacementAdvice(virDomainDefPtr def)
+    ATTRIBUTE_NONNULL(1);
+
 #endif /* __DOMAIN_CONF_H */
diff --git a/src/conf/numatune_conf.c b/src/conf/numatune_conf.c
index 14300f0..6ce1e2d 100644
--- a/src/conf/numatune_conf.c
+++ b/src/conf/numatune_conf.c
@@ -24,6 +24,12 @@

 #include "numatune_conf.h"

+#include "domain_conf.h"
+#include "viralloc.h"
+#include "virstring.h"
+
+#define VIR_FROM_THIS VIR_FROM_DOMAIN
+
 VIR_ENUM_IMPL(virDomainNumatuneMemMode,
               VIR_DOMAIN_NUMATUNE_MEM_LAST,
               "strict",
@@ -35,3 +41,315 @@ VIR_ENUM_IMPL(virDomainNumatunePlacement,
               "default",
               "static",
               "auto");
+
+struct _virDomainNumatune {
+    struct {
+        virBitmapPtr nodeset;
+        virDomainNumatuneMemMode mode;
+        virDomainNumatunePlacement placement;
+    } memory;               /* pinning for all the memory */
+
+    /* Future NUMA tuning related stuff should go here. */
+};
+
+
+int
+virDomainNumatuneParseXML(virDomainDefPtr def,
+                          xmlXPathContextPtr ctxt)
+{
+    char *tmp = NULL;
+    int mode = -1;
+    int n = 0;
+    int placement = -1;
+    int ret = -1;
+    virBitmapPtr nodeset = NULL;
+    xmlNodePtr node = NULL;
+
+    if (virXPathInt("count(./numatune)", ctxt, &n) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("cannot extract numatune nodes"));
+        goto cleanup;
+    } else if (n > 1) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("only one numatune is supported"));
+        goto cleanup;
+    }
+
+    node = virXPathNode("./numatune/memory[1]", ctxt);
+
+    if (def->numatune) {
+        virDomainNumatuneFree(def->numatune);
+        def->numatune = NULL;
+    }
+
+    if (!node && def->placement_mode != VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO)
+        return 0;
+
+    if (!node) {
+        /* We know that def->placement_mode is "auto" if we're here */
+        if (virDomainNumatuneSet(def, VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO,
+                                 -1, NULL) < 0)
+            goto cleanup;
+        return 0;
+    }
+
+    tmp = virXMLPropString(node, "mode");
+    if (tmp) {
+        mode = virDomainNumatuneMemModeTypeFromString(tmp);
+        if (mode < 0) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("Unsupported NUMA memory tuning mode '%s'"),
+                           tmp);
+            goto cleanup;
+        }
+    }
+    VIR_FREE(tmp);
+
+    tmp = virXMLPropString(node, "placement");
+    if (tmp) {
+        placement = virDomainNumatunePlacementTypeFromString(tmp);
+        if (placement < 0) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("Unsupported NUMA memory placement mode '%s'"),
+                           tmp);
+            goto cleanup;
+        }
+    }
+    VIR_FREE(tmp);
+
+    tmp = virXMLPropString(node, "nodeset");
+    if (tmp && virBitmapParse(tmp, 0, &nodeset, VIR_DOMAIN_CPUMASK_LEN) < 0)
+        goto cleanup;
+    VIR_FREE(tmp);
+
+    if (virDomainNumatuneSet(def, placement, mode, nodeset) < 0)
+        goto cleanup;
+
+    if (!n) {
+        ret = 0;
+        goto cleanup;
+    }
+
+    ret = 0;
+ cleanup:
+    virBitmapFree(nodeset);
+    VIR_FREE(tmp);
+    return ret;
+}
+
+int
+virDomainNumatuneFormatXML(virBufferPtr buf,
+                           virDomainNumatunePtr numatune)
+{
+    const char *tmp = NULL;
+    char *nodeset = NULL;
+
+    if (!numatune)
+        return 0;
+
+    virBufferAddLit(buf, "<numatune>\n");
+    virBufferAdjustIndent(buf, 2);
+
+    tmp = virDomainNumatuneMemModeTypeToString(numatune->memory.mode);
+    virBufferAsprintf(buf, "<memory mode='%s' ", tmp);
+
+    if (numatune->memory.placement == VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC) {
+        if (!(nodeset = virBitmapFormat(numatune->memory.nodeset)))
+            return -1;
+        virBufferAsprintf(buf, "nodeset='%s'/>\n", nodeset);
+        VIR_FREE(nodeset);
+    } else if (numatune->memory.placement) {
+        tmp = virDomainNumatunePlacementTypeToString(numatune->memory.placement);
+        virBufferAsprintf(buf, "placement='%s'/>\n", tmp);
+    }
+
+    virBufferAdjustIndent(buf, -2);
+    virBufferAddLit(buf, "</numatune>\n");
+    return 0;
+}
+
+void
+virDomainNumatuneFree(virDomainNumatunePtr numatune)
+{
+    if (!numatune)
+        return;
+
+    virBitmapFree(numatune->memory.nodeset);
+
+    VIR_FREE(numatune);
+}
+
+virDomainNumatuneMemMode
+virDomainNumatuneGetMode(virDomainNumatunePtr numatune)
+{
+    return numatune ? numatune->memory.mode : 0;
+}
+
+virBitmapPtr
+virDomainNumatuneGetNodeset(virDomainNumatunePtr numatune,
+                            virBitmapPtr auto_nodeset)
+{
+    if (!numatune)
+        return NULL;
+
+    if (numatune->memory.placement == VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO)
+        return auto_nodeset;
+
+    /*
+     * This weird logic has the same meaning as switch with
+     * auto/static/default, but can be more readably changed later.
+     */
+    if (numatune->memory.placement != VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC)
+        return NULL;
+
+    return numatune->memory.nodeset;
+}
+
+char *
+virDomainNumatuneFormatNodeset(virDomainNumatunePtr numatune,
+                               virBitmapPtr auto_nodeset)
+{
+    return virBitmapFormat(virDomainNumatuneGetNodeset(numatune,
+                                                       auto_nodeset));
+}
+
+int
+virDomainNumatuneMaybeFormatNodeset(virDomainNumatunePtr numatune,
+                                    virBitmapPtr auto_nodeset,
+                                    char **mask)
+{
+    *mask = NULL;
+
+    if (!numatune)
+        return 0;
+
+    if (numatune->memory.placement == VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO &&
+        !auto_nodeset) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Advice from numad is needed in case of "
+                         "automatic numa placement"));
+        return -1;
+    }
+
+    *mask = virDomainNumatuneFormatNodeset(numatune, auto_nodeset);
+    if (!*mask)
+        return -1;
+
+    return 0;
+}
+
+int
+virDomainNumatuneSet(virDomainDefPtr def,
+                     int placement,
+                     int mode,
+                     virBitmapPtr nodeset)
+{
+    bool create = !def->numatune;  /* Whether we are creating new struct */
+    int ret = -1;
+    virDomainNumatunePtr numatune = NULL;
+
+    /* No need to do anything in this case */
+    if (mode == -1 && placement == -1 && !nodeset)
+        return 0;
+
+    /* Range checks */
+    if (mode != -1 &&
+        (mode < 0 || mode >= VIR_DOMAIN_NUMATUNE_MEM_LAST)) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("Unsupported numatune mode '%d'"),
+                       mode);
+        goto cleanup;
+    }
+    if (placement != -1 &&
+        (placement < 0 || placement >= VIR_DOMAIN_NUMATUNE_PLACEMENT_LAST)) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("Unsupported numatune placement '%d'"),
+                       mode);
+        goto cleanup;
+    }
+
+    if (create && VIR_ALLOC(def->numatune) < 0)
+        goto cleanup;
+    numatune = def->numatune;
+
+    if (create) {
+        /* Defaults for new struct */
+        if (mode == -1)
+            mode = VIR_DOMAIN_NUMATUNE_MEM_STRICT;
+
+        if (placement == -1)
+            placement = VIR_DOMAIN_NUMATUNE_PLACEMENT_DEFAULT;
+    }
+
+    if (mode != -1)
+        numatune->memory.mode = mode;
+    if (nodeset) {
+        virBitmapFree(numatune->memory.nodeset);
+        numatune->memory.nodeset = virBitmapNewCopy(nodeset);
+        if (!numatune->memory.nodeset)
+            goto cleanup;
+        if (placement == -1)
+            placement = VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC;
+    }
+
+    if (placement == VIR_DOMAIN_NUMATUNE_PLACEMENT_DEFAULT) {
+        if (numatune->memory.nodeset ||
+            def->placement_mode == VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC)
+            placement = VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC;
+        else
+            placement = VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO;
+    }
+
+    if (placement == VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC &&
+        !numatune->memory.nodeset) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("nodeset for NUMA memory tuning must be set "
+                         "if 'placement' is 'static'"));
+        goto cleanup;
+    }
+
+    if (placement == VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO) {
+        virBitmapFree(numatune->memory.nodeset);
+        numatune->memory.nodeset = NULL;
+        if (!def->cpumask)
+            def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO;
+    }
+
+    if (placement != -1)
+        numatune->memory.placement = placement;
+
+    ret = 0;
+ cleanup:
+    return ret;
+}
+
+bool
+virDomainNumatuneEquals(virDomainNumatunePtr n1,
+                        virDomainNumatunePtr n2)
+{
+    if (!n1 && !n2)
+        return true;
+
+    if (!n1 || !n2)
+        return false;
+
+    if (n1->memory.mode != n2->memory.mode)
+        return false;
+
+    if (n1->memory.placement != n2->memory.placement)
+        return false;
+
+    return virBitmapEqual(n1->memory.nodeset, n2->memory.nodeset);
+}
+
+bool
+virDomainNumatuneHasPlacementAuto(virDomainNumatunePtr numatune)
+{
+    if (!numatune)
+        return false;
+
+    if (numatune->memory.placement == VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO)
+        return true;
+
+    return false;
+}
diff --git a/src/conf/numatune_conf.h b/src/conf/numatune_conf.h
index baac7bf..888cff1 100644
--- a/src/conf/numatune_conf.h
+++ b/src/conf/numatune_conf.h
@@ -23,9 +23,25 @@
 #ifndef __NUMATUNE_CONF_H__
 # define __NUMATUNE_CONF_H__

+# include <libxml/xpath.h>
+
 # include "internal.h"
 # include "virutil.h"
 # include "virbitmap.h"
+# include "virbuffer.h"
+
+/*
+ * Since numatune configuration is closely bound to the whole config,
+ * and because we don't have separate domain_conf headers for
+ * typedefs, structs and functions, we need to have a forward
+ * declaration here for virDomainDef due to circular dependencies.
+ */
+typedef struct _virDomainDef virDomainDef;
+typedef virDomainDef *virDomainDefPtr;
+
+
+typedef struct _virDomainNumatune virDomainNumatune;
+typedef virDomainNumatune *virDomainNumatunePtr;

 typedef enum {
     VIR_DOMAIN_NUMATUNE_PLACEMENT_DEFAULT = 0,
@@ -36,19 +52,51 @@ typedef enum {
 } virDomainNumatunePlacement;

 VIR_ENUM_DECL(virDomainNumatunePlacement)
-
 VIR_ENUM_DECL(virDomainNumatuneMemMode)

-typedef struct _virDomainNumatune virDomainNumatune;
-typedef virDomainNumatune *virDomainNumatunePtr;
-struct _virDomainNumatune {
-    struct {
-        virBitmapPtr nodemask;
-        int mode;           /* enum virDomainNumatuneMemMode */
-        int placement_mode; /* enum virDomainNumatunePlacement */
-    } memory;               /* pinning for all the memory */
-
-    /* Future NUMA tuning related stuff should go here. */
-};
+
+void virDomainNumatuneFree(virDomainNumatunePtr numatune);
+
+/*
+ * XML Parse/Format functions
+ */
+int virDomainNumatuneParseXML(virDomainDefPtr def, xmlXPathContextPtr ctxt)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+
+int virDomainNumatuneFormatXML(virBufferPtr buf, virDomainNumatunePtr numatune)
+    ATTRIBUTE_NONNULL(1);
+
+/*
+ * Getters
+ */
+virDomainNumatuneMemMode virDomainNumatuneGetMode(virDomainNumatunePtr numatune);
+
+virBitmapPtr virDomainNumatuneGetNodeset(virDomainNumatunePtr numatune,
+                                         virBitmapPtr auto_nodeset);
+
+/*
+ * Formatters
+ */
+char *virDomainNumatuneFormatNodeset(virDomainNumatunePtr numatune,
+                                     virBitmapPtr auto_nodeset);
+
+int virDomainNumatuneMaybeFormatNodeset(virDomainNumatunePtr numatune,
+                                        virBitmapPtr auto_nodeset,
+                                        char **mask);
+
+/*
+ * Setters
+ */
+int virDomainNumatuneSet(virDomainDefPtr def, int placement,
+                         int mode, virBitmapPtr nodeset)
+    ATTRIBUTE_NONNULL(1);
+
+/*
+ * Other accessors
+ */
+bool virDomainNumatuneEquals(virDomainNumatunePtr n1,
+                             virDomainNumatunePtr n2);
+
+bool virDomainNumatuneHasPlacementAuto(virDomainNumatunePtr numatune);

 #endif /* __NUMATUNE_CONF_H__ */
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index cf8bb91..0494d9d 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -200,6 +200,7 @@ virDomainDefGetDefaultEmulator;
 virDomainDefGetSecurityLabelDef;
 virDomainDefMaybeAddController;
 virDomainDefMaybeAddInput;
+virDomainDefNeedsPlacementAdvice;
 virDomainDefNew;
 virDomainDefParseFile;
 virDomainDefParseNode;
@@ -607,10 +608,20 @@ virNodeDeviceObjUnlock;


 # conf/numatune_conf.h
+virDomainNumatuneEquals;
+virDomainNumatuneFormatNodeset;
+virDomainNumatuneFormatXML;
+virDomainNumatuneFree;
+virDomainNumatuneGetMode;
+virDomainNumatuneGetNodeset;
+virDomainNumatuneHasPlacementAuto;
+virDomainNumatuneMaybeFormatNodeset;
 virDomainNumatuneMemModeTypeFromString;
 virDomainNumatuneMemModeTypeToString;
+virDomainNumatuneParseXML;
 virDomainNumatunePlacementTypeFromString;
 virDomainNumatunePlacementTypeToString;
+virDomainNumatuneSet;


 # conf/nwfilter_conf.h
diff --git a/src/lxc/lxc_cgroup.c b/src/lxc/lxc_cgroup.c
index b584e33..cc93823 100644
--- a/src/lxc/lxc_cgroup.c
+++ b/src/lxc/lxc_cgroup.c
@@ -79,22 +79,11 @@ static int virLXCCgroupSetupCpusetTune(virDomainDefPtr def,
             goto cleanup;
     }

-    if ((def->numatune.memory.nodemask ||
-         (def->numatune.memory.placement_mode ==
-          VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO)) &&
-          def->numatune.memory.mode == VIR_DOMAIN_NUMATUNE_MEM_STRICT) {
-        if (def->numatune.memory.placement_mode ==
-            VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO)
-            mask = virBitmapFormat(nodemask);
-        else
-            mask = virBitmapFormat(def->numatune.memory.nodemask);
-
-        if (!mask)
-            return -1;
+    if (virDomainNumatuneMaybeFormatNodeset(def->numatune, nodemask, &mask) < 0)
+        goto cleanup;

-        if (virCgroupSetCpusetMems(cgroup, mask) < 0)
-            goto cleanup;
-    }
+    if (mask && virCgroupSetCpusetMems(cgroup, mask) < 0)
+        goto cleanup;

     ret = 0;
  cleanup:
diff --git a/src/lxc/lxc_controller.c b/src/lxc/lxc_controller.c
index 03b1d31..fd93f47 100644
--- a/src/lxc/lxc_controller.c
+++ b/src/lxc/lxc_controller.c
@@ -653,10 +653,7 @@ static int virLXCControllerGetNumadAdvice(virLXCControllerPtr ctrl,
     /* Get the advisory nodeset from numad if 'placement' of
      * either <vcpu> or <numatune> is 'auto'.
      */
-    if ((ctrl->def->placement_mode ==
-         VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO) ||
-        (ctrl->def->numatune.memory.placement_mode ==
-         VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO)) {
+    if (virDomainDefNeedsPlacementAdvice(ctrl->def)) {
         nodeset = virNumaGetAutoPlacementAdvice(ctrl->def->vcpus,
                                                 ctrl->def->mem.cur_balloon);
         if (!nodeset)
diff --git a/src/lxc/lxc_native.c b/src/lxc/lxc_native.c
index 30f7e55..bb15a36 100644
--- a/src/lxc/lxc_native.c
+++ b/src/lxc/lxc_native.c
@@ -1,6 +1,7 @@
 /*
  * lxc_native.c: LXC native configuration import
  *
+ * Copyright (c) 2014 Red Hat, Inc.
  * Copyright (c) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.
  *
  * This library is free software; you can redistribute it and/or
@@ -708,6 +709,7 @@ static int
 lxcSetCpusetTune(virDomainDefPtr def, virConfPtr properties)
 {
     virConfValuePtr value;
+    virBitmapPtr nodeset = NULL;

     if ((value = virConfGetValue(properties, "lxc.cgroup.cpuset.cpus")) &&
             value->str) {
@@ -719,12 +721,15 @@ lxcSetCpusetTune(virDomainDefPtr def, virConfPtr properties)
     }

     if ((value = virConfGetValue(properties, "lxc.cgroup.cpuset.mems")) &&
-            value->str) {
-        def->numatune.memory.placement_mode = VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC;
-        def->numatune.memory.mode = VIR_DOMAIN_NUMATUNE_MEM_STRICT;
-        if (virBitmapParse(value->str, 0, &def->numatune.memory.nodemask,
-                           VIR_DOMAIN_CPUMASK_LEN) < 0)
+        value->str) {
+        if (virBitmapParse(value->str, 0, &nodeset, VIR_DOMAIN_CPUMASK_LEN) < 0)
             return -1;
+        if (virDomainNumatuneSet(def, VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC,
+                                 VIR_DOMAIN_NUMATUNE_MEM_STRICT, nodeset) < 0) {
+            virBitmapFree(nodeset);
+            return -1;
+        }
+        virBitmapFree(nodeset);
     }

     return 0;
diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c
index 43568d1..a503dea 100644
--- a/src/parallels/parallels_driver.c
+++ b/src/parallels/parallels_driver.c
@@ -2078,12 +2078,7 @@ parallelsApplyChanges(virDomainObjPtr dom, virDomainDefPtr new)
         return -1;
     }

-    if (old->numatune.memory.mode != new->numatune.memory.mode ||
-        old->numatune.memory.placement_mode != new->numatune.memory.placement_mode ||
-        ((old->numatune.memory.nodemask != NULL || new->numatune.memory.nodemask != NULL) &&
-         (old->numatune.memory.nodemask == NULL || new->numatune.memory.nodemask == NULL ||
-        !virBitmapEqual(old->numatune.memory.nodemask, new->numatune.memory.nodemask)))){
-
+    if (!virDomainNumatuneEquals(old->numatune, new->numatune)) {
         virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                         _("numa parameters are not supported "
                           "by parallels driver"));
diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c
index a24975b..dd393ee 100644
--- a/src/qemu/qemu_cgroup.c
+++ b/src/qemu/qemu_cgroup.c
@@ -601,23 +601,14 @@ qemuSetupCpusetCgroup(virDomainObjPtr vm,
     if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_CPUSET))
         return 0;

-    if ((vm->def->numatune.memory.nodemask ||
-         (vm->def->numatune.memory.placement_mode ==
-          VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO)) &&
-        vm->def->numatune.memory.mode == VIR_DOMAIN_NUMATUNE_MEM_STRICT) {
-
-        if (vm->def->numatune.memory.placement_mode ==
-            VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO)
-            mem_mask = virBitmapFormat(nodemask);
-        else
-            mem_mask = virBitmapFormat(vm->def->numatune.memory.nodemask);
-
-        if (!mem_mask)
-            goto cleanup;
+    if (virDomainNumatuneMaybeFormatNodeset(vm->def->numatune,
+                                            nodemask,
+                                            &mem_mask) < 0)
+        goto cleanup;

-        if (virCgroupSetCpusetMems(priv->cgroup, mem_mask) < 0)
-            goto cleanup;
-    }
+    if (mem_mask &&
+        virCgroupSetCpusetMems(priv->cgroup, mem_mask) < 0)
+        goto cleanup;

     if (vm->def->cpumask ||
         (vm->def->placement_mode == VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO)) {
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 85c3684..e166ca4 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -8637,7 +8637,8 @@ qemuDomainSetNumaParamsLive(virDomainObjPtr vm,
     size_t i = 0;
     int ret = -1;

-    if (vm->def->numatune.memory.mode != VIR_DOMAIN_NUMATUNE_MEM_STRICT) {
+    if (virDomainNumatuneGetMode(vm->def->numatune) !=
+        VIR_DOMAIN_NUMATUNE_MEM_STRICT) {
         virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                        _("change of nodeset for running domain "
                          "requires strict numa mode"));
@@ -8712,6 +8713,8 @@ qemuDomainSetNumaParameters(virDomainPtr dom,
     virQEMUDriverConfigPtr cfg = NULL;
     virCapsPtr caps = NULL;
     qemuDomainObjPrivatePtr priv;
+    virBitmapPtr nodeset = NULL;
+    int mode = -1;

     virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
                   VIR_DOMAIN_AFFECT_CONFIG, -1);
@@ -8752,65 +8755,47 @@ qemuDomainSetNumaParameters(virDomainPtr dom,
         virTypedParameterPtr param = &params[i];

         if (STREQ(param->field, VIR_DOMAIN_NUMA_MODE)) {
-            int mode = param->value.i;
+            mode = param->value.i;

-            if (mode >= VIR_DOMAIN_NUMATUNE_PLACEMENT_LAST ||
-                mode < VIR_DOMAIN_NUMATUNE_PLACEMENT_DEFAULT)
-            {
+            if (mode < 0 || mode >= VIR_DOMAIN_NUMATUNE_MEM_LAST) {
                 virReportError(VIR_ERR_INVALID_ARG,
-                               _("unsupported numa_mode: '%d'"), mode);
+                               _("unsupported numatune mode: '%d'"), mode);
                 goto cleanup;
             }

-            if ((flags & VIR_DOMAIN_AFFECT_LIVE) &&
-                vm->def->numatune.memory.mode != mode) {
-                virReportError(VIR_ERR_OPERATION_INVALID, "%s",
-                               _("can't change numa mode for running domain"));
-                goto cleanup;
-            }
-
-            if (flags & VIR_DOMAIN_AFFECT_CONFIG)
-                persistentDef->numatune.memory.mode = mode;
-
         } else if (STREQ(param->field, VIR_DOMAIN_NUMA_NODESET)) {
-            virBitmapPtr nodeset = NULL;
-
             if (virBitmapParse(param->value.s, 0, &nodeset,
-                               VIR_DOMAIN_CPUMASK_LEN) < 0) {
+                               VIR_DOMAIN_CPUMASK_LEN) < 0)
                 goto cleanup;
-            }

-            if (flags & VIR_DOMAIN_AFFECT_LIVE) {
-                if (qemuDomainSetNumaParamsLive(vm, caps, nodeset) < 0) {
-                    virBitmapFree(nodeset);
-                    goto cleanup;
-                }
-
-                /* update vm->def here so that dumpxml can read the new
-                 * values from vm->def. */
-                virBitmapFree(vm->def->numatune.memory.nodemask);
-
-                vm->def->numatune.memory.placement_mode =
-                    VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC;
-                vm->def->numatune.memory.nodemask = virBitmapNewCopy(nodeset);
+            if (virBitmapIsAllClear(nodeset)) {
+                virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+                               _("Invalid nodeset for numatune"));
+                goto cleanup;
             }
+        }
+    }

-            if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
-                virBitmapFree(persistentDef->numatune.memory.nodemask);
-
-                persistentDef->numatune.memory.nodemask = nodeset;
-                persistentDef->numatune.memory.placement_mode =
-                    VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC;
-                nodeset = NULL;
-            }
-            virBitmapFree(nodeset);
+    if (flags & VIR_DOMAIN_AFFECT_LIVE) {
+        if (mode != -1 &&
+            virDomainNumatuneGetMode(vm->def->numatune) != mode) {
+            virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+                           _("can't change numatune mode for running domain"));
+            goto cleanup;
         }
+
+        if (nodeset &&
+            qemuDomainSetNumaParamsLive(vm, caps, nodeset) < 0)
+            goto cleanup;
+
+        if (virDomainNumatuneSet(vm->def, -1, mode, nodeset) < 0)
+            goto cleanup;
     }

     if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
-        if (!persistentDef->numatune.memory.placement_mode)
-            persistentDef->numatune.memory.placement_mode =
-                VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO;
+        if (virDomainNumatuneSet(persistentDef, -1, mode, nodeset) < 0)
+            goto cleanup;
+
         if (virDomainSaveConfig(cfg->configDir, persistentDef) < 0)
             goto cleanup;
     }
@@ -8818,6 +8803,7 @@ qemuDomainSetNumaParameters(virDomainPtr dom,
     ret = 0;

  cleanup:
+    virBitmapFree(nodeset);
     if (vm)
         virObjectUnlock(vm);
     virObjectUnref(caps);
@@ -8886,15 +8872,17 @@ qemuDomainGetNumaParameters(virDomainPtr dom,
             if (virTypedParameterAssign(param, VIR_DOMAIN_NUMA_MODE,
                                         VIR_TYPED_PARAM_INT, 0) < 0)
                 goto cleanup;
+
             if (flags & VIR_DOMAIN_AFFECT_CONFIG)
-                param->value.i = persistentDef->numatune.memory.mode;
+                param->value.i = virDomainNumatuneGetMode(persistentDef->numatune);
             else
-                param->value.i = vm->def->numatune.memory.mode;
+                param->value.i = virDomainNumatuneGetMode(vm->def->numatune);
             break;

         case 1: /* fill numa nodeset here */
             if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
-                nodeset = virBitmapFormat(persistentDef->numatune.memory.nodemask);
+                nodeset = virDomainNumatuneFormatNodeset(persistentDef->numatune,
+                                                         NULL);
                 if (!nodeset)
                     goto cleanup;
             } else {
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index d8bb679..4c57f15 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -3898,10 +3898,7 @@ int qemuProcessStart(virConnectPtr conn,
     /* Get the advisory nodeset from numad if 'placement' of
      * either <vcpu> or <numatune> is 'auto'.
      */
-    if ((vm->def->placement_mode ==
-         VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO) ||
-        (vm->def->numatune.memory.placement_mode ==
-         VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO)) {
+    if (virDomainDefNeedsPlacementAdvice(vm->def)) {
         nodeset = virNumaGetAutoPlacementAdvice(vm->def->vcpus,
                                                 vm->def->mem.max_balloon);
         if (!nodeset)
@@ -3909,8 +3906,7 @@ int qemuProcessStart(virConnectPtr conn,

         VIR_DEBUG("Nodeset returned from numad: %s", nodeset);

-        if (virBitmapParse(nodeset, 0, &nodemask,
-                           VIR_DOMAIN_CPUMASK_LEN) < 0)
+        if (virBitmapParse(nodeset, 0, &nodemask, VIR_DOMAIN_CPUMASK_LEN) < 0)
             goto cleanup;
     }
     hookData.nodemask = nodemask;
diff --git a/src/util/virnuma.c b/src/util/virnuma.c
index bc5180a..087f679 100644
--- a/src/util/virnuma.c
+++ b/src/util/virnuma.c
@@ -87,11 +87,10 @@ virNumaGetAutoPlacementAdvice(unsigned short vcpus ATTRIBUTE_UNUSED,

 #if WITH_NUMACTL
 int
-virNumaSetupMemoryPolicy(virDomainNumatune numatune,
+virNumaSetupMemoryPolicy(virDomainNumatunePtr numatune,
                          virBitmapPtr nodemask)
 {
     nodemask_t mask;
-    int mode = -1;
     int node = -1;
     int ret = -1;
     int bit = 0;
@@ -99,19 +98,9 @@ virNumaSetupMemoryPolicy(virDomainNumatune numatune,
     int maxnode = 0;
     virBitmapPtr tmp_nodemask = NULL;

-    if (numatune.memory.placement_mode ==
-        VIR_DOMAIN_NUMATUNE_PLACEMENT_STATIC) {
-        if (!numatune.memory.nodemask)
-            return 0;
-        VIR_DEBUG("Set NUMA memory policy with specified nodeset");
-        tmp_nodemask = numatune.memory.nodemask;
-    } else if (numatune.memory.placement_mode ==
-               VIR_DOMAIN_NUMATUNE_PLACEMENT_AUTO) {
-        VIR_DEBUG("Set NUMA memory policy with advisory nodeset from numad");
-        tmp_nodemask = nodemask;
-    } else {
+    tmp_nodemask = virDomainNumatuneGetNodeset(numatune, nodemask);
+    if (!tmp_nodemask)
         return 0;
-    }

     if (numa_available() < 0) {
         virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -134,13 +123,15 @@ virNumaSetupMemoryPolicy(virDomainNumatune numatune,
         nodemask_set(&mask, bit);
     }

-    mode = numatune.memory.mode;
-
-    if (mode == VIR_DOMAIN_NUMATUNE_MEM_STRICT) {
+    switch (virDomainNumatuneGetMode(numatune)) {
+    case VIR_DOMAIN_NUMATUNE_MEM_STRICT:
         numa_set_bind_policy(1);
         numa_set_membind(&mask);
         numa_set_bind_policy(0);
-    } else if (mode == VIR_DOMAIN_NUMATUNE_MEM_PREFERRED) {
+        break;
+
+    case VIR_DOMAIN_NUMATUNE_MEM_PREFERRED:
+    {
         int nnodes = 0;
         for (i = 0; i < NUMA_NUM_NODES; i++) {
             if (nodemask_isset(&mask, i)) {
@@ -158,17 +149,16 @@ virNumaSetupMemoryPolicy(virDomainNumatune numatune,

         numa_set_bind_policy(0);
         numa_set_preferred(node);
-    } else if (mode == VIR_DOMAIN_NUMATUNE_MEM_INTERLEAVE) {
-        numa_set_interleave_mask(&mask);
-    } else {
-        /* XXX: Shouldn't go here, as we already do checking when
-         * parsing domain XML.
-         */
-        virReportError(VIR_ERR_XML_ERROR,
-                       "%s", _("Invalid mode for memory NUMA tuning."));
-        goto cleanup;
     }
+    break;

+    case VIR_DOMAIN_NUMATUNE_MEM_INTERLEAVE:
+        numa_set_interleave_mask(&mask);
+        break;
+
+    case VIR_DOMAIN_NUMATUNE_MEM_LAST:
+        break;
+    }
     ret = 0;

  cleanup:
@@ -327,10 +317,10 @@ virNumaGetNodeCPUs(int node,

 #else
 int
-virNumaSetupMemoryPolicy(virDomainNumatune numatune,
+virNumaSetupMemoryPolicy(virDomainNumatunePtr numatune,
                          virBitmapPtr nodemask ATTRIBUTE_UNUSED)
 {
-    if (numatune.memory.nodemask) {
+    if (virDomainNumatuneGetNodeset(numatune, NULL)) {
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                        _("libvirt is compiled without NUMA tuning support"));

diff --git a/src/util/virnuma.h b/src/util/virnuma.h
index d2ab6af..13ebec6 100644
--- a/src/util/virnuma.h
+++ b/src/util/virnuma.h
@@ -31,7 +31,7 @@
 char *virNumaGetAutoPlacementAdvice(unsigned short vcups,
                                     unsigned long long balloon);

-int virNumaSetupMemoryPolicy(virDomainNumatune numatune,
+int virNumaSetupMemoryPolicy(virDomainNumatunePtr numatune,
                              virBitmapPtr nodemask);

 bool virNumaIsAvailable(void);
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-numatune-auto-prefer.xml b/tests/qemuxml2argvdata/qemuxml2argv-numatune-auto-prefer.xml
new file mode 100644
index 0000000..63f0d1f
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-numatune-auto-prefer.xml
@@ -0,0 +1,29 @@
+<domain type='qemu'>
+  <name>QEMUGuest</name>
+  <uuid>9f4b6512-e73a-4a25-93e8-5307802821ce</uuid>
+  <memory unit='KiB'>65536</memory>
+  <currentMemory unit='KiB'>65536</currentMemory>
+  <vcpu placement='auto'>1</vcpu>
+  <numatune>
+    <memory mode='preferred'/>
+  </numatune>
+  <os>
+    <type arch='x86_64' machine='pc'>hvm</type>
+    <boot dev='hd'/>
+  </os>
+  <cpu>
+    <numa>
+      <cell id='0' cpus='0' memory='65536'/>
+    </numa>
+  </cpu>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <emulator>/usr/bin/kvm</emulator>
+    <controller type='usb' index='0'/>
+    <controller type='pci' index='0' model='pci-root'/>
+    <memballoon model='virtio'/>
+  </devices>
+</domain>
diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-numatune-auto-prefer.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-numatune-auto-prefer.xml
new file mode 100644
index 0000000..19761b4
--- /dev/null
+++ b/tests/qemuxml2xmloutdata/qemuxml2xmlout-numatune-auto-prefer.xml
@@ -0,0 +1,29 @@
+<domain type='qemu'>
+  <name>QEMUGuest</name>
+  <uuid>9f4b6512-e73a-4a25-93e8-5307802821ce</uuid>
+  <memory unit='KiB'>65536</memory>
+  <currentMemory unit='KiB'>65536</currentMemory>
+  <vcpu placement='auto'>1</vcpu>
+  <numatune>
+    <memory mode='preferred' placement='auto'/>
+  </numatune>
+  <os>
+    <type arch='x86_64' machine='pc'>hvm</type>
+    <boot dev='hd'/>
+  </os>
+  <cpu>
+    <numa>
+      <cell id='0' cpus='0' memory='65536'/>
+    </numa>
+  </cpu>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <emulator>/usr/bin/kvm</emulator>
+    <controller type='usb' index='0'/>
+    <controller type='pci' index='0' model='pci-root'/>
+    <memballoon model='virtio'/>
+  </devices>
+</domain>
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index 27281c8..b650f54 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -372,6 +372,8 @@ mymain(void)
     DO_TEST_DIFFERENT("cpu-numa1");
     DO_TEST_DIFFERENT("cpu-numa2");

+    DO_TEST_DIFFERENT("numatune-auto-prefer");
+
     virObjectUnref(driver.caps);
     virObjectUnref(driver.xmlopt);

-- 
2.0.0




More information about the libvir-list mailing list