Commit 3067b89a authored by Benjamin Gray's avatar Benjamin Gray Committed by Michael Ellerman
Browse files

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: default avatarBenjamin Gray <bgray@linux.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20230406043320.125138-6-bgray@linux.ibm.com
parent fda81588
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -86,8 +86,4 @@ void set_default_dscr(unsigned long val)
	}
}

double uniform_deviate(int seed)
{
	return seed * (1.0 / (RAND_MAX + 1.0));
}
#endif	/* _SELFTESTS_POWERPC_DSCR_DSCR_H */
+60 −80
Original line number Diff line number Diff line
@@ -69,105 +69,85 @@ int dscr_default_lockstep_test(void)
	return 0;
}

static unsigned long dscr;		/* System DSCR default */
static unsigned long sequence;
static unsigned long result[THREADS];

static void *do_test(void *in)
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)
{
	unsigned long thread = (unsigned long)in;
	unsigned long i;
	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;

	for (i = 0; i < COUNT; i++) {
		unsigned long d, cur_dscr, cur_dscr_usr;
		unsigned long s1, s2;
	srand(gettid());

		s1 = READ_ONCE(sequence);
		if (s1 & 1)
			continue;
		rmb();
	err = pthread_barrier_wait(args->barrier);
	FAIL_IF_EXIT(err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD);

		d = dscr;
		cur_dscr = get_dscr();
		cur_dscr_usr = get_dscr_usr();
	for (int i = 0; i < COUNT; i++) {
		unsigned long expected_dscr;
		unsigned long current_dscr;
		unsigned long current_dscr_usr;

		rmb();
		s2 = sequence;
		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));

		if (s1 != s2)
			continue;
		FAIL_IF_EXIT(current_dscr != expected_dscr);
		FAIL_IF_EXIT(current_dscr_usr != expected_dscr);

		if (cur_dscr != d) {
			fprintf(stderr, "thread %ld kernel DSCR should be %ld "
				"but is %ld\n", thread, d, cur_dscr);
			result[thread] = 1;
			pthread_exit(&result[thread]);
		}
		if (rand() % 10 == 0) {
			unsigned long next_dscr;

		if (cur_dscr_usr != d) {
			fprintf(stderr, "thread %ld user DSCR should be %ld "
				"but is %ld\n", thread, d, cur_dscr_usr);
			result[thread] = 1;
			pthread_exit(&result[thread]);
			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));
		}
	}
	result[thread] = 0;
	pthread_exit(&result[thread]);

	pthread_exit((void *)0);
}

int dscr_default_random_test(void)
{
	pthread_t threads[THREADS];
	unsigned long i, *status[THREADS];
	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));

	/* Initial DSCR default */
	dscr = 1;
	set_default_dscr(dscr);

	/* Spawn all testing threads */
	for (i = 0; i < THREADS; i++) {
		if (pthread_create(&threads[i], NULL, do_test, (void *)i)) {
			perror("pthread_create() failed");
			return 1;
		}
	}

	srand(getpid());
	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));

	/* Keep changing the DSCR default */
	for (i = 0; i < COUNT; i++) {
		double ret = uniform_deviate(rand());
	set_default_dscr(expected_system_dscr);

		if (ret < 0.0001) {
			sequence++;
			wmb();
	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;

			dscr++;
			if (dscr > DSCR_MAX)
				dscr = 0;

			set_default_dscr(dscr);

			wmb();
			sequence++;
		}
		FAIL_IF(pthread_create(&threads[i].thread_id, NULL,
				       dscr_default_random_thread, (void *)&threads[i]));
	}

	/* Individual testing thread exit status */
	for (i = 0; i < THREADS; i++) {
		if (pthread_join(threads[i], (void **)&(status[i]))) {
			perror("pthread_join() failed");
			return 1;
		}
	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));

		if (*status[i]) {
			printf("%ldth thread failed to join with %ld status\n",
								i, *status[i]);
			return 1;
		}
	}
	return 0;
}

+53 −31
Original line number Diff line number Diff line
@@ -86,50 +86,72 @@ int dscr_explicit_lockstep_test(void)
	return 0;
}

int dscr_explicit_random_test(void)
struct random_thread_args {
	pthread_t thread_id;
	bool do_yields;
	pthread_barrier_t *barrier;
};

void *dscr_explicit_random_thread(void *in)
{
	unsigned long i, dscr = 0;
	struct random_thread_args *args = (struct random_thread_args *)in;
	unsigned long expected_dscr = 0;
	int err;

	SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR));
	srand(gettid());

	srand(getpid());
	set_dscr(dscr);
	err = pthread_barrier_wait(args->barrier);
	FAIL_IF_EXIT(err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD);

	for (i = 0; i < COUNT; i++) {
		unsigned long cur_dscr, cur_dscr_usr;
		double ret = uniform_deviate(rand());
	for (int i = 0; i < COUNT; i++) {
		expected_dscr = rand() % DSCR_MAX;
		set_dscr(expected_dscr);

		if (ret < 0.001) {
			dscr++;
			if (dscr > DSCR_MAX)
				dscr = 0;
		for (int j = rand() % 5; j > 0; --j) {
			FAIL_IF_EXIT(get_dscr() != expected_dscr);
			FAIL_IF_EXIT(get_dscr_usr() != expected_dscr);

			set_dscr(dscr);
			if (args->do_yields && rand() % 2)
				sched_yield();
		}

		cur_dscr = get_dscr();
		if (cur_dscr != dscr) {
			fprintf(stderr, "Kernel DSCR should be %ld but "
					"is %ld\n", dscr, cur_dscr);
			return 1;
		}
		expected_dscr = rand() % DSCR_MAX;
		set_dscr_usr(expected_dscr);

		ret = uniform_deviate(rand());
		if (ret < 0.001) {
			dscr++;
			if (dscr > DSCR_MAX)
				dscr = 0;
		for (int j = rand() % 5; j > 0; --j) {
			FAIL_IF_EXIT(get_dscr() != expected_dscr);
			FAIL_IF_EXIT(get_dscr_usr() != expected_dscr);

			set_dscr_usr(dscr);
			if (args->do_yields && rand() % 2)
				sched_yield();
		}
	}

		cur_dscr_usr = get_dscr_usr();
		if (cur_dscr_usr != dscr) {
			fprintf(stderr, "User DSCR should be %ld but "
					"is %ld\n", dscr, cur_dscr_usr);
			return 1;
	return NULL;
}

int dscr_explicit_random_test(void)
{
	struct random_thread_args threads[THREADS];
	pthread_barrier_t barrier;

	SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR));

	FAIL_IF(pthread_barrier_init(&barrier, NULL, THREADS));

	for (int i = 0; i < THREADS; i++) {
		threads[i].do_yields = i % 2 == 0;
		threads[i].barrier = &barrier;

		FAIL_IF(pthread_create(&threads[i].thread_id, NULL,
				       dscr_explicit_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));

	return 0;
}