Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2
3#define _GNU_SOURCE
4#include <err.h>
5#include <errno.h>
6#include <pthread.h>
7#include <setjmp.h>
8#include <stdio.h>
9#include <string.h>
10#include <stdbool.h>
11#include <unistd.h>
12#include <x86intrin.h>
13
14#include <sys/auxv.h>
15#include <sys/mman.h>
16#include <sys/shm.h>
17#include <sys/syscall.h>
18#include <sys/wait.h>
19
20#include "../kselftest.h" /* For __cpuid_count() */
21
22#ifndef __x86_64__
23# error This test is 64-bit only
24#endif
25
26#define XSAVE_HDR_OFFSET 512
27#define XSAVE_HDR_SIZE 64
28
29struct xsave_buffer {
30 union {
31 struct {
32 char legacy[XSAVE_HDR_OFFSET];
33 char header[XSAVE_HDR_SIZE];
34 char extended[0];
35 };
36 char bytes[0];
37 };
38};
39
40static inline uint64_t xgetbv(uint32_t index)
41{
42 uint32_t eax, edx;
43
44 asm volatile("xgetbv;"
45 : "=a" (eax), "=d" (edx)
46 : "c" (index));
47 return eax + ((uint64_t)edx << 32);
48}
49
50static inline void xsave(struct xsave_buffer *xbuf, uint64_t rfbm)
51{
52 uint32_t rfbm_lo = rfbm;
53 uint32_t rfbm_hi = rfbm >> 32;
54
55 asm volatile("xsave (%%rdi)"
56 : : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi)
57 : "memory");
58}
59
60static inline void xrstor(struct xsave_buffer *xbuf, uint64_t rfbm)
61{
62 uint32_t rfbm_lo = rfbm;
63 uint32_t rfbm_hi = rfbm >> 32;
64
65 asm volatile("xrstor (%%rdi)"
66 : : "D" (xbuf), "a" (rfbm_lo), "d" (rfbm_hi));
67}
68
69/* err() exits and will not return */
70#define fatal_error(msg, ...) err(1, "[FAIL]\t" msg, ##__VA_ARGS__)
71
72static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
73 int flags)
74{
75 struct sigaction sa;
76
77 memset(&sa, 0, sizeof(sa));
78 sa.sa_sigaction = handler;
79 sa.sa_flags = SA_SIGINFO | flags;
80 sigemptyset(&sa.sa_mask);
81 if (sigaction(sig, &sa, 0))
82 fatal_error("sigaction");
83}
84
85static void clearhandler(int sig)
86{
87 struct sigaction sa;
88
89 memset(&sa, 0, sizeof(sa));
90 sa.sa_handler = SIG_DFL;
91 sigemptyset(&sa.sa_mask);
92 if (sigaction(sig, &sa, 0))
93 fatal_error("sigaction");
94}
95
96#define XFEATURE_XTILECFG 17
97#define XFEATURE_XTILEDATA 18
98#define XFEATURE_MASK_XTILECFG (1 << XFEATURE_XTILECFG)
99#define XFEATURE_MASK_XTILEDATA (1 << XFEATURE_XTILEDATA)
100#define XFEATURE_MASK_XTILE (XFEATURE_MASK_XTILECFG | XFEATURE_MASK_XTILEDATA)
101
102#define CPUID_LEAF1_ECX_XSAVE_MASK (1 << 26)
103#define CPUID_LEAF1_ECX_OSXSAVE_MASK (1 << 27)
104static inline void check_cpuid_xsave(void)
105{
106 uint32_t eax, ebx, ecx, edx;
107
108 /*
109 * CPUID.1:ECX.XSAVE[bit 26] enumerates general
110 * support for the XSAVE feature set, including
111 * XGETBV.
112 */
113 __cpuid_count(1, 0, eax, ebx, ecx, edx);
114 if (!(ecx & CPUID_LEAF1_ECX_XSAVE_MASK))
115 fatal_error("cpuid: no CPU xsave support");
116 if (!(ecx & CPUID_LEAF1_ECX_OSXSAVE_MASK))
117 fatal_error("cpuid: no OS xsave support");
118}
119
120static uint32_t xbuf_size;
121
122static struct {
123 uint32_t xbuf_offset;
124 uint32_t size;
125} xtiledata;
126
127#define CPUID_LEAF_XSTATE 0xd
128#define CPUID_SUBLEAF_XSTATE_USER 0x0
129#define TILE_CPUID 0x1d
130#define TILE_PALETTE_ID 0x1
131
132static void check_cpuid_xtiledata(void)
133{
134 uint32_t eax, ebx, ecx, edx;
135
136 __cpuid_count(CPUID_LEAF_XSTATE, CPUID_SUBLEAF_XSTATE_USER,
137 eax, ebx, ecx, edx);
138
139 /*
140 * EBX enumerates the size (in bytes) required by the XSAVE
141 * instruction for an XSAVE area containing all the user state
142 * components corresponding to bits currently set in XCR0.
143 *
144 * Stash that off so it can be used to allocate buffers later.
145 */
146 xbuf_size = ebx;
147
148 __cpuid_count(CPUID_LEAF_XSTATE, XFEATURE_XTILEDATA,
149 eax, ebx, ecx, edx);
150 /*
151 * eax: XTILEDATA state component size
152 * ebx: XTILEDATA state component offset in user buffer
153 */
154 if (!eax || !ebx)
155 fatal_error("xstate cpuid: invalid tile data size/offset: %d/%d",
156 eax, ebx);
157
158 xtiledata.size = eax;
159 xtiledata.xbuf_offset = ebx;
160}
161
162/* The helpers for managing XSAVE buffer and tile states: */
163
164struct xsave_buffer *alloc_xbuf(void)
165{
166 struct xsave_buffer *xbuf;
167
168 /* XSAVE buffer should be 64B-aligned. */
169 xbuf = aligned_alloc(64, xbuf_size);
170 if (!xbuf)
171 fatal_error("aligned_alloc()");
172 return xbuf;
173}
174
175static inline void clear_xstate_header(struct xsave_buffer *buffer)
176{
177 memset(&buffer->header, 0, sizeof(buffer->header));
178}
179
180static inline uint64_t get_xstatebv(struct xsave_buffer *buffer)
181{
182 /* XSTATE_BV is at the beginning of the header: */
183 return *(uint64_t *)&buffer->header;
184}
185
186static inline void set_xstatebv(struct xsave_buffer *buffer, uint64_t bv)
187{
188 /* XSTATE_BV is at the beginning of the header: */
189 *(uint64_t *)(&buffer->header) = bv;
190}
191
192static void set_rand_tiledata(struct xsave_buffer *xbuf)
193{
194 int *ptr = (int *)&xbuf->bytes[xtiledata.xbuf_offset];
195 int data;
196 int i;
197
198 /*
199 * Ensure that 'data' is never 0. This ensures that
200 * the registers are never in their initial configuration
201 * and thus never tracked as being in the init state.
202 */
203 data = rand() | 1;
204
205 for (i = 0; i < xtiledata.size / sizeof(int); i++, ptr++)
206 *ptr = data;
207}
208
209struct xsave_buffer *stashed_xsave;
210
211static void init_stashed_xsave(void)
212{
213 stashed_xsave = alloc_xbuf();
214 if (!stashed_xsave)
215 fatal_error("failed to allocate stashed_xsave\n");
216 clear_xstate_header(stashed_xsave);
217}
218
219static void free_stashed_xsave(void)
220{
221 free(stashed_xsave);
222}
223
224/* See 'struct _fpx_sw_bytes' at sigcontext.h */
225#define SW_BYTES_OFFSET 464
226/* N.B. The struct's field name varies so read from the offset. */
227#define SW_BYTES_BV_OFFSET (SW_BYTES_OFFSET + 8)
228
229static inline struct _fpx_sw_bytes *get_fpx_sw_bytes(void *buffer)
230{
231 return (struct _fpx_sw_bytes *)(buffer + SW_BYTES_OFFSET);
232}
233
234static inline uint64_t get_fpx_sw_bytes_features(void *buffer)
235{
236 return *(uint64_t *)(buffer + SW_BYTES_BV_OFFSET);
237}
238
239/* Work around printf() being unsafe in signals: */
240#define SIGNAL_BUF_LEN 1000
241char signal_message_buffer[SIGNAL_BUF_LEN];
242void sig_print(char *msg)
243{
244 int left = SIGNAL_BUF_LEN - strlen(signal_message_buffer) - 1;
245
246 strncat(signal_message_buffer, msg, left);
247}
248
249static volatile bool noperm_signaled;
250static int noperm_errs;
251/*
252 * Signal handler for when AMX is used but
253 * permission has not been obtained.
254 */
255static void handle_noperm(int sig, siginfo_t *si, void *ctx_void)
256{
257 ucontext_t *ctx = (ucontext_t *)ctx_void;
258 void *xbuf = ctx->uc_mcontext.fpregs;
259 struct _fpx_sw_bytes *sw_bytes;
260 uint64_t features;
261
262 /* Reset the signal message buffer: */
263 signal_message_buffer[0] = '\0';
264 sig_print("\tAt SIGILL handler,\n");
265
266 if (si->si_code != ILL_ILLOPC) {
267 noperm_errs++;
268 sig_print("[FAIL]\tInvalid signal code.\n");
269 } else {
270 sig_print("[OK]\tValid signal code (ILL_ILLOPC).\n");
271 }
272
273 sw_bytes = get_fpx_sw_bytes(xbuf);
274 /*
275 * Without permission, the signal XSAVE buffer should not
276 * have room for AMX register state (aka. xtiledata).
277 * Check that the size does not overlap with where xtiledata
278 * will reside.
279 *
280 * This also implies that no state components *PAST*
281 * XTILEDATA (features >=19) can be present in the buffer.
282 */
283 if (sw_bytes->xstate_size <= xtiledata.xbuf_offset) {
284 sig_print("[OK]\tValid xstate size\n");
285 } else {
286 noperm_errs++;
287 sig_print("[FAIL]\tInvalid xstate size\n");
288 }
289
290 features = get_fpx_sw_bytes_features(xbuf);
291 /*
292 * Without permission, the XTILEDATA feature
293 * bit should not be set.
294 */
295 if ((features & XFEATURE_MASK_XTILEDATA) == 0) {
296 sig_print("[OK]\tValid xstate mask\n");
297 } else {
298 noperm_errs++;
299 sig_print("[FAIL]\tInvalid xstate mask\n");
300 }
301
302 noperm_signaled = true;
303 ctx->uc_mcontext.gregs[REG_RIP] += 3; /* Skip the faulting XRSTOR */
304}
305
306/* Return true if XRSTOR is successful; otherwise, false. */
307static inline bool xrstor_safe(struct xsave_buffer *xbuf, uint64_t mask)
308{
309 noperm_signaled = false;
310 xrstor(xbuf, mask);
311
312 /* Print any messages produced by the signal code: */
313 printf("%s", signal_message_buffer);
314 /*
315 * Reset the buffer to make sure any future printing
316 * only outputs new messages:
317 */
318 signal_message_buffer[0] = '\0';
319
320 if (noperm_errs)
321 fatal_error("saw %d errors in noperm signal handler\n", noperm_errs);
322
323 return !noperm_signaled;
324}
325
326/*
327 * Use XRSTOR to populate the XTILEDATA registers with
328 * random data.
329 *
330 * Return true if successful; otherwise, false.
331 */
332static inline bool load_rand_tiledata(struct xsave_buffer *xbuf)
333{
334 clear_xstate_header(xbuf);
335 set_xstatebv(xbuf, XFEATURE_MASK_XTILEDATA);
336 set_rand_tiledata(xbuf);
337 return xrstor_safe(xbuf, XFEATURE_MASK_XTILEDATA);
338}
339
340/* Return XTILEDATA to its initial configuration. */
341static inline void init_xtiledata(void)
342{
343 clear_xstate_header(stashed_xsave);
344 xrstor_safe(stashed_xsave, XFEATURE_MASK_XTILEDATA);
345}
346
347enum expected_result { FAIL_EXPECTED, SUCCESS_EXPECTED };
348
349/* arch_prctl() and sigaltstack() test */
350
351#define ARCH_GET_XCOMP_PERM 0x1022
352#define ARCH_REQ_XCOMP_PERM 0x1023
353
354static void req_xtiledata_perm(void)
355{
356 syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_PERM, XFEATURE_XTILEDATA);
357}
358
359static void validate_req_xcomp_perm(enum expected_result exp)
360{
361 unsigned long bitmask, expected_bitmask;
362 long rc;
363
364 rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_PERM, &bitmask);
365 if (rc) {
366 fatal_error("prctl(ARCH_GET_XCOMP_PERM) error: %ld", rc);
367 } else if (!(bitmask & XFEATURE_MASK_XTILECFG)) {
368 fatal_error("ARCH_GET_XCOMP_PERM returns XFEATURE_XTILECFG off.");
369 }
370
371 rc = syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_PERM, XFEATURE_XTILEDATA);
372 if (exp == FAIL_EXPECTED) {
373 if (rc) {
374 printf("[OK]\tARCH_REQ_XCOMP_PERM saw expected failure..\n");
375 return;
376 }
377
378 fatal_error("ARCH_REQ_XCOMP_PERM saw unexpected success.\n");
379 } else if (rc) {
380 fatal_error("ARCH_REQ_XCOMP_PERM saw unexpected failure.\n");
381 }
382
383 expected_bitmask = bitmask | XFEATURE_MASK_XTILEDATA;
384
385 rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_PERM, &bitmask);
386 if (rc) {
387 fatal_error("prctl(ARCH_GET_XCOMP_PERM) error: %ld", rc);
388 } else if (bitmask != expected_bitmask) {
389 fatal_error("ARCH_REQ_XCOMP_PERM set a wrong bitmask: %lx, expected: %lx.\n",
390 bitmask, expected_bitmask);
391 } else {
392 printf("\tARCH_REQ_XCOMP_PERM is successful.\n");
393 }
394}
395
396static void validate_xcomp_perm(enum expected_result exp)
397{
398 bool load_success = load_rand_tiledata(stashed_xsave);
399
400 if (exp == FAIL_EXPECTED) {
401 if (load_success) {
402 noperm_errs++;
403 printf("[FAIL]\tLoad tiledata succeeded.\n");
404 } else {
405 printf("[OK]\tLoad tiledata failed.\n");
406 }
407 } else if (exp == SUCCESS_EXPECTED) {
408 if (load_success) {
409 printf("[OK]\tLoad tiledata succeeded.\n");
410 } else {
411 noperm_errs++;
412 printf("[FAIL]\tLoad tiledata failed.\n");
413 }
414 }
415}
416
417#ifndef AT_MINSIGSTKSZ
418# define AT_MINSIGSTKSZ 51
419#endif
420
421static void *alloc_altstack(unsigned int size)
422{
423 void *altstack;
424
425 altstack = mmap(NULL, size, PROT_READ | PROT_WRITE,
426 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
427
428 if (altstack == MAP_FAILED)
429 fatal_error("mmap() for altstack");
430
431 return altstack;
432}
433
434static void setup_altstack(void *addr, unsigned long size, enum expected_result exp)
435{
436 stack_t ss;
437 int rc;
438
439 memset(&ss, 0, sizeof(ss));
440 ss.ss_size = size;
441 ss.ss_sp = addr;
442
443 rc = sigaltstack(&ss, NULL);
444
445 if (exp == FAIL_EXPECTED) {
446 if (rc) {
447 printf("[OK]\tsigaltstack() failed.\n");
448 } else {
449 fatal_error("sigaltstack() succeeded unexpectedly.\n");
450 }
451 } else if (rc) {
452 fatal_error("sigaltstack()");
453 }
454}
455
456static void test_dynamic_sigaltstack(void)
457{
458 unsigned int small_size, enough_size;
459 unsigned long minsigstksz;
460 void *altstack;
461
462 minsigstksz = getauxval(AT_MINSIGSTKSZ);
463 printf("\tAT_MINSIGSTKSZ = %lu\n", minsigstksz);
464 /*
465 * getauxval() itself can return 0 for failure or
466 * success. But, in this case, AT_MINSIGSTKSZ
467 * will always return a >=0 value if implemented.
468 * Just check for 0.
469 */
470 if (minsigstksz == 0) {
471 printf("no support for AT_MINSIGSTKSZ, skipping sigaltstack tests\n");
472 return;
473 }
474
475 enough_size = minsigstksz * 2;
476
477 altstack = alloc_altstack(enough_size);
478 printf("\tAllocate memory for altstack (%u bytes).\n", enough_size);
479
480 /*
481 * Try setup_altstack() with a size which can not fit
482 * XTILEDATA. ARCH_REQ_XCOMP_PERM should fail.
483 */
484 small_size = minsigstksz - xtiledata.size;
485 printf("\tAfter sigaltstack() with small size (%u bytes).\n", small_size);
486 setup_altstack(altstack, small_size, SUCCESS_EXPECTED);
487 validate_req_xcomp_perm(FAIL_EXPECTED);
488
489 /*
490 * Try setup_altstack() with a size derived from
491 * AT_MINSIGSTKSZ. It should be more than large enough
492 * and thus ARCH_REQ_XCOMP_PERM should succeed.
493 */
494 printf("\tAfter sigaltstack() with enough size (%u bytes).\n", enough_size);
495 setup_altstack(altstack, enough_size, SUCCESS_EXPECTED);
496 validate_req_xcomp_perm(SUCCESS_EXPECTED);
497
498 /*
499 * Try to coerce setup_altstack() to again accept a
500 * too-small altstack. This ensures that big-enough
501 * sigaltstacks can not shrink to a too-small value
502 * once XTILEDATA permission is established.
503 */
504 printf("\tThen, sigaltstack() with small size (%u bytes).\n", small_size);
505 setup_altstack(altstack, small_size, FAIL_EXPECTED);
506}
507
508static void test_dynamic_state(void)
509{
510 pid_t parent, child, grandchild;
511
512 parent = fork();
513 if (parent < 0) {
514 /* fork() failed */
515 fatal_error("fork");
516 } else if (parent > 0) {
517 int status;
518 /* fork() succeeded. Now in the parent. */
519
520 wait(&status);
521 if (!WIFEXITED(status) || WEXITSTATUS(status))
522 fatal_error("arch_prctl test parent exit");
523 return;
524 }
525 /* fork() succeeded. Now in the child . */
526
527 printf("[RUN]\tCheck ARCH_REQ_XCOMP_PERM around process fork() and sigaltack() test.\n");
528
529 printf("\tFork a child.\n");
530 child = fork();
531 if (child < 0) {
532 fatal_error("fork");
533 } else if (child > 0) {
534 int status;
535
536 wait(&status);
537 if (!WIFEXITED(status) || WEXITSTATUS(status))
538 fatal_error("arch_prctl test child exit");
539 _exit(0);
540 }
541
542 /*
543 * The permission request should fail without an
544 * XTILEDATA-compatible signal stack
545 */
546 printf("\tTest XCOMP_PERM at child.\n");
547 validate_xcomp_perm(FAIL_EXPECTED);
548
549 /*
550 * Set up an XTILEDATA-compatible signal stack and
551 * also obtain permission to populate XTILEDATA.
552 */
553 printf("\tTest dynamic sigaltstack at child:\n");
554 test_dynamic_sigaltstack();
555
556 /* Ensure that XTILEDATA can be populated. */
557 printf("\tTest XCOMP_PERM again at child.\n");
558 validate_xcomp_perm(SUCCESS_EXPECTED);
559
560 printf("\tFork a grandchild.\n");
561 grandchild = fork();
562 if (grandchild < 0) {
563 /* fork() failed */
564 fatal_error("fork");
565 } else if (!grandchild) {
566 /* fork() succeeded. Now in the (grand)child. */
567 printf("\tTest XCOMP_PERM at grandchild.\n");
568
569 /*
570 * Ensure that the grandchild inherited
571 * permission and a compatible sigaltstack:
572 */
573 validate_xcomp_perm(SUCCESS_EXPECTED);
574 } else {
575 int status;
576 /* fork() succeeded. Now in the parent. */
577
578 wait(&status);
579 if (!WIFEXITED(status) || WEXITSTATUS(status))
580 fatal_error("fork test grandchild");
581 }
582
583 _exit(0);
584}
585
586/*
587 * Save current register state and compare it to @xbuf1.'
588 *
589 * Returns false if @xbuf1 matches the registers.
590 * Returns true if @xbuf1 differs from the registers.
591 */
592static inline bool __validate_tiledata_regs(struct xsave_buffer *xbuf1)
593{
594 struct xsave_buffer *xbuf2;
595 int ret;
596
597 xbuf2 = alloc_xbuf();
598 if (!xbuf2)
599 fatal_error("failed to allocate XSAVE buffer\n");
600
601 xsave(xbuf2, XFEATURE_MASK_XTILEDATA);
602 ret = memcmp(&xbuf1->bytes[xtiledata.xbuf_offset],
603 &xbuf2->bytes[xtiledata.xbuf_offset],
604 xtiledata.size);
605
606 free(xbuf2);
607
608 if (ret == 0)
609 return false;
610 return true;
611}
612
613static inline void validate_tiledata_regs_same(struct xsave_buffer *xbuf)
614{
615 int ret = __validate_tiledata_regs(xbuf);
616
617 if (ret != 0)
618 fatal_error("TILEDATA registers changed");
619}
620
621static inline void validate_tiledata_regs_changed(struct xsave_buffer *xbuf)
622{
623 int ret = __validate_tiledata_regs(xbuf);
624
625 if (ret == 0)
626 fatal_error("TILEDATA registers did not change");
627}
628
629/* tiledata inheritance test */
630
631static void test_fork(void)
632{
633 pid_t child, grandchild;
634
635 child = fork();
636 if (child < 0) {
637 /* fork() failed */
638 fatal_error("fork");
639 } else if (child > 0) {
640 /* fork() succeeded. Now in the parent. */
641 int status;
642
643 wait(&status);
644 if (!WIFEXITED(status) || WEXITSTATUS(status))
645 fatal_error("fork test child");
646 return;
647 }
648 /* fork() succeeded. Now in the child. */
649 printf("[RUN]\tCheck tile data inheritance.\n\tBefore fork(), load tiledata\n");
650
651 load_rand_tiledata(stashed_xsave);
652
653 grandchild = fork();
654 if (grandchild < 0) {
655 /* fork() failed */
656 fatal_error("fork");
657 } else if (grandchild > 0) {
658 /* fork() succeeded. Still in the first child. */
659 int status;
660
661 wait(&status);
662 if (!WIFEXITED(status) || WEXITSTATUS(status))
663 fatal_error("fork test grand child");
664 _exit(0);
665 }
666 /* fork() succeeded. Now in the (grand)child. */
667
668 /*
669 * TILEDATA registers are not preserved across fork().
670 * Ensure that their value has changed:
671 */
672 validate_tiledata_regs_changed(stashed_xsave);
673
674 _exit(0);
675}
676
677/* Context switching test */
678
679static struct _ctxtswtest_cfg {
680 unsigned int iterations;
681 unsigned int num_threads;
682} ctxtswtest_config;
683
684struct futex_info {
685 pthread_t thread;
686 int nr;
687 pthread_mutex_t mutex;
688 struct futex_info *next;
689};
690
691static void *check_tiledata(void *info)
692{
693 struct futex_info *finfo = (struct futex_info *)info;
694 struct xsave_buffer *xbuf;
695 int i;
696
697 xbuf = alloc_xbuf();
698 if (!xbuf)
699 fatal_error("unable to allocate XSAVE buffer");
700
701 /*
702 * Load random data into 'xbuf' and then restore
703 * it to the tile registers themselves.
704 */
705 load_rand_tiledata(xbuf);
706 for (i = 0; i < ctxtswtest_config.iterations; i++) {
707 pthread_mutex_lock(&finfo->mutex);
708
709 /*
710 * Ensure the register values have not
711 * diverged from those recorded in 'xbuf'.
712 */
713 validate_tiledata_regs_same(xbuf);
714
715 /* Load new, random values into xbuf and registers */
716 load_rand_tiledata(xbuf);
717
718 /*
719 * The last thread's last unlock will be for
720 * thread 0's mutex. However, thread 0 will
721 * have already exited the loop and the mutex
722 * will already be unlocked.
723 *
724 * Because this is not an ERRORCHECK mutex,
725 * that inconsistency will be silently ignored.
726 */
727 pthread_mutex_unlock(&finfo->next->mutex);
728 }
729
730 free(xbuf);
731 /*
732 * Return this thread's finfo, which is
733 * a unique value for this thread.
734 */
735 return finfo;
736}
737
738static int create_threads(int num, struct futex_info *finfo)
739{
740 int i;
741
742 for (i = 0; i < num; i++) {
743 int next_nr;
744
745 finfo[i].nr = i;
746 /*
747 * Thread 'i' will wait on this mutex to
748 * be unlocked. Lock it immediately after
749 * initialization:
750 */
751 pthread_mutex_init(&finfo[i].mutex, NULL);
752 pthread_mutex_lock(&finfo[i].mutex);
753
754 next_nr = (i + 1) % num;
755 finfo[i].next = &finfo[next_nr];
756
757 if (pthread_create(&finfo[i].thread, NULL, check_tiledata, &finfo[i]))
758 fatal_error("pthread_create()");
759 }
760 return 0;
761}
762
763static void affinitize_cpu0(void)
764{
765 cpu_set_t cpuset;
766
767 CPU_ZERO(&cpuset);
768 CPU_SET(0, &cpuset);
769
770 if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
771 fatal_error("sched_setaffinity to CPU 0");
772}
773
774static void test_context_switch(void)
775{
776 struct futex_info *finfo;
777 int i;
778
779 /* Affinitize to one CPU to force context switches */
780 affinitize_cpu0();
781
782 req_xtiledata_perm();
783
784 printf("[RUN]\tCheck tiledata context switches, %d iterations, %d threads.\n",
785 ctxtswtest_config.iterations,
786 ctxtswtest_config.num_threads);
787
788
789 finfo = malloc(sizeof(*finfo) * ctxtswtest_config.num_threads);
790 if (!finfo)
791 fatal_error("malloc()");
792
793 create_threads(ctxtswtest_config.num_threads, finfo);
794
795 /*
796 * This thread wakes up thread 0
797 * Thread 0 will wake up 1
798 * Thread 1 will wake up 2
799 * ...
800 * the last thread will wake up 0
801 *
802 * ... this will repeat for the configured
803 * number of iterations.
804 */
805 pthread_mutex_unlock(&finfo[0].mutex);
806
807 /* Wait for all the threads to finish: */
808 for (i = 0; i < ctxtswtest_config.num_threads; i++) {
809 void *thread_retval;
810 int rc;
811
812 rc = pthread_join(finfo[i].thread, &thread_retval);
813
814 if (rc)
815 fatal_error("pthread_join() failed for thread %d err: %d\n",
816 i, rc);
817
818 if (thread_retval != &finfo[i])
819 fatal_error("unexpected thread retval for thread %d: %p\n",
820 i, thread_retval);
821
822 }
823
824 printf("[OK]\tNo incorrect case was found.\n");
825
826 free(finfo);
827}
828
829int main(void)
830{
831 /* Check hardware availability at first */
832 check_cpuid_xsave();
833 check_cpuid_xtiledata();
834
835 init_stashed_xsave();
836 sethandler(SIGILL, handle_noperm, 0);
837
838 test_dynamic_state();
839
840 /* Request permission for the following tests */
841 req_xtiledata_perm();
842
843 test_fork();
844
845 ctxtswtest_config.iterations = 10;
846 ctxtswtest_config.num_threads = 5;
847 test_context_switch();
848
849 clearhandler(SIGILL);
850 free_stashed_xsave();
851
852 return 0;
853}