[PATCH] splice-bench: error checking
[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 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 int accept_loop(int listen_sk)
130 {
131         unsigned long received;
132         struct sockaddr addr;
133         unsigned int len = sizeof(addr);
134         int sk;
135
136 again:
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
160 int 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
190 static 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
207 static 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
215 int 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);
230 again:
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
265 int 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);
285 again:
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
313 int client_splice_loop(int out_fd, int fd, int *pfd, int offset)
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);
326 again:
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
348         loops--;
349
350         if ((mtime_since_now(&start) < max_client_run * 1000) && loops)
351                 goto again;
352
353         size = sb.st_size >> 10;
354         size *= (client_loops - loops);
355         msecs = mtime_since_now(&start);
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
360 int 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
373 int do_client(int out_fd, int file_fd, int offset)
374 {
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         }
392         return 0;
393 }
394
395 static 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
416 int client(int offset)
417 {
418         int file_fd, out_fd;
419         char fname[64];
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);
433         file_fd = open(fname, O_RDONLY);
434         if (file_fd < 0)
435                 return error("open");
436
437         return do_client(out_fd, file_fd, offset);
438 }
439
440 int 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
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 }