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 | |
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 | |
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 | |
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 | |
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 | |
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 | |