1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * 4 * sched-messaging.c 5 * 6 * messaging: Benchmark for scheduler and IPC mechanisms 7 * 8 * Based on hackbench by Rusty Russell <rusty@rustcorp.com.au> 9 * Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> 10 * 11 */ 12 13 #include <subcmd/parse-options.h> 14 #include "bench.h" 15 16 /* Test groups of 20 processes spraying to 20 receivers */ 17 #include <pthread.h> 18 #include <stdio.h> 19 #include <stdlib.h> 20 #include <string.h> 21 #include <errno.h> 22 #include <unistd.h> 23 #include <sys/types.h> 24 #include <sys/socket.h> 25 #include <sys/wait.h> 26 #include <sys/time.h> 27 #include <poll.h> 28 #include <limits.h> 29 #include <err.h> 30 #include <linux/list.h> 31 #include <linux/time64.h> 32 33 #define DATASIZE 100 34 35 static bool use_pipes = false; 36 static unsigned int nr_loops = 100; 37 static bool thread_mode = false; 38 static unsigned int num_groups = 10; 39 static struct list_head sender_contexts = LIST_HEAD_INIT(sender_contexts); 40 static struct list_head receiver_contexts = LIST_HEAD_INIT(receiver_contexts); 41 42 struct sender_context { 43 struct list_head list; 44 unsigned int num_fds; 45 int ready_out; 46 int wakefd; 47 int out_fds[]; 48 }; 49 50 struct receiver_context { 51 struct list_head list; 52 unsigned int num_packets; 53 int in_fds[2]; 54 int ready_out; 55 int wakefd; 56 }; 57 58 static void fdpair(int fds[2]) 59 { 60 if (use_pipes) { 61 if (pipe(fds) == 0) 62 return; 63 } else { 64 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0) 65 return; 66 } 67 68 err(EXIT_FAILURE, use_pipes ? "pipe()" : "socketpair()"); 69 } 70 71 /* Block until we're ready to go */ 72 static void ready(int ready_out, int wakefd) 73 { 74 struct pollfd pollfd = { .fd = wakefd, .events = POLLIN }; 75 76 /* Tell them we're ready. */ 77 if (write(ready_out, "R", 1) != 1) 78 err(EXIT_FAILURE, "CLIENT: ready write"); 79 80 /* Wait for "GO" signal */ 81 if (poll(&pollfd, 1, -1) != 1) 82 err(EXIT_FAILURE, "poll"); 83 } 84 85 /* Sender sprays nr_loops messages down each file descriptor */ 86 static void *sender(struct sender_context *ctx) 87 { 88 char data[DATASIZE]; 89 unsigned int i, j; 90 91 ready(ctx->ready_out, ctx->wakefd); 92 memset(data, 'S', sizeof(data)); 93 94 /* Now pump to every receiver. */ 95 for (i = 0; i < nr_loops; i++) { 96 for (j = 0; j < ctx->num_fds; j++) { 97 int ret, done = 0; 98 99 again: 100 ret = write(ctx->out_fds[j], data + done, 101 sizeof(data)-done); 102 if (ret < 0) 103 err(EXIT_FAILURE, "SENDER: write"); 104 done += ret; 105 if (done < DATASIZE) 106 goto again; 107 } 108 } 109 110 return NULL; 111 } 112 113 114 /* One receiver per fd */ 115 static void *receiver(struct receiver_context* ctx) 116 { 117 unsigned int i; 118 119 if (!thread_mode) 120 close(ctx->in_fds[1]); 121 122 /* Wait for start... */ 123 ready(ctx->ready_out, ctx->wakefd); 124 125 /* Receive them all */ 126 for (i = 0; i < ctx->num_packets; i++) { 127 char data[DATASIZE]; 128 int ret, done = 0; 129 130 again: 131 ret = read(ctx->in_fds[0], data + done, DATASIZE - done); 132 if (ret < 0) 133 err(EXIT_FAILURE, "SERVER: read"); 134 done += ret; 135 if (done < DATASIZE) 136 goto again; 137 } 138 139 return NULL; 140 } 141 142 static pthread_t create_worker(void *ctx, void *(*func)(void *)) 143 { 144 pthread_attr_t attr; 145 pthread_t childid; 146 int ret; 147 148 if (!thread_mode) { 149 /* process mode */ 150 /* Fork the receiver. */ 151 switch (fork()) { 152 case -1: 153 err(EXIT_FAILURE, "fork()"); 154 break; 155 case 0: 156 (*func) (ctx); 157 exit(0); 158 break; 159 default: 160 break; 161 } 162 163 return (pthread_t)0; 164 } 165 166 if (pthread_attr_init(&attr) != 0) 167 err(EXIT_FAILURE, "pthread_attr_init:"); 168 169 #ifndef __ia64__ 170 if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0) 171 err(EXIT_FAILURE, "pthread_attr_setstacksize"); 172 #endif 173 174 ret = pthread_create(&childid, &attr, func, ctx); 175 if (ret != 0) 176 err(EXIT_FAILURE, "pthread_create failed"); 177 178 pthread_attr_destroy(&attr); 179 return childid; 180 } 181 182 static void reap_worker(pthread_t id) 183 { 184 int proc_status; 185 void *thread_status; 186 187 if (!thread_mode) { 188 /* process mode */ 189 wait(&proc_status); 190 if (!WIFEXITED(proc_status)) 191 exit(1); 192 } else { 193 pthread_join(id, &thread_status); 194 } 195 } 196 197 /* One group of senders and receivers */ 198 static unsigned int group(pthread_t *pth, 199 unsigned int num_fds, 200 int ready_out, 201 int wakefd) 202 { 203 unsigned int i; 204 struct sender_context *snd_ctx = malloc(sizeof(struct sender_context) 205 + num_fds * sizeof(int)); 206 207 if (!snd_ctx) 208 err(EXIT_FAILURE, "malloc()"); 209 210 list_add(&snd_ctx->list, &sender_contexts); 211 for (i = 0; i < num_fds; i++) { 212 int fds[2]; 213 struct receiver_context *ctx = malloc(sizeof(*ctx)); 214 215 if (!ctx) 216 err(EXIT_FAILURE, "malloc()"); 217 218 list_add(&ctx->list, &receiver_contexts); 219 220 /* Create the pipe between client and server */ 221 fdpair(fds); 222 223 ctx->num_packets = num_fds * nr_loops; 224 ctx->in_fds[0] = fds[0]; 225 ctx->in_fds[1] = fds[1]; 226 ctx->ready_out = ready_out; 227 ctx->wakefd = wakefd; 228 229 pth[i] = create_worker(ctx, (void *)receiver); 230 231 snd_ctx->out_fds[i] = fds[1]; 232 if (!thread_mode) 233 close(fds[0]); 234 } 235 236 /* Now we have all the fds, fork the senders */ 237 for (i = 0; i < num_fds; i++) { 238 snd_ctx->ready_out = ready_out; 239 snd_ctx->wakefd = wakefd; 240 snd_ctx->num_fds = num_fds; 241 242 pth[num_fds+i] = create_worker(snd_ctx, (void *)sender); 243 } 244 245 /* Close the fds we have left */ 246 if (!thread_mode) 247 for (i = 0; i < num_fds; i++) 248 close(snd_ctx->out_fds[i]); 249 250 /* Return number of children to reap */ 251 return num_fds * 2; 252 } 253 254 static const struct option options[] = { 255 OPT_BOOLEAN('p', "pipe", &use_pipes, 256 "Use pipe() instead of socketpair()"), 257 OPT_BOOLEAN('t', "thread", &thread_mode, 258 "Be multi thread instead of multi process"), 259 OPT_UINTEGER('g', "group", &num_groups, "Specify number of groups"), 260 OPT_UINTEGER('l', "nr_loops", &nr_loops, "Specify the number of loops to run (default: 100)"), 261 OPT_END() 262 }; 263 264 static const char * const bench_sched_message_usage[] = { 265 "perf bench sched messaging <options>", 266 NULL 267 }; 268 269 int bench_sched_messaging(int argc, const char **argv) 270 { 271 unsigned int i, total_children; 272 struct timeval start, stop, diff; 273 unsigned int num_fds = 20; 274 int readyfds[2], wakefds[2]; 275 char dummy; 276 pthread_t *pth_tab; 277 struct sender_context *pos, *n; 278 279 argc = parse_options(argc, argv, options, 280 bench_sched_message_usage, 0); 281 282 pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t)); 283 if (!pth_tab) 284 err(EXIT_FAILURE, "main:malloc()"); 285 286 fdpair(readyfds); 287 fdpair(wakefds); 288 289 total_children = 0; 290 for (i = 0; i < num_groups; i++) 291 total_children += group(pth_tab+total_children, num_fds, 292 readyfds[1], wakefds[0]); 293 294 /* Wait for everyone to be ready */ 295 for (i = 0; i < total_children; i++) 296 if (read(readyfds[0], &dummy, 1) != 1) 297 err(EXIT_FAILURE, "Reading for readyfds"); 298 299 gettimeofday(&start, NULL); 300 301 /* Kick them off */ 302 if (write(wakefds[1], &dummy, 1) != 1) 303 err(EXIT_FAILURE, "Writing to start them"); 304 305 /* Reap them all */ 306 for (i = 0; i < total_children; i++) 307 reap_worker(pth_tab[i]); 308 309 gettimeofday(&stop, NULL); 310 311 timersub(&stop, &start, &diff); 312 313 switch (bench_format) { 314 case BENCH_FORMAT_DEFAULT: 315 printf("# %d sender and receiver %s per group\n", 316 num_fds, thread_mode ? "threads" : "processes"); 317 printf("# %d groups == %d %s run\n\n", 318 num_groups, num_groups * 2 * num_fds, 319 thread_mode ? "threads" : "processes"); 320 printf(" %14s: %lu.%03lu [sec]\n", "Total time", 321 (unsigned long) diff.tv_sec, 322 (unsigned long) (diff.tv_usec / USEC_PER_MSEC)); 323 break; 324 case BENCH_FORMAT_SIMPLE: 325 printf("%lu.%03lu\n", (unsigned long) diff.tv_sec, 326 (unsigned long) (diff.tv_usec / USEC_PER_MSEC)); 327 break; 328 default: 329 /* reaching here is something disaster */ 330 fprintf(stderr, "Unknown format:%d\n", bench_format); 331 exit(1); 332 break; 333 } 334 335 free(pth_tab); 336 list_for_each_entry_safe(pos, n, &sender_contexts, list) { 337 list_del_init(&pos->list); 338 free(pos); 339 } 340 list_for_each_entry_safe(pos, n, &receiver_contexts, list) { 341 list_del_init(&pos->list); 342 free(pos); 343 } 344 return 0; 345 } 346