]> xenbits.xensource.com Git - people/liuw/libxenctrl-split/libvirt.git/commitdiff
blkiotune: add qemu support for blkiotune.device_weight
authorHu Tao <hutao@cn.fujitsu.com>
Tue, 8 Nov 2011 11:00:34 +0000 (19:00 +0800)
committerEric Blake <eblake@redhat.com>
Tue, 29 Nov 2011 19:26:21 +0000 (12:26 -0700)
Implement setting/getting per-device blkio weights in qemu,
using the cgroups blkio.weight_device tunable.

src/libvirt_private.syms
src/qemu/qemu_cgroup.c
src/qemu/qemu_driver.c
src/util/cgroup.c
src/util/cgroup.h
tests/qemuxml2argvdata/qemuxml2argv-blkiotune-device.args [new file with mode: 0644]
tests/qemuxml2argvtest.c
tests/qemuxml2xmltest.c

index fdf61bb849bd71ebc778b2bb6d15bb2514f16294..dceb20d702fa422fea5f8cf3d626ccb2a710eaae 100644 (file)
@@ -89,6 +89,7 @@ virCgroupKillRecursive;
 virCgroupMounted;
 virCgroupPathOfController;
 virCgroupRemove;
+virCgroupSetBlkioDeviceWeight;
 virCgroupSetBlkioWeight;
 virCgroupSetCpuShares;
 virCgroupSetCpuCfsPeriod;
index 2a10bd2c1d2a1e2b3ca3e9ed02cf283ccf218c32..e5ca8f30e5c1f6f882b19fc8ba80fd7d0242a1f1 100644 (file)
@@ -309,6 +309,28 @@ int qemuSetupCgroup(struct qemud_driver *driver,
         } else {
             qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                             _("Block I/O tuning is not available on this host"));
+            goto cleanup;
+        }
+    }
+
+    if (vm->def->blkio.ndevices) {
+        if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_BLKIO)) {
+            for (i = 0; i < vm->def->blkio.ndevices; i++) {
+                virBlkioDeviceWeightPtr dw = &vm->def->blkio.devices[i];
+                rc = virCgroupSetBlkioDeviceWeight(cgroup, dw->path,
+                                                   dw->weight);
+                if (rc != 0) {
+                    virReportSystemError(-rc,
+                                         _("Unable to set io device weight "
+                                           "for domain %s"),
+                                         vm->def->name);
+                    goto cleanup;
+                }
+            }
+        } else {
+            qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                            _("Block I/O tuning is not available on this host"));
+            goto cleanup;
         }
     }
 
index 7e87369c459bf43e772cbaf2a52d121667de5dda..105bdde05584f03a314e0a2c1d0d67ae4a6718de 100644 (file)
 # define KVM_CAP_NR_VCPUS 9       /* returns max vcpus per vm */
 #endif
 
-#define QEMU_NB_BLKIO_PARAM  1
+#define QEMU_NB_BLKIO_PARAM  2
 
 static void processWatchdogEvent(void *data, void *opaque);
 
@@ -5883,6 +5883,88 @@ cleanup:
     return ret;
 }
 
+/* deviceWeightStr in the form of /device/path,weight,/device/path,weight
+ * for example, /dev/disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0,800
+ */
+static int
+parseBlkioWeightDeviceStr(char *deviceWeightStr,
+                          virBlkioDeviceWeightPtr *dw, int *size)
+{
+    char *temp;
+    int ndevices = 0;
+    int nsep = 0;
+    int i;
+    virBlkioDeviceWeightPtr result = NULL;
+
+    temp = deviceWeightStr;
+    while (temp) {
+        temp = strchr(temp, ',');
+        if (temp) {
+            temp++;
+            nsep++;
+        }
+    }
+
+    /* A valid string must have even number of fields, hence an odd
+     * number of commas.  */
+    if (!(nsep & 1))
+        goto error;
+
+    ndevices = (nsep + 1) / 2;
+
+    if (VIR_ALLOC_N(result, ndevices) < 0) {
+        virReportOOMError();
+        return -1;
+    }
+
+    i = 0;
+    temp = deviceWeightStr;
+    while (temp) {
+        char *p = temp;
+
+        /* device path */
+        p = strchr(p, ',');
+        if (!p)
+            goto error;
+
+        result[i].path = strndup(temp, p - temp);
+        if (!result[i].path) {
+            virReportOOMError();
+            goto cleanup;
+        }
+
+        /* weight */
+        temp = p + 1;
+
+        if (virStrToLong_ui(temp, &p, 10, &result[i].weight) < 0)
+            goto error;
+
+        i++;
+
+        if (*p == '\0')
+            break;
+        else if (*p != ',')
+            goto error;
+        temp = p + 1;
+    }
+
+    if (!i)
+        VIR_FREE(result);
+
+    *dw = result;
+    *size = i;
+
+    return 0;
+
+error:
+    qemuReportError(VIR_ERR_INVALID_ARG,
+                    _("unable to parse %s"), deviceWeightStr);
+cleanup:
+    virBlkioDeviceWeightArrayClear(result, ndevices);
+    VIR_FREE(result);
+    return -1;
+}
+
 static int qemuDomainSetBlkioParameters(virDomainPtr dom,
                                          virTypedParameterPtr params,
                                          int nparams,
@@ -5949,10 +6031,10 @@ static int qemuDomainSetBlkioParameters(virDomainPtr dom,
     ret = 0;
     if (flags & VIR_DOMAIN_AFFECT_LIVE) {
         for (i = 0; i < nparams; i++) {
+            int rc;
             virTypedParameterPtr param = &params[i];
 
             if (STREQ(param->field, VIR_DOMAIN_BLKIO_WEIGHT)) {
-                int rc;
                 if (param->type != VIR_TYPED_PARAM_UINT) {
                     qemuReportError(VIR_ERR_INVALID_ARG, "%s",
                                     _("invalid type for blkio weight tunable, expected a 'unsigned int'"));
@@ -5973,6 +6055,44 @@ static int qemuDomainSetBlkioParameters(virDomainPtr dom,
                                          _("unable to set blkio weight tunable"));
                     ret = -1;
                 }
+            } else if (STREQ(param->field, VIR_DOMAIN_BLKIO_DEVICE_WEIGHT)) {
+                int ndevices;
+                virBlkioDeviceWeightPtr devices = NULL;
+                if (param->type != VIR_TYPED_PARAM_STRING) {
+                    qemuReportError(VIR_ERR_INVALID_ARG, "%s",
+                                    _("invalid type for device_weight tunable, "
+                                      "expected a 'char *'"));
+                    ret = -1;
+                    continue;
+                }
+
+                if (parseBlkioWeightDeviceStr(params[i].value.s,
+                                              &devices,
+                                              &ndevices) < 0) {
+                    ret = -1;
+                    continue;
+                }
+                for (i = 0; i < ndevices; i++) {
+                    rc = virCgroupSetBlkioDeviceWeight(group,
+                                                       devices[i].path,
+                                                       devices[i].weight);
+                    if (rc < 0) {
+                        virReportSystemError(-rc,
+                                             _("Unable to set io device weight "
+                                               "for path %s"),
+                                             devices[i].path);
+                        break;
+                    }
+                }
+                if (i != ndevices) {
+                    ret = -1;
+                    continue;
+                }
+                virBlkioDeviceWeightArrayClear(vm->def->blkio.devices,
+                                               vm->def->blkio.ndevices);
+                VIR_FREE(vm->def->blkio.devices);
+                vm->def->blkio.devices = devices;
+                vm->def->blkio.ndevices = ndevices;
             } else {
                 qemuReportError(VIR_ERR_INVALID_ARG,
                                 _("Parameter `%s' not supported"), param->field);
@@ -6005,9 +6125,31 @@ static int qemuDomainSetBlkioParameters(virDomainPtr dom,
                 }
 
                 persistentDef->blkio.weight = params[i].value.ui;
+            } else if (STREQ(param->field, VIR_DOMAIN_BLKIO_DEVICE_WEIGHT)) {
+                virBlkioDeviceWeightPtr devices = NULL;
+                int ndevices;
+                if (param->type != VIR_TYPED_PARAM_STRING) {
+                    qemuReportError(VIR_ERR_INVALID_ARG, "%s",
+                                    _("invalid type for device_weight tunable, "
+                                      "expected a 'char *'"));
+                    ret = -1;
+                    continue;
+                }
+                if (parseBlkioWeightDeviceStr(params[i].value.s,
+                                              &devices,
+                                              &ndevices) < 0) {
+                    ret = -1;
+                    continue;
+                }
+                virBlkioDeviceWeightArrayClear(persistentDef->blkio.devices,
+                                               persistentDef->blkio.ndevices);
+                VIR_FREE(persistentDef->blkio.devices);
+                persistentDef->blkio.devices = devices;
+                persistentDef->blkio.ndevices = ndevices;
             } else {
                 qemuReportError(VIR_ERR_INVALID_ARG,
-                                _("Parameter `%s' not supported"), param->field);
+                                _("Parameter `%s' not supported"),
+                                param->field);
                 ret = -1;
             }
         }
@@ -6030,7 +6172,7 @@ static int qemuDomainGetBlkioParameters(virDomainPtr dom,
                                          unsigned int flags)
 {
     struct qemud_driver *driver = dom->conn->privateData;
-    int i;
+    int i, j;
     virCgroupPtr group = NULL;
     virDomainObjPtr vm = NULL;
     virDomainDefPtr persistentDef = NULL;
@@ -6044,7 +6186,9 @@ static int qemuDomainGetBlkioParameters(virDomainPtr dom,
                   VIR_TYPED_PARAM_STRING_OKAY, -1);
     qemuDriverLock(driver);
 
-    /* We don't return strings, and thus trivially support this flag.  */
+    /* We blindly return a string, and let libvirt.c and
+     * remote_driver.c do the filtering on behalf of older clients
+     * that can't parse it.  */
     flags &= ~VIR_TYPED_PARAM_STRING_OKAY;
 
     vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -6123,6 +6267,43 @@ static int qemuDomainGetBlkioParameters(virDomainPtr dom,
                 }
                 param->value.ui = val;
                 break;
+            case 1: /* blkiotune.device_weight */
+                if (vm->def->blkio.ndevices > 0) {
+                    virBuffer buf = VIR_BUFFER_INITIALIZER;
+                    bool comma = false;
+                    for (j = 0; j < vm->def->blkio.ndevices; j++) {
+                        if (!vm->def->blkio.devices[j].weight)
+                            continue;
+                        if (comma)
+                            virBufferAddChar(&buf, ',');
+                        else
+                            comma = true;
+                        virBufferAsprintf(&buf, "%s,%u",
+                                          vm->def->blkio.devices[j].path,
+                                          vm->def->blkio.devices[j].weight);
+                    }
+                    if (virBufferError(&buf)) {
+                        virReportOOMError();
+                        goto cleanup;
+                    }
+                    param->value.s = virBufferContentAndReset(&buf);
+                }
+                if (!param->value.s) {
+                    param->value.s = strdup("");
+                    if (!param->value.s) {
+                        virReportOOMError();
+                        goto cleanup;
+                    }
+                }
+                param->type = VIR_TYPED_PARAM_STRING;
+                if (virStrcpyStatic(param->field,
+                                    VIR_DOMAIN_BLKIO_DEVICE_WEIGHT) == NULL) {
+                    qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                                    _("Field name '%s' too long"),
+                                    VIR_DOMAIN_BLKIO_DEVICE_WEIGHT);
+                    goto cleanup;
+                }
+                break;
 
             default:
                 break;
@@ -6147,6 +6328,38 @@ static int qemuDomainGetBlkioParameters(virDomainPtr dom,
                 param->value.ui = persistentDef->blkio.weight;
                 break;
 
+            case 1: /* blkiotune.device_weight */
+                if (persistentDef->blkio.ndevices > 0) {
+                    virBuffer buf = VIR_BUFFER_INITIALIZER;
+                    for (j = 0; j < persistentDef->blkio.ndevices; j++) {
+                        if (j)
+                            virBufferAddChar(&buf, ',');
+                        virBufferAsprintf(&buf, "%s,%u",
+                                          persistentDef->blkio.devices[j].path,
+                                          persistentDef->blkio.devices[j].weight);
+                    }
+                    if (virBufferError(&buf)) {
+                        virReportOOMError();
+                        goto cleanup;
+                    }
+                    param->value.s = virBufferContentAndReset(&buf);
+                } else {
+                    param->value.s = strdup("");
+                    if (!param->value.s) {
+                        virReportOOMError();
+                        goto cleanup;
+                    }
+                }
+                param->type = VIR_TYPED_PARAM_STRING;
+                if (virStrcpyStatic(param->field,
+                                    VIR_DOMAIN_BLKIO_DEVICE_WEIGHT) == NULL) {
+                    qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                                    _("Field name '%s' too long"),
+                                    VIR_DOMAIN_BLKIO_DEVICE_WEIGHT);
+                    goto cleanup;
+                }
+                break;
+
             default:
                 break;
                 /* should not hit here */
index 71dedeb58f7229265589f0817e27128dd29a2a2a..b4d3d8b1b64eaabee4fba38fc28738729db13d03 100644 (file)
@@ -981,6 +981,57 @@ int virCgroupGetBlkioWeight(virCgroupPtr group, unsigned int *weight)
     return ret;
 }
 
+/**
+ * virCgroupSetBlkioDeviceWeight:
+ *
+ * @group: The cgroup to change io device weight device for
+ * @path: The device with a weight to alter
+ * @weight: The new device weight (100-1000), or 0 to clear
+ *
+ * device_weight is treated as a write-only parameter, so
+ * there isn't a getter counterpart.
+ *
+ * Returns: 0 on success, -errno on failure
+ */
+#if defined(major) && defined(minor)
+int virCgroupSetBlkioDeviceWeight(virCgroupPtr group,
+                                  const char *path,
+                                  unsigned int weight)
+{
+    char *str;
+    struct stat sb;
+    int ret;
+
+    if (weight && (weight > 1000 || weight < 100))
+        return -EINVAL;
+
+    if (stat(path, &sb) < 0)
+        return -errno;
+
+    if (!S_ISBLK(sb.st_mode))
+        return -EINVAL;
+
+    if (virAsprintf(&str, "%d:%d %d", major(sb.st_rdev), minor(sb.st_rdev),
+                    weight) < 0)
+        return -errno;
+
+    ret = virCgroupSetValueStr(group,
+                               VIR_CGROUP_CONTROLLER_BLKIO,
+                               "blkio.weight_device",
+                               str);
+    VIR_FREE(str);
+    return ret;
+}
+#else
+        int
+ virCgroupSetBlkioDeviceWeight(virCgroupPtr group ATTRIBUTE_UNUSED,
+                                  const char *path ATTRIBUTE_UNUSED,
+                                  unsigned int weight ATTRIBUTE_UNUSED)
+{
+    return -ENOSYS;
+}
+#endif
+
 /**
  * virCgroupSetMemory:
  *
index d190bb3c56961d837b9632922a68f2b946e56b0e..70dd3925f2f8f885587e8cd863893e9aee6cdf50 100644 (file)
@@ -55,6 +55,10 @@ int virCgroupAddTask(virCgroupPtr group, pid_t pid);
 int virCgroupSetBlkioWeight(virCgroupPtr group, unsigned int weight);
 int virCgroupGetBlkioWeight(virCgroupPtr group, unsigned int *weight);
 
+int virCgroupSetBlkioDeviceWeight(virCgroupPtr group,
+                                  const char *path,
+                                  unsigned int weight);
+
 int virCgroupSetMemory(virCgroupPtr group, unsigned long long kb);
 int virCgroupGetMemoryUsage(virCgroupPtr group, unsigned long *kb);
 
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-blkiotune-device.args b/tests/qemuxml2argvdata/qemuxml2argv-blkiotune-device.args
new file mode 100644 (file)
index 0000000..651793d
--- /dev/null
@@ -0,0 +1,4 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M \
+pc -m 214 -smp 1 -name QEMUGuest1 -nographic -monitor unix:/tmp/test-monitor,\
+server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -net none -serial \
+none -parallel none -usb
index fe243542c4d1dc2ed8ca32057a2c0ff19cd70b6a..51fab7b3f010dcbd5b4aff5ad5fa22d5f95b12d3 100644 (file)
@@ -643,6 +643,7 @@ mymain(void)
 
     DO_TEST("memtune", false, QEMU_CAPS_NAME);
     DO_TEST("blkiotune", false, QEMU_CAPS_NAME);
+    DO_TEST("blkiotune-device", false, QEMU_CAPS_NAME);
     DO_TEST("cputune", false, QEMU_CAPS_NAME);
     DO_TEST("numatune-memory", false, NONE);
 
index 3f375209a42c2f67fe595d258e3d43c942319883..32445fd2a63defd7393b1a08de3cd8b0b38d2ddc 100644 (file)
@@ -184,6 +184,7 @@ mymain(void)
     DO_TEST("encrypted-disk");
     DO_TEST("memtune");
     DO_TEST("blkiotune");
+    DO_TEST("blkiotune-device");
     DO_TEST("cputune");
 
     DO_TEST("smp");