1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 2f08c3bdfSopenharmony_ci/* 3f08c3bdfSopenharmony_ci * Copyright (C) 2015 Cyril Hrubis <chrubis@suse.cz> 4f08c3bdfSopenharmony_ci * 5f08c3bdfSopenharmony_ci * Block several threads on a private mutex, then wake them up. 6f08c3bdfSopenharmony_ci */ 7f08c3bdfSopenharmony_ci 8f08c3bdfSopenharmony_ci#include <sys/types.h> 9f08c3bdfSopenharmony_ci 10f08c3bdfSopenharmony_ci#include "futextest.h" 11f08c3bdfSopenharmony_ci#include "futex_utils.h" 12f08c3bdfSopenharmony_ci#include "tst_safe_pthread.h" 13f08c3bdfSopenharmony_ci 14f08c3bdfSopenharmony_cistatic futex_t futex = FUTEX_INITIALIZER; 15f08c3bdfSopenharmony_ci 16f08c3bdfSopenharmony_cistatic volatile int threads_flags[55]; 17f08c3bdfSopenharmony_ci 18f08c3bdfSopenharmony_cistatic struct futex_test_variants variants[] = { 19f08c3bdfSopenharmony_ci#if (__NR_futex != __LTP__NR_INVALID_SYSCALL) 20f08c3bdfSopenharmony_ci { .fntype = FUTEX_FN_FUTEX, .desc = "syscall with old kernel spec"}, 21f08c3bdfSopenharmony_ci#endif 22f08c3bdfSopenharmony_ci 23f08c3bdfSopenharmony_ci#if (__NR_futex_time64 != __LTP__NR_INVALID_SYSCALL) 24f08c3bdfSopenharmony_ci { .fntype = FUTEX_FN_FUTEX64, .desc = "syscall time64 with kernel spec"}, 25f08c3bdfSopenharmony_ci#endif 26f08c3bdfSopenharmony_ci}; 27f08c3bdfSopenharmony_ci 28f08c3bdfSopenharmony_cistatic int threads_awake(void) 29f08c3bdfSopenharmony_ci{ 30f08c3bdfSopenharmony_ci int ret = 0; 31f08c3bdfSopenharmony_ci unsigned int i; 32f08c3bdfSopenharmony_ci 33f08c3bdfSopenharmony_ci for (i = 0; i < ARRAY_SIZE(threads_flags); i++) { 34f08c3bdfSopenharmony_ci if (threads_flags[i]) 35f08c3bdfSopenharmony_ci ret++; 36f08c3bdfSopenharmony_ci } 37f08c3bdfSopenharmony_ci 38f08c3bdfSopenharmony_ci return ret; 39f08c3bdfSopenharmony_ci} 40f08c3bdfSopenharmony_ci 41f08c3bdfSopenharmony_cistatic void clear_threads_awake(void) 42f08c3bdfSopenharmony_ci{ 43f08c3bdfSopenharmony_ci unsigned int i; 44f08c3bdfSopenharmony_ci 45f08c3bdfSopenharmony_ci for (i = 0; i < ARRAY_SIZE(threads_flags); i++) 46f08c3bdfSopenharmony_ci threads_flags[i] = 0; 47f08c3bdfSopenharmony_ci} 48f08c3bdfSopenharmony_ci 49f08c3bdfSopenharmony_cistatic void *threaded(void *arg) 50f08c3bdfSopenharmony_ci{ 51f08c3bdfSopenharmony_ci struct futex_test_variants *tv = &variants[tst_variant]; 52f08c3bdfSopenharmony_ci long i = (long)arg; 53f08c3bdfSopenharmony_ci 54f08c3bdfSopenharmony_ci futex_wait(tv->fntype, &futex, futex, NULL, FUTEX_PRIVATE_FLAG); 55f08c3bdfSopenharmony_ci 56f08c3bdfSopenharmony_ci threads_flags[i] = 1; 57f08c3bdfSopenharmony_ci 58f08c3bdfSopenharmony_ci return NULL; 59f08c3bdfSopenharmony_ci} 60f08c3bdfSopenharmony_ci 61f08c3bdfSopenharmony_cistatic void do_child(void) 62f08c3bdfSopenharmony_ci{ 63f08c3bdfSopenharmony_ci struct futex_test_variants *tv = &variants[tst_variant]; 64f08c3bdfSopenharmony_ci int i, j, awake; 65f08c3bdfSopenharmony_ci pthread_t t[55]; 66f08c3bdfSopenharmony_ci 67f08c3bdfSopenharmony_ci for (i = 0; i < (int)ARRAY_SIZE(t); i++) 68f08c3bdfSopenharmony_ci SAFE_PTHREAD_CREATE(&t[i], NULL, threaded, (void*)((long)i)); 69f08c3bdfSopenharmony_ci 70f08c3bdfSopenharmony_ci while (wait_for_threads(ARRAY_SIZE(t))) 71f08c3bdfSopenharmony_ci usleep(100); 72f08c3bdfSopenharmony_ci 73f08c3bdfSopenharmony_ci for (i = 1; i <= 10; i++) { 74f08c3bdfSopenharmony_ci clear_threads_awake(); 75f08c3bdfSopenharmony_ci TEST(futex_wake(tv->fntype, &futex, i, FUTEX_PRIVATE_FLAG)); 76f08c3bdfSopenharmony_ci if (i != TST_RET) { 77f08c3bdfSopenharmony_ci tst_res(TFAIL | TTERRNO, 78f08c3bdfSopenharmony_ci "futex_wake() woken up %li threads, expected %i", 79f08c3bdfSopenharmony_ci TST_RET, i); 80f08c3bdfSopenharmony_ci } 81f08c3bdfSopenharmony_ci 82f08c3bdfSopenharmony_ci for (j = 0; j < 100000; j++) { 83f08c3bdfSopenharmony_ci awake = threads_awake(); 84f08c3bdfSopenharmony_ci if (awake == i) 85f08c3bdfSopenharmony_ci break; 86f08c3bdfSopenharmony_ci 87f08c3bdfSopenharmony_ci usleep(100); 88f08c3bdfSopenharmony_ci } 89f08c3bdfSopenharmony_ci 90f08c3bdfSopenharmony_ci if (awake == i) { 91f08c3bdfSopenharmony_ci tst_res(TPASS, "futex_wake() woken up %i threads", i); 92f08c3bdfSopenharmony_ci } else { 93f08c3bdfSopenharmony_ci tst_res(TFAIL, "Woken up %i threads, expected %i", 94f08c3bdfSopenharmony_ci awake, i); 95f08c3bdfSopenharmony_ci } 96f08c3bdfSopenharmony_ci } 97f08c3bdfSopenharmony_ci 98f08c3bdfSopenharmony_ci TEST(futex_wake(tv->fntype, &futex, 1, FUTEX_PRIVATE_FLAG)); 99f08c3bdfSopenharmony_ci if (TST_RET) { 100f08c3bdfSopenharmony_ci tst_res(TFAIL | TTERRNO, "futex_wake() woken up %li, none were waiting", 101f08c3bdfSopenharmony_ci TST_RET); 102f08c3bdfSopenharmony_ci } else { 103f08c3bdfSopenharmony_ci tst_res(TPASS, "futex_wake() woken up 0 threads"); 104f08c3bdfSopenharmony_ci } 105f08c3bdfSopenharmony_ci 106f08c3bdfSopenharmony_ci for (i = 0; i < (int)ARRAY_SIZE(t); i++) 107f08c3bdfSopenharmony_ci SAFE_PTHREAD_JOIN(t[i], NULL); 108f08c3bdfSopenharmony_ci 109f08c3bdfSopenharmony_ci exit(0); 110f08c3bdfSopenharmony_ci} 111f08c3bdfSopenharmony_ci 112f08c3bdfSopenharmony_ci/* 113f08c3bdfSopenharmony_ci * We do the real test in a child because with the test -i parameter the loop 114f08c3bdfSopenharmony_ci * that checks that all threads are sleeping may fail with ENOENT. That is 115f08c3bdfSopenharmony_ci * because some of the threads from previous run may still be there. 116f08c3bdfSopenharmony_ci * 117f08c3bdfSopenharmony_ci * Which is because the userspace part of pthread_join() sleeps in a futex on a 118f08c3bdfSopenharmony_ci * pthread tid which is woken up at the end of the exit_mm(tsk) which is before 119f08c3bdfSopenharmony_ci * the process is removed from the parent thread_group list. So there is a 120f08c3bdfSopenharmony_ci * small race window where the readdir() returns the process tid as a directory 121f08c3bdfSopenharmony_ci * under /proc/$PID/tasks/, but the subsequent open() fails with ENOENT because 122f08c3bdfSopenharmony_ci * the thread was removed meanwhile. 123f08c3bdfSopenharmony_ci */ 124f08c3bdfSopenharmony_cistatic void run(void) 125f08c3bdfSopenharmony_ci{ 126f08c3bdfSopenharmony_ci if (!SAFE_FORK()) 127f08c3bdfSopenharmony_ci do_child(); 128f08c3bdfSopenharmony_ci} 129f08c3bdfSopenharmony_ci 130f08c3bdfSopenharmony_cistatic void setup(void) 131f08c3bdfSopenharmony_ci{ 132f08c3bdfSopenharmony_ci struct futex_test_variants *tv = &variants[tst_variant]; 133f08c3bdfSopenharmony_ci 134f08c3bdfSopenharmony_ci tst_res(TINFO, "Testing variant: %s", tv->desc); 135f08c3bdfSopenharmony_ci futex_supported_by_kernel(tv->fntype); 136f08c3bdfSopenharmony_ci} 137f08c3bdfSopenharmony_ci 138f08c3bdfSopenharmony_cistatic struct tst_test test = { 139f08c3bdfSopenharmony_ci .setup = setup, 140f08c3bdfSopenharmony_ci .test_all = run, 141f08c3bdfSopenharmony_ci .test_variants = ARRAY_SIZE(variants), 142f08c3bdfSopenharmony_ci .forks_child = 1, 143f08c3bdfSopenharmony_ci}; 144