Currently the tm-unavailable test spins for a fixed amount of time in
an attempt to ensure the FPU/VMX/VSX facilities are off. This value was
experimentally tested to be long enough.

Problems may arise if kernel heuristics were to change. This patch
should future proof this test.

Signed-off-by: Cyril Bur <cyril...@gmail.com>
---
Because the test no longer needs to use such a conservative time for
the busy wait, it actually runs much faster.


 .../testing/selftests/powerpc/tm/tm-unavailable.c  | 92 ++++++++++++++++++++--
 1 file changed, 84 insertions(+), 8 deletions(-)

diff --git a/tools/testing/selftests/powerpc/tm/tm-unavailable.c 
b/tools/testing/selftests/powerpc/tm/tm-unavailable.c
index e6a0fad2bfd0..54aeb7a7fbb1 100644
--- a/tools/testing/selftests/powerpc/tm/tm-unavailable.c
+++ b/tools/testing/selftests/powerpc/tm/tm-unavailable.c
@@ -33,6 +33,11 @@
 #define VEC_UNA_EXCEPTION      1
 #define VSX_UNA_EXCEPTION      2
 
+#define ERR_RETRY 1
+#define ERR_ADJUST 2
+
+#define COUNTER_INCREMENT (0x1000000)
+
 #define NUM_EXCEPTIONS         3
 #define err_at_line(status, errnum, format, ...) \
        error_at_line(status, errnum,  __FILE__, __LINE__, format ##__VA_ARGS__)
@@ -45,6 +50,7 @@ struct Flags {
        int touch_vec;
        int result;
        int exception;
+       uint64_t counter;
 } flags;
 
 bool expecting_failure(void)
@@ -87,14 +93,12 @@ void *ping(void *input)
         * Expected values for vs0 and vs32 after a TM failure. They must never
         * change, otherwise they got corrupted.
         */
+       long rc = 0;
        uint64_t high_vs0 = 0x5555555555555555;
        uint64_t low_vs0 = 0xffffffffffffffff;
        uint64_t high_vs32 = 0x5555555555555555;
        uint64_t low_vs32 = 0xffffffffffffffff;
 
-       /* Counter for busy wait */
-       uint64_t counter = 0x1ff000000;
-
        /*
         * Variable to keep a copy of CR register content taken just after we
         * leave the transactional state.
@@ -217,7 +221,7 @@ void *ping(void *input)
                  [ex_fp]     "i"  (FP_UNA_EXCEPTION),
                  [ex_vec]    "i"  (VEC_UNA_EXCEPTION),
                  [ex_vsx]    "i"  (VSX_UNA_EXCEPTION),
-                 [counter]   "r"  (counter)
+                 [counter]   "r"  (flags.counter)
 
                : "cr0", "ctr", "v10", "vs0", "vs10", "vs3", "vs32", "vs33",
                  "vs34", "fr10"
@@ -232,14 +236,14 @@ void *ping(void *input)
        if (expecting_failure() && !is_failure(cr_)) {
                printf("\n\tExpecting the transaction to fail, %s",
                        "but it didn't\n\t");
-               flags.result++;
+               rc = ERR_ADJUST;
        }
 
        /* Check if we were not expecting a failure and a it occurred. */
        if (!expecting_failure() && is_failure(cr_)) {
                printf("\n\tUnexpected transaction failure 0x%02lx\n\t",
                        failure_code());
-               return (void *) -1;
+               rc = ERR_RETRY;
        }
 
        /*
@@ -249,7 +253,7 @@ void *ping(void *input)
        if (is_failure(cr_) && !failure_is_unavailable()) {
                printf("\n\tUnexpected failure cause 0x%02lx\n\t",
                        failure_code());
-               return (void *) -1;
+               rc = ERR_RETRY;
        }
 
        /* 0x4 is a success and 0xa is a fail. See comment in is_failure(). */
@@ -276,7 +280,7 @@ void *ping(void *input)
 
        putchar('\n');
 
-       return NULL;
+       return (void *)rc;
 }
 
 /* Thread to force context switch */
@@ -291,6 +295,55 @@ void *pong(void *not_used)
                sched_yield();
 }
 
+static void flags_set_counter(struct Flags *flags)
+{
+       uint64_t cr_;
+       int count = 0;
+
+       do {
+               if (count == 0)
+                       printf("\tTrying 0x%08" PRIx64 "... ", flags->counter);
+               else
+                       printf("%d, ", count);
+               fflush(stdout);
+               asm (
+                       /*
+                        * Wait an amount of context switches so
+                        * load_fp and load_vec overflow and MSR.FP,
+                        * MSR.VEC, and MSR.VSX become zero (off).
+                        */
+                       "       mtctr           %[counter]              ;"
+
+                       /* Decrement CTR branch if CTR non zero. */
+                       "1:     bdnz 1b                                 ;"
+                       "       tbegin.                                 ;"
+                       "       beq             tfail                   ;"
+
+                       /* Get a facility unavailable */
+                       "       fadd            10, 10, 10              ;"
+                       "       tend.                                   ;"
+                       "tfail:                                         ;"
+
+                       /*
+                        * Give CR back to C so that it can check what
+                        * happened.
+                        */
+                       "       mfcr            %[cr_]                  ;"
+
+                       : [cr_]       "+r" (cr_)
+                       : [counter]   "r"  (flags->counter)
+                       : "cr0", "ctr", "fr10"
+                       );
+               count++;
+               if (!is_failure(cr_) || !failure_is_unavailable()) {
+                       count = 0;
+                       flags->counter += COUNTER_INCREMENT;
+                       putchar('\n');
+               }
+       } while (count < 3);
+       printf("3\n");
+}
+
 /* Function that creates a thread and launches the "ping" task. */
 void test_fp_vec(int fp, int vec, pthread_attr_t *attr)
 {
@@ -322,6 +375,17 @@ void test_fp_vec(int fp, int vec, pthread_attr_t *attr)
                if (rc)
                        pr_err(rc, "pthread_join");
 
+               if ((long)ret_value == ERR_ADJUST) {
+                       printf("Adjusting the facility unavailable spin 
time...\n");
+                       /*
+                        * Be a bit more agressive just now - we'd
+                        * really like to have it work
+                        */
+                       flags.counter += (2 * COUNTER_INCREMENT);
+                       flags_set_counter(&flags);
+                       printf("Now using 0x%08" PRIx64 "\n", flags.counter);
+               }
+
                retries--;
        } while (ret_value != NULL && retries);
 
@@ -340,6 +404,18 @@ int main(int argc, char **argv)
        pthread_attr_t attr;
        cpu_set_t cpuset;
 
+       /*
+        * Default counter for busy wait.
+        * 0x18000000 is a good baseline determined by experimentation
+        * on a POWER8
+        * The autodetecting code will bump it up if it too low.
+        */
+       flags.counter = 0x18000000;
+
+       printf("Testing required spin time required for facility 
unavailable...\n");
+       flags_set_counter(&flags);
+       printf("Spin time required for a reliable facility unavailable TM 
failure: 0x%" PRIx64 "\n",
+                       flags.counter);
        /* Set only CPU 0 in the mask. Both threads will be bound to CPU 0. */
        CPU_ZERO(&cpuset);
        CPU_SET(0, &cpuset);
-- 
2.15.0

Reply via email to