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