Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf tools: sched-pipe bench: add (-n) nonblocking benchmark

The -n mode will benchmark pipes in a non-blocking mode using
epoll_wait.

This specific mode was added to demonstrate the broken sync nature
of epoll: https://lore.kernel.org/lkml/20240426-zupfen-jahrzehnt-5be786bcdf04@brauner

Signed-off-by: Brian Geffon <bgeffon@google.com>
Reviewed-by: Ian Rogers <irogers@google.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Link: https://lore.kernel.org/r/20241016190009.866615-1-bgeffon@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

authored by

Brian Geffon and committed by
Namhyung Kim
3e2d4df5 915a3776

+36 -7
+36 -7
tools/perf/bench/sched-pipe.c
··· 23 23 #include <errno.h> 24 24 #include <fcntl.h> 25 25 #include <assert.h> 26 + #include <sys/epoll.h> 26 27 #include <sys/time.h> 27 28 #include <sys/types.h> 28 29 #include <sys/syscall.h> ··· 35 34 int nr; 36 35 int pipe_read; 37 36 int pipe_write; 37 + struct epoll_event epoll_ev; 38 + int epoll_fd; 38 39 bool cgroup_failed; 39 40 pthread_t pthread; 40 41 }; ··· 47 44 /* Use processes by default: */ 48 45 static bool threaded; 49 46 47 + static bool nonblocking; 50 48 static char *cgrp_names[2]; 51 49 static struct cgroup *cgrps[2]; 52 50 ··· 85 81 } 86 82 87 83 static const struct option options[] = { 84 + OPT_BOOLEAN('n', "nonblocking", &nonblocking, "Use non-blocking operations"), 88 85 OPT_INTEGER('l', "loop", &loops, "Specify number of loops"), 89 86 OPT_BOOLEAN('T', "threaded", &threaded, "Specify threads/process based task setup"), 90 87 OPT_CALLBACK('G', "cgroups", NULL, "SEND,RECV", ··· 170 165 free(cgrp_names[nr]); 171 166 } 172 167 168 + static inline int read_pipe(struct thread_data *td) 169 + { 170 + int ret, m; 171 + retry: 172 + if (nonblocking) { 173 + ret = epoll_wait(td->epoll_fd, &td->epoll_ev, 1, -1); 174 + if (ret < 0) 175 + return ret; 176 + } 177 + ret = read(td->pipe_read, &m, sizeof(int)); 178 + if (nonblocking && ret < 0 && errno == EWOULDBLOCK) 179 + goto retry; 180 + return ret; 181 + } 182 + 173 183 static void *worker_thread(void *__tdata) 174 184 { 175 185 struct thread_data *td = __tdata; 176 - int m = 0, i; 177 - int ret; 186 + int i, ret, m = 0; 178 187 179 188 ret = enter_cgroup(td->nr); 180 189 if (ret < 0) { ··· 196 177 return NULL; 197 178 } 198 179 180 + if (nonblocking) { 181 + td->epoll_ev.events = EPOLLIN; 182 + td->epoll_fd = epoll_create(1); 183 + BUG_ON(td->epoll_fd < 0); 184 + BUG_ON(epoll_ctl(td->epoll_fd, EPOLL_CTL_ADD, td->pipe_read, &td->epoll_ev) < 0); 185 + } 186 + 199 187 for (i = 0; i < loops; i++) { 200 188 if (!td->nr) { 201 - ret = read(td->pipe_read, &m, sizeof(int)); 189 + ret = read_pipe(td); 202 190 BUG_ON(ret != sizeof(int)); 203 191 ret = write(td->pipe_write, &m, sizeof(int)); 204 192 BUG_ON(ret != sizeof(int)); 205 193 } else { 206 194 ret = write(td->pipe_write, &m, sizeof(int)); 207 195 BUG_ON(ret != sizeof(int)); 208 - ret = read(td->pipe_read, &m, sizeof(int)); 196 + ret = read_pipe(td); 209 197 BUG_ON(ret != sizeof(int)); 210 198 } 211 199 } ··· 235 209 * discarding returned value of read(), write() 236 210 * causes error in building environment for perf 237 211 */ 238 - int __maybe_unused ret, wait_stat; 212 + int __maybe_unused ret, wait_stat, flags = 0; 239 213 pid_t pid, retpid __maybe_unused; 240 214 241 215 argc = parse_options(argc, argv, options, bench_sched_pipe_usage, 0); 242 216 243 - BUG_ON(pipe(pipe_1)); 244 - BUG_ON(pipe(pipe_2)); 217 + if (nonblocking) 218 + flags |= O_NONBLOCK; 219 + 220 + BUG_ON(pipe2(pipe_1, flags)); 221 + BUG_ON(pipe2(pipe_2, flags)); 245 222 246 223 gettimeofday(&start, NULL); 247 224