1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * POWER Data Stream Control Register (DSCR) default test
4  *
5  * This test modifies the system wide default DSCR through
6  * it's sysfs interface and then verifies that all threads
7  * see the correct changed DSCR value immediately.
8  *
9  * Copyright 2012, Anton Blanchard, IBM Corporation.
10  * Copyright 2015, Anshuman Khandual, IBM Corporation.
11  */
12 
13 #define _GNU_SOURCE
14 
15 #include "dscr.h"
16 
17 #include <pthread.h>
18 #include <semaphore.h>
19 #include <unistd.h>
20 
dscr_default_lockstep_writer(void * arg)21 static void *dscr_default_lockstep_writer(void *arg)
22 {
23 	sem_t *reader_sem = (sem_t *)arg;
24 	sem_t *writer_sem = (sem_t *)arg + 1;
25 	unsigned long expected_dscr = 0;
26 
27 	for (int i = 0; i < COUNT; i++) {
28 		FAIL_IF_EXIT(sem_wait(writer_sem));
29 
30 		set_default_dscr(expected_dscr);
31 		expected_dscr = (expected_dscr + 1) % DSCR_MAX;
32 
33 		FAIL_IF_EXIT(sem_post(reader_sem));
34 	}
35 
36 	return NULL;
37 }
38 
dscr_default_lockstep_test(void)39 int dscr_default_lockstep_test(void)
40 {
41 	pthread_t writer;
42 	sem_t rw_semaphores[2];
43 	sem_t *reader_sem = &rw_semaphores[0];
44 	sem_t *writer_sem = &rw_semaphores[1];
45 	unsigned long expected_dscr = 0;
46 
47 	SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR));
48 
49 	FAIL_IF(sem_init(reader_sem, 0, 0));
50 	FAIL_IF(sem_init(writer_sem, 0, 1));  /* writer starts first */
51 	FAIL_IF(bind_to_cpu(BIND_CPU_ANY) < 0);
52 	FAIL_IF(pthread_create(&writer, NULL, dscr_default_lockstep_writer, (void *)rw_semaphores));
53 
54 	for (int i = 0; i < COUNT ; i++) {
55 		FAIL_IF(sem_wait(reader_sem));
56 
57 		FAIL_IF(get_dscr() != expected_dscr);
58 		FAIL_IF(get_dscr_usr() != expected_dscr);
59 
60 		expected_dscr = (expected_dscr + 1) % DSCR_MAX;
61 
62 		FAIL_IF(sem_post(writer_sem));
63 	}
64 
65 	FAIL_IF(pthread_join(writer, NULL));
66 	FAIL_IF(sem_destroy(reader_sem));
67 	FAIL_IF(sem_destroy(writer_sem));
68 
69 	return 0;
70 }
71 
72 struct random_thread_args {
73 	pthread_t thread_id;
74 	unsigned long *expected_system_dscr;
75 	pthread_rwlock_t *rw_lock;
76 	pthread_barrier_t *barrier;
77 };
78 
dscr_default_random_thread(void * in)79 static void *dscr_default_random_thread(void *in)
80 {
81 	struct random_thread_args *args = (struct random_thread_args *)in;
82 	unsigned long *expected_dscr_p = args->expected_system_dscr;
83 	pthread_rwlock_t *rw_lock = args->rw_lock;
84 	int err;
85 
86 	srand(gettid());
87 
88 	err = pthread_barrier_wait(args->barrier);
89 	FAIL_IF_EXIT(err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD);
90 
91 	for (int i = 0; i < COUNT; i++) {
92 		unsigned long expected_dscr;
93 		unsigned long current_dscr;
94 		unsigned long current_dscr_usr;
95 
96 		FAIL_IF_EXIT(pthread_rwlock_rdlock(rw_lock));
97 		expected_dscr = *expected_dscr_p;
98 		current_dscr = get_dscr();
99 		current_dscr_usr = get_dscr_usr();
100 		FAIL_IF_EXIT(pthread_rwlock_unlock(rw_lock));
101 
102 		FAIL_IF_EXIT(current_dscr != expected_dscr);
103 		FAIL_IF_EXIT(current_dscr_usr != expected_dscr);
104 
105 		if (rand() % 10 == 0) {
106 			unsigned long next_dscr;
107 
108 			FAIL_IF_EXIT(pthread_rwlock_wrlock(rw_lock));
109 			next_dscr = (*expected_dscr_p + 1) % DSCR_MAX;
110 			set_default_dscr(next_dscr);
111 			*expected_dscr_p = next_dscr;
112 			FAIL_IF_EXIT(pthread_rwlock_unlock(rw_lock));
113 		}
114 	}
115 
116 	pthread_exit((void *)0);
117 }
118 
dscr_default_random_test(void)119 int dscr_default_random_test(void)
120 {
121 	struct random_thread_args threads[THREADS];
122 	unsigned long expected_system_dscr = 0;
123 	pthread_rwlockattr_t rwlock_attr;
124 	pthread_rwlock_t rw_lock;
125 	pthread_barrier_t barrier;
126 
127 	SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR));
128 
129 	FAIL_IF(pthread_rwlockattr_setkind_np(&rwlock_attr,
130 					      PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP));
131 	FAIL_IF(pthread_rwlock_init(&rw_lock, &rwlock_attr));
132 	FAIL_IF(pthread_barrier_init(&barrier, NULL, THREADS));
133 
134 	set_default_dscr(expected_system_dscr);
135 
136 	for (int i = 0; i < THREADS; i++) {
137 		threads[i].expected_system_dscr = &expected_system_dscr;
138 		threads[i].rw_lock = &rw_lock;
139 		threads[i].barrier = &barrier;
140 
141 		FAIL_IF(pthread_create(&threads[i].thread_id, NULL,
142 				       dscr_default_random_thread, (void *)&threads[i]));
143 	}
144 
145 	for (int i = 0; i < THREADS; i++)
146 		FAIL_IF(pthread_join(threads[i].thread_id, NULL));
147 
148 	FAIL_IF(pthread_barrier_destroy(&barrier));
149 	FAIL_IF(pthread_rwlock_destroy(&rw_lock));
150 
151 	return 0;
152 }
153 
main(int argc,char * argv[])154 int main(int argc, char *argv[])
155 {
156 	unsigned long orig_dscr_default = 0;
157 	int err = 0;
158 
159 	if (have_hwcap2(PPC_FEATURE2_DSCR))
160 		orig_dscr_default = get_default_dscr();
161 
162 	err |= test_harness(dscr_default_lockstep_test, "dscr_default_lockstep_test");
163 	err |= test_harness(dscr_default_random_test, "dscr_default_random_test");
164 
165 	if (have_hwcap2(PPC_FEATURE2_DSCR))
166 		set_default_dscr(orig_dscr_default);
167 
168 	return err;
169 }
170