~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/perf/bench/sched-pipe.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 /*
  3  *
  4  * sched-pipe.c
  5  *
  6  * pipe: Benchmark for pipe()
  7  *
  8  * Based on pipe-test-1m.c by Ingo Molnar <mingo@redhat.com>
  9  *  http://people.redhat.com/mingo/cfs-scheduler/tools/pipe-test-1m.c
 10  * Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
 11  */
 12 #include <subcmd/parse-options.h>
 13 #include <api/fs/fs.h>
 14 #include "bench.h"
 15 #include "util/cgroup.h"
 16 
 17 #include <unistd.h>
 18 #include <stdio.h>
 19 #include <stdlib.h>
 20 #include <signal.h>
 21 #include <sys/wait.h>
 22 #include <string.h>
 23 #include <errno.h>
 24 #include <fcntl.h>
 25 #include <assert.h>
 26 #include <sys/time.h>
 27 #include <sys/types.h>
 28 #include <sys/syscall.h>
 29 #include <linux/time64.h>
 30 
 31 #include <pthread.h>
 32 
 33 struct thread_data {
 34         int                     nr;
 35         int                     pipe_read;
 36         int                     pipe_write;
 37         bool                    cgroup_failed;
 38         pthread_t               pthread;
 39 };
 40 
 41 #define LOOPS_DEFAULT 1000000
 42 static  int                     loops = LOOPS_DEFAULT;
 43 
 44 /* Use processes by default: */
 45 static bool                     threaded;
 46 
 47 static char                     *cgrp_names[2];
 48 static struct cgroup            *cgrps[2];
 49 
 50 static int parse_two_cgroups(const struct option *opt __maybe_unused,
 51                              const char *str, int unset __maybe_unused)
 52 {
 53         char *p = strdup(str);
 54         char *q;
 55         int ret = -1;
 56 
 57         if (p == NULL) {
 58                 fprintf(stderr, "memory allocation failure\n");
 59                 return -1;
 60         }
 61 
 62         q = strchr(p, ',');
 63         if (q == NULL) {
 64                 fprintf(stderr, "it should have two cgroup names: %s\n", p);
 65                 goto out;
 66         }
 67         *q = '\0';
 68 
 69         cgrp_names[0] = strdup(p);
 70         cgrp_names[1] = strdup(q + 1);
 71 
 72         if (cgrp_names[0] == NULL || cgrp_names[1] == NULL) {
 73                 fprintf(stderr, "memory allocation failure\n");
 74                 goto out;
 75         }
 76         ret = 0;
 77 
 78 out:
 79         free(p);
 80         return ret;
 81 }
 82 
 83 static const struct option options[] = {
 84         OPT_INTEGER('l', "loop",        &loops,         "Specify number of loops"),
 85         OPT_BOOLEAN('T', "threaded",    &threaded,      "Specify threads/process based task setup"),
 86         OPT_CALLBACK('G', "cgroups", NULL, "SEND,RECV",
 87                      "Put sender and receivers in given cgroups",
 88                      parse_two_cgroups),
 89         OPT_END()
 90 };
 91 
 92 static const char * const bench_sched_pipe_usage[] = {
 93         "perf bench sched pipe <options>",
 94         NULL
 95 };
 96 
 97 static int enter_cgroup(int nr)
 98 {
 99         char buf[32];
100         int fd, len, ret;
101         int saved_errno;
102         struct cgroup *cgrp;
103         pid_t pid;
104 
105         if (cgrp_names[nr] == NULL)
106                 return 0;
107 
108         if (cgrps[nr] == NULL) {
109                 cgrps[nr] = cgroup__new(cgrp_names[nr], /*do_open=*/true);
110                 if (cgrps[nr] == NULL)
111                         goto err;
112         }
113         cgrp = cgrps[nr];
114 
115         if (threaded)
116                 pid = syscall(__NR_gettid);
117         else
118                 pid = getpid();
119 
120         snprintf(buf, sizeof(buf), "%d\n", pid);
121         len = strlen(buf);
122 
123         /* try cgroup v2 interface first */
124         if (threaded)
125                 fd = openat(cgrp->fd, "cgroup.threads", O_WRONLY);
126         else
127                 fd = openat(cgrp->fd, "cgroup.procs", O_WRONLY);
128 
129         /* try cgroup v1 if failed */
130         if (fd < 0 && errno == ENOENT)
131                 fd = openat(cgrp->fd, "tasks", O_WRONLY);
132 
133         if (fd < 0)
134                 goto err;
135 
136         ret = write(fd, buf, len);
137         close(fd);
138 
139         if (ret != len) {
140                 printf("Cannot enter to cgroup: %s\n", cgrp->name);
141                 return -1;
142         }
143         return 0;
144 
145 err:
146         saved_errno = errno;
147         printf("Failed to open cgroup file in %s\n", cgrp_names[nr]);
148 
149         if (saved_errno == ENOENT) {
150                 char mnt[PATH_MAX];
151 
152                 if (cgroupfs_find_mountpoint(mnt, sizeof(mnt), "perf_event") == 0)
153                         printf(" Hint: create the cgroup first, like 'mkdir %s/%s'\n",
154                                mnt, cgrp_names[nr]);
155         } else if (saved_errno == EACCES && geteuid() > 0) {
156                 printf(" Hint: try to run as root\n");
157         }
158 
159         return -1;
160 }
161 
162 static void exit_cgroup(int nr)
163 {
164         cgroup__put(cgrps[nr]);
165         free(cgrp_names[nr]);
166 }
167 
168 static void *worker_thread(void *__tdata)
169 {
170         struct thread_data *td = __tdata;
171         int m = 0, i;
172         int ret;
173 
174         ret = enter_cgroup(td->nr);
175         if (ret < 0) {
176                 td->cgroup_failed = true;
177                 return NULL;
178         }
179 
180         for (i = 0; i < loops; i++) {
181                 if (!td->nr) {
182                         ret = read(td->pipe_read, &m, sizeof(int));
183                         BUG_ON(ret != sizeof(int));
184                         ret = write(td->pipe_write, &m, sizeof(int));
185                         BUG_ON(ret != sizeof(int));
186                 } else {
187                         ret = write(td->pipe_write, &m, sizeof(int));
188                         BUG_ON(ret != sizeof(int));
189                         ret = read(td->pipe_read, &m, sizeof(int));
190                         BUG_ON(ret != sizeof(int));
191                 }
192         }
193 
194         return NULL;
195 }
196 
197 int bench_sched_pipe(int argc, const char **argv)
198 {
199         struct thread_data threads[2] = {};
200         struct thread_data *td;
201         int pipe_1[2], pipe_2[2];
202         struct timeval start, stop, diff;
203         unsigned long long result_usec = 0;
204         int nr_threads = 2;
205         int t;
206 
207         /*
208          * why does "ret" exist?
209          * discarding returned value of read(), write()
210          * causes error in building environment for perf
211          */
212         int __maybe_unused ret, wait_stat;
213         pid_t pid, retpid __maybe_unused;
214 
215         argc = parse_options(argc, argv, options, bench_sched_pipe_usage, 0);
216 
217         BUG_ON(pipe(pipe_1));
218         BUG_ON(pipe(pipe_2));
219 
220         gettimeofday(&start, NULL);
221 
222         for (t = 0; t < nr_threads; t++) {
223                 td = threads + t;
224 
225                 td->nr = t;
226 
227                 if (t == 0) {
228                         td->pipe_read = pipe_1[0];
229                         td->pipe_write = pipe_2[1];
230                 } else {
231                         td->pipe_write = pipe_1[1];
232                         td->pipe_read = pipe_2[0];
233                 }
234         }
235 
236         if (threaded) {
237                 for (t = 0; t < nr_threads; t++) {
238                         td = threads + t;
239 
240                         ret = pthread_create(&td->pthread, NULL, worker_thread, td);
241                         BUG_ON(ret);
242                 }
243 
244                 for (t = 0; t < nr_threads; t++) {
245                         td = threads + t;
246 
247                         ret = pthread_join(td->pthread, NULL);
248                         BUG_ON(ret);
249                 }
250         } else {
251                 pid = fork();
252                 assert(pid >= 0);
253 
254                 if (!pid) {
255                         worker_thread(threads + 0);
256                         exit(0);
257                 } else {
258                         worker_thread(threads + 1);
259                 }
260 
261                 retpid = waitpid(pid, &wait_stat, 0);
262                 assert((retpid == pid) && WIFEXITED(wait_stat));
263         }
264 
265         gettimeofday(&stop, NULL);
266         timersub(&stop, &start, &diff);
267 
268         exit_cgroup(0);
269         exit_cgroup(1);
270 
271         if (threads[0].cgroup_failed || threads[1].cgroup_failed)
272                 return 0;
273 
274         switch (bench_format) {
275         case BENCH_FORMAT_DEFAULT:
276                 printf("# Executed %d pipe operations between two %s\n\n",
277                         loops, threaded ? "threads" : "processes");
278 
279                 result_usec = diff.tv_sec * USEC_PER_SEC;
280                 result_usec += diff.tv_usec;
281 
282                 printf(" %14s: %lu.%03lu [sec]\n\n", "Total time",
283                        (unsigned long) diff.tv_sec,
284                        (unsigned long) (diff.tv_usec / USEC_PER_MSEC));
285 
286                 printf(" %14lf usecs/op\n",
287                        (double)result_usec / (double)loops);
288                 printf(" %14d ops/sec\n",
289                        (int)((double)loops /
290                              ((double)result_usec / (double)USEC_PER_SEC)));
291                 break;
292 
293         case BENCH_FORMAT_SIMPLE:
294                 printf("%lu.%03lu\n",
295                        (unsigned long) diff.tv_sec,
296                        (unsigned long) (diff.tv_usec / USEC_PER_MSEC));
297                 break;
298 
299         default:
300                 /* reaching here is something disaster */
301                 fprintf(stderr, "Unknown format:%d\n", bench_format);
302                 exit(1);
303                 break;
304         }
305 
306         return 0;
307 }
308 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php