[PATCH] splice-bench: error checking
[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";
be35036e
JA
31static int max_client_run = 15;
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
108int bind_to_cpu(int index)
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
129int accept_loop(int listen_sk)
130{
131 unsigned long received;
132 struct sockaddr addr;
133 unsigned int len = sizeof(addr);
134 int sk;
135
136again:
137 sk = accept(listen_sk, &addr, &len);
138 if (sk < 0)
139 return error("accept");
140
141 /* read forever */
142 received = 0;
143 for (;;) {
144 int ret = recv(sk, NULL, 128*1024*1024, MSG_TRUNC);
145 if (ret > 0) {
146 received += ret;
147 continue;
148 }
149 if (!ret)
150 break;
151 if (errno == EAGAIN || errno == EINTR)
152 continue;
153 break;
154 }
155
156 close(sk);
157 goto again;
158}
159
160int server(int offset)
161{
162 struct sockaddr addr;
163 struct sockaddr_in saddr_in;
164 unsigned int len;
165 int sk;
166
167 bind_to_cpu(offset);
168 nice(-20);
169
170 sk = socket(PF_INET, SOCK_STREAM, 0);
171 if (sk < 0)
172 return error("socket");
173
174 saddr_in.sin_addr.s_addr = htonl(INADDR_ANY);
175 saddr_in.sin_port = htons(net_port + offset);
176
177 if (bind(sk, (struct sockaddr*)&saddr_in, sizeof(saddr_in)) < 0)
178 return error("bind");
179
180 if (listen(sk, 1) < 0)
181 return error("listen");
182
183 len = sizeof(addr);
184 if (getsockname(sk, &addr, &len) < 0)
185 return error("getsockname");
186
187 return accept_loop(sk);
188}
189
190static unsigned long mtime_since(struct timeval *s, struct timeval *e)
191{
192 double sec, usec;
193
194 sec = e->tv_sec - s->tv_sec;
195 usec = e->tv_usec - s->tv_usec;
196 if (sec > 0 && usec < 0) {
197 sec--;
198 usec += 1000000;
199 }
200
201 sec *= (double) 1000;
202 usec /= (double) 1000;
203
204 return sec + usec;
205}
206
207static unsigned long mtime_since_now(struct timeval *s)
208{
209 struct timeval t;
210
211 gettimeofday(&t, NULL);
212 return mtime_since(s, &t);
213}
214
be35036e
JA
215int client_rw(int out_fd, int file_fd, int offset)
216{
217 int loops = client_loops;
218 struct timeval start;
219 struct stat sb;
220 char *buf;
221 unsigned long long size;
222 unsigned long msecs;
223
224 if (fstat(file_fd, &sb) < 0)
225 return error("fstat");
226
227 buf = malloc(splice_size);
228
229 gettimeofday(&start, NULL);
230again:
231 if (lseek(file_fd, 0, SEEK_SET) < 0)
232 return error("lseek");
233
234 size = sb.st_size;
235 while (size) {
236 int this_len = min(size, (unsigned long long) splice_size);
237 int ret = read(file_fd, buf, this_len);
238
239 if (ret < 0)
240 return error("read");
241
242 size -= ret;
243 while (ret) {
244 int written = write(out_fd, buf, ret);
245
246 if (written < 0)
247 return error("write");
248
249 ret -= written;
250 }
251 }
252
253 loops--;
254
255 if ((mtime_since_now(&start) < max_client_run * 1000) && loops)
256 goto again;
257
258 size = sb.st_size >> 10;
259 size *= (client_loops - loops);
260 msecs = mtime_since_now(&start);
261 fprintf(stdout, "Client%d (rw): %Lu MiB/sec (%LuMiB in %lu msecs)\n", offset, size / (unsigned long long) msecs, size >> 10, msecs);
262 return 0;
263}
264
265int client_mmap(int out_fd, int file_fd, int offset)
266{
267 int loops = client_loops;
268 struct timeval start;
269 struct stat sb;
270 void *mmap_area, *buf;
271 unsigned long long size;
272 unsigned long msecs;
273
274 if (fstat(file_fd, &sb) < 0)
275 return error("fstat");
276
277 mmap_area = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, file_fd, 0);
278 if (mmap_area == MAP_FAILED)
279 return error("mmap");
280
281 if (madvise(mmap_area, sb.st_size, MADV_WILLNEED) < 0)
282 return error("madvise");
283
284 gettimeofday(&start, NULL);
285again:
286 buf = mmap_area;
287 size = sb.st_size;
288 while (size) {
289 int this_len = min(size, (unsigned long long) splice_size);
290 int ret = write(out_fd, buf, this_len);
291
292 if (ret < 0)
293 return error("write");
294
295 buf += ret;
296 size -= ret;
297 }
298
299 loops--;
300
301 if ((mtime_since_now(&start) < max_client_run * 1000) && loops)
302 goto again;
303
304 size = sb.st_size >> 10;
305 size *= (client_loops - loops);
306 msecs = mtime_since_now(&start);
307 fprintf(stdout, "Client%d (mmap): %Lu MiB/sec (%LuMiB in %lu msecs)\n", offset, size / (unsigned long long) msecs, size >> 10, msecs);
308 munmap(mmap_area, sb.st_size);
309 return 0;
310
311}
312
313int client_splice_loop(int out_fd, int fd, int *pfd, int offset)
d7b40d65
JA
314{
315 struct timeval start;
316 unsigned long long size;
317 unsigned long msecs;
318 struct stat sb;
319 int loops = client_loops;
320 loff_t off;
321
322 if (fstat(fd, &sb) < 0)
323 return error("fstat");
324
325 gettimeofday(&start, NULL);
d7b40d65
JA
326again:
327 size = sb.st_size;
328 off = 0;
329
330 do {
331 int ret = splice(fd, &off, pfd[1], NULL, min(size, (unsigned long long) splice_size), 0);
332
333 if (ret <= 0)
334 return error("splice-in");
335
336 size -= ret;
337 while (ret > 0) {
338 int flags = size ? SPLICE_F_MORE : 0;
339 int written = splice(pfd[0], NULL, out_fd, NULL, ret, flags);
340
341 if (written <= 0)
342 return error("splice-out");
343
344 ret -= written;
345 }
346 } while (size);
347
be35036e
JA
348 loops--;
349
350 if ((mtime_since_now(&start) < max_client_run * 1000) && loops)
d7b40d65
JA
351 goto again;
352
353 size = sb.st_size >> 10;
be35036e 354 size *= (client_loops - loops);
d7b40d65 355 msecs = mtime_since_now(&start);
be35036e
JA
356 fprintf(stdout, "Client%d (splice): %Lu MiB/sec (%LuMiB in %lu msecs)\n", offset, size / (unsigned long long) msecs, size >> 10, msecs);
357 return 0;
358}
359
360int client_splice(int out_fd, int file_fd, int offset)
361{
362 int pfd[2], ret;
363
364 if (pipe(pfd) < 0)
365 return error("pipe");
366
367 ret = client_splice_loop(out_fd, file_fd, pfd, offset);
368 close(pfd[0]);
369 close(pfd[1]);
370 return ret;
371}
372
373int do_client(int out_fd, int file_fd, int offset)
374{
4f4118c9
JA
375 int ret;
376
377 if (run_splice) {
378 ret = client_splice(out_fd, file_fd, offset);
379 if (ret)
380 return ret;
381 }
382 if (run_mmap) {
383 ret = client_mmap(out_fd, file_fd, offset);
384 if (ret)
385 return ret;
386 }
387 if (run_rw) {
388 ret = client_rw(out_fd, file_fd, offset);
389 if (ret)
390 return ret;
391 }
d7b40d65
JA
392 return 0;
393}
394
395static int client_open_net(int offset)
396{
397 int sk = socket(PF_INET, SOCK_STREAM, 0);
398 struct sockaddr_in s_to;
399 struct hostent *hp;
400
401 hp = gethostbyname("localhost");
402 if (!hp)
403 return error("gethostbyname");
404
405 bzero((char *) &s_to, sizeof (s_to));
406 bcopy((char *) hp->h_addr, (char *) &(s_to.sin_addr), hp->h_length);
407 s_to.sin_family = hp->h_addrtype;
408 s_to.sin_port = htons(net_port + offset);
409
410 if (connect(sk, (struct sockaddr *)&s_to, sizeof(s_to)) < 0)
411 return error("connect");
412
413 return sk;
414}
415
416int client(int offset)
417{
418 int file_fd, out_fd;
419 char fname[64];
d7b40d65
JA
420
421 bind_to_cpu(offset);
422 nice(-20);
423
424 if (!write_to_null)
425 out_fd = client_open_net(offset);
426 else
427 out_fd = open("/dev/null", O_WRONLY);
428
429 if (out_fd < 0)
430 return error("socket");
431
432 sprintf(fname, "%s%d", filename, same_file ? 0 : offset);
be35036e 433 file_fd = open(fname, O_RDONLY);
d7b40d65
JA
434 if (file_fd < 0)
435 return error("open");
436
be35036e 437 return do_client(out_fd, file_fd, offset);
d7b40d65
JA
438}
439
440int main(int argc, char *argv[])
441{
442 pid_t *spids, *cpids;
443 int i, index;
444
445 index = parse_options(argc, argv);
446 if (index < 0)
447 return usage(argv[0]);
448
449 if (index < argc)
450 filename = argv[index];
451
452 spids = malloc(nr_clients * sizeof(pid_t));
453 cpids = malloc(nr_clients * sizeof(pid_t));
454 memset(spids, 0, nr_clients * sizeof(pid_t));
455 memset(cpids, 0, nr_clients * sizeof(pid_t));
456
457 nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
458 if (nr_cpus < 0)
459 return error("_SC_NPROCESSORS_ONLN");
460
d7b40d65
JA
461 /*
462 * fork servers
463 */
464 if (!write_to_null) {
465 for (i = 0; i < nr_clients; i++) {
466 pid_t pid = fork();
467
468 if (pid)
469 spids[i] = pid;
470 else {
471 server(i);
472 spids[i] = 0;
473 exit(0);
474 }
475 }
476 sleep(1); /* should have servers started now */
477 }
478
479 /*
480 * fork clients
481 */
482 for (i = 0; i < nr_clients; i++) {
483 pid_t pid = fork();
484
485 if (pid)
486 cpids[i] = pid;
487 else {
488 client(i);
489 cpids[i] = 0;
490 exit(0);
491 }
492 }
493
494 /*
495 * wait for clients to exit
496 */
497 fprintf(stdout, "Waiting for clients\n");
498 for (i = 0; i < nr_clients; i++) {
499 if (cpids[i]) {
500 waitpid(cpids[i], NULL, 0);
501 cpids[i] = 0;
502 }
503 }
504
505 /*
506 * then kill servers
507 */
508 for (i = 0; i < nr_clients; i++) {
509 if (spids[i]) {
510 kill(spids[i], SIGKILL);
511 waitpid(spids[i], NULL, 0);
512 spids[i] = 0;
513 }
514 }
515
516 return 0;
517}