[libvirt] [PATCH 4/5] blkiotune: add interface for blkiotune.device_weight

Eric Blake eblake at redhat.com
Mon Nov 14 18:34:11 UTC 2011


On 11/08/2011 05:43 AM, Stefan Berger wrote:
> On 11/08/2011 06:00 AM, Hu Tao wrote:
>> This adds per-device weights to<blkiotune>.  Note that the
>> cgroups implementation only supports weights per block device,
>> and not per-file within the device; hence this option must be
>> global to the domain definition rather than tied to individual
>> <device>/<disk>  entries:
>>
>> <domain ...>
>>    <blkiotune>
>>      <device>
>>        <path>/path/to/block</path>
>>        <weight>1000</weight>
>>      </device>
>>    </blkiotune>
>> ...
>>
>> This patch also adds a parameter --device-weight to virsh command
>> blkiotune for setting/getting blkiotune.weight_device for any
>> hypervisor that supports it.  All<device>  entries under
>> <blkiotune>  are concatenated into a single string attribute under
>> virDomain{Get,Set}BlkioParameters, named "device_weight".
>>
>>
>> +/**
>> + * VIR_DOMAIN_BLKIO_DEVICE_WEIGHT:
>> + *
>> + * Macro for the blkio tunable weight_device: it represents the
>> + * per-device weight, as a string.  The string is parsed as a
>> + * series of /path/to/device,weight elements, separated by ';'.

It looks like the rest of the code swapped over to "separated by ','",
so I made that tweak.

>> + * This function returns a string representing device weights that is
>> + * suitable for writing to /cgroup/blkio/blkio.weight_device, given
>> + * a list of per-device weights.
>> + */
>> +#if defined(major)&&  defined(minor)
>> +int virBlkioDeviceWeightToStr(virBlkioDeviceWeightPtr devices,
>> +                              int ndevices,
>> +                              char **result)
>> +{
>> +    int i;
>> +    struct stat s;
>> +    virBuffer buf = VIR_BUFFER_INITIALIZER;
>> +
>> +    for (i = 0; i<  ndevices; i++) {
>> +        if (stat(devices[i].path,&s) == -1)
>> +            return -1;
>> +        if ((s.st_mode&  S_IFMT) != S_IFBLK)
>> +            return -1;
>> +        virBufferAsprintf(&buf, "%d:%d %d\n",
>> +                          major(s.st_rdev),
>> +                          minor(s.st_rdev),
>> +                          devices[i].weight);
>> +    }
>> +
>> +    if (virBufferError(&buf)) {
>> +        virBufferFreeAndReset(&buf);
>> +        return -1;
>> +    }
>> +
>> +    *result = virBufferContentAndReset(&buf);
>> +    return 0;

You fail for more than one reason, but don't output a failure log
message, which means the caller has to do it and can't guess why things
failed (OOM? not a block device?).  I don't see any callers in this
patch, so I checked 5/5; there, both callers just reported an internal
error stating the string could not be parsed.  But internal error for a
user-visible message is rather harsh, so I think this is worth improving.

>> +}
>> +#else
>> +int virBlkioDeviceWeightToStr(virBlkioDeviceWeightPtr devices
>> ATTRIBUTE_UNUSED,
>> +                              int ndevices ATTRIBUTE_UNUSED,
>> +                              char **result ATTRIBUTE_UNUSED)
>> +{
>> +    return -1;

And if we improve the error reporting above, then we also have to report
an error here.

>> +static int virDomainBlkioDeviceWeightParseXML(xmlNodePtr root,
>> +                                              virBlkioDeviceWeightPtr dw)
>> +{
>> +    char *c;
>> +    xmlNodePtr node;
>> +
>> +    if (!dw)
>> +        return -1;

Dead check - this is a static function, and we know the caller gives us
valid inputs.

>> +    node = root->children;
>> +    while (node) {
>> +        if (node->type == XML_ELEMENT_NODE) {
>> +            if (xmlStrEqual(node->name, BAD_CAST "path")) {
>> +                dw->path = (char *)xmlNodeGetContent(node);

Memory leak; if the user (mistakenly) has more than one <path>
subelement, then you are blindly overwriting dw->path on the second
instance of path.

>> +            } else if (xmlStrEqual(node->name, BAD_CAST "weight")) {
>> +                c = (char *)xmlNodeGetContent(node);
>> +                if (virStrToLong_ui(c, NULL, 10,&dw->weight)<  0) {
>> +                    VIR_FREE(c);
>> +                    VIR_FREE(dw->path);
>> +                    return -1;

Missing error reporting - either we must report the error here or in the
caller (I elected for here, to match precedence with other functions
used by the caller).

>> +                }
>> +                VIR_FREE(c);
>> +            }
>> +        }
>> +        node = node->next;
>> +    }

You never validated that path was a mandatory element.

>> @@ -6711,6 +6813,24 @@ static virDomainDefPtr
>> virDomainDefParseXML(virCapsPtr caps,
>>                        &def->blkio.weight)<  0)
>>           def->blkio.weight = 0;
>>
>> +    n = virXPathNodeSet("./blkiotune/device", ctxt,&nodes);
>> +    if (n>  0) {

Missing an OOM memory check.

>> +        if (VIR_ALLOC_N(def->blkio.devices, n)<  0) {
>> +            virReportOOMError();
>> +            goto error;
>> +        }
>> +
>> +        for (i = 0; i<  n; i++) {
>> +            if (virDomainBlkioDeviceWeightParseXML(nodes[i],
>> +&def->blkio.devices[i])<  0) {
>> +                virBlkioDeviceWeightArrayClear(def->blkio.devices, i);

Redundant, if you had been updating def->blkio.ndevices as you go, for
consistency with some of the other clients of virXPathNodeSet in this
method.

>> +++ b/tools/virsh.c
>> @@ -4648,6 +4648,8 @@ static const vshCmdOptDef opts_blkiotune[] = {
>>       {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or
>> uuid")},
>>       {"weight", VSH_OT_INT, VSH_OFLAG_NONE,
>>        N_("IO Weight in range [100, 1000]")},
>> +    {"device-weight", VSH_OT_STRING, VSH_OFLAG_NONE,
>> +     N_("per-device IO Weights, in the form of
>> /path/to/device,weight;...")},

Another place where we want to use ',', not ';'.

>> @@ -4749,6 +4762,10 @@ cmdBlkiotune(vshControl * ctl, const vshCmd * cmd)
>>                       vshPrint(ctl, "%-15s: %d\n", params[i].field,
>>                                params[i].value.b);
>>                       break;
>> +                case VIR_TYPED_PARAM_STRING:
>> +                    vshPrint(ctl, "%-15s: %s\n", params[i].field,
>> +                             params[i].value.s);
>> +                    break;
>>                   default:
>>                       vshPrint(ctl, "unimplemented blkio parameter
>> type\n");
>>               }

Memory leak - we aren't calling virTypedParameterArrayClear to free up
any returned strings that we just printed.  But we can't blindly call it
in the cleanup: label of the function, because...

>> @@ -4765,15 +4782,32 @@ cmdBlkiotune(vshControl * ctl, const vshCmd *
>> cmd)
>>
>>               if (weight) {
>>                   temp->value.ui = weight;
>> -                strncpy(temp->field, VIR_DOMAIN_BLKIO_WEIGHT,
>> -                        sizeof(temp->field));
>> +                if (!virStrcpy(temp->field, VIR_DOMAIN_BLKIO_WEIGHT,
>> +                               sizeof(temp->field))) {
>> +                    virTypedParameterArrayClear(params, nparams);
>> +                    ret = false;
>> +                    goto cleanup;
>> +                }
>>                   weight = 0;
>>               }
>> +
>> +            if (device_weight) {
>> +                /* Safe to cast away const here.  */
>> +                temp->value.s = (char *)device_weight;

...here we are not storing malloc'd memory there in the first place.
Using a judicious strdup simplifies the code.

>> +                temp->type = VIR_TYPED_PARAM_STRING;
>> +                if (!virStrcpy(temp->field,
>> VIR_DOMAIN_BLKIO_DEVICE_WEIGHT,
>> +                               sizeof(temp->field))) {
>> +                    virTypedParameterArrayClear(params, nparams);
>> +                    ret = false;

ret is already false, we can skip this assignment.

>> +                    goto cleanup;
>> +                }
>> +            }
>>           }
>> -        if (virDomainSetBlkioParameters(dom, params, nparams, flags)
>> != 0)
>> +        ret = true;
>> +        if (virDomainSetBlkioParameters(dom, params, nparams, flags)
>> != 0) {

Pre-existing, but as long as we are touching this code, it's better to
check for < 0 instead of != 0 for errors on libvirt calls.

>> +B<device-weights>  is a single string listing one or more device/weight
>> +pairs, in the format of /path/to/device,weight;/path/to/device,weight.

Another comma.

>> +
>>   If I<--live>  is specified, affect a running guest.
>>   If I<--config>  is specified, affect the next boot of a persistent
>> guest.
>>   If I<--current>  is specified, affect the current guest state.
> ACK

I won't push this until I've tweaked and tested patch 5/5, but here's
what I plan on squashing in provided testing goes well.

diff --git i/include/libvirt/libvirt.h.in w/include/libvirt/libvirt.h.in
index 0fd9302..ff4f51b 100644
--- i/include/libvirt/libvirt.h.in
+++ w/include/libvirt/libvirt.h.in
@@ -1241,7 +1241,7 @@ char *
virDomainGetSchedulerType(virDomainPtr domain,
  *
  * Macro for the blkio tunable weight_device: it represents the
  * per-device weight, as a string.  The string is parsed as a
- * series of /path/to/device,weight elements, separated by ';'.
+ * series of /path/to/device,weight elements, separated by ','.
  */

 #define VIR_DOMAIN_BLKIO_DEVICE_WEIGHT "device_weight"
diff --git i/src/conf/domain_conf.c w/src/conf/domain_conf.c
index e4ae64c..8ac8d6f 100644
--- i/src/conf/domain_conf.c
+++ w/src/conf/domain_conf.c
@@ -604,8 +604,9 @@ VIR_ENUM_IMPL(virDomainStartupPolicy,
VIR_DOMAIN_STARTUP_POLICY_LAST,
 #define VIR_DOMAIN_XML_READ_FLAGS   VIR_DOMAIN_XML_INACTIVE


-void virBlkioDeviceWeightArrayClear(virBlkioDeviceWeightPtr deviceWeights,
-                                    int ndevices)
+void
+virBlkioDeviceWeightArrayClear(virBlkioDeviceWeightPtr deviceWeights,
+                               int ndevices)
 {
     int i;

@@ -618,22 +619,26 @@ void
virBlkioDeviceWeightArrayClear(virBlkioDeviceWeightPtr deviceWeights,
  *
  * This function returns a string representing device weights that is
  * suitable for writing to /cgroup/blkio/blkio.weight_device, given
- * a list of per-device weights.
+ * a list of per-device weights, or reports an error on failure.
  */
 #if defined(major) && defined(minor)
-int virBlkioDeviceWeightToStr(virBlkioDeviceWeightPtr devices,
-                              int ndevices,
-                              char **result)
+int
+virBlkioDeviceWeightToStr(virBlkioDeviceWeightPtr devices,
+                          int ndevices,
+                          char **result)
 {
     int i;
     struct stat s;
     virBuffer buf = VIR_BUFFER_INITIALIZER;

     for (i = 0; i < ndevices; i++) {
-        if (stat(devices[i].path, &s) == -1)
-            return -1;
-        if ((s.st_mode & S_IFMT) != S_IFBLK)
+        if (stat(devices[i].path, &s) == -1 ||
+            (s.st_mode & S_IFMT) != S_IFBLK) {
+            virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                 _("path '%s' must be a block device"),
+                                 devices[i].path);
             return -1;
+        }
         virBufferAsprintf(&buf, "%d:%d %d\n",
                           major(s.st_rdev),
                           minor(s.st_rdev),
@@ -641,6 +646,7 @@ int
virBlkioDeviceWeightToStr(virBlkioDeviceWeightPtr devices,
     }

     if (virBufferError(&buf)) {
+        virReportOOMError();
         virBufferFreeAndReset(&buf);
         return -1;
     }
@@ -649,10 +655,13 @@ int
virBlkioDeviceWeightToStr(virBlkioDeviceWeightPtr devices,
     return 0;
 }
 #else
-int virBlkioDeviceWeightToStr(virBlkioDeviceWeightPtr devices
ATTRIBUTE_UNUSED,
-                              int ndevices ATTRIBUTE_UNUSED,
-                              char **result ATTRIBUTE_UNUSED)
+int
+virBlkioDeviceWeightToStr(virBlkioDeviceWeightPtr devices ATTRIBUTE_UNUSED,
+                          int ndevices ATTRIBUTE_UNUSED,
+                          char **result ATTRIBUTE_UNUSED)
 {
+    virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                         _("this binary lacks per-device weight support"));
     return -1;
 }
 #endif
@@ -669,23 +678,24 @@ int
virBlkioDeviceWeightToStr(virBlkioDeviceWeightPtr devices ATTRIBUTE_UNUSED,
  *
  * and fills a virBlkioDeviceWeight struct.
  */
-static int virDomainBlkioDeviceWeightParseXML(xmlNodePtr root,
-                                              virBlkioDeviceWeightPtr dw)
+static int
+virDomainBlkioDeviceWeightParseXML(xmlNodePtr root,
+                                   virBlkioDeviceWeightPtr dw)
 {
     char *c;
     xmlNodePtr node;

-    if (!dw)
-        return -1;
-
     node = root->children;
     while (node) {
         if (node->type == XML_ELEMENT_NODE) {
-            if (xmlStrEqual(node->name, BAD_CAST "path")) {
+            if (xmlStrEqual(node->name, BAD_CAST "path") && !dw->path) {
                 dw->path = (char *)xmlNodeGetContent(node);
             } else if (xmlStrEqual(node->name, BAD_CAST "weight")) {
                 c = (char *)xmlNodeGetContent(node);
                 if (virStrToLong_ui(c, NULL, 10, &dw->weight) < 0) {
+                    virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                         _("could not parse weight %s"),
+                                         c);
                     VIR_FREE(c);
                     VIR_FREE(dw->path);
                     return -1;
@@ -695,6 +705,11 @@ static int
virDomainBlkioDeviceWeightParseXML(xmlNodePtr root,
         }
         node = node->next;
     }
+    if (!dw->path) {
+        virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                             _("missing per-device path"));
+        return -1;
+    }

     return 0;
 }
@@ -6813,23 +6828,21 @@ static virDomainDefPtr
virDomainDefParseXML(virCapsPtr caps,
                      &def->blkio.weight) < 0)
         def->blkio.weight = 0;

-    n = virXPathNodeSet("./blkiotune/device", ctxt, &nodes);
-    if (n > 0) {
-        if (VIR_ALLOC_N(def->blkio.devices, n) < 0) {
-            virReportOOMError();
-            goto error;
-        }
+    if ((n = virXPathNodeSet("./blkiotune/device", ctxt, &nodes)) < 0) {
+        virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("cannot extract blkiotune nodes"));
+        goto error;
+    }
+    if (n && VIR_ALLOC_N(def->blkio.devices, n) < 0)
+        goto no_memory;

-        for (i = 0; i < n; i++) {
-            if (virDomainBlkioDeviceWeightParseXML(nodes[i],
-
&def->blkio.devices[i]) < 0) {
-                virBlkioDeviceWeightArrayClear(def->blkio.devices, i);
-                goto error;
-            }
-        }
-        def->blkio.ndevices = n;
-        VIR_FREE(nodes);
+    for (i = 0; i < n; i++) {
+        if (virDomainBlkioDeviceWeightParseXML(nodes[i],
+                                               &def->blkio.devices[i]) < 0)
+            goto error;
+        def->blkio.ndevices++;
     }
+    VIR_FREE(nodes);

     /* Extract other memory tunables */
     if (virXPathULong("string(./memtune/hard_limit)", ctxt,
diff --git i/tools/virsh.c w/tools/virsh.c
index 4060e3d..30504be 100644
--- i/tools/virsh.c
+++ w/tools/virsh.c
@@ -4649,7 +4649,7 @@ static const vshCmdOptDef opts_blkiotune[] = {
     {"weight", VSH_OT_INT, VSH_OFLAG_NONE,
      N_("IO Weight in range [100, 1000]")},
     {"device-weight", VSH_OT_STRING, VSH_OFLAG_NONE,
-     N_("per-device IO Weights, in the form of
/path/to/device,weight;...")},
+     N_("per-device IO Weights, in the form of
/path/to/device,weight,...")},
     {"config", VSH_OT_BOOL, 0, N_("affect next boot")},
     {"live", VSH_OT_BOOL, 0, N_("affect running domain")},
     {"current", VSH_OT_BOOL, 0, N_("affect current domain")},
@@ -4770,8 +4770,6 @@ cmdBlkiotune(vshControl * ctl, const vshCmd * cmd)
                     vshPrint(ctl, "unimplemented blkio parameter type\n");
             }
         }
-
-        ret = true;
     } else {
         /* set the blkio parameters */
         params = vshCalloc(ctl, nparams, sizeof(*params));
@@ -4783,34 +4781,29 @@ cmdBlkiotune(vshControl * ctl, const vshCmd * cmd)
             if (weight) {
                 temp->value.ui = weight;
                 if (!virStrcpy(temp->field, VIR_DOMAIN_BLKIO_WEIGHT,
-                               sizeof(temp->field))) {
-                    virTypedParameterArrayClear(params, nparams);
-                    ret = false;
+                               sizeof(temp->field)))
                     goto cleanup;
-                }
-                weight = 0;
             }

             if (device_weight) {
-                /* Safe to cast away const here.  */
-                temp->value.s = (char *)device_weight;
+                temp->value.s = vshStrdup(ctl, device_weight);
                 temp->type = VIR_TYPED_PARAM_STRING;
                 if (!virStrcpy(temp->field, VIR_DOMAIN_BLKIO_DEVICE_WEIGHT,
-                               sizeof(temp->field))) {
-                    virTypedParameterArrayClear(params, nparams);
-                    ret = false;
+                               sizeof(temp->field)))
                     goto cleanup;
-                }
             }
         }
-        ret = true;
-        if (virDomainSetBlkioParameters(dom, params, nparams, flags) !=
0) {
+
+        if (virDomainSetBlkioParameters(dom, params, nparams, flags) < 0) {
             vshError(ctl, "%s", _("Unable to change blkio parameters"));
-            ret = false;
+            goto cleanup;
         }
     }

+    ret = true;
+
   cleanup:
+    virTypedParameterArrayClear(params, nparams);
     VIR_FREE(params);
     virDomainFree(dom);
     return ret;
diff --git i/tools/virsh.pod w/tools/virsh.pod
index a117e84..70e299f 100644
--- i/tools/virsh.pod
+++ w/tools/virsh.pod
@@ -1040,7 +1040,7 @@ Display or set the blkio parameters. QEMU/KVM
supports I<--weight>.
 I<--weight> is in range [100, 1000].

 B<device-weights> is a single string listing one or more device/weight
-pairs, in the format of /path/to/device,weight;/path/to/device,weight.
+pairs, in the format of /path/to/device,weight,/path/to/device,weight.

 If I<--live> is specified, affect a running guest.
 If I<--config> is specified, affect the next boot of a persistent guest.


-- 
Eric Blake   eblake at redhat.com    +1-801-349-2682
Libvirt virtualization library http://libvirt.org

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 620 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20111114/c5ad56bd/attachment-0001.sig>


More information about the libvir-list mailing list