]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
Make QEMU cgroups use configurable
authorDaniel P. Berrange <berrange@redhat.com>
Wed, 22 Jul 2009 15:08:04 +0000 (16:08 +0100)
committerDaniel P. Berrange <berrange@redhat.com>
Thu, 23 Jul 2009 16:38:06 +0000 (17:38 +0100)
 * 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.

qemud/libvirtd_qemu.aug
qemud/test_libvirtd_qemu.aug
src/Makefile.am
src/cgroup.c
src/cgroup.h
src/qemu.conf
src/qemu_conf.c
src/qemu_conf.h
src/qemu_driver.c

index 8cf0461c5442ce427a4929d090b61bb9be67ff2d..8b8aab254e2901c5c74d7b9edaa5ee335d2b4877 100644 (file)
@@ -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
index f62da0106f17b134a22b65b0418e8678989e1638..274c89d8b15d61fb1784a98d39641568b2131850 100644 (file)
@@ -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" }
+}
index 79826b153dd76e56e105b8f3e9e3f93df7995c49..b9b0bf75ff12d9444b1f68579ad4953d4bd33f75 100644 (file)
@@ -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                           \
index 35fedadf7b24ce0be3265ddbd088f2ee1dbd9dc0..e6785178a1d96f0fcbbeac2d31156d79e19abb4e 100644 (file)
 
 #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");
 
index efc337074e949b7d02b0513804a08ea325418e83..6d43b14fc5ed7bfd6e44ccf72df6cd618ac26c0a 100644 (file)
 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,
index 300972548a8a91667740a261ba0f728ca58eb704..653f487f539e819c6bf30db17578aeeee3e6191f 100644 (file)
 
 # 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",
+#]
index 96f83cb809a100a11b75eca403c4fca945c7d594..bf4d4a905a2bb6e627abcca34f253903b30671a4 100644 (file)
@@ -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;
 }
index a5c8b3f54754f9635e767c400589f4e93b972e83..e753ba07ea49c1f772def46cc5f5560e8dcee132 100644 (file)
@@ -79,6 +79,9 @@ struct qemud_driver {
     int nextvmid;
 
     virCgroupPtr cgroup;
+    int cgroupControllers;
+    char **cgroupDeviceACL;
+
     virDomainObjList domains;
 
     brControl *brctl;
index 9a181c515486230219ce185b6a5b5d8b8a8adf84..ac0b80fb2f969424c80308f87218d10b2e053c2d 100644 (file)
@@ -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;