linux-stable/tools/testing/selftests/powerpc/dscr/dscr_default_test.c
Benjamin Gray 3067b89ab6 selftests/powerpc/dscr: Improve DSCR explicit random test case
The tests currently have a single writer thread updating the system
DSCR with a 1/1000 chance looped only 100 times. So only around one in
10 runs actually do anything.

* Add multiple threads to the dscr_explicit_random_test case.
* Use a barrier to make all the threads start work as simultaneously as
  possible.
* Use a rwlock and make all threads have a reasonable chance to write to
  the DSCR on each iteration.
  PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP is used to prevent
  writers from starving while all the other threads keep reading.
  Logging the reads/writes shows a decent mix across the whole test.
* Allow all threads a chance to write.
* Make the chance of writing more likely.

Signed-off-by: Benjamin Gray <bgray@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20230406043320.125138-6-bgray@linux.ibm.com
2023-04-20 13:21:45 +10:00

169 lines
4.3 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* POWER Data Stream Control Register (DSCR) default test
*
* This test modifies the system wide default DSCR through
* it's sysfs interface and then verifies that all threads
* see the correct changed DSCR value immediately.
*
* Copyright 2012, Anton Blanchard, IBM Corporation.
* Copyright 2015, Anshuman Khandual, IBM Corporation.
*/
#define _GNU_SOURCE
#include "dscr.h"
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
static void *dscr_default_lockstep_writer(void *arg)
{
sem_t *reader_sem = (sem_t *)arg;
sem_t *writer_sem = (sem_t *)arg + 1;
unsigned long expected_dscr = 0;
for (int i = 0; i < COUNT; i++) {
FAIL_IF_EXIT(sem_wait(writer_sem));
set_default_dscr(expected_dscr);
expected_dscr = (expected_dscr + 1) % DSCR_MAX;
FAIL_IF_EXIT(sem_post(reader_sem));
}
return NULL;
}
int dscr_default_lockstep_test(void)
{
pthread_t writer;
sem_t rw_semaphores[2];
sem_t *reader_sem = &rw_semaphores[0];
sem_t *writer_sem = &rw_semaphores[1];
unsigned long expected_dscr = 0;
SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR));
FAIL_IF(sem_init(reader_sem, 0, 0));
FAIL_IF(sem_init(writer_sem, 0, 1)); /* writer starts first */
FAIL_IF(bind_to_cpu(BIND_CPU_ANY) < 0);
FAIL_IF(pthread_create(&writer, NULL, dscr_default_lockstep_writer, (void *)rw_semaphores));
for (int i = 0; i < COUNT ; i++) {
FAIL_IF(sem_wait(reader_sem));
FAIL_IF(get_dscr() != expected_dscr);
FAIL_IF(get_dscr_usr() != expected_dscr);
expected_dscr = (expected_dscr + 1) % DSCR_MAX;
FAIL_IF(sem_post(writer_sem));
}
FAIL_IF(pthread_join(writer, NULL));
FAIL_IF(sem_destroy(reader_sem));
FAIL_IF(sem_destroy(writer_sem));
return 0;
}
struct random_thread_args {
pthread_t thread_id;
unsigned long *expected_system_dscr;
pthread_rwlock_t *rw_lock;
pthread_barrier_t *barrier;
};
static void *dscr_default_random_thread(void *in)
{
struct random_thread_args *args = (struct random_thread_args *)in;
unsigned long *expected_dscr_p = args->expected_system_dscr;
pthread_rwlock_t *rw_lock = args->rw_lock;
int err;
srand(gettid());
err = pthread_barrier_wait(args->barrier);
FAIL_IF_EXIT(err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD);
for (int i = 0; i < COUNT; i++) {
unsigned long expected_dscr;
unsigned long current_dscr;
unsigned long current_dscr_usr;
FAIL_IF_EXIT(pthread_rwlock_rdlock(rw_lock));
expected_dscr = *expected_dscr_p;
current_dscr = get_dscr();
current_dscr_usr = get_dscr_usr();
FAIL_IF_EXIT(pthread_rwlock_unlock(rw_lock));
FAIL_IF_EXIT(current_dscr != expected_dscr);
FAIL_IF_EXIT(current_dscr_usr != expected_dscr);
if (rand() % 10 == 0) {
unsigned long next_dscr;
FAIL_IF_EXIT(pthread_rwlock_wrlock(rw_lock));
next_dscr = (*expected_dscr_p + 1) % DSCR_MAX;
set_default_dscr(next_dscr);
*expected_dscr_p = next_dscr;
FAIL_IF_EXIT(pthread_rwlock_unlock(rw_lock));
}
}
pthread_exit((void *)0);
}
int dscr_default_random_test(void)
{
struct random_thread_args threads[THREADS];
unsigned long expected_system_dscr = 0;
pthread_rwlockattr_t rwlock_attr;
pthread_rwlock_t rw_lock;
pthread_barrier_t barrier;
SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR));
FAIL_IF(pthread_rwlockattr_setkind_np(&rwlock_attr,
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP));
FAIL_IF(pthread_rwlock_init(&rw_lock, &rwlock_attr));
FAIL_IF(pthread_barrier_init(&barrier, NULL, THREADS));
set_default_dscr(expected_system_dscr);
for (int i = 0; i < THREADS; i++) {
threads[i].expected_system_dscr = &expected_system_dscr;
threads[i].rw_lock = &rw_lock;
threads[i].barrier = &barrier;
FAIL_IF(pthread_create(&threads[i].thread_id, NULL,
dscr_default_random_thread, (void *)&threads[i]));
}
for (int i = 0; i < THREADS; i++)
FAIL_IF(pthread_join(threads[i].thread_id, NULL));
FAIL_IF(pthread_barrier_destroy(&barrier));
FAIL_IF(pthread_rwlock_destroy(&rw_lock));
return 0;
}
int main(int argc, char *argv[])
{
unsigned long orig_dscr_default = 0;
int err = 0;
if (have_hwcap2(PPC_FEATURE2_DSCR))
orig_dscr_default = get_default_dscr();
err |= test_harness(dscr_default_lockstep_test, "dscr_default_lockstep_test");
err |= test_harness(dscr_default_random_test, "dscr_default_random_test");
if (have_hwcap2(PPC_FEATURE2_DSCR))
set_default_dscr(orig_dscr_default);
return err;
}