[libvirt] [PATCH 3/3] xml: allow scaled memory on input

Eric Blake eblake at redhat.com
Thu Feb 23 04:59:47 UTC 2012


Output is still in kibibytes, but input can now be in different
scales for ease of typing.

* src/conf/domain_conf.c (virDomainParseMemory): New helper.
(virDomainDefParseXML): Use it when parsing.
* docs/schemas/domaincommon.rng: Expand XML.
* docs/formatdomain.html.in (elementsMemoryAllocation): Document
scaling.
---
 docs/formatdomain.html.in     |   39 ++++++++++---
 docs/schemas/domaincommon.rng |    4 +-
 src/conf/domain_conf.c        |  129 ++++++++++++++++++++++++++++++++++------
 3 files changed, 142 insertions(+), 30 deletions(-)

diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 5305f82..7a4a197 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -415,8 +415,8 @@
 <pre>
 <domain>
   ...
-  <memory>524288</memory>
-  <currentMemory>524288</currentMemory>
+  <memory units='KiB'>524288</memory>
+  <currentMemory units='KiB'>524288</currentMemory>
   ...
 </domain>
 </pre>
@@ -424,12 +424,27 @@
     <dl>
       <dt><code>memory</code></dt>
       <dd>The maximum allocation of memory for the guest at boot time.
-        The units for this value are kilobytes (i.e. blocks of 1024 bytes)</dd>
+        The units for this value are determined by the optional
+        atttribute <code>units</code>, which defaults to "KiB"
+        (kibibytes, or blocks of 1024 bytes).  Valid units are "b" or
+        "bytes" for bytes, "KB" for kilobytes (1,000), "k" or "KiB"
+        for kibibytes (1024), "MB" for megabytes (1,000,000), "M" or
+        "MiB" for mebibytes (1,048,576), "GB" for gigabytes
+        (1,000,000,000), "G" or "GiB" for gibibytes (1,073,741,824),
+        "TB" for terabytes (1,000,000,000,000), or "T" or "TiB" for
+        tebibytes (1,099,511,627,776).  However, the value will be
+        rounded up to the nearest kibibyte by libvirt, and may be
+        further rounded to the granularity supported by the
+        hypervisor.  As a sanity check, values less than 4000KiB are
+        not permitted.  <span class='since'><code>units</code> since
+        0.9.11</span></dd>
       <dt><code>currentMemory</code></dt>
       <dd>The actual allocation of memory for the guest. This value can
         be less than the maximum allocation, to allow for ballooning
         up the guests memory on the fly. If this is omitted, it defaults
-        to the same value as the <code>memory</code> element</dd>
+        to the same value as the <code>memory</code> element.
+        The <code>units</code> attribute behaves the same as
+        for <code>memory</code>.</dd>
     </dl>


@@ -460,10 +475,10 @@
 <domain>
   ...
   <memtune>
-    <hard_limit>1048576</hard_limit>
-    <soft_limit>131072</soft_limit>
-    <swap_hard_limit>2097152</swap_hard_limit>
-    <min_guarantee>65536</min_guarantee>
+    <hard_limit units='G'>1</hard_limit>
+    <soft_limit units='M'>128</soft_limit>
+    <swap_hard_limit units='G'>2</swap_hard_limit>
+    <min_guarantee units='bytes'>67108864</min_guarantee>
   </memtune>
   ...
 </domain>
@@ -477,7 +492,13 @@
         parameters are applied to the QEMU process as a whole. Thus, when
         counting them, one needs to add up guest RAM, guest video RAM, and
         some memory overhead of QEMU itself. The last piece is hard to
-        determine so one needs guess and try.</dd>
+        determine so one needs guess and try.  For each tunable, it
+        is possible to designate which unit the number is in on
+        input, using the same values as
+        for <code><memory></code>.  For backwards
+        compatibility, output is always in
+        KiB.  <span class='since'><code>units</code>
+        since 0.9.11</span></dd>
       <dt><code>hard_limit</code></dt>
       <dd> The optional <code>hard_limit</code> element is the maximum memory
         the guest can use. The units for this value are kilobytes (i.e. blocks
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 68e3fdc..3d2e9f5 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -3082,7 +3082,9 @@
   <define name='memoryKBElement'>
     <optional>
       <attribute name='units'>
-        <value>KiB</value>
+        <data type='string'>
+          <param name='pattern'>([bB]([yY][tT][eE][sS]?)?)|([kKmMgGtT]([iI]?[bB])?)</param>
+        </data>
       </attribute>
     </optional>
     <data type='unsignedInt'>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 9dac731..a869c70 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -7141,6 +7141,96 @@ static int virDomainDefMaybeAddController(virDomainDefPtr def,
     return 0;
 }

+
+/* Parse a memory element located at XPATH within CTXT, and store the
+ * result into MEM.  If REQUIRED, then the value must exist;
+ * otherwise, the value is optional, but must result in at least 4000
+ * blocks if present.  Return 0 on success, -1 on failure after
+ * issuing error.  */
+static int
+virDomainParseMemory(const char *xpath, xmlXPathContextPtr ctxt,
+                     unsigned long *mem, bool required)
+{
+    char *xpath_full = NULL;
+    char *units = NULL;
+    int ret = -1;
+    unsigned long long bytes;
+    unsigned long long scale = 1;
+
+    *mem = 0;
+    if (virAsprintf(&xpath_full, "string(%s)", xpath) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+    if (virXPathULongLong(xpath_full, ctxt, &bytes) < 0) {
+        if (required)
+            virDomainReportError(VIR_ERR_XML_ERROR,
+                                 "%s", _("missing memory element"));
+        else
+            ret = 0;
+        goto cleanup;
+    }
+    VIR_FREE(xpath_full);
+
+    if (virAsprintf(&xpath_full, "string(%s/@units)", xpath) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+    units = virXPathString(xpath_full, ctxt);
+    if (!units) {
+        scale = 1024;
+    } else if (STRCASEEQ(units, "b") || STRCASEEQ(units, "byte") ||
+               STRCASEEQ(units, "bytes")) {
+        /* no change */
+    } else {
+        unsigned long long multiplier;
+        if (STRCASEEQ(units + 1, "") || STRCASEEQ(units + 1, "iB")) {
+            multiplier = 1024;
+        } else if (STRCASEEQ(units + 1, "B")) {
+            multiplier = 1000;
+        } else {
+            virDomainReportError(VIR_ERR_XML_ERROR,
+                                 _("unknown units '%s'"), units);
+            goto cleanup;
+        }
+        switch (c_tolower(*units)) {
+        case 't':
+            scale *= multiplier;
+            /* fallthrough */
+        case 'g':
+            scale *= multiplier;
+            /* fallthrough */
+        case 'm':
+            scale *= multiplier;
+            /* fallthrough */
+        case 'k':
+            scale *= multiplier;
+            break;
+        default:
+            virDomainReportError(VIR_ERR_XML_ERROR,
+                                 _("unknown units '%s'"), units);
+            goto cleanup;
+        }
+    }
+
+    if (bytes >= (ULLONG_MAX - 1023) / scale) {
+        virDomainReportError(VIR_ERR_XML_ERROR,
+                             _("memory value %llu%s too large"),
+                             bytes, units ? units : "KiB");
+        goto cleanup;
+    }
+    bytes *= scale;
+
+    /* Yes, we really do use kibibytes for our internal sizing.  */
+    *mem = VIR_DIV_UP(bytes, 1024);
+    ret = 0;
+cleanup:
+    VIR_FREE(xpath_full);
+    VIR_FREE(units);
+    return ret;
+}
+
+
 static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
                                             xmlDocPtr xml,
                                             xmlNodePtr root,
@@ -7264,22 +7354,21 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
         goto error;

     /* Extract domain memory */
-    if (virXPathULong("string(./memory[1])", ctxt,
-                      &def->mem.max_balloon) < 0) {
-        virDomainReportError(VIR_ERR_INTERNAL_ERROR,
-                             "%s", _("missing memory element"));
+    if (virDomainParseMemory("./memory[1]", ctxt,
+                             &def->mem.max_balloon, true) < 0)
         goto error;
-    }

-    if (virXPathULong("string(./currentMemory[1])", ctxt,
-                      &def->mem.cur_balloon) < 0)
-        def->mem.cur_balloon = def->mem.max_balloon;
+    if (virDomainParseMemory("./currentMemory[1]", ctxt,
+                             &def->mem.cur_balloon, false) < 0)
+        goto error;

     if (def->mem.cur_balloon > def->mem.max_balloon) {
         virDomainReportError(VIR_ERR_INTERNAL_ERROR,
                              _("current memory '%luk' exceeds maximum '%luk'"),
                              def->mem.cur_balloon, def->mem.max_balloon);
         goto error;
+    } else if (def->mem.cur_balloon == 0) {
+        def->mem.cur_balloon = def->mem.max_balloon;
     }

     node = virXPathNode("./memoryBacking/hugepages", ctxt);
@@ -7318,21 +7407,21 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
     VIR_FREE(nodes);

     /* Extract other memory tunables */
-    if (virXPathULong("string(./memtune/hard_limit)", ctxt,
-                      &def->mem.hard_limit) < 0)
-        def->mem.hard_limit = 0;
+    if (virDomainParseMemory("./memtune/hard_limit[1]", ctxt,
+                             &def->mem.hard_limit, false) < 0)
+        goto error;

-    if (virXPathULong("string(./memtune/soft_limit[1])", ctxt,
-                      &def->mem.soft_limit) < 0)
-        def->mem.soft_limit = 0;
+    if (virDomainParseMemory("./memtune/soft_limit[1]", ctxt,
+                             &def->mem.soft_limit, false) < 0)
+        goto error;

-    if (virXPathULong("string(./memtune/min_guarantee[1])", ctxt,
-                      &def->mem.min_guarantee) < 0)
-        def->mem.min_guarantee = 0;
+    if (virDomainParseMemory("./memtune/min_guarantee[1]", ctxt,
+                             &def->mem.min_guarantee, false) < 0)
+        goto error;

-    if (virXPathULong("string(./memtune/swap_hard_limit[1])", ctxt,
-                      &def->mem.swap_hard_limit) < 0)
-        def->mem.swap_hard_limit = 0;
+    if (virDomainParseMemory("./memtune/swap_hard_limit[1]", ctxt,
+                             &def->mem.swap_hard_limit, false) < 0)
+        goto error;

     n = virXPathULong("string(./vcpu[1])", ctxt, &count);
     if (n == -2) {
-- 
1.7.7.6




More information about the libvir-list mailing list