[PATCH] vmsplice: remember to update 'buffer' for multiple iterations
[splice.git] / vmsplice.c
1 /*
2  * Use vmsplice to fill some user memory into a pipe. vmsplice writes
3  * to stdout, so that must be a pipe.
4  */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <limits.h>
10 #include <sys/poll.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
13
14 #include "splice.h"
15
16 #define ALIGN_BUF
17
18 #ifdef ALIGN_BUF
19 #define ALIGN_MASK      (65535) /* 64k-1, should just be PAGE_SIZE - 1 */
20 #define ALIGN(buf)      (void *) (((unsigned long) (buf) + ALIGN_MASK) & ~ALIGN_MASK)
21 #else
22 #define ALIGN_MASK      (0)
23 #define ALIGN(buf)      (buf)
24 #endif
25
26 int do_vmsplice(int fd, void *buffer, int len)
27 {
28         struct pollfd pfd = { .fd = fd, .events = POLLOUT, };
29         int written;
30
31         while (len) {
32                 /*
33                  * in a real app you'd be more clever with poll of course,
34                  * here we are basically just blocking on output room and
35                  * not using the free time for anything interesting.
36                  */
37                 if (poll(&pfd, 1, -1) < 0)
38                         return error("poll");
39
40                 written = vmsplice(fd, buffer, min(SPLICE_SIZE, len), 0);
41
42                 if (written <= 0)
43                         return error("vmsplice");
44
45                 len -= written;
46                 buffer += written;
47         }
48
49         return 0;
50 }
51
52 int main(int argc, char *argv[])
53 {
54         unsigned char *buffer;
55         struct stat sb;
56         long page_size;
57         int i, ret;
58
59         if (fstat(STDOUT_FILENO, &sb) < 0)
60                 return error("stat");
61         if (!S_ISFIFO(sb.st_mode)) {
62                 fprintf(stderr, "stdout must be a pipe\n");
63                 return 1;
64         }
65
66         ret = fcntl(STDOUT_FILENO, F_GETPSZ);
67         if (ret < 0)
68                 return error("F_GETPSZ");
69
70         page_size = sysconf(_SC_PAGESIZE);
71         if (page_size < 0)
72                 return error("_SC_PAGESIZE");
73
74         fprintf(stderr, "Pipe size: %d pages / %ld bytes\n", ret, ret * page_size);
75
76         buffer = ALIGN(malloc(2 * SPLICE_SIZE + ALIGN_MASK));
77         for (i = 0; i < 2 * SPLICE_SIZE; i++)
78                 buffer[i] = (i & 0xff);
79
80         do {
81                 /*
82                  * vmsplice the first half of the buffer into the pipe
83                  */
84                 if (do_vmsplice(STDOUT_FILENO, buffer, SPLICE_SIZE))
85                         break;
86
87                 /*
88                  * first half is now in pipe, but we don't quite know when
89                  * we can reuse it.
90                  */
91
92                 /*
93                  * vmsplice second half
94                  */
95                 if (do_vmsplice(STDOUT_FILENO, buffer + SPLICE_SIZE, SPLICE_SIZE))
96                         break;
97
98                 /*
99                  * We still don't know when we can reuse the second half of
100                  * the buffer, but we do now know that all parts of the first
101                  * half have been consumed from the pipe - so we can reuse that.
102                  */
103         } while (0);
104
105         return 0;
106 }