sched-messaging.c 7.0 KB

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