#include "qemu/notify.h"
#include "qemu-thread-common.h"
#include "qemu/tsan.h"
+#include "qemu/bitmap.h"
+
+#ifdef CONFIG_PTHREAD_SET_NAME_NP
+#include <pthread_np.h>
+#endif
static bool name_threads;
name_threads = enable;
#if !defined CONFIG_PTHREAD_SETNAME_NP_W_TID && \
- !defined CONFIG_PTHREAD_SETNAME_NP_WO_TID
+ !defined CONFIG_PTHREAD_SETNAME_NP_WO_TID && \
+ !defined CONFIG_PTHREAD_SET_NAME_NP
/* This is a debugging option, not fatal */
if (enable) {
fprintf(stderr, "qemu: thread naming not supported on this host\n");
error_exit(err, __func__);
}
-bool qemu_cond_timedwait_impl(QemuCond *cond, QemuMutex *mutex, int ms,
- const char *file, const int line)
+static bool TSA_NO_TSA
+qemu_cond_timedwait_ts(QemuCond *cond, QemuMutex *mutex, struct timespec *ts,
+ const char *file, const int line)
{
int err;
- struct timespec ts;
assert(cond->initialized);
trace_qemu_mutex_unlock(mutex, file, line);
- compute_abs_deadline(&ts, ms);
- err = pthread_cond_timedwait(&cond->cond, &mutex->lock, &ts);
+ err = pthread_cond_timedwait(&cond->cond, &mutex->lock, ts);
trace_qemu_mutex_locked(mutex, file, line);
if (err && err != ETIMEDOUT) {
error_exit(err, __func__);
return err != ETIMEDOUT;
}
+bool qemu_cond_timedwait_impl(QemuCond *cond, QemuMutex *mutex, int ms,
+ const char *file, const int line)
+{
+ struct timespec ts;
+
+ compute_abs_deadline(&ts, ms);
+ return qemu_cond_timedwait_ts(cond, mutex, &ts, file, line);
+}
+
void qemu_sem_init(QemuSemaphore *sem, int init)
{
- pthread_condattr_t attr;
- int rc;
+ qemu_mutex_init(&sem->mutex);
+ qemu_cond_init(&sem->cond);
- rc = pthread_mutex_init(&sem->lock, NULL);
- if (rc != 0) {
- error_exit(rc, __func__);
- }
- rc = pthread_condattr_init(&attr);
- if (rc != 0) {
- error_exit(rc, __func__);
- }
-#ifdef CONFIG_PTHREAD_CONDATTR_SETCLOCK
- rc = pthread_condattr_setclock(&attr, qemu_timedwait_clockid());
- if (rc != 0) {
- error_exit(rc, __func__);
- }
-#endif
- rc = pthread_cond_init(&sem->cond, &attr);
- if (rc != 0) {
- error_exit(rc, __func__);
- }
- rc = pthread_condattr_destroy(&attr);
- if (rc < 0) {
- error_exit(rc, __func__);
- }
if (init < 0) {
error_exit(EINVAL, __func__);
}
sem->count = init;
- sem->initialized = true;
}
void qemu_sem_destroy(QemuSemaphore *sem)
{
- int rc;
-
- assert(sem->initialized);
- sem->initialized = false;
- rc = pthread_cond_destroy(&sem->cond);
- if (rc < 0) {
- error_exit(rc, __func__);
- }
- rc = pthread_mutex_destroy(&sem->lock);
- if (rc < 0) {
- error_exit(rc, __func__);
- }
+ qemu_cond_destroy(&sem->cond);
+ qemu_mutex_destroy(&sem->mutex);
}
void qemu_sem_post(QemuSemaphore *sem)
{
- int rc;
-
- assert(sem->initialized);
- pthread_mutex_lock(&sem->lock);
+ qemu_mutex_lock(&sem->mutex);
if (sem->count == UINT_MAX) {
- rc = EINVAL;
+ error_exit(EINVAL, __func__);
} else {
sem->count++;
- rc = pthread_cond_signal(&sem->cond);
- }
- pthread_mutex_unlock(&sem->lock);
- if (rc != 0) {
- error_exit(rc, __func__);
+ qemu_cond_signal(&sem->cond);
}
+ qemu_mutex_unlock(&sem->mutex);
}
int qemu_sem_timedwait(QemuSemaphore *sem, int ms)
{
- int rc;
+ bool rc = true;
struct timespec ts;
- assert(sem->initialized);
- rc = 0;
compute_abs_deadline(&ts, ms);
- pthread_mutex_lock(&sem->lock);
+ qemu_mutex_lock(&sem->mutex);
while (sem->count == 0) {
- rc = pthread_cond_timedwait(&sem->cond, &sem->lock, &ts);
- if (rc == ETIMEDOUT) {
- break;
+ if (ms == 0) {
+ rc = false;
+ } else {
+ rc = qemu_cond_timedwait_ts(&sem->cond, &sem->mutex, &ts,
+ __FILE__, __LINE__);
}
- if (rc != 0) {
- error_exit(rc, __func__);
+ if (!rc) { /* timeout */
+ break;
}
}
- if (rc != ETIMEDOUT) {
+ if (rc) {
--sem->count;
}
- pthread_mutex_unlock(&sem->lock);
- return (rc == ETIMEDOUT ? -1 : 0);
+ qemu_mutex_unlock(&sem->mutex);
+ return (rc ? 0 : -1);
}
void qemu_sem_wait(QemuSemaphore *sem)
{
- int rc;
-
- assert(sem->initialized);
- pthread_mutex_lock(&sem->lock);
+ qemu_mutex_lock(&sem->mutex);
while (sem->count == 0) {
- rc = pthread_cond_wait(&sem->cond, &sem->lock);
- if (rc != 0) {
- error_exit(rc, __func__);
- }
+ qemu_cond_wait(&sem->cond, &sem->mutex);
}
--sem->count;
- pthread_mutex_unlock(&sem->lock);
+ qemu_mutex_unlock(&sem->mutex);
}
#ifdef __linux__
void qemu_event_set(QemuEvent *ev)
{
- /* qemu_event_set has release semantics, but because it *loads*
+ assert(ev->initialized);
+
+ /*
+ * Pairs with both qemu_event_reset() and qemu_event_wait().
+ *
+ * qemu_event_set has release semantics, but because it *loads*
* ev->value we need a full memory barrier here.
*/
- assert(ev->initialized);
smp_mb();
if (qatomic_read(&ev->value) != EV_SET) {
- if (qatomic_xchg(&ev->value, EV_SET) == EV_BUSY) {
+ int old = qatomic_xchg(&ev->value, EV_SET);
+
+ /* Pairs with memory barrier in kernel futex_wait system call. */
+ smp_mb__after_rmw();
+ if (old == EV_BUSY) {
/* There were waiters, wake them up. */
qemu_futex_wake(ev, INT_MAX);
}
void qemu_event_reset(QemuEvent *ev)
{
- unsigned value;
-
assert(ev->initialized);
- value = qatomic_read(&ev->value);
- smp_mb_acquire();
- if (value == EV_SET) {
- /*
- * If there was a concurrent reset (or even reset+wait),
- * do nothing. Otherwise change EV_SET->EV_FREE.
- */
- qatomic_or(&ev->value, EV_FREE);
- }
+
+ /*
+ * If there was a concurrent reset (or even reset+wait),
+ * do nothing. Otherwise change EV_SET->EV_FREE.
+ */
+ qatomic_or(&ev->value, EV_FREE);
+
+ /*
+ * Order reset before checking the condition in the caller.
+ * Pairs with the first memory barrier in qemu_event_set().
+ */
+ smp_mb__after_rmw();
}
void qemu_event_wait(QemuEvent *ev)
unsigned value;
assert(ev->initialized);
- value = qatomic_read(&ev->value);
- smp_mb_acquire();
+
+ /*
+ * qemu_event_wait must synchronize with qemu_event_set even if it does
+ * not go down the slow path, so this load-acquire is needed that
+ * synchronizes with the first memory barrier in qemu_event_set().
+ *
+ * If we do go down the slow path, there is no requirement at all: we
+ * might miss a qemu_event_set() here but ultimately the memory barrier in
+ * qemu_futex_wait() will ensure the check is done correctly.
+ */
+ value = qatomic_load_acquire(&ev->value);
if (value != EV_SET) {
if (value == EV_FREE) {
/*
- * Leave the event reset and tell qemu_event_set that there
- * are waiters. No need to retry, because there cannot be
- * a concurrent busy->free transition. After the CAS, the
- * event will be either set or busy.
+ * Leave the event reset and tell qemu_event_set that there are
+ * waiters. No need to retry, because there cannot be a concurrent
+ * busy->free transition. After the CAS, the event will be either
+ * set or busy.
+ *
+ * This cmpxchg doesn't have particular ordering requirements if it
+ * succeeds (moving the store earlier can only cause qemu_event_set()
+ * to issue _more_ wakeups), the failing case needs acquire semantics
+ * like the load above.
*/
if (qatomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) {
return;
}
}
+
+ /*
+ * This is the final check for a concurrent set, so it does need
+ * a smp_mb() pairing with the second barrier of qemu_event_set().
+ * The barrier is inside the FUTEX_WAIT system call.
+ */
qemu_futex_wait(ev, EV_BUSY);
}
}
pthread_setname_np(pthread_self(), qemu_thread_args->name);
# elif defined(CONFIG_PTHREAD_SETNAME_NP_WO_TID)
pthread_setname_np(qemu_thread_args->name);
+# elif defined(CONFIG_PTHREAD_SET_NAME_NP)
+ pthread_set_name_np(pthread_self(), qemu_thread_args->name);
# endif
}
QEMU_TSAN_ANNOTATE_THREAD_NAME(qemu_thread_args->name);
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)
{
thread->thread = pthread_self();