Fix various compile warnings
[splice.git] / splice-bench.c
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>
14 #include <sys/mman.h>
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
23 static int nr_clients = 8;
24 static int net_port = 8888;
25 static int client_loops = 10;
26 static int bind_cpu;
27 static int write_to_null;
28 static int same_file;
29 static int splice_size = SPLICE_SIZE;
30 static char *filename = "splice-file";
31 static unsigned int max_client_run = 15;
32 static int run_rw = 1;
33 static int run_splice = 1;
34 static int run_mmap = 1;
35
36 static int nr_cpus;
37
38 static 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");
48         fprintf(stderr, "\t[-t] (max client runtime in seconds)\n");
49         fprintf(stderr, "\t[-c] (clients to run (rw/mmap/splice)\n");
50         return 1;
51 }
52
53 static int parse_options(int argc, char *argv[])
54 {
55         int c, index = 1;
56
57         while ((c = getopt(argc, argv, "n:p:l:azsb:t:c:")) != -1) {
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;
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;
100                 default:
101                         return -1;
102                 }
103         }
104
105         return index;
106 }
107
108 static int 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
129 static int accept_loop(int listen_sk)
130 {
131         struct sockaddr addr;
132         unsigned int len = sizeof(addr);
133         int sk;
134
135 again:
136         sk = accept(listen_sk, &addr, &len);
137         if (sk < 0)
138                 return error("accept");
139
140         /* read forever */
141         for (;;) {
142                 int ret = recv(sk, NULL, 128*1024*1024, MSG_TRUNC);
143                 if (ret > 0)
144                         continue;
145                 else if (!ret)
146                         break;
147                 if (errno == EAGAIN || errno == EINTR)
148                         continue;
149                 break;
150         }
151
152         close(sk);
153         goto again;
154 }
155
156 static int server(int offset)
157 {
158         struct sockaddr_in saddr_in;
159         struct sockaddr addr;
160         unsigned int len;
161         int sk, opt;
162
163         bind_to_cpu(offset);
164         if (nice(-20) < 0)
165                 perror("nice");
166
167         sk = socket(PF_INET, SOCK_STREAM, 0);
168         if (sk < 0)
169                 return error("socket");
170
171         opt = 1;
172         if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
173                 return error("setsockopt");
174
175         saddr_in.sin_addr.s_addr = htonl(INADDR_ANY);
176         saddr_in.sin_port = htons(net_port + offset);
177
178         if (bind(sk, (struct sockaddr *) &saddr_in, sizeof(saddr_in)) < 0)
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
191 static 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
208 static 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
216 static int client_rw(int out_fd, int file_fd, int offset)
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);
231 again:
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
259         free(buf);
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
267 static int client_mmap(int out_fd, int file_fd, int offset)
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);
287 again:
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
315 static int client_splice_loop(int out_fd, int fd, int *pfd, int offset)
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);
328 again:
329         size = sb.st_size;
330         off = 0;
331
332         do {
333                 int ret = ssplice(fd, &off, pfd[1], NULL, min(size, (unsigned long long) splice_size), 0);
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;
341                         int written = ssplice(pfd[0], NULL, out_fd, NULL, ret, flags);
342
343                         if (written <= 0)
344                                 return error("splice-out");
345
346                         ret -= written;
347                 }
348         } while (size);
349
350         loops--;
351
352         if ((mtime_since_now(&start) < max_client_run * 1000) && loops)
353                 goto again;
354
355         size = sb.st_size >> 10;
356         size *= (client_loops - loops);
357         msecs = mtime_since_now(&start);
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
362 static int client_splice(int out_fd, int file_fd, int offset)
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
375 static int do_client(int out_fd, int file_fd, int offset)
376 {
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         }
394         return 0;
395 }
396
397 static 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
418 static int client(int offset)
419 {
420         int file_fd, out_fd;
421         char fname[64];
422
423         bind_to_cpu(offset);
424         if (nice(-20) < 0)
425                 perror("nice");
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);
436         file_fd = open(fname, O_RDONLY);
437         if (file_fd < 0)
438                 return error("open");
439
440         return do_client(out_fd, file_fd, offset);
441 }
442
443 int 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
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 }