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