Test thread-safety of tld_create_key(). Since tld_create_key() does not rely on locks but memory barriers and atomic operations to protect the shared metadata, the thread-safety of the function is non-trivial. Make sure concurrent tld_key_create(), both valid and invalid, can not race and corrupt metatada, which may leads to TLDs not being thread- specific or duplicate TLDs with the same name. Signed-off-by: Amery Hung <ameryhung@xxxxxxxxx> --- .../bpf/prog_tests/test_task_local_data.c | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/test_task_local_data.c b/tools/testing/selftests/bpf/prog_tests/test_task_local_data.c index 738fc1c9d8a4..5743b753a4a1 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_task_local_data.c +++ b/tools/testing/selftests/bpf/prog_tests/test_task_local_data.c @@ -156,8 +156,99 @@ static void test_task_local_data_basic(void) pthread_join(thread[i], NULL); } +#define TEST_RACE_THREAD_NUM 61 + +void *test_task_local_data_race_thread(void *arg) +{ + char key_name[32]; + tld_key_t key; + int id, fd; + + id = (intptr_t)arg & 0x0000ffff; + fd = ((intptr_t)arg & 0xffff0000) >> 16; + + key = tld_create_key(fd, "value_not_exist", PAGE_SIZE + 1); + ASSERT_EQ(tld_key_err_or_zero(key), -E2BIG, "tld_create_key"); + + /* + * If more than one thread succeed in creating value1 or value2, + * some threads will fail to create thread_<id> later. + */ + key = tld_create_key(fd, "value1", sizeof(int)); + if (!tld_key_is_err(key)) + tld_keys[TEST_RACE_THREAD_NUM] = key; + key = tld_create_key(fd, "value2", sizeof(struct test_struct)); + if (!tld_key_is_err(key)) + tld_keys[TEST_RACE_THREAD_NUM + 1] = key; + + snprintf(key_name, 32, "thread_%d", id); + tld_keys[id] = tld_create_key(fd, key_name, sizeof(int)); + ASSERT_FALSE(tld_key_is_err(tld_keys[id]), "tld_create_key"); + + pthread_exit(NULL); +} + +static void test_task_local_data_race(void) +{ + LIBBPF_OPTS(bpf_test_run_opts, opts); + pthread_t thread[TEST_RACE_THREAD_NUM]; + struct test_task_local_data *skel; + int fd, i, j, err, *data, arg; + + skel = test_task_local_data__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) + return; + + fd = bpf_map__fd(skel->maps.tld_data_map); + + for (j = 0; j < 100; j++) { + reset_tld(); + + for (i = 0; i < TEST_RACE_THREAD_NUM; i++) { + /* + * Try to make tld_create_key() race with each other. Call + * tld_create_key(), both valid and invalid, from different threads. + */ + arg = i | fd << 16; + err = pthread_create(&thread[i], NULL, test_task_local_data_race_thread, + (void *)(intptr_t)arg); + if (!ASSERT_OK(err, "pthread_create")) + goto out; + } + + /* Wait for all tld_create_key() to return */ + for (i = 0; i < TEST_RACE_THREAD_NUM; i++) + pthread_join(thread[i], NULL); + + /* Run task_init to make sure no invalid TLDs are added */ + err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.task_init), &opts); + ASSERT_OK(err, "run task_init"); + ASSERT_OK(opts.retval, "task_init retval"); + + /* Write a unique number in the range of [0, TEST_RACE_THREAD_NUM) to each TLD */ + for (i = 0; i < TEST_RACE_THREAD_NUM; i++) { + data = tld_get_data(fd, tld_keys[i]); + if (!ASSERT_OK_PTR(data, "tld_get_data")) + goto out; + *data = i; + } + + /* Read TLDs and check the value to see if any address collides with another */ + for (i = 0; i < TEST_RACE_THREAD_NUM; i++) { + data = tld_get_data(fd, tld_keys[i]); + if (!ASSERT_OK_PTR(data, "tld_get_data")) + goto out; + ASSERT_EQ(*data, i, "check TLD"); + } + } +out: + tld_free(); +} + void test_task_local_data(void) { if (test__start_subtest("task_local_data_basic")) test_task_local_data_basic(); + if (test__start_subtest("task_local_data_race")) + test_task_local_data_race(); } -- 2.47.1