Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

authored by

Benjamin Gray and committed by
Michael Ellerman
3067b89a fda81588

+114 -116
-4
tools/testing/selftests/powerpc/dscr/dscr.h
··· 86 86 } 87 87 } 88 88 89 - double uniform_deviate(int seed) 90 - { 91 - return seed * (1.0 / (RAND_MAX + 1.0)); 92 - } 93 89 #endif /* _SELFTESTS_POWERPC_DSCR_DSCR_H */
+56 -76
tools/testing/selftests/powerpc/dscr/dscr_default_test.c
··· 69 69 return 0; 70 70 } 71 71 72 - static unsigned long dscr; /* System DSCR default */ 73 - static unsigned long sequence; 74 - static unsigned long result[THREADS]; 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 + }; 75 78 76 - static void *do_test(void *in) 79 + static void *dscr_default_random_thread(void *in) 77 80 { 78 - unsigned long thread = (unsigned long)in; 79 - unsigned long i; 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; 80 85 81 - for (i = 0; i < COUNT; i++) { 82 - unsigned long d, cur_dscr, cur_dscr_usr; 83 - unsigned long s1, s2; 86 + srand(gettid()); 84 87 85 - s1 = READ_ONCE(sequence); 86 - if (s1 & 1) 87 - continue; 88 - rmb(); 88 + err = pthread_barrier_wait(args->barrier); 89 + FAIL_IF_EXIT(err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD); 89 90 90 - d = dscr; 91 - cur_dscr = get_dscr(); 92 - cur_dscr_usr = get_dscr_usr(); 91 + for (int i = 0; i < COUNT; i++) { 92 + unsigned long expected_dscr; 93 + unsigned long current_dscr; 94 + unsigned long current_dscr_usr; 93 95 94 - rmb(); 95 - s2 = sequence; 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)); 96 101 97 - if (s1 != s2) 98 - continue; 102 + FAIL_IF_EXIT(current_dscr != expected_dscr); 103 + FAIL_IF_EXIT(current_dscr_usr != expected_dscr); 99 104 100 - if (cur_dscr != d) { 101 - fprintf(stderr, "thread %ld kernel DSCR should be %ld " 102 - "but is %ld\n", thread, d, cur_dscr); 103 - result[thread] = 1; 104 - pthread_exit(&result[thread]); 105 - } 105 + if (rand() % 10 == 0) { 106 + unsigned long next_dscr; 106 107 107 - if (cur_dscr_usr != d) { 108 - fprintf(stderr, "thread %ld user DSCR should be %ld " 109 - "but is %ld\n", thread, d, cur_dscr_usr); 110 - result[thread] = 1; 111 - pthread_exit(&result[thread]); 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)); 112 113 } 113 114 } 114 - result[thread] = 0; 115 - pthread_exit(&result[thread]); 115 + 116 + pthread_exit((void *)0); 116 117 } 117 118 118 119 int dscr_default_random_test(void) 119 120 { 120 - pthread_t threads[THREADS]; 121 - unsigned long i, *status[THREADS]; 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; 122 126 123 127 SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR)); 124 128 125 - /* Initial DSCR default */ 126 - dscr = 1; 127 - set_default_dscr(dscr); 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)); 128 133 129 - /* Spawn all testing threads */ 130 - for (i = 0; i < THREADS; i++) { 131 - if (pthread_create(&threads[i], NULL, do_test, (void *)i)) { 132 - perror("pthread_create() failed"); 133 - return 1; 134 - } 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])); 135 143 } 136 144 137 - srand(getpid()); 145 + for (int i = 0; i < THREADS; i++) 146 + FAIL_IF(pthread_join(threads[i].thread_id, NULL)); 138 147 139 - /* Keep changing the DSCR default */ 140 - for (i = 0; i < COUNT; i++) { 141 - double ret = uniform_deviate(rand()); 148 + FAIL_IF(pthread_barrier_destroy(&barrier)); 149 + FAIL_IF(pthread_rwlock_destroy(&rw_lock)); 142 150 143 - if (ret < 0.0001) { 144 - sequence++; 145 - wmb(); 146 - 147 - dscr++; 148 - if (dscr > DSCR_MAX) 149 - dscr = 0; 150 - 151 - set_default_dscr(dscr); 152 - 153 - wmb(); 154 - sequence++; 155 - } 156 - } 157 - 158 - /* Individual testing thread exit status */ 159 - for (i = 0; i < THREADS; i++) { 160 - if (pthread_join(threads[i], (void **)&(status[i]))) { 161 - perror("pthread_join() failed"); 162 - return 1; 163 - } 164 - 165 - if (*status[i]) { 166 - printf("%ldth thread failed to join with %ld status\n", 167 - i, *status[i]); 168 - return 1; 169 - } 170 - } 171 151 return 0; 172 152 } 173 153
+58 -36
tools/testing/selftests/powerpc/dscr/dscr_explicit_test.c
··· 86 86 return 0; 87 87 } 88 88 89 + struct random_thread_args { 90 + pthread_t thread_id; 91 + bool do_yields; 92 + pthread_barrier_t *barrier; 93 + }; 94 + 95 + void *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 + 89 133 int dscr_explicit_random_test(void) 90 134 { 91 - unsigned long i, dscr = 0; 135 + struct random_thread_args threads[THREADS]; 136 + pthread_barrier_t barrier; 92 137 93 138 SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR)); 94 139 95 - srand(getpid()); 96 - set_dscr(dscr); 140 + FAIL_IF(pthread_barrier_init(&barrier, NULL, THREADS)); 97 141 98 - for (i = 0; i < COUNT; i++) { 99 - unsigned long cur_dscr, cur_dscr_usr; 100 - double ret = uniform_deviate(rand()); 142 + for (int i = 0; i < THREADS; i++) { 143 + threads[i].do_yields = i % 2 == 0; 144 + threads[i].barrier = &barrier; 101 145 102 - if (ret < 0.001) { 103 - dscr++; 104 - if (dscr > DSCR_MAX) 105 - dscr = 0; 106 - 107 - set_dscr(dscr); 108 - } 109 - 110 - cur_dscr = get_dscr(); 111 - if (cur_dscr != dscr) { 112 - fprintf(stderr, "Kernel DSCR should be %ld but " 113 - "is %ld\n", dscr, cur_dscr); 114 - return 1; 115 - } 116 - 117 - ret = uniform_deviate(rand()); 118 - if (ret < 0.001) { 119 - dscr++; 120 - if (dscr > DSCR_MAX) 121 - dscr = 0; 122 - 123 - set_dscr_usr(dscr); 124 - } 125 - 126 - cur_dscr_usr = get_dscr_usr(); 127 - if (cur_dscr_usr != dscr) { 128 - fprintf(stderr, "User DSCR should be %ld but " 129 - "is %ld\n", dscr, cur_dscr_usr); 130 - return 1; 131 - } 146 + FAIL_IF(pthread_create(&threads[i].thread_id, NULL, 147 + dscr_explicit_random_thread, (void *)&threads[i])); 132 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 + 133 155 return 0; 134 156 } 135 157