134689Sbde// SPDX-License-Identifier: GPL-2.0-only
250476Speter/*
31573Srgrimes * POWER Data Stream Control Register (DSCR) explicit test
434689Sbde *
534689Sbde * This test modifies the DSCR value using mtspr instruction and
634689Sbde * verifies the change with mfspr instruction. It uses both the
738752Sbde * privilege state SPR and the problem state SPR for this purpose.
834689Sbde *
940306Sbde * When using the privilege state SPR, the instructions such as
1045691Sobrien * mfspr or mtspr are privileged and the kernel emulates them
1141123Sjdp * for us. Instructions using problem state SPR can be executed
1241123Sjdp * directly without any emulation if the HW supports them. Else
1350731Speter * they also get emulated by the kernel.
1441257Sjdp *
1541257Sjdp * Copyright 2012, Anton Blanchard, IBM Corporation.
1641257Sjdp * Copyright 2015, Anshuman Khandual, IBM Corporation.
1734689Sbde */
1834689Sbde
1934689Sbde#define _GNU_SOURCE
2050633Speter
2150633Speter#include "dscr.h"
2239271Sphk#include "utils.h"
2350633Speter
2441912Sdfr#include <pthread.h>
2541257Sjdp#include <sched.h>
2644746Smarkm#include <semaphore.h>
2734689Sbde
2850633Spetervoid *dscr_explicit_lockstep_thread(void *args)
2950633Speter{
3038632Sjb	sem_t *prev = (sem_t *)args;
3138632Sjb	sem_t *next = (sem_t *)args + 1;
3234330Sjb	unsigned long expected_dscr = 0;
3334689Sbde
341573Srgrimes	set_dscr(expected_dscr);
351573Srgrimes	srand(gettid());
3634330Sjb
3734689Sbde	for (int i = 0; i < COUNT; i++) {
3817706Sjulian		FAIL_IF_EXIT(sem_wait(prev));
3917706Sjulian
4034738Smarkm		FAIL_IF_EXIT(expected_dscr != get_dscr());
4134738Smarkm		FAIL_IF_EXIT(expected_dscr != get_dscr_usr());
4236428Speter
4336428Speter		expected_dscr = (expected_dscr + 1) % DSCR_MAX;
4436428Speter		set_dscr(expected_dscr);
4534689Sbde
4611071Sache		FAIL_IF_EXIT(sem_post(next));
4711071Sache	}
4834689Sbde
4938477Sgpalmer	return NULL;
5034689Sbde}
5136026Sjb
5234689Sbdeint dscr_explicit_lockstep_test(void)
5332386Sjb{
5434689Sbde	pthread_t thread;
552119Sjkh	sem_t semaphores[2];
5634689Sbde	sem_t *prev = &semaphores[1];  /* reversed prev/next than for the other thread */
572119Sjkh	sem_t *next = &semaphores[0];
582119Sjkh	unsigned long expected_dscr = 0;
5941912Sdfr
6041912Sdfr	SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR));
6141912Sdfr
6241912Sdfr	srand(gettid());
6334689Sbde	set_dscr(expected_dscr);
6434689Sbde
6534689Sbde	FAIL_IF(sem_init(prev, 0, 0));
6634689Sbde	FAIL_IF(sem_init(next, 0, 1));  /* other thread starts first */
6720845Speter	FAIL_IF(bind_to_cpu(BIND_CPU_ANY) < 0);
681573Srgrimes	FAIL_IF(pthread_create(&thread, NULL, dscr_explicit_lockstep_thread, (void *)semaphores));
69
70	for (int i = 0; i < COUNT; i++) {
71		FAIL_IF(sem_wait(prev));
72
73		FAIL_IF(expected_dscr != get_dscr());
74		FAIL_IF(expected_dscr != get_dscr_usr());
75
76		expected_dscr = (expected_dscr - 1) % DSCR_MAX;
77		set_dscr(expected_dscr);
78
79		FAIL_IF(sem_post(next));
80	}
81
82	FAIL_IF(pthread_join(thread, NULL));
83	FAIL_IF(sem_destroy(prev));
84	FAIL_IF(sem_destroy(next));
85
86	return 0;
87}
88
89struct random_thread_args {
90	pthread_t thread_id;
91	bool do_yields;
92	pthread_barrier_t *barrier;
93};
94
95void *dscr_explicit_random_thread(void *in)
96{
97	struct random_thread_args *args = (struct random_thread_args *)in;
98	unsigned long expected_dscr = 0;
99	int err;
100
101	srand(gettid());
102
103	err = pthread_barrier_wait(args->barrier);
104	FAIL_IF_EXIT(err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD);
105
106	for (int i = 0; i < COUNT; i++) {
107		expected_dscr = rand() % DSCR_MAX;
108		set_dscr(expected_dscr);
109
110		for (int j = rand() % 5; j > 0; --j) {
111			FAIL_IF_EXIT(get_dscr() != expected_dscr);
112			FAIL_IF_EXIT(get_dscr_usr() != expected_dscr);
113
114			if (args->do_yields && rand() % 2)
115				sched_yield();
116		}
117
118		expected_dscr = rand() % DSCR_MAX;
119		set_dscr_usr(expected_dscr);
120
121		for (int j = rand() % 5; j > 0; --j) {
122			FAIL_IF_EXIT(get_dscr() != expected_dscr);
123			FAIL_IF_EXIT(get_dscr_usr() != expected_dscr);
124
125			if (args->do_yields && rand() % 2)
126				sched_yield();
127		}
128	}
129
130	return NULL;
131}
132
133int dscr_explicit_random_test(void)
134{
135	struct random_thread_args threads[THREADS];
136	pthread_barrier_t barrier;
137
138	SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR));
139
140	FAIL_IF(pthread_barrier_init(&barrier, NULL, THREADS));
141
142	for (int i = 0; i < THREADS; i++) {
143		threads[i].do_yields = i % 2 == 0;
144		threads[i].barrier = &barrier;
145
146		FAIL_IF(pthread_create(&threads[i].thread_id, NULL,
147				       dscr_explicit_random_thread, (void *)&threads[i]));
148	}
149
150	for (int i = 0; i < THREADS; i++)
151		FAIL_IF(pthread_join(threads[i].thread_id, NULL));
152
153	FAIL_IF(pthread_barrier_destroy(&barrier));
154
155	return 0;
156}
157
158int main(int argc, char *argv[])
159{
160	unsigned long orig_dscr_default = 0;
161	int err = 0;
162
163	if (have_hwcap2(PPC_FEATURE2_DSCR))
164		orig_dscr_default = get_default_dscr();
165
166	err |= test_harness(dscr_explicit_lockstep_test, "dscr_explicit_lockstep_test");
167	err |= test_harness(dscr_explicit_random_test, "dscr_explicit_random_test");
168
169	if (have_hwcap2(PPC_FEATURE2_DSCR))
170		set_default_dscr(orig_dscr_default);
171
172	return err;
173}
174