virprocess: Core Scheduling support

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@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
This commit is contained in:
Michal Privoznik 2022-04-26 14:05:27 +02:00
parent 060d4c83ef
commit c935cead2d
3 changed files with 136 additions and 0 deletions

View File

@ -3152,6 +3152,10 @@ virProcessKillPainfullyDelay;
virProcessNamespaceAvailable;
virProcessRunInFork;
virProcessRunInMountNamespace;
virProcessSchedCoreAvailable;
virProcessSchedCoreCreate;
virProcessSchedCoreShareFrom;
virProcessSchedCoreShareTo;
virProcessSchedPolicyTypeFromString;
virProcessSchedPolicyTypeToString;
virProcessSetAffinity;

View File

@ -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__ */

View File

@ -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);