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