Add the external swtpm to the emulator cgroup so that upper limits of CPU
usage can be enforced on the emulated TPM.
To enable this we need to have the swtpm write its process id (pid) into a
file. We then read it from the file to configure the emulator cgroup.
The PID file is created in /var/run/libvirt/qemu/swtpm:
[root@localhost swtpm]# ls -lZ /var/run/libvirt/qemu/swtpm/
total 4
-rw-r--r--. 1 tss tss system_u:object_r:qemu_var_run_t:s0 5 Apr 10 12:26
1-testvm-swtpm.pid
srw-rw----. 1 qemu qemu system_u:object_r:svirt_image_t:s0:c597,c632 0 Apr 10 12:26
1-testvm-swtpm.sock
The swtpm command line now looks as follows:
root@localhost testvm]# ps auxZ | grep swtpm | grep socket | grep -v grep
system_u:system_r:virtd_t:s0:c597,c632 tss 18697 0.0 0.0 28172 3892 ? Ss 16:46
0:00 /usr/bin/swtpm socket --daemon --ctrl
type=unixio,path=/var/run/libvirt/qemu/swtpm/1-testvm-swtpm.sock,mode=0600 --tpmstate
dir=/var/lib/libvirt/swtpm/485d0004-a48f-436a-8457-8a3b73e28568/tpm1.2/ --log
file=/var/log/swtpm/libvirt/qemu/testvm-swtpm.log --pid
file=/var/run/libvirt/qemu/swtpm/1-testvm-swtpm.pid
Signed-off-by: Stefan Berger <stefanb(a)linux.vnet.ibm.com>
---
src/conf/domain_conf.c | 1 +
src/conf/domain_conf.h | 1 +
src/qemu/qemu_cgroup.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_cgroup.h | 1 +
src/qemu/qemu_extdevice.c | 19 +++++++++++++++++
src/qemu/qemu_process.c | 4 ++++
src/util/virtpm.c | 33 +++++++++++++++++++++++++++++
7 files changed, 112 insertions(+)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index c98d26a..f542c8e 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2624,6 +2624,7 @@ void virDomainTPMDefFree(virDomainTPMDefPtr def)
VIR_FREE(def->data.emulator.source.data.nix.path);
VIR_FREE(def->data.emulator.storagepath);
VIR_FREE(def->data.emulator.logfile);
+ VIR_FREE(def->data.emulator.pidfile);
break;
case VIR_DOMAIN_TPM_TYPE_LAST:
break;
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 826ff26..49c77f7 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1311,6 +1311,7 @@ struct _virDomainTPMDef {
virDomainChrSourceDef source;
char *storagepath;
char *logfile;
+ char *pidfile;
} emulator;
} data;
};
diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c
index 1a5adca..f86ac3f 100644
--- a/src/qemu/qemu_cgroup.c
+++ b/src/qemu/qemu_cgroup.c
@@ -38,6 +38,7 @@
#include "virnuma.h"
#include "virsystemd.h"
#include "virdevmapper.h"
+#include "virpidfile.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
@@ -1146,6 +1147,58 @@ qemuSetupCgroupCpusetCpus(virCgroupPtr cgroup,
int
+qemuSetupCgroupForExtDevices(virDomainObjPtr vm)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ virDomainTPMDefPtr tpm = vm->def->tpm;
+ virCgroupPtr cgroup_temp = NULL;
+ pid_t pid;
+ int ret = -1;
+
+ if (priv->cgroup == NULL)
+ return 0; /* Not supported, so claim success */
+
+ /*
+ * If CPU cgroup controller is not initialized here, then we need
+ * neither period nor quota settings. And if CPUSET controller is
+ * not initialized either, then there's nothing to do anyway.
+ */
+ if (!virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_CPU) &&
+ !virCgroupHasController(priv->cgroup, VIR_CGROUP_CONTROLLER_CPUSET))
+ return 0;
+
+ if (virCgroupNewThread(priv->cgroup, VIR_CGROUP_THREAD_EMULATOR, 0,
+ false, &cgroup_temp) < 0)
+ goto cleanup;
+
+ if (tpm) {
+ switch (tpm->type) {
+ case VIR_DOMAIN_TPM_TYPE_EMULATOR:
+ if (virPidFileReadPath(tpm->data.emulator.pidfile, &pid) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Could not read swtpm's pidfile %s"),
+ tpm->data.emulator.pidfile);
+ goto cleanup;
+ }
+ if (virCgroupAddTask(cgroup_temp, pid) < 0)
+ goto cleanup;
+ break;
+ case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
+ case VIR_DOMAIN_TPM_TYPE_LAST:
+ break;
+ }
+ }
+
+ ret = 0;
+
+cleanup:
+ virCgroupFree(&cgroup_temp);
+
+ return ret;
+}
+
+
+int
qemuSetupGlobalCpuCgroup(virDomainObjPtr vm)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
diff --git a/src/qemu/qemu_cgroup.h b/src/qemu/qemu_cgroup.h
index 3b8ff60..478bf7e 100644
--- a/src/qemu/qemu_cgroup.h
+++ b/src/qemu/qemu_cgroup.h
@@ -69,6 +69,7 @@ int qemuSetupCgroupVcpuBW(virCgroupPtr cgroup,
long long quota);
int qemuSetupCgroupCpusetCpus(virCgroupPtr cgroup, virBitmapPtr cpumask);
int qemuSetupGlobalCpuCgroup(virDomainObjPtr vm);
+int qemuSetupCgroupForExtDevices(virDomainObjPtr vm);
int qemuRemoveCgroup(virDomainObjPtr vm);
typedef struct _qemuCgroupEmulatorAllNodesData qemuCgroupEmulatorAllNodesData;
diff --git a/src/qemu/qemu_extdevice.c b/src/qemu/qemu_extdevice.c
index eb7220d..779e759 100644
--- a/src/qemu/qemu_extdevice.c
+++ b/src/qemu/qemu_extdevice.c
@@ -148,6 +148,9 @@ qemuExtTPMStartEmulator(virQEMUDriverPtr driver,
virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
virDomainTPMDefPtr tpm = def->tpm;
char *shortName = virDomainDefGetShortName(def);
+ char *pidfiledata = NULL;
+ int timeout;
+ int len;
if (!shortName)
return -1;
@@ -195,6 +198,22 @@ qemuExtTPMStartEmulator(virQEMUDriverPtr driver,
goto error;
}
+ /* check that the swtpm has written its pid into the file */
+ timeout = 1000; /* ms */
+ while ((len = virFileReadHeaderQuiet(tpm->data.emulator.pidfile,
+ 10, &pidfiledata)) <= 0) {
+ if (len == 0 && timeout > 0) {
+ timeout -= 50;
+ usleep(50 * 1000);
+ continue;
+ }
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("swtpm did not write pidfile '%s'"),
+ tpm->data.emulator.pidfile);
+ goto error;
+ }
+ VIR_FREE(pidfiledata);
+
ret = 0;
cleanup:
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 2b07530..bf0e4da 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -6076,6 +6076,10 @@ qemuProcessLaunch(virConnectPtr conn,
if (qemuProcessSetupEmulator(vm) < 0)
goto cleanup;
+ VIR_DEBUG("Setting cgroup for external devices (if required)");
+ if (qemuSetupCgroupForExtDevices(vm) < 0)
+ goto cleanup;
+
VIR_DEBUG("Setting up resctrl");
if (qemuProcessResctrlCreate(driver, vm) < 0)
goto cleanup;
diff --git a/src/util/virtpm.c b/src/util/virtpm.c
index 0617326..3d8a195 100644
--- a/src/util/virtpm.c
+++ b/src/util/virtpm.c
@@ -39,6 +39,7 @@
#include "virlog.h"
#include "virtpm.h"
#include "virutil.h"
+#include "virpidfile.h"
#include "configmake.h"
#define VIR_FROM_THIS VIR_FROM_NONE
@@ -376,6 +377,25 @@ int virTPMEmulatorInitPaths(virDomainTPMDefPtr tpm,
}
/*
+ * virTPMCreatePidFilename
+ */
+static char *virTPMCreatePidFilename(const char *swtpmStateDir,
+ const char *shortName)
+{
+ char *pidfile = NULL;
+ char *devname = NULL;
+
+ if (virAsprintf(&devname, "%s-swtpm", shortName) < 0)
+ return NULL;
+
+ pidfile = virPidFileBuildPath(swtpmStateDir, devname);
+
+ VIR_FREE(devname);
+
+ return pidfile;
+}
+
+/*
* virTPMEmulatorPrepareHost:
*
* @tpm: tpm definition
@@ -442,6 +462,10 @@ int virTPMEmulatorPrepareHost(virDomainTPMDefPtr tpm,
goto cleanup;
tpm->data.emulator.source.type = VIR_DOMAIN_CHR_TYPE_UNIX;
+ if (!(tpm->data.emulator.pidfile =
+ virTPMCreatePidFilename(swtpmStateDir, shortName)))
+ goto cleanup;
+
ret = 0;
cleanup:
@@ -619,6 +643,9 @@ virTPMEmulatorBuildCommand(virDomainTPMDefPtr tpm, const char
*vmname,
break;
}
+ virCommandAddArg(cmd, "--pid");
+ virCommandAddArgFormat(cmd, "file=%s", tpm->data.emulator.pidfile);
+
return cmd;
error:
@@ -646,6 +673,7 @@ virTPMEmulatorStop(const char *swtpmStateDir, const char *shortName)
virCommandPtr cmd;
char *pathname;
char *errbuf = NULL;
+ char *pidfile;
if (virTPMEmulatorInit() < 0)
return;
@@ -674,6 +702,11 @@ virTPMEmulatorStop(const char *swtpmStateDir, const char *shortName)
unlink(pathname);
cleanup:
+ /* clean up the PID file */
+ if ((pidfile = virTPMCreatePidFilename(swtpmStateDir, shortName))) {
+ unlink(pidfile);
+ VIR_FREE(pidfile);
+ }
VIR_FREE(pathname);
VIR_FREE(errbuf);
}
--
2.5.5