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