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(a)redhat.com +1-801-349-2682
Libvirt virtualization library
http://libvirt.org