1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 4 #include <errno.h> 5 #include <fcntl.h> 6 #include <linux/perf_event.h> 7 #include <stddef.h> 8 #include <sched.h> 9 #include <signal.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <sys/ioctl.h> 13 #include <sys/mman.h> 14 #include <sys/syscall.h> 15 #include <sys/wait.h> 16 #include <unistd.h> 17 18 #include "../kselftest_harness.h" 19 20 static int sigio_count; 21 22 static void handle_sigio(int signum __maybe_unused, 23 siginfo_t *oh __maybe_unused, 24 void *uc __maybe_unused) 25 { 26 ++sigio_count; 27 } 28 29 static void do_child(void) 30 { 31 raise(SIGSTOP); 32 33 for (int i = 0; i < 20; ++i) 34 sleep(1); 35 36 raise(SIGSTOP); 37 38 exit(0); 39 } 40 41 TEST(watermark_signal) 42 { 43 struct perf_event_attr attr; 44 struct perf_event_mmap_page *p = NULL; 45 struct sigaction previous_sigio, sigio = { 0 }; 46 pid_t child = -1; 47 int child_status; 48 int fd = -1; 49 long page_size = sysconf(_SC_PAGE_SIZE); 50 51 sigio.sa_sigaction = handle_sigio; 52 EXPECT_EQ(sigaction(SIGIO, &sigio, &previous_sigio), 0); 53 54 memset(&attr, 0, sizeof(attr)); 55 attr.size = sizeof(attr); 56 attr.type = PERF_TYPE_SOFTWARE; 57 attr.config = PERF_COUNT_SW_DUMMY; 58 attr.sample_period = 1; 59 attr.disabled = 1; 60 attr.watermark = 1; 61 attr.context_switch = 1; 62 attr.wakeup_watermark = 1; 63 64 child = fork(); 65 EXPECT_GE(child, 0); 66 if (child == 0) 67 do_child(); 68 else if (child < 0) { 69 perror("fork()"); 70 goto cleanup; 71 } 72 73 if (waitpid(child, &child_status, WSTOPPED) != child || 74 !(WIFSTOPPED(child_status) && WSTOPSIG(child_status) == SIGSTOP)) { 75 fprintf(stderr, 76 "failed to synchronize with child errno=%d status=%x\n", 77 errno, 78 child_status); 79 goto cleanup; 80 } 81 82 fd = syscall(__NR_perf_event_open, &attr, child, -1, -1, 83 PERF_FLAG_FD_CLOEXEC); 84 if (fd < 0) { 85 fprintf(stderr, "failed opening event %llx\n", attr.config); 86 goto cleanup; 87 } 88 89 if (fcntl(fd, F_SETFL, FASYNC)) { 90 perror("F_SETFL FASYNC"); 91 goto cleanup; 92 } 93 94 if (fcntl(fd, F_SETOWN, getpid())) { 95 perror("F_SETOWN getpid()"); 96 goto cleanup; 97 } 98 99 if (fcntl(fd, F_SETSIG, SIGIO)) { 100 perror("F_SETSIG SIGIO"); 101 goto cleanup; 102 } 103 104 p = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 105 if (p == NULL) { 106 perror("mmap"); 107 goto cleanup; 108 } 109 110 if (ioctl(fd, PERF_EVENT_IOC_ENABLE, 0)) { 111 perror("PERF_EVENT_IOC_ENABLE"); 112 goto cleanup; 113 } 114 115 if (kill(child, SIGCONT) < 0) { 116 perror("SIGCONT"); 117 goto cleanup; 118 } 119 120 if (waitpid(child, &child_status, WSTOPPED) != -1 || errno != EINTR) 121 fprintf(stderr, 122 "expected SIGIO to terminate wait errno=%d status=%x\n%d", 123 errno, 124 child_status, 125 sigio_count); 126 127 EXPECT_GE(sigio_count, 1); 128 129 cleanup: 130 if (p != NULL) 131 munmap(p, 2 * page_size); 132 133 if (fd >= 0) 134 close(fd); 135 136 if (child > 0) { 137 kill(child, SIGKILL); 138 waitpid(child, NULL, 0); 139 } 140 141 sigaction(SIGIO, &previous_sigio, NULL); 142 } 143 144 TEST_HARNESS_MAIN 145