selftests/powerpc/dscr: Improve DSCR explicit random test case
authorBenjamin Gray <bgray@linux.ibm.com>
Thu, 6 Apr 2023 04:33:18 +0000 (14:33 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Thu, 20 Apr 2023 03:21:45 +0000 (13:21 +1000)
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
tools/testing/selftests/powerpc/dscr/dscr.h
tools/testing/selftests/powerpc/dscr/dscr_default_test.c
tools/testing/selftests/powerpc/dscr/dscr_explicit_test.c

index 2c54998d47156aa88be1b8514323b8e68c4eb0b6..b281659071e8f72eea1fcb560835a604c2f6bf8b 100644 (file)
@@ -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 */
index 18e533d46c9a9ef33265a7a766885f8ba6bccd86..60ab02525b79aef9531696f6937ea76396ed5c4f 100644 (file)
@@ -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;
-
-       for (i = 0; i < COUNT; i++) {
-               unsigned long d, cur_dscr, cur_dscr_usr;
-               unsigned long s1, s2;
-
-               s1 = READ_ONCE(sequence);
-               if (s1 & 1)
-                       continue;
-               rmb();
-
-               d = dscr;
-               cur_dscr = get_dscr();
-               cur_dscr_usr = get_dscr_usr();
-
-               rmb();
-               s2 = sequence;
+       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;
 
-               if (s1 != s2)
-                       continue;
+       srand(gettid());
 
-               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]);
-               }
+       err = pthread_barrier_wait(args->barrier);
+       FAIL_IF_EXIT(err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD);
 
-               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]);
+       for (int i = 0; i < COUNT; i++) {
+               unsigned long expected_dscr;
+               unsigned long current_dscr;
+               unsigned long current_dscr_usr;
+
+               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));
+
+               FAIL_IF_EXIT(current_dscr != expected_dscr);
+               FAIL_IF_EXIT(current_dscr_usr != expected_dscr);
+
+               if (rand() % 10 == 0) {
+                       unsigned long next_dscr;
+
+                       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);
+       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));
 
-       /* 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;
-               }
-       }
+       set_default_dscr(expected_system_dscr);
 
-       srand(getpid());
+       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;
 
-       /* Keep changing the DSCR default */
-       for (i = 0; i < COUNT; i++) {
-               double ret = uniform_deviate(rand());
-
-               if (ret < 0.0001) {
-                       sequence++;
-                       wmb();
-
-                       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;
 }
 
index 3b98b9a8820758be3478effc48a90828b4bf8fad..e2268e9183a87138c164ffe24ab2ff549f3f88f9 100644 (file)
@@ -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;
 }