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