Fix various compile warnings
[splice.git] / splice-bench.c
CommitLineData
d7b40d65
JA
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <limits.h>
5#include <string.h>
6#include <getopt.h>
7#include <errno.h>
8#include <fcntl.h>
9#include <sys/poll.h>
10#include <sys/stat.h>
11#include <sys/types.h>
12#include <sys/wait.h>
13#include <sys/time.h>
be35036e 14#include <sys/mman.h>
d7b40d65
JA
15#include <signal.h>
16#include <netinet/in.h>
17#include <arpa/inet.h>
18#include <netdb.h>
19#include <sched.h>
20
21#include "splice.h"
22
23static int nr_clients = 8;
24static int net_port = 8888;
25static int client_loops = 10;
26static int bind_cpu;
27static int write_to_null;
28static int same_file;
29static int splice_size = SPLICE_SIZE;
30static char *filename = "splice-file";
000b72a8 31static unsigned int max_client_run = 15;
be35036e
JA
32static int run_rw = 1;
33static int run_splice = 1;
34static int run_mmap = 1;
d7b40d65
JA
35
36static int nr_cpus;
37
38static int usage(char *name)
39{
40 fprintf(stderr, "Usage %s [options] [filename]:\n", name);
41 fprintf(stderr, "\t[-n] (number of clients]\n");
42 fprintf(stderr, "\t[-p] (port number)\n");
43 fprintf(stderr, "\t[-l] (number of loops)\n");
44 fprintf(stderr, "\t[-z] (write to /dev/null)\n");
45 fprintf(stderr, "\t[-s] (use 1 file for all)\n");
46 fprintf(stderr, "\t[-a] (set CPU affinity)\n");
47 fprintf(stderr, "\t[-b] (splice chunk size)\n");
be35036e
JA
48 fprintf(stderr, "\t[-t] (max client runtime in seconds)\n");
49 fprintf(stderr, "\t[-c] (clients to run (rw/mmap/splice)\n");
d7b40d65
JA
50 return 1;
51}
52
53static int parse_options(int argc, char *argv[])
54{
55 int c, index = 1;
56
be35036e 57 while ((c = getopt(argc, argv, "n:p:l:azsb:t:c:")) != -1) {
d7b40d65
JA
58 switch (c) {
59 case 'n':
60 nr_clients = atoi(optarg);
61 index++;
62 break;
63 case 'p':
64 net_port = atoi(optarg);
65 index++;
66 break;
67 case 'l':
68 client_loops = atoi(optarg);
69 index++;
70 break;
71 case 'a':
72 bind_cpu = 1;
73 index++;
74 break;
75 case 'z':
76 write_to_null = 1;
77 index++;
78 break;
79 case 's':
80 same_file = 1;
81 index++;
82 break;
83 case 'b':
84 splice_size = atoi(optarg);
85 index++;
86 break;
be35036e
JA
87 case 't':
88 max_client_run = atoi(optarg);
89 index++;
90 break;
91 case 'c':
92 if (!strstr(optarg, "rw"))
93 run_rw = 0;
94 if (!strstr(optarg, "splice"))
95 run_splice = 0;
96 if (!strstr(optarg, "mmap"))
97 run_mmap = 0;
98 index++;
99 break;
d7b40d65
JA
100 default:
101 return -1;
102 }
103 }
104
105 return index;
106}
107
a4ecdc0a 108static int bind_to_cpu(int index)
d7b40d65
JA
109{
110 cpu_set_t cpu_mask;
111 pid_t pid;
112 int cpu;
113
114 if (!bind_cpu || nr_cpus == 1)
115 return 0;
116
117 cpu = index % nr_cpus;
118
119 CPU_ZERO(&cpu_mask);
120 CPU_SET((cpu), &cpu_mask);
121
122 pid = getpid();
123 if (sched_setaffinity(pid, sizeof(cpu_mask), &cpu_mask) == -1)
124 return error("set affinity");
125
126 return 0;
127}
128
a4ecdc0a 129static int accept_loop(int listen_sk)
d7b40d65 130{
d7b40d65
JA
131 struct sockaddr addr;
132 unsigned int len = sizeof(addr);
133 int sk;
134
135again:
136 sk = accept(listen_sk, &addr, &len);
137 if (sk < 0)
138 return error("accept");
139
140 /* read forever */
d7b40d65
JA
141 for (;;) {
142 int ret = recv(sk, NULL, 128*1024*1024, MSG_TRUNC);
a4ecdc0a 143 if (ret > 0)
d7b40d65 144 continue;
a4ecdc0a 145 else if (!ret)
d7b40d65
JA
146 break;
147 if (errno == EAGAIN || errno == EINTR)
148 continue;
149 break;
150 }
151
152 close(sk);
153 goto again;
154}
155
a4ecdc0a 156static int server(int offset)
d7b40d65 157{
d7b40d65 158 struct sockaddr_in saddr_in;
a4ecdc0a 159 struct sockaddr addr;
d7b40d65 160 unsigned int len;
659cd2cc 161 int sk, opt;
d7b40d65
JA
162
163 bind_to_cpu(offset);
fb02ff37
JA
164 if (nice(-20) < 0)
165 perror("nice");
d7b40d65
JA
166
167 sk = socket(PF_INET, SOCK_STREAM, 0);
168 if (sk < 0)
169 return error("socket");
170
659cd2cc
JA
171 opt = 1;
172 if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
173 return error("setsockopt");
174
d7b40d65
JA
175 saddr_in.sin_addr.s_addr = htonl(INADDR_ANY);
176 saddr_in.sin_port = htons(net_port + offset);
177
659cd2cc 178 if (bind(sk, (struct sockaddr *) &saddr_in, sizeof(saddr_in)) < 0)
d7b40d65
JA
179 return error("bind");
180
181 if (listen(sk, 1) < 0)
182 return error("listen");
183
184 len = sizeof(addr);
185 if (getsockname(sk, &addr, &len) < 0)
186 return error("getsockname");
187
188 return accept_loop(sk);
189}
190
191static unsigned long mtime_since(struct timeval *s, struct timeval *e)
192{
193 double sec, usec;
194
195 sec = e->tv_sec - s->tv_sec;
196 usec = e->tv_usec - s->tv_usec;
197 if (sec > 0 && usec < 0) {
198 sec--;
199 usec += 1000000;
200 }
201
202 sec *= (double) 1000;
203 usec /= (double) 1000;
204
205 return sec + usec;
206}
207
208static unsigned long mtime_since_now(struct timeval *s)
209{
210 struct timeval t;
211
212 gettimeofday(&t, NULL);
213 return mtime_since(s, &t);
214}
215
a4ecdc0a 216static int client_rw(int out_fd, int file_fd, int offset)
be35036e
JA
217{
218 int loops = client_loops;
219 struct timeval start;
220 struct stat sb;
221 char *buf;
222 unsigned long long size;
223 unsigned long msecs;
224
225 if (fstat(file_fd, &sb) < 0)
226 return error("fstat");
227
228 buf = malloc(splice_size);
229
230 gettimeofday(&start, NULL);
231again:
232 if (lseek(file_fd, 0, SEEK_SET) < 0)
233 return error("lseek");
234
235 size = sb.st_size;
236 while (size) {
237 int this_len = min(size, (unsigned long long) splice_size);
238 int ret = read(file_fd, buf, this_len);
239
240 if (ret < 0)
241 return error("read");
242
243 size -= ret;
244 while (ret) {
245 int written = write(out_fd, buf, ret);
246
247 if (written < 0)
248 return error("write");
249
250 ret -= written;
251 }
252 }
253
254 loops--;
255
256 if ((mtime_since_now(&start) < max_client_run * 1000) && loops)
257 goto again;
258
a4ecdc0a 259 free(buf);
be35036e
JA
260 size = sb.st_size >> 10;
261 size *= (client_loops - loops);
262 msecs = mtime_since_now(&start);
263 fprintf(stdout, "Client%d (rw): %Lu MiB/sec (%LuMiB in %lu msecs)\n", offset, size / (unsigned long long) msecs, size >> 10, msecs);
264 return 0;
265}
266
a4ecdc0a 267static int client_mmap(int out_fd, int file_fd, int offset)
be35036e
JA
268{
269 int loops = client_loops;
270 struct timeval start;
271 struct stat sb;
272 void *mmap_area, *buf;
273 unsigned long long size;
274 unsigned long msecs;
275
276 if (fstat(file_fd, &sb) < 0)
277 return error("fstat");
278
279 mmap_area = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, file_fd, 0);
280 if (mmap_area == MAP_FAILED)
281 return error("mmap");
282
283 if (madvise(mmap_area, sb.st_size, MADV_WILLNEED) < 0)
284 return error("madvise");
285
286 gettimeofday(&start, NULL);
287again:
288 buf = mmap_area;
289 size = sb.st_size;
290 while (size) {
291 int this_len = min(size, (unsigned long long) splice_size);
292 int ret = write(out_fd, buf, this_len);
293
294 if (ret < 0)
295 return error("write");
296
297 buf += ret;
298 size -= ret;
299 }
300
301 loops--;
302
303 if ((mtime_since_now(&start) < max_client_run * 1000) && loops)
304 goto again;
305
306 size = sb.st_size >> 10;
307 size *= (client_loops - loops);
308 msecs = mtime_since_now(&start);
309 fprintf(stdout, "Client%d (mmap): %Lu MiB/sec (%LuMiB in %lu msecs)\n", offset, size / (unsigned long long) msecs, size >> 10, msecs);
310 munmap(mmap_area, sb.st_size);
311 return 0;
312
313}
314
a4ecdc0a 315static int client_splice_loop(int out_fd, int fd, int *pfd, int offset)
d7b40d65
JA
316{
317 struct timeval start;
318 unsigned long long size;
319 unsigned long msecs;
320 struct stat sb;
321 int loops = client_loops;
322 loff_t off;
323
324 if (fstat(fd, &sb) < 0)
325 return error("fstat");
326
327 gettimeofday(&start, NULL);
d7b40d65
JA
328again:
329 size = sb.st_size;
330 off = 0;
331
332 do {
13b72067 333 int ret = ssplice(fd, &off, pfd[1], NULL, min(size, (unsigned long long) splice_size), 0);
d7b40d65
JA
334
335 if (ret <= 0)
336 return error("splice-in");
337
338 size -= ret;
339 while (ret > 0) {
340 int flags = size ? SPLICE_F_MORE : 0;
13b72067 341 int written = ssplice(pfd[0], NULL, out_fd, NULL, ret, flags);
d7b40d65
JA
342
343 if (written <= 0)
344 return error("splice-out");
345
346 ret -= written;
347 }
348 } while (size);
349
be35036e
JA
350 loops--;
351
352 if ((mtime_since_now(&start) < max_client_run * 1000) && loops)
d7b40d65
JA
353 goto again;
354
355 size = sb.st_size >> 10;
be35036e 356 size *= (client_loops - loops);
d7b40d65 357 msecs = mtime_since_now(&start);
be35036e
JA
358 fprintf(stdout, "Client%d (splice): %Lu MiB/sec (%LuMiB in %lu msecs)\n", offset, size / (unsigned long long) msecs, size >> 10, msecs);
359 return 0;
360}
361
a4ecdc0a 362static int client_splice(int out_fd, int file_fd, int offset)
be35036e
JA
363{
364 int pfd[2], ret;
365
366 if (pipe(pfd) < 0)
367 return error("pipe");
368
369 ret = client_splice_loop(out_fd, file_fd, pfd, offset);
370 close(pfd[0]);
371 close(pfd[1]);
372 return ret;
373}
374
a4ecdc0a 375static int do_client(int out_fd, int file_fd, int offset)
be35036e 376{
4f4118c9
JA
377 int ret;
378
379 if (run_splice) {
380 ret = client_splice(out_fd, file_fd, offset);
381 if (ret)
382 return ret;
383 }
384 if (run_mmap) {
385 ret = client_mmap(out_fd, file_fd, offset);
386 if (ret)
387 return ret;
388 }
389 if (run_rw) {
390 ret = client_rw(out_fd, file_fd, offset);
391 if (ret)
392 return ret;
393 }
d7b40d65
JA
394 return 0;
395}
396
397static int client_open_net(int offset)
398{
399 int sk = socket(PF_INET, SOCK_STREAM, 0);
400 struct sockaddr_in s_to;
401 struct hostent *hp;
402
403 hp = gethostbyname("localhost");
404 if (!hp)
405 return error("gethostbyname");
406
407 bzero((char *) &s_to, sizeof (s_to));
408 bcopy((char *) hp->h_addr, (char *) &(s_to.sin_addr), hp->h_length);
409 s_to.sin_family = hp->h_addrtype;
410 s_to.sin_port = htons(net_port + offset);
411
412 if (connect(sk, (struct sockaddr *)&s_to, sizeof(s_to)) < 0)
413 return error("connect");
414
415 return sk;
416}
417
a4ecdc0a 418static int client(int offset)
d7b40d65
JA
419{
420 int file_fd, out_fd;
421 char fname[64];
d7b40d65
JA
422
423 bind_to_cpu(offset);
fb02ff37
JA
424 if (nice(-20) < 0)
425 perror("nice");
d7b40d65
JA
426
427 if (!write_to_null)
428 out_fd = client_open_net(offset);
429 else
430 out_fd = open("/dev/null", O_WRONLY);
431
432 if (out_fd < 0)
433 return error("socket");
434
435 sprintf(fname, "%s%d", filename, same_file ? 0 : offset);
be35036e 436 file_fd = open(fname, O_RDONLY);
d7b40d65
JA
437 if (file_fd < 0)
438 return error("open");
439
be35036e 440 return do_client(out_fd, file_fd, offset);
d7b40d65
JA
441}
442
443int main(int argc, char *argv[])
444{
445 pid_t *spids, *cpids;
446 int i, index;
447
448 index = parse_options(argc, argv);
449 if (index < 0)
450 return usage(argv[0]);
451
452 if (index < argc)
453 filename = argv[index];
454
455 spids = malloc(nr_clients * sizeof(pid_t));
456 cpids = malloc(nr_clients * sizeof(pid_t));
457 memset(spids, 0, nr_clients * sizeof(pid_t));
458 memset(cpids, 0, nr_clients * sizeof(pid_t));
459
460 nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
461 if (nr_cpus < 0)
462 return error("_SC_NPROCESSORS_ONLN");
463
d7b40d65
JA
464 /*
465 * fork servers
466 */
467 if (!write_to_null) {
468 for (i = 0; i < nr_clients; i++) {
469 pid_t pid = fork();
470
471 if (pid)
472 spids[i] = pid;
473 else {
474 server(i);
475 spids[i] = 0;
476 exit(0);
477 }
478 }
479 sleep(1); /* should have servers started now */
480 }
481
482 /*
483 * fork clients
484 */
485 for (i = 0; i < nr_clients; i++) {
486 pid_t pid = fork();
487
488 if (pid)
489 cpids[i] = pid;
490 else {
491 client(i);
492 cpids[i] = 0;
493 exit(0);
494 }
495 }
496
497 /*
498 * wait for clients to exit
499 */
500 fprintf(stdout, "Waiting for clients\n");
501 for (i = 0; i < nr_clients; i++) {
502 if (cpids[i]) {
503 waitpid(cpids[i], NULL, 0);
504 cpids[i] = 0;
505 }
506 }
507
508 /*
509 * then kill servers
510 */
511 for (i = 0; i < nr_clients; i++) {
512 if (spids[i]) {
513 kill(spids[i], SIGKILL);
514 waitpid(spids[i], NULL, 0);
515 spids[i] = 0;
516 }
517 }
518
519 return 0;
520}