[libvirt] [PATCH 8/8] Make QEMU cgroups use configurable

Daniel P. Berrange berrange at redhat.com
Wed Jul 22 15:23:47 UTC 2009


 * qemud/libvirtd_qemu.aug, qemud/test_libvirtd_qemu.aug,
   src/qemu.conf: Add 'cgroups_controllers' and 'cgroups_device_acl'
   parameters
 * src/qemu_conf.h, src/qemu_conf.c: Load & parse configuration params
   for cgroups
 * src/qemu_driver.c: Only use cgroups controllers that are activated,
   and use configured device whitelist instead of default, if set.

Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
---
 qemud/libvirtd_qemu.aug      |    2 +
 qemud/test_libvirtd_qemu.aug |   21 +++++++-
 src/Makefile.am              |    3 +-
 src/cgroup.c                 |   11 ----
 src/cgroup.h                 |   12 +++++
 src/qemu.conf                |   34 +++++++++++++
 src/qemu_conf.c              |   61 ++++++++++++++++++++++++
 src/qemu_conf.h              |    3 +
 src/qemu_driver.c            |  106 ++++++++++++++++++++++++------------------
 9 files changed, 192 insertions(+), 61 deletions(-)

diff --git a/qemud/libvirtd_qemu.aug b/qemud/libvirtd_qemu.aug
index 8cf0461..8b8aab2 100644
--- a/qemud/libvirtd_qemu.aug
+++ b/qemud/libvirtd_qemu.aug
@@ -31,6 +31,8 @@ module Libvirtd_qemu =
                  | str_entry "vnc_sasl_dir"
                  | str_entry "user"
                  | str_entry "group"
+                 | str_array_entry "cgroup_controllers"
+                 | str_array_entry "cgroup_device_acl"
 
    (* Each enty in the config is one of the following three ... *)
    let entry = vnc_entry
diff --git a/qemud/test_libvirtd_qemu.aug b/qemud/test_libvirtd_qemu.aug
index f62da01..274c89d 100644
--- a/qemud/test_libvirtd_qemu.aug
+++ b/qemud/test_libvirtd_qemu.aug
@@ -83,6 +83,10 @@ vnc_sasl_dir = \"/some/directory/sasl2\"
 user = \"root\"
 
 group = \"root\"
+
+cgroup_controllers = [ \"cpu\", \"devices\" ]
+
+cgroup_device_acl = [ \"/dev/null\", \"/dev/full\", \"/dev/zero\" ]
 "
 
    test Libvirtd_qemu.lns get conf =
@@ -165,7 +169,18 @@ group = \"root\"
 { "#comment" = "point to the directory, and create a qemu.conf in that location" }
 { "#comment" = "" }
 { "vnc_sasl_dir" = "/some/directory/sasl2" }
-{ "#comment" = "" }
+{ "#empty" }
 { "user"= "root" }
-{ "#comment" = "" }
-{ "group" = "root" }
\ No newline at end of file
+{ "#empty" }
+{ "group" = "root" }
+{ "#empty" }
+{ "cgroup_controllers"
+    { "1" = "cpu" }
+    { "2" = "devices" }
+}
+{ "#empty" }
+{ "cgroup_device_acl"
+    { "1" = "/dev/null" }
+    { "2" = "/dev/full" }
+    { "3" = "/dev/zero" }
+}
diff --git a/src/Makefile.am b/src/Makefile.am
index 79826b1..b9b0bf7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -142,7 +142,8 @@ VBOX_DRIVER_EXTRA_DIST = vbox/vbox_tmpl.c vbox/README
 
 QEMU_DRIVER_SOURCES =						\
 		qemu_conf.c qemu_conf.h				\
-		qemu_driver.c qemu_driver.h
+		qemu_driver.c qemu_driver.h			\
+		cgroup.c cgroup.h
 
 UML_DRIVER_SOURCES =						\
 		uml_conf.c uml_conf.h				\
diff --git a/src/cgroup.c b/src/cgroup.c
index 35fedad..e678517 100644
--- a/src/cgroup.c
+++ b/src/cgroup.c
@@ -31,17 +31,6 @@
 
 #define CGROUP_MAX_VAL 512
 
-enum {
-    VIR_CGROUP_CONTROLLER_CPU,
-    VIR_CGROUP_CONTROLLER_CPUACCT,
-    VIR_CGROUP_CONTROLLER_CPUSET,
-    VIR_CGROUP_CONTROLLER_MEMORY,
-    VIR_CGROUP_CONTROLLER_DEVICES,
-
-    VIR_CGROUP_CONTROLLER_LAST
-};
-
-VIR_ENUM_DECL(virCgroupController);
 VIR_ENUM_IMPL(virCgroupController, VIR_CGROUP_CONTROLLER_LAST,
               "cpu", "cpuacct", "cpuset", "memory", "devices");
 
diff --git a/src/cgroup.h b/src/cgroup.h
index efc3370..6d43b14 100644
--- a/src/cgroup.h
+++ b/src/cgroup.h
@@ -15,6 +15,18 @@
 struct virCgroup;
 typedef struct virCgroup *virCgroupPtr;
 
+enum {
+    VIR_CGROUP_CONTROLLER_CPU,
+    VIR_CGROUP_CONTROLLER_CPUACCT,
+    VIR_CGROUP_CONTROLLER_CPUSET,
+    VIR_CGROUP_CONTROLLER_MEMORY,
+    VIR_CGROUP_CONTROLLER_DEVICES,
+
+    VIR_CGROUP_CONTROLLER_LAST
+};
+
+VIR_ENUM_DECL(virCgroupController);
+
 int virCgroupForDriver(const char *name,
                        virCgroupPtr *group,
                        int privileged,
diff --git a/src/qemu.conf b/src/qemu.conf
index 3009725..653f487 100644
--- a/src/qemu.conf
+++ b/src/qemu.conf
@@ -95,3 +95,37 @@
 
 # The group ID for QEMU processes run by the system instance
 #group = "root"
+
+
+# What cgroup controllers to make use of with QEMU guests
+#
+#  - 'cpu' - use for schedular tunables
+#  - 'devices' - use for device whitelisting
+#
+# NB, even if configured here, they won't be used unless
+# the adminsitrator has mounted cgroups. eg
+#
+#  mkdir /dev/cgroup
+#  mount -t cgroup -o devices,cpu none /dev/cgroup
+#
+# They can be mounted anywhere, and different controlers
+# can be mounted in different locations. libvirt will detect
+# where they are located.
+#
+# cgroup_controllers = [ "cpu", "devices" ]
+
+# This is the basic set of devices allowed / required by
+# all virtual machines.
+#
+# As well as this, any configured block backed disks,
+# all sound device, and all PTY devices are allowed.
+#
+# This will only need setting if newer QEMU suddenly
+# wants some device we don't already know a bout.
+#
+#cgroup_device_acl = [
+#    "/dev/null", "/dev/full", "/dev/zero",
+#    "/dev/random", "/dev/urandom",
+#    "/dev/ptmx", "/dev/kvm", "/dev/kqemu",
+#    "/dev/rtc", "/dev/hpet", "/dev/net/tun",
+#]
diff --git a/src/qemu_conf.c b/src/qemu_conf.c
index f483a51..3739fff 100644
--- a/src/qemu_conf.c
+++ b/src/qemu_conf.c
@@ -94,6 +94,7 @@ int qemudLoadDriverConfig(struct qemud_driver *driver,
     virConfValuePtr p;
     char *user;
     char *group;
+    int i;
 
     /* Setup 2 critical defaults */
     if (!(driver->vncListen = strdup("127.0.0.1"))) {
@@ -218,6 +219,66 @@ int qemudLoadDriverConfig(struct qemud_driver *driver,
     }
     VIR_FREE(group);
 
+    p = virConfGetValue (conf, "cgroup_controllers");
+    CHECK_TYPE ("cgroup_controllers", VIR_CONF_LIST);
+    if (p) {
+        virConfValuePtr pp;
+        for (i = 0, pp = p->list; pp; ++i, pp = pp->next) {
+            int ctl;
+            if (pp->type != VIR_CONF_STRING) {
+                VIR_ERROR("%s", _("cgroup_device_acl must be a list of strings"));
+                virConfFree(conf);
+                return -1;
+            }
+            ctl = virCgroupControllerTypeFromString(pp->str);
+            if (ctl < 0) {
+                VIR_ERROR("Unknown cgroup controller '%s'", pp->str);
+                virConfFree(conf);
+                return -1;
+            }
+            driver->cgroupControllers |= (1 << ctl);
+        }
+    } else {
+        driver->cgroupControllers =
+            (1 << VIR_CGROUP_CONTROLLER_CPU) |
+            (1 << VIR_CGROUP_CONTROLLER_DEVICES);
+    }
+    for (i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i++) {
+        if (driver->cgroupControllers & (1 << i)) {
+            VIR_INFO("Configured cgroup controller '%s'",
+                     virCgroupControllerTypeToString(i));
+        }
+    }
+
+    p = virConfGetValue (conf, "cgroup_device_acl");
+    CHECK_TYPE ("cgroup_device_acl", VIR_CONF_LIST);
+    if (p) {
+        int len = 0;
+        virConfValuePtr pp;
+        for (pp = p->list; pp; pp = pp->next)
+            len++;
+        if (VIR_ALLOC_N(driver->cgroupDeviceACL, 1+len) < 0) {
+            virReportOOMError(NULL);
+            virConfFree(conf);
+            return -1;
+        }
+        for (i = 0, pp = p->list; pp; ++i, pp = pp->next) {
+            if (pp->type != VIR_CONF_STRING) {
+                VIR_ERROR("%s", _("cgroup_device_acl must be a list of strings"));
+                virConfFree(conf);
+                return -1;
+            }
+            driver->cgroupDeviceACL[i] = strdup (pp->str);
+            if (driver->cgroupDeviceACL[i] == NULL) {
+                virReportOOMError(NULL);
+                virConfFree(conf);
+                return -1;
+            }
+
+        }
+        driver->cgroupDeviceACL[i] = NULL;
+    }
+
     virConfFree (conf);
     return 0;
 }
diff --git a/src/qemu_conf.h b/src/qemu_conf.h
index a5c8b3f..e753ba0 100644
--- a/src/qemu_conf.h
+++ b/src/qemu_conf.h
@@ -79,6 +79,9 @@ struct qemud_driver {
     int nextvmid;
 
     virCgroupPtr cgroup;
+    int cgroupControllers;
+    char **cgroupDeviceACL;
+
     virDomainObjList domains;
 
     brControl *brctl;
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index 19572b4..60963db 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -125,6 +125,15 @@ static int qemudDetectVcpuPIDs(virConnectPtr conn,
 
 static struct qemud_driver *qemu_driver = NULL;
 
+static int qemuCgroupControllerActive(struct qemud_driver *driver,
+                                      int controller)
+{
+    if (driver->cgroup == NULL)
+        return 0;
+    if (driver->cgroupControllers & (1 << controller))
+        return 1;
+    return 0;
+}
 
 static int
 qemudLogFD(virConnectPtr conn, struct qemud_driver *driver, const char* name)
@@ -1405,7 +1414,10 @@ static int qemuSetupCgroup(virConnectPtr conn,
     virCgroupPtr cgroup = NULL;
     int rc;
     unsigned int i;
-    const char *const *deviceACL = defaultDeviceACL;
+    const char *const *deviceACL =
+        driver->cgroupDeviceACL ?
+        (const char *const *)driver->cgroupDeviceACL :
+        defaultDeviceACL;
 
     if (driver->cgroup == NULL)
         return 0; /* Not supported, so claim success */
@@ -1418,58 +1430,60 @@ static int qemuSetupCgroup(virConnectPtr conn,
         goto cleanup;
     }
 
-    rc = virCgroupDenyAllDevices(cgroup);
-    if (rc != 0) {
-        if (rc == -EPERM) {
-            VIR_WARN0("Group devices ACL is not accessible, disabling whitelisting");
-            goto done;
-        }
-
-        virReportSystemError(conn, -rc,
-                             _("Unable to deny all devices for %s"), vm->def->name);
-        goto cleanup;
-    }
-
-    for (i = 0; i < vm->def->ndisks ; i++) {
-        if (vm->def->disks[i]->type != VIR_DOMAIN_DISK_TYPE_BLOCK ||
-            vm->def->disks[i]->src == NULL)
-            continue;
-
-        rc = virCgroupAllowDevicePath(cgroup,
-                                      vm->def->disks[i]->src);
+    if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) {
+        rc = virCgroupDenyAllDevices(cgroup);
         if (rc != 0) {
+            if (rc == -EPERM) {
+                VIR_WARN0("Group devices ACL is not accessible, disabling whitelisting");
+                goto done;
+            }
+
             virReportSystemError(conn, -rc,
-                                 _("Unable to allow device %s for %s"),
-                                 vm->def->disks[i]->src, vm->def->name);
+                                 _("Unable to deny all devices for %s"), vm->def->name);
             goto cleanup;
         }
-    }
 
-    rc = virCgroupAllowDeviceMajor(cgroup, 'c', DEVICE_PTY_MAJOR);
-    if (rc != 0) {
-        virReportSystemError(conn, -rc, "%s",
-                             _("unable to allow /dev/pts/ devices"));
-        goto cleanup;
-    }
+        for (i = 0; i < vm->def->ndisks ; i++) {
+            if (vm->def->disks[i]->type != VIR_DOMAIN_DISK_TYPE_BLOCK ||
+                vm->def->disks[i]->src == NULL)
+                continue;
+
+            rc = virCgroupAllowDevicePath(cgroup,
+                                          vm->def->disks[i]->src);
+            if (rc != 0) {
+                virReportSystemError(conn, -rc,
+                                     _("Unable to allow device %s for %s"),
+                                     vm->def->disks[i]->src, vm->def->name);
+                goto cleanup;
+            }
+        }
 
-    if (vm->def->nsounds) {
-        rc = virCgroupAllowDeviceMajor(cgroup, 'c', DEVICE_SND_MAJOR);
+        rc = virCgroupAllowDeviceMajor(cgroup, 'c', DEVICE_PTY_MAJOR);
         if (rc != 0) {
             virReportSystemError(conn, -rc, "%s",
-                                 _("unable to allow /dev/snd/ devices"));
+                                 _("unable to allow /dev/pts/ devices"));
             goto cleanup;
         }
-    }
 
-    for (i = 0; deviceACL[i] != NULL ; i++) {
-        rc = virCgroupAllowDevicePath(cgroup,
-                                      deviceACL[i]);
-        if (rc < 0 &&
-            rc != -ENOENT) {
-            virReportSystemError(conn, -rc,
-                                 _("unable to allow device %s"),
-                                 deviceACL[i]);
-            goto cleanup;
+        if (vm->def->nsounds) {
+            rc = virCgroupAllowDeviceMajor(cgroup, 'c', DEVICE_SND_MAJOR);
+            if (rc != 0) {
+                virReportSystemError(conn, -rc, "%s",
+                                     _("unable to allow /dev/snd/ devices"));
+                goto cleanup;
+            }
+        }
+
+        for (i = 0; deviceACL[i] != NULL ; i++) {
+            rc = virCgroupAllowDevicePath(cgroup,
+                                          deviceACL[i]);
+            if (rc < 0 &&
+                rc != -ENOENT) {
+                virReportSystemError(conn, -rc,
+                                     _("unable to allow device %s"),
+                                     deviceACL[i]);
+                goto cleanup;
+            }
         }
     }
 
@@ -4934,7 +4948,7 @@ static int qemudDomainAttachDevice(virDomainPtr dom,
         goto cleanup;
 
     if (dev->type == VIR_DOMAIN_DEVICE_DISK) {
-        if (driver->cgroup != NULL) {
+        if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) {
             if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) !=0 ) {
                 qemudReportError(dom->conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                                  _("Unable to find cgroup for %s\n"),
@@ -5379,7 +5393,7 @@ static char *qemuGetSchedulerType(virDomainPtr dom,
     struct qemud_driver *driver = dom->conn->privateData;
     char *ret;
 
-    if (driver->cgroup == NULL) {
+    if (!qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPU)) {
         qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
                          __FUNCTION__);
         return NULL;
@@ -5404,7 +5418,7 @@ static int qemuSetSchedulerParameters(virDomainPtr dom,
     virDomainObjPtr vm = NULL;
     int ret = -1;
 
-    if (driver->cgroup == NULL) {
+    if (!qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPU)) {
         qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
                          __FUNCTION__);
         return -1;
@@ -5469,7 +5483,7 @@ static int qemuGetSchedulerParameters(virDomainPtr dom,
     int ret = -1;
     int rc;
 
-    if (driver->cgroup == NULL) {
+    if (!qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_CPU)) {
         qemudReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
                          __FUNCTION__);
         return -1;
-- 
1.6.2.5




More information about the libvir-list mailing list