util: Introduce qemu_thread_set_affinity() and qemu_thread_get_affinity()

Usually, we let upper layers handle CPU pinning, because
pthread_setaffinity_np() (-> sched_setaffinity()) is blocked via
seccomp when starting QEMU with
    -sandbox enable=on,resourcecontrol=deny

However, we want to configure and observe the CPU affinity of threads
from QEMU directly in some cases when the sandbox option is either not
enabled or not active yet.

So let's add a way to configure CPU pinning via
qemu_thread_set_affinity() and obtain CPU affinity via
qemu_thread_get_affinity() and implement them under POSIX using
pthread_setaffinity_np() + pthread_getaffinity_np().

Implementation under Windows is possible using SetProcessAffinityMask()
+ GetProcessAffinityMask(), however, that is left as future work.

Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Message-Id: <20221014134720.168738-3-david@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
This commit is contained in:
David Hildenbrand 2022-10-14 15:47:15 +02:00
parent 6556aadc18
commit 7730f32c28
4 changed files with 102 additions and 0 deletions

View File

@ -185,6 +185,10 @@ void qemu_event_destroy(QemuEvent *ev);
void qemu_thread_create(QemuThread *thread, const char *name, void qemu_thread_create(QemuThread *thread, const char *name,
void *(*start_routine)(void *), void *(*start_routine)(void *),
void *arg, int mode); void *arg, int mode);
int qemu_thread_set_affinity(QemuThread *thread, unsigned long *host_cpus,
unsigned long nbits);
int qemu_thread_get_affinity(QemuThread *thread, unsigned long **host_cpus,
unsigned long *nbits);
void *qemu_thread_join(QemuThread *thread); void *qemu_thread_join(QemuThread *thread);
void qemu_thread_get_self(QemuThread *thread); void qemu_thread_get_self(QemuThread *thread);
bool qemu_thread_is_self(QemuThread *thread); bool qemu_thread_is_self(QemuThread *thread);

View File

@ -2114,7 +2114,23 @@ config_host_data.set('CONFIG_PTHREAD_CONDATTR_SETCLOCK', cc.links(gnu_source_pre
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
return 0; return 0;
}''', dependencies: threads)) }''', dependencies: threads))
config_host_data.set('CONFIG_PTHREAD_AFFINITY_NP', cc.links(gnu_source_prefix + '''
#include <pthread.h>
static void *f(void *p) { return NULL; }
int main(void)
{
int setsize = CPU_ALLOC_SIZE(64);
pthread_t thread;
cpu_set_t *cpuset;
pthread_create(&thread, 0, f, 0);
cpuset = CPU_ALLOC(64);
CPU_ZERO_S(setsize, cpuset);
pthread_setaffinity_np(thread, setsize, cpuset);
pthread_getaffinity_np(thread, setsize, cpuset);
CPU_FREE(cpuset);
return 0;
}''', dependencies: threads))
config_host_data.set('CONFIG_SIGNALFD', cc.links(gnu_source_prefix + ''' config_host_data.set('CONFIG_SIGNALFD', cc.links(gnu_source_prefix + '''
#include <sys/signalfd.h> #include <sys/signalfd.h>
#include <stddef.h> #include <stddef.h>

View File

@ -16,6 +16,7 @@
#include "qemu/notify.h" #include "qemu/notify.h"
#include "qemu-thread-common.h" #include "qemu-thread-common.h"
#include "qemu/tsan.h" #include "qemu/tsan.h"
#include "qemu/bitmap.h"
static bool name_threads; static bool name_threads;
@ -552,6 +553,75 @@ void qemu_thread_create(QemuThread *thread, const char *name,
pthread_attr_destroy(&attr); pthread_attr_destroy(&attr);
} }
int qemu_thread_set_affinity(QemuThread *thread, unsigned long *host_cpus,
unsigned long nbits)
{
#if defined(CONFIG_PTHREAD_AFFINITY_NP)
const size_t setsize = CPU_ALLOC_SIZE(nbits);
unsigned long value;
cpu_set_t *cpuset;
int err;
cpuset = CPU_ALLOC(nbits);
g_assert(cpuset);
CPU_ZERO_S(setsize, cpuset);
value = find_first_bit(host_cpus, nbits);
while (value < nbits) {
CPU_SET_S(value, setsize, cpuset);
value = find_next_bit(host_cpus, nbits, value + 1);
}
err = pthread_setaffinity_np(thread->thread, setsize, cpuset);
CPU_FREE(cpuset);
return err;
#else
return -ENOSYS;
#endif
}
int qemu_thread_get_affinity(QemuThread *thread, unsigned long **host_cpus,
unsigned long *nbits)
{
#if defined(CONFIG_PTHREAD_AFFINITY_NP)
unsigned long tmpbits;
cpu_set_t *cpuset;
size_t setsize;
int i, err;
tmpbits = CPU_SETSIZE;
while (true) {
setsize = CPU_ALLOC_SIZE(tmpbits);
cpuset = CPU_ALLOC(tmpbits);
g_assert(cpuset);
err = pthread_getaffinity_np(thread->thread, setsize, cpuset);
if (err) {
CPU_FREE(cpuset);
if (err != -EINVAL) {
return err;
}
tmpbits *= 2;
} else {
break;
}
}
/* Convert the result into a proper bitmap. */
*nbits = tmpbits;
*host_cpus = bitmap_new(tmpbits);
for (i = 0; i < tmpbits; i++) {
if (CPU_ISSET(i, cpuset)) {
set_bit(i, *host_cpus);
}
}
CPU_FREE(cpuset);
return 0;
#else
return -ENOSYS;
#endif
}
void qemu_thread_get_self(QemuThread *thread) void qemu_thread_get_self(QemuThread *thread)
{ {
thread->thread = pthread_self(); thread->thread = pthread_self();

View File

@ -477,6 +477,18 @@ void qemu_thread_create(QemuThread *thread, const char *name,
thread->data = data; thread->data = data;
} }
int qemu_thread_set_affinity(QemuThread *thread, unsigned long *host_cpus,
unsigned long nbits)
{
return -ENOSYS;
}
int qemu_thread_get_affinity(QemuThread *thread, unsigned long **host_cpus,
unsigned long *nbits)
{
return -ENOSYS;
}
void qemu_thread_get_self(QemuThread *thread) void qemu_thread_get_self(QemuThread *thread)
{ {
thread->data = qemu_thread_data; thread->data = qemu_thread_data;