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

selftests/powerpc: Check FP/VEC on exception in TM

Add a self test to check if FP/VEC/VSX registers are sane (restored
correctly) after a FP/VEC/VSX unavailable exception is caught during a
transaction.

This test checks all possibilities in a thread regarding the combination
of MSR.[FP|VEC] states in a thread and for each scenario raises a
FP/VEC/VSX unavailable exception in transactional state, verifying if
vs0 and vs32 registers, which are representatives of FP/VEC/VSX reg
sets, are not corrupted.

Signed-off-by: Gustavo Romero <gromero@linux.vnet.ibm.com>
Signed-off-by: Breno Leitao <leitao@debian.org>
Signed-off-by: Cyril Bur <cyrilbur@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

authored by

Gustavo Romero and committed by
Michael Ellerman
77fad8bf 80eff6c4

+379 -1
+1
tools/testing/selftests/powerpc/tm/.gitignore
··· 12 12 tm-signal-context-chk-vmx 13 13 tm-signal-context-chk-vsx 14 14 tm-vmx-unavail 15 + tm-unavailable
+2 -1
tools/testing/selftests/powerpc/tm/Makefile
··· 2 2 tm-signal-context-chk-vmx tm-signal-context-chk-vsx 3 3 4 4 TEST_GEN_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack \ 5 - tm-vmxcopy tm-fork tm-tar tm-tmspr tm-vmx-unavail \ 5 + tm-vmxcopy tm-fork tm-tar tm-tmspr tm-vmx-unavail tm-unavailable \ 6 6 $(SIGNAL_CONTEXT_CHK_TESTS) 7 7 8 8 include ../../lib.mk ··· 16 16 $(OUTPUT)/tm-tmspr: CFLAGS += -pthread 17 17 $(OUTPUT)/tm-vmx-unavail: CFLAGS += -pthread -m64 18 18 $(OUTPUT)/tm-resched-dscr: ../pmu/lib.o 19 + $(OUTPUT)/tm-unavailable: CFLAGS += -O0 -pthread -m64 -Wno-error=uninitialized -mvsx 19 20 20 21 SIGNAL_CONTEXT_CHK_TESTS := $(patsubst %,$(OUTPUT)/%,$(SIGNAL_CONTEXT_CHK_TESTS)) 21 22 $(SIGNAL_CONTEXT_CHK_TESTS): tm-signal.S
+371
tools/testing/selftests/powerpc/tm/tm-unavailable.c
··· 1 + /* 2 + * Copyright 2017, Gustavo Romero, Breno Leitao, Cyril Bur, IBM Corp. 3 + * Licensed under GPLv2. 4 + * 5 + * Force FP, VEC and VSX unavailable exception during transaction in all 6 + * possible scenarios regarding the MSR.FP and MSR.VEC state, e.g. when FP 7 + * is enable and VEC is disable, when FP is disable and VEC is enable, and 8 + * so on. Then we check if the restored state is correctly set for the 9 + * FP and VEC registers to the previous state we set just before we entered 10 + * in TM, i.e. we check if it corrupts somehow the recheckpointed FP and 11 + * VEC/Altivec registers on abortion due to an unavailable exception in TM. 12 + * N.B. In this test we do not test all the FP/Altivec/VSX registers for 13 + * corruption, but only for registers vs0 and vs32, which are respectively 14 + * representatives of FP and VEC/Altivec reg sets. 15 + */ 16 + 17 + #define _GNU_SOURCE 18 + #include <stdio.h> 19 + #include <stdlib.h> 20 + #include <unistd.h> 21 + #include <inttypes.h> 22 + #include <stdbool.h> 23 + #include <pthread.h> 24 + #include <sched.h> 25 + 26 + #include "tm.h" 27 + 28 + #define DEBUG 0 29 + 30 + /* Unavailable exceptions to test in HTM */ 31 + #define FP_UNA_EXCEPTION 0 32 + #define VEC_UNA_EXCEPTION 1 33 + #define VSX_UNA_EXCEPTION 2 34 + 35 + #define NUM_EXCEPTIONS 3 36 + 37 + struct Flags { 38 + int touch_fp; 39 + int touch_vec; 40 + int result; 41 + int exception; 42 + } flags; 43 + 44 + bool expecting_failure(void) 45 + { 46 + if (flags.touch_fp && flags.exception == FP_UNA_EXCEPTION) 47 + return false; 48 + 49 + if (flags.touch_vec && flags.exception == VEC_UNA_EXCEPTION) 50 + return false; 51 + 52 + /* 53 + * If both FP and VEC are touched it does not mean that touching VSX 54 + * won't raise an exception. However since FP and VEC state are already 55 + * correctly loaded, the transaction is not aborted (i.e. 56 + * treclaimed/trecheckpointed) and MSR.VSX is just set as 1, so a TM 57 + * failure is not expected also in this case. 58 + */ 59 + if ((flags.touch_fp && flags.touch_vec) && 60 + flags.exception == VSX_UNA_EXCEPTION) 61 + return false; 62 + 63 + return true; 64 + } 65 + 66 + /* Check if failure occurred whilst in transaction. */ 67 + bool is_failure(uint64_t condition_reg) 68 + { 69 + /* 70 + * When failure handling occurs, CR0 is set to 0b1010 (0xa). Otherwise 71 + * transaction completes without failure and hence reaches out 'tend.' 72 + * that sets CR0 to 0b0100 (0x4). 73 + */ 74 + return ((condition_reg >> 28) & 0xa) == 0xa; 75 + } 76 + 77 + void *ping(void *input) 78 + { 79 + 80 + /* 81 + * Expected values for vs0 and vs32 after a TM failure. They must never 82 + * change, otherwise they got corrupted. 83 + */ 84 + uint64_t high_vs0 = 0x5555555555555555; 85 + uint64_t low_vs0 = 0xffffffffffffffff; 86 + uint64_t high_vs32 = 0x5555555555555555; 87 + uint64_t low_vs32 = 0xffffffffffffffff; 88 + 89 + /* Counter for busy wait */ 90 + uint64_t counter = 0x1ff000000; 91 + 92 + /* 93 + * Variable to keep a copy of CR register content taken just after we 94 + * leave the transactional state. 95 + */ 96 + uint64_t cr_ = 0; 97 + 98 + /* 99 + * Wait a bit so thread can get its name "ping". This is not important 100 + * to reproduce the issue but it's nice to have for systemtap debugging. 101 + */ 102 + if (DEBUG) 103 + sleep(1); 104 + 105 + printf("If MSR.FP=%d MSR.VEC=%d: ", flags.touch_fp, flags.touch_vec); 106 + 107 + if (flags.exception != FP_UNA_EXCEPTION && 108 + flags.exception != VEC_UNA_EXCEPTION && 109 + flags.exception != VSX_UNA_EXCEPTION) { 110 + printf("No valid exception specified to test.\n"); 111 + return NULL; 112 + } 113 + 114 + asm ( 115 + /* Prepare to merge low and high. */ 116 + " mtvsrd 33, %[high_vs0] ;" 117 + " mtvsrd 34, %[low_vs0] ;" 118 + 119 + /* 120 + * Adjust VS0 expected value after an TM failure, 121 + * i.e. vs0 = 0x5555555555555555555FFFFFFFFFFFFFFFF 122 + */ 123 + " xxmrghd 0, 33, 34 ;" 124 + 125 + /* 126 + * Adjust VS32 expected value after an TM failure, 127 + * i.e. vs32 = 0x5555555555555555555FFFFFFFFFFFFFFFF 128 + */ 129 + " xxmrghd 32, 33, 34 ;" 130 + 131 + /* 132 + * Wait an amount of context switches so load_fp and load_vec 133 + * overflow and MSR.FP, MSR.VEC, and MSR.VSX become zero (off). 134 + */ 135 + " mtctr %[counter] ;" 136 + 137 + /* Decrement CTR branch if CTR non zero. */ 138 + "1: bdnz 1b ;" 139 + 140 + /* 141 + * Check if we want to touch FP prior to the test in order 142 + * to set MSR.FP = 1 before provoking an unavailable 143 + * exception in TM. 144 + */ 145 + " cmpldi %[touch_fp], 0 ;" 146 + " beq no_fp ;" 147 + " fadd 10, 10, 10 ;" 148 + "no_fp: ;" 149 + 150 + /* 151 + * Check if we want to touch VEC prior to the test in order 152 + * to set MSR.VEC = 1 before provoking an unavailable 153 + * exception in TM. 154 + */ 155 + " cmpldi %[touch_vec], 0 ;" 156 + " beq no_vec ;" 157 + " vaddcuw 10, 10, 10 ;" 158 + "no_vec: ;" 159 + 160 + /* 161 + * Perhaps it would be a better idea to do the 162 + * compares outside transactional context and simply 163 + * duplicate code. 164 + */ 165 + " tbegin. ;" 166 + " beq trans_fail ;" 167 + 168 + /* Do we do FP Unavailable? */ 169 + " cmpldi %[exception], %[ex_fp] ;" 170 + " bne 1f ;" 171 + " fadd 10, 10, 10 ;" 172 + " b done ;" 173 + 174 + /* Do we do VEC Unavailable? */ 175 + "1: cmpldi %[exception], %[ex_vec] ;" 176 + " bne 2f ;" 177 + " vaddcuw 10, 10, 10 ;" 178 + " b done ;" 179 + 180 + /* 181 + * Not FP or VEC, therefore VSX. Ensure this 182 + * instruction always generates a VSX Unavailable. 183 + * ISA 3.0 is tricky here. 184 + * (xxmrghd will on ISA 2.07 and ISA 3.0) 185 + */ 186 + "2: xxmrghd 10, 10, 10 ;" 187 + 188 + "done: tend. ;" 189 + 190 + "trans_fail: ;" 191 + 192 + /* Give values back to C. */ 193 + " mfvsrd %[high_vs0], 0 ;" 194 + " xxsldwi 3, 0, 0, 2 ;" 195 + " mfvsrd %[low_vs0], 3 ;" 196 + " mfvsrd %[high_vs32], 32 ;" 197 + " xxsldwi 3, 32, 32, 2 ;" 198 + " mfvsrd %[low_vs32], 3 ;" 199 + 200 + /* Give CR back to C so that it can check what happened. */ 201 + " mfcr %[cr_] ;" 202 + 203 + : [high_vs0] "+r" (high_vs0), 204 + [low_vs0] "+r" (low_vs0), 205 + [high_vs32] "=r" (high_vs32), 206 + [low_vs32] "=r" (low_vs32), 207 + [cr_] "+r" (cr_) 208 + : [touch_fp] "r" (flags.touch_fp), 209 + [touch_vec] "r" (flags.touch_vec), 210 + [exception] "r" (flags.exception), 211 + [ex_fp] "i" (FP_UNA_EXCEPTION), 212 + [ex_vec] "i" (VEC_UNA_EXCEPTION), 213 + [ex_vsx] "i" (VSX_UNA_EXCEPTION), 214 + [counter] "r" (counter) 215 + 216 + : "cr0", "ctr", "v10", "vs0", "vs10", "vs3", "vs32", "vs33", 217 + "vs34", "fr10" 218 + 219 + ); 220 + 221 + /* 222 + * Check if we were expecting a failure and it did not occur by checking 223 + * CR0 state just after we leave the transaction. Either way we check if 224 + * vs0 or vs32 got corrupted. 225 + */ 226 + if (expecting_failure() && !is_failure(cr_)) { 227 + printf("\n\tExpecting the transaction to fail, %s", 228 + "but it didn't\n\t"); 229 + flags.result++; 230 + } 231 + 232 + /* Check if we were not expecting a failure and a it occurred. */ 233 + if (!expecting_failure() && is_failure(cr_)) { 234 + printf("\n\tUnexpected transaction failure 0x%02lx\n\t", 235 + failure_code()); 236 + return (void *) -1; 237 + } 238 + 239 + /* 240 + * Check if TM failed due to the cause we were expecting. 0xda is a 241 + * TM_CAUSE_FAC_UNAV cause, otherwise it's an unexpected cause. 242 + */ 243 + if (is_failure(cr_) && !failure_is_unavailable()) { 244 + printf("\n\tUnexpected failure cause 0x%02lx\n\t", 245 + failure_code()); 246 + return (void *) -1; 247 + } 248 + 249 + /* 0x4 is a success and 0xa is a fail. See comment in is_failure(). */ 250 + if (DEBUG) 251 + printf("CR0: 0x%1lx ", cr_ >> 28); 252 + 253 + /* Check FP (vs0) for the expected value. */ 254 + if (high_vs0 != 0x5555555555555555 || low_vs0 != 0xFFFFFFFFFFFFFFFF) { 255 + printf("FP corrupted!"); 256 + printf(" high = %#16" PRIx64 " low = %#16" PRIx64 " ", 257 + high_vs0, low_vs0); 258 + flags.result++; 259 + } else 260 + printf("FP ok "); 261 + 262 + /* Check VEC (vs32) for the expected value. */ 263 + if (high_vs32 != 0x5555555555555555 || low_vs32 != 0xFFFFFFFFFFFFFFFF) { 264 + printf("VEC corrupted!"); 265 + printf(" high = %#16" PRIx64 " low = %#16" PRIx64, 266 + high_vs32, low_vs32); 267 + flags.result++; 268 + } else 269 + printf("VEC ok"); 270 + 271 + putchar('\n'); 272 + 273 + return NULL; 274 + } 275 + 276 + /* Thread to force context switch */ 277 + void *pong(void *not_used) 278 + { 279 + /* Wait thread get its name "pong". */ 280 + if (DEBUG) 281 + sleep(1); 282 + 283 + /* Classed as an interactive-like thread. */ 284 + while (1) 285 + sched_yield(); 286 + } 287 + 288 + /* Function that creates a thread and launches the "ping" task. */ 289 + void test_fp_vec(int fp, int vec, pthread_attr_t *attr) 290 + { 291 + int retries = 2; 292 + void *ret_value; 293 + pthread_t t0; 294 + 295 + flags.touch_fp = fp; 296 + flags.touch_vec = vec; 297 + 298 + /* 299 + * Without luck it's possible that the transaction is aborted not due to 300 + * the unavailable exception caught in the middle as we expect but also, 301 + * for instance, due to a context switch or due to a KVM reschedule (if 302 + * it's running on a VM). Thus we try a few times before giving up, 303 + * checking if the failure cause is the one we expect. 304 + */ 305 + do { 306 + /* Bind 'ping' to CPU 0, as specified in 'attr'. */ 307 + pthread_create(&t0, attr, ping, (void *) &flags); 308 + pthread_setname_np(t0, "ping"); 309 + pthread_join(t0, &ret_value); 310 + retries--; 311 + } while (ret_value != NULL && retries); 312 + 313 + if (!retries) { 314 + flags.result = 1; 315 + if (DEBUG) 316 + printf("All transactions failed unexpectedly\n"); 317 + 318 + } 319 + } 320 + 321 + int main(int argc, char **argv) 322 + { 323 + int exception; /* FP = 0, VEC = 1, VSX = 2 */ 324 + pthread_t t1; 325 + pthread_attr_t attr; 326 + cpu_set_t cpuset; 327 + 328 + /* Set only CPU 0 in the mask. Both threads will be bound to CPU 0. */ 329 + CPU_ZERO(&cpuset); 330 + CPU_SET(0, &cpuset); 331 + 332 + /* Init pthread attribute. */ 333 + pthread_attr_init(&attr); 334 + 335 + /* Set CPU 0 mask into the pthread attribute. */ 336 + pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset); 337 + 338 + pthread_create(&t1, &attr /* Bind 'pong' to CPU 0 */, pong, NULL); 339 + pthread_setname_np(t1, "pong"); /* Name it for systemtap convenience */ 340 + 341 + flags.result = 0; 342 + 343 + for (exception = 0; exception < NUM_EXCEPTIONS; exception++) { 344 + printf("Checking if FP/VEC registers are sane after"); 345 + 346 + if (exception == FP_UNA_EXCEPTION) 347 + printf(" a FP unavailable exception...\n"); 348 + 349 + else if (exception == VEC_UNA_EXCEPTION) 350 + printf(" a VEC unavailable exception...\n"); 351 + 352 + else 353 + printf(" a VSX unavailable exception...\n"); 354 + 355 + flags.exception = exception; 356 + 357 + test_fp_vec(0, 0, &attr); 358 + test_fp_vec(1, 0, &attr); 359 + test_fp_vec(0, 1, &attr); 360 + test_fp_vec(1, 1, &attr); 361 + 362 + } 363 + 364 + if (flags.result > 0) { 365 + printf("result: failed!\n"); 366 + exit(1); 367 + } else { 368 + printf("result: success\n"); 369 + exit(0); 370 + } 371 + }
+5
tools/testing/selftests/powerpc/tm/tm.h
··· 47 47 return (failure_code() & TM_CAUSE_SYSCALL) == TM_CAUSE_SYSCALL; 48 48 } 49 49 50 + static inline bool failure_is_unavailable(void) 51 + { 52 + return (failure_code() & TM_CAUSE_FAC_UNAV) == TM_CAUSE_FAC_UNAV; 53 + } 54 + 50 55 static inline bool failure_is_nesting(void) 51 56 { 52 57 return (__builtin_get_texasru() & 0x400000);