Since its 5.14 release the Linux kernel allows userspace to
define trusted groups of processes/threads that can run on
sibling Hyper Threads (HT) at the same time. This is to mitigate
side channel attacks like L1TF or MDS. If there are no tasks to
fully utilize all HTs, then a HT will idle instead of running a
task from another (un-)trusted group.
On low level, this is implemented by cookies (effectively an UL
value): processes in the same trusted group share the same cookie
and cookie is unique to the group. There are four basic
operations:
1) PR_SCHED_CORE_GET -- get cookie of given PID,
2) PR_SCHED_CORE_CREATE -- create a new unique cookie for PID,
3) PR_SCHED_CORE_SHARE_TO -- push cookie of the caller onto
another PID,
4) PR_SCHED_CORE_SHARE_FROM -- pull cookie of another PID into
the caller.
Since a system where the code is built can be different to the
one where the code is ran let's provide declaration of some
values. It's not unusual for distros to ship older linux-headers
than the actual kernel.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange(a)redhat.com>
---
src/libvirt_private.syms | 4 ++
src/util/virprocess.c | 124 +++++++++++++++++++++++++++++++++++++++
src/util/virprocess.h | 8 +++
3 files changed, 136 insertions(+)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 5be40dbefe..28a2f9ac2e 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -3164,6 +3164,10 @@ virProcessKillPainfullyDelay;
virProcessNamespaceAvailable;
virProcessRunInFork;
virProcessRunInMountNamespace;
+virProcessSchedCoreAvailable;
+virProcessSchedCoreCreate;
+virProcessSchedCoreShareFrom;
+virProcessSchedCoreShareTo;
virProcessSchedPolicyTypeFromString;
virProcessSchedPolicyTypeToString;
virProcessSetAffinity;
diff --git a/src/util/virprocess.c b/src/util/virprocess.c
index 11f36e00a8..39ca5de811 100644
--- a/src/util/virprocess.c
+++ b/src/util/virprocess.c
@@ -56,6 +56,10 @@
# include <windows.h>
#endif
+#ifdef __linux__
+# include <sys/prctl.h>
+#endif
+
#include "virprocess.h"
#include "virerror.h"
#include "viralloc.h"
@@ -1885,3 +1889,123 @@ virProcessGetSchedInfo(unsigned long long *cpuWait,
return 0;
}
#endif /* __linux__ */
+
+#ifdef __linux__
+# ifndef PR_SCHED_CORE
+/* Copied from linux/prctl.h */
+# define PR_SCHED_CORE 62
+# define PR_SCHED_CORE_GET 0
+# define PR_SCHED_CORE_CREATE 1 /* create unique core_sched cookie */
+# define PR_SCHED_CORE_SHARE_TO 2 /* push core_sched cookie to pid */
+# define PR_SCHED_CORE_SHARE_FROM 3 /* pull core_sched cookie to pid */
+# endif
+
+/* Unfortunately, kernel-headers forgot to export these. */
+# ifndef PR_SCHED_CORE_SCOPE_THREAD
+# define PR_SCHED_CORE_SCOPE_THREAD 0
+# define PR_SCHED_CORE_SCOPE_THREAD_GROUP 1
+# define PR_SCHED_CORE_SCOPE_PROCESS_GROUP 2
+# endif
+
+/**
+ * virProcessSchedCoreAvailable:
+ *
+ * Check whether kernel supports Core Scheduling (CONFIG_SCHED_CORE), i.e. only
+ * a defined set of PIDs/TIDs can run on sibling Hyper Threads at the same
+ * time.
+ *
+ * Returns: 1 if Core Scheduling is available,
+ * 0 if Core Scheduling is NOT available,
+ * -1 otherwise.
+ */
+int
+virProcessSchedCoreAvailable(void)
+{
+ unsigned long cookie = 0;
+ int rc;
+
+ /* Let's just see if we can get our own sched cookie, and if yes we can
+ * safely assume CONFIG_SCHED_CORE kernel is available. */
+ rc = prctl(PR_SCHED_CORE, PR_SCHED_CORE_GET, 0,
+ PR_SCHED_CORE_SCOPE_THREAD, &cookie);
+
+ return rc == 0 ? 1 : errno == EINVAL ? 0 : -1;
+}
+
+/**
+ * virProcessSchedCoreCreate:
+ *
+ * Creates a new trusted group for the caller process.
+ *
+ * Returns: 0 on success,
+ * -1 otherwise, with errno set.
+ */
+int
+virProcessSchedCoreCreate(void)
+{
+ /* pid = 0 (3rd argument) means the calling process. */
+ return prctl(PR_SCHED_CORE, PR_SCHED_CORE_CREATE, 0,
+ PR_SCHED_CORE_SCOPE_THREAD_GROUP, 0);
+}
+
+/**
+ * virProcessSchedCoreShareFrom:
+ * @pid: PID to share group with
+ *
+ * Places the current caller process into the trusted group of @pid.
+ *
+ * Returns: 0 on success,
+ * -1 otherwise, with errno set.
+ */
+int
+virProcessSchedCoreShareFrom(pid_t pid)
+{
+ return prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_FROM, pid,
+ PR_SCHED_CORE_SCOPE_THREAD, 0);
+}
+
+/**
+ * virProcessSchedCoreShareTo:
+ * @pid: PID to share group with
+ *
+ * Places foreign @pid into the trusted group of the current caller process.
+ *
+ * Returns: 0 on success,
+ * -1 otherwise, with errno set.
+ */
+int
+virProcessSchedCoreShareTo(pid_t pid)
+{
+ return prctl(PR_SCHED_CORE, PR_SCHED_CORE_SHARE_TO, pid,
+ PR_SCHED_CORE_SCOPE_THREAD, 0);
+}
+
+#else /* !__linux__ */
+
+int
+virProcessSchedCoreAvailable(void)
+{
+ return 0;
+}
+
+int
+virProcessSchedCoreCreate(void)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int
+virProcessSchedCoreShareFrom(pid_t pid G_GNUC_UNUSED)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+int
+virProcessSchedCoreShareTo(pid_t pid G_GNUC_UNUSED)
+{
+ errno = ENOSYS;
+ return -1;
+}
+#endif /* !__linux__ */
diff --git a/src/util/virprocess.h b/src/util/virprocess.h
index 91ad5618db..4e21678838 100644
--- a/src/util/virprocess.h
+++ b/src/util/virprocess.h
@@ -204,3 +204,11 @@ int virProcessGetStatInfo(unsigned long long *cpuTime,
int virProcessGetSchedInfo(unsigned long long *cpuWait,
pid_t pid,
pid_t tid);
+
+int virProcessSchedCoreAvailable(void);
+
+int virProcessSchedCoreCreate(void);
+
+int virProcessSchedCoreShareFrom(pid_t pid);
+
+int virProcessSchedCoreShareTo(pid_t pid);
--
2.35.1