Path: blob/master/tools/testing/selftests/futex/functional/futex_numa.c
29271 views
// SPDX-License-Identifier: GPL-2.012#include <pthread.h>3#include <sys/shm.h>4#include <sys/mman.h>5#include <fcntl.h>6#include <stdbool.h>7#include <stdio.h>8#include <stdlib.h>9#include <time.h>10#include <assert.h>11#include "futextest.h"12#include "futex2test.h"1314typedef u_int32_t u32;15typedef int32_t s32;16typedef u_int64_t u64;1718static unsigned int fflags = (FUTEX2_SIZE_U32 | FUTEX2_PRIVATE);19static int fnode = FUTEX_NO_NODE;2021/* fairly stupid test-and-set lock with a waiter flag */2223#define N_LOCK 0x000000124#define N_WAITERS 0x00010002526struct futex_numa_32 {27union {28u64 full;29struct {30u32 val;31u32 node;32};33};34};3536void futex_numa_32_lock(struct futex_numa_32 *lock)37{38for (;;) {39struct futex_numa_32 new, old = {40.full = __atomic_load_n(&lock->full, __ATOMIC_RELAXED),41};4243for (;;) {44new = old;45if (old.val == 0) {46/* no waiter, no lock -> first lock, set no-node */47new.node = fnode;48}49if (old.val & N_LOCK) {50/* contention, set waiter */51new.val |= N_WAITERS;52}53new.val |= N_LOCK;5455/* nothing changed, ready to block */56if (old.full == new.full)57break;5859/*60* Use u64 cmpxchg to set the futex value and node in a61* consistent manner.62*/63if (__atomic_compare_exchange_n(&lock->full,64&old.full, new.full,65/* .weak */ false,66__ATOMIC_ACQUIRE,67__ATOMIC_RELAXED)) {6869/* if we just set N_LOCK, we own it */70if (!(old.val & N_LOCK))71return;7273/* go block */74break;75}76}7778futex2_wait(lock, new.val, fflags, NULL, 0);79}80}8182void futex_numa_32_unlock(struct futex_numa_32 *lock)83{84u32 val = __atomic_sub_fetch(&lock->val, N_LOCK, __ATOMIC_RELEASE);85assert((s32)val >= 0);86if (val & N_WAITERS) {87int woken = futex2_wake(lock, 1, fflags);88assert(val == N_WAITERS);89if (!woken) {90__atomic_compare_exchange_n(&lock->val, &val, 0U,91false, __ATOMIC_RELAXED,92__ATOMIC_RELAXED);93}94}95}9697static long nanos = 50000;9899struct thread_args {100pthread_t tid;101volatile int * done;102struct futex_numa_32 *lock;103int val;104int *val1, *val2;105int node;106};107108static void *threadfn(void *_arg)109{110struct thread_args *args = _arg;111struct timespec ts = {112.tv_nsec = nanos,113};114int node;115116while (!*args->done) {117118futex_numa_32_lock(args->lock);119args->val++;120121assert(*args->val1 == *args->val2);122(*args->val1)++;123nanosleep(&ts, NULL);124(*args->val2)++;125126node = args->lock->node;127futex_numa_32_unlock(args->lock);128129if (node != args->node) {130args->node = node;131printf("node: %d\n", node);132}133134nanosleep(&ts, NULL);135}136137return NULL;138}139140static void *contendfn(void *_arg)141{142struct thread_args *args = _arg;143144while (!*args->done) {145/*146* futex2_wait() will take hb-lock, verify *var == val and147* queue/abort. By knowingly setting val 'wrong' this will148* abort and thereby generate hb-lock contention.149*/150futex2_wait(&args->lock->val, ~0U, fflags, NULL, 0);151args->val++;152}153154return NULL;155}156157static volatile int done = 0;158static struct futex_numa_32 lock = { .val = 0, };159static int val1, val2;160161int main(int argc, char *argv[])162{163struct thread_args *tas[512], *cas[512];164int c, t, threads = 2, contenders = 0;165int sleeps = 10;166int total = 0;167168while ((c = getopt(argc, argv, "c:t:s:n:N::")) != -1) {169switch (c) {170case 'c':171contenders = atoi(optarg);172break;173case 't':174threads = atoi(optarg);175break;176case 's':177sleeps = atoi(optarg);178break;179case 'n':180nanos = atoi(optarg);181break;182case 'N':183fflags |= FUTEX2_NUMA;184if (optarg)185fnode = atoi(optarg);186break;187default:188exit(1);189break;190}191}192193for (t = 0; t < contenders; t++) {194struct thread_args *args = calloc(1, sizeof(*args));195if (!args) {196perror("thread_args");197exit(-1);198}199200args->done = &done;201args->lock = &lock;202args->val1 = &val1;203args->val2 = &val2;204args->node = -1;205206if (pthread_create(&args->tid, NULL, contendfn, args)) {207perror("pthread_create");208exit(-1);209}210211cas[t] = args;212}213214for (t = 0; t < threads; t++) {215struct thread_args *args = calloc(1, sizeof(*args));216if (!args) {217perror("thread_args");218exit(-1);219}220221args->done = &done;222args->lock = &lock;223args->val1 = &val1;224args->val2 = &val2;225args->node = -1;226227if (pthread_create(&args->tid, NULL, threadfn, args)) {228perror("pthread_create");229exit(-1);230}231232tas[t] = args;233}234235sleep(sleeps);236237done = true;238239for (t = 0; t < threads; t++) {240struct thread_args *args = tas[t];241242pthread_join(args->tid, NULL);243total += args->val;244// printf("tval: %d\n", args->val);245}246printf("total: %d\n", total);247248if (contenders) {249total = 0;250for (t = 0; t < contenders; t++) {251struct thread_args *args = cas[t];252253pthread_join(args->tid, NULL);254total += args->val;255// printf("tval: %d\n", args->val);256}257printf("contenders: %d\n", total);258}259260return 0;261}262263264265