diff options
| author | Sohil Mehta <[email protected]> | 2021-06-16 17:09:54 -0700 |
|---|---|---|
| committer | Sohil Mehta <[email protected]> | 2021-09-12 19:22:18 -0700 |
| commit | f20fa04c4553c6140dc8dc49e55fa282d2360ece (patch) | |
| tree | bf67f84749600be04ca8d4553dc151de9c186ef0 /tools/testing/selftests/uintr/ipc_base.c | |
| parent | ecf00695f241969452cba62c749ce9890bacc1f0 (diff) | |
!REVIEW: selftests/x86/uintr: Add a test suite for UINTR
The test suite currently covers:
1. SENDUIPI base IPC tests
2. UINTR Syscall unit tests
3. SENDUIPI instruction fault tests
4. A configurable IPC test to stress context switching
Planning to add the following in the future:
1. More context switching tests
2. UIF related tests
3. uintr_wait() syscall tests
4. Kernel to user notification tests
5. Stack adjust tests
Signed-off-by: Gayatri Kammela <[email protected]>
Signed-off-by: Sohil Mehta <[email protected]>
Diffstat (limited to 'tools/testing/selftests/uintr/ipc_base.c')
| -rw-r--r-- | tools/testing/selftests/uintr/ipc_base.c | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/tools/testing/selftests/uintr/ipc_base.c b/tools/testing/selftests/uintr/ipc_base.c new file mode 100644 index 000000000000..2d795f70b48a --- /dev/null +++ b/tools/testing/selftests/uintr/ipc_base.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, Intel Corporation. + * + * Sohil Mehta <[email protected]> + */ +#define _GNU_SOURCE +#include <stdio.h> +#include <x86gprintrin.h> +#include <pthread.h> +#include <signal.h> + +#include "uintr_common.h" + +unsigned int uintr_received; +unsigned int client_received; +unsigned int server_received; +unsigned int nerrs; + +static void __attribute__((interrupt)) uintr_handler(struct __uintr_frame *ui_frame, + unsigned long long vector) +{ + uintr_received = 1; +} + +static void __attribute__((interrupt)) client_handler(struct __uintr_frame *ui_frame, + unsigned long long vector) +{ + client_received = 1; +} + +static void __attribute__((interrupt)) server_handler(struct __uintr_frame *ui_frame, + unsigned long long vector) +{ + server_received = 1; +} + +/* This check doesn't fail. */ +static void print_uintr_support(void) +{ + printf("[RUN]\tCheck if User Interrupts (UINTR) is supported\n"); + if (uintr_supported()) + printf("[OK]\tUser Interrupts (UINTR) is supported\n"); + else + printf("[OK]\tUser Interrupts (UINTR) is not supported. Skipping rest of the tests silently\n"); +} + +static void *sender_thread(void *arg) +{ + int uintr_fd = *(int *)arg; + int uipi_index; + + uipi_index = uintr_register_sender(uintr_fd, 0); + if (uipi_index < 0) { + printf("[FAIL]\tSender register error\n"); + return NULL; + } + + /* Sleep before sending IPI to allow the receiver to start waiting */ + usleep(100); + + printf("\tother thread: sending IPI\n"); + _senduipi(uipi_index); + + uintr_unregister_sender(uintr_fd, 0); + + return NULL; +} + +static inline void cpu_delay(void) +{ + long long dl = 1000; + volatile long long cnt = dl << 10; + + while (cnt--) + dl++; +} + +static void test_thread_ipi(void) +{ + int wait_for_delay = 1000; + int vector = 0; + int uintr_fd; + pthread_t pt; + + /* Register interrupt handler */ + if (uintr_register_handler(uintr_handler, 0)) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + + /* Create uintr_fd */ + uintr_fd = uintr_create_fd(vector, 0); + if (uintr_fd < 0) { + uintr_unregister_handler(0); + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + + /* Enable interrupts */ + _stui(); + + uintr_received = 0; + if (pthread_create(&pt, NULL, &sender_thread, (void *)&uintr_fd)) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + + printf("[RUN]\tBase UIPI test: pthreads\n"); + printf("\tSpin in userspace (waiting for interrupts)\n"); + // Keep spinning until interrupt received + while (wait_for_delay-- && !uintr_received) + cpu_delay(); + + if (uintr_received) { + printf("[OK]\tUser interrupt received\n"); + } else { + printf("[FAIL]\tUser interrupt not received\n"); + nerrs++; + } + + pthread_join(pt, NULL); + close(uintr_fd); + uintr_unregister_handler(0); +} + +static void test_blocking_ipi(void) +{ + int ret, uintr_fd; + int vector = 0; + pthread_t pt; + + /* Register interrupt handler */ + if (uintr_register_handler(uintr_handler, 0)) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + + /* Create uintr_fd */ + uintr_fd = uintr_create_fd(vector, 0); + if (uintr_fd < 0) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + + /* Enable interrupts */ + _stui(); + + uintr_received = 0; + if (pthread_create(&pt, NULL, &sender_thread, (void *)&uintr_fd)) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + + printf("[RUN]\tBase UIPI test: pthreads\n"); + printf("\tBlock in the kernel (waiting for interrupts)\n"); + uintr_wait(0); + if (uintr_received) { + printf("[OK]\tUser interrupt received\n"); + } else { + printf("[FAIL]\tUser interrupt not received\n"); + nerrs++; + } + + pthread_join(pt, NULL); + close(uintr_fd); + uintr_unregister_handler(0); +} + +static void sender_process_uni(int uintr_fd) +{ + int uipi_index; + + uipi_index = uintr_register_sender(uintr_fd, 0); + if (uipi_index < 0) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + exit(EXIT_FAILURE); + } + + _senduipi(uipi_index); + + uintr_unregister_sender(uintr_fd, 0); + /* Close sender copy of uintr_fd */ + close(uintr_fd); + + pause(); +} + +static void test_process_ipi_unidirectional(void) +{ + int wait_for_usec = 100; + int uintr_fd, pid; + + if (uintr_register_handler(uintr_handler, 0)) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + + uintr_fd = uintr_create_fd(0, 0); + if (uintr_fd < 0) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + + uintr_received = 0; + _stui(); + + printf("[RUN]\tBase User IPI test: Process uni-directional\n"); + + pid = fork(); + if (pid == 0) { + /* Child sender process */ + sender_process_uni(uintr_fd); + exit(EXIT_SUCCESS); + } + + while (wait_for_usec-- && !uintr_received) + usleep(1); + + close(uintr_fd); + uintr_unregister_handler(0); + kill(pid, SIGKILL); + + if (!uintr_received) { + printf("[FAIL]\tUser interrupt not received\n"); + nerrs++; + } else { + printf("[OK]\tUser interrupt received\n"); + } +} + +static void client_process_bi(int server_fd, int sock) +{ + int uipi_index; + int client_fd; + ssize_t size; + + uipi_index = uintr_register_sender(server_fd, 0); + if (uipi_index < 0) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + exit(EXIT_FAILURE); + } + + if (uintr_register_handler(client_handler, 0)) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + + /* Create uintr_fd with vector 1 */ + client_fd = uintr_create_fd(1, 0); + if (client_fd < 0) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + _stui(); + + // Share client_fd + if (sock_fd_write(sock, "1", 1, client_fd) != 1) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + + while (!client_received) + usleep(1); + + _senduipi(uipi_index); + + pause(); +} + +static void test_process_ipi_bidirectional(void) +{ + int server_fd, client_fd, pid; + int sv[2]; + ssize_t size; + char buf[16]; + int uipi_index; + + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sv) < 0) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + + if (uintr_register_handler(server_handler, 0)) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + + /* Create uintr_fd with vector 0 */ + server_fd = uintr_create_fd(0, 0); + if (server_fd < 0) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + + _stui(); + + printf("[RUN]\tBase User IPI test: Process bi-directional\n"); + + pid = fork(); + if (pid == 0) { + /* Child client process */ + close(sv[0]); + client_process_bi(server_fd, sv[1]); + exit(EXIT_SUCCESS); + } + + close(sv[1]); + if (sock_fd_read(sv[0], buf, sizeof(buf), &client_fd) != 1) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + + //Register server as sender + uipi_index = uintr_register_sender(client_fd, 0); + if (uipi_index < 0) { + printf("[SKIP]\t%s:%d\n", __func__, __LINE__); + return; + } + + _senduipi(uipi_index); + + while (!server_received) + usleep(1); + + //close(uintr_fd); + uintr_unregister_handler(0); + kill(pid, SIGKILL); + + if (!server_received) { + printf("[FAIL]\tUser interrupt not received\n"); + nerrs++; + } else { + printf("[OK]\tUser interrupt received\n"); + } +} + +/* TODO: Use some better method for failure rather than the 45sec KSFT timeout */ +int main(int argc, char *argv[]) +{ + print_uintr_support(); + + if (!uintr_supported()) + return EXIT_SUCCESS; + + test_thread_ipi(); + + test_blocking_ipi(); + + test_process_ipi_unidirectional(); + + test_process_ipi_bidirectional(); + + return nerrs ? EXIT_FAILURE : EXIT_SUCCESS; +} |
