btrfs: test delayed subvolume deletion on mount and remount
[xfstests-dev.git] / src / splice-test.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2019 RedHat Inc.  All Rights Reserved.
4  * Author: Andreas Gruenbacher <agruenba@redhat.com>
5  *
6  * Make sure that reading and writing to a pipe via splice.
7  */
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <sys/wait.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <err.h>
14
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <stdbool.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <malloc.h>
21
22 #define SECTOR_SIZE 512
23 #define BUFFER_SIZE (150 * SECTOR_SIZE)
24
25 void read_from_pipe(int fd, const char *filename, size_t size)
26 {
27         char buffer[SECTOR_SIZE];
28         size_t sz;
29         ssize_t ret;
30
31         while (size) {
32                 sz = size;
33                 if (sz > sizeof buffer)
34                         sz = sizeof buffer;
35                 ret = read(fd, buffer, sz);
36                 if (ret < 0)
37                         err(1, "read: %s", filename);
38                 if (ret == 0) {
39                         fprintf(stderr, "read: %s: unexpected EOF\n", filename);
40                         exit(1);
41                 }
42                 size -= sz;
43         }
44 }
45
46 void do_splice1(int fd, const char *filename, size_t size)
47 {
48         bool retried = false;
49         int pipefd[2];
50
51         if (pipe(pipefd) == -1)
52                 err(1, "pipe");
53         while (size) {
54                 ssize_t spliced;
55
56                 spliced = splice(fd, NULL, pipefd[1], NULL, size, SPLICE_F_MOVE);
57                 if (spliced == -1) {
58                         if (errno == EAGAIN && !retried) {
59                                 retried = true;
60                                 fprintf(stderr, "retrying splice\n");
61                                 sleep(1);
62                                 continue;
63                         }
64                         err(1, "splice");
65                 }
66                 read_from_pipe(pipefd[0], filename, spliced);
67                 size -= spliced;
68         }
69         close(pipefd[0]);
70         close(pipefd[1]);
71 }
72
73 void do_splice2(int fd, const char *filename, size_t size)
74 {
75         bool retried = false;
76         int pipefd[2];
77         int pid;
78
79         if (pipe(pipefd) == -1)
80                 err(1, "pipe");
81
82         pid = fork();
83         if (pid == 0) {
84                 close(pipefd[1]);
85                 read_from_pipe(pipefd[0], filename, size);
86                 exit(0);
87         } else {
88                 close(pipefd[0]);
89                 while (size) {
90                         ssize_t spliced;
91
92                         spliced = splice(fd, NULL, pipefd[1], NULL, size, SPLICE_F_MOVE);
93                         if (spliced == -1) {
94                                 if (errno == EAGAIN && !retried) {
95                                         retried = true;
96                                         fprintf(stderr, "retrying splice\n");
97                                         sleep(1);
98                                         continue;
99                                 }
100                                 err(1, "splice");
101                         }
102                         size -= spliced;
103                 }
104                 close(pipefd[1]);
105                 waitpid(pid, NULL, 0);
106         }
107 }
108
109 void usage(const char *argv0)
110 {
111         fprintf(stderr, "USAGE: %s [-rd] {filename}\n", basename(argv0));
112         exit(2);
113 }
114
115 int main(int argc, char *argv[])
116 {
117         void (*do_splice)(int fd, const char *filename, size_t size);
118         const char *filename;
119         char *buffer;
120         int opt, open_flags, fd;
121         ssize_t ret;
122
123         do_splice = do_splice1;
124         open_flags = O_CREAT | O_TRUNC | O_RDWR | O_DIRECT;
125
126         while ((opt = getopt(argc, argv, "rd")) != -1) {
127                 switch(opt) {
128                 case 'r':
129                         do_splice = do_splice2;
130                         break;
131                 case 'd':
132                         open_flags &= ~O_DIRECT;
133                         break;
134                 default:  /* '?' */
135                         usage(argv[0]);
136                 }
137         }
138
139         if (optind >= argc)
140                 usage(argv[0]);
141         filename = argv[optind];
142
143         printf("%s reader %s O_DIRECT\n",
144                    do_splice == do_splice1 ? "sequential" : "concurrent",
145                    (open_flags & O_DIRECT) ? "with" : "without");
146
147         buffer = memalign(SECTOR_SIZE, BUFFER_SIZE);
148         if (buffer == NULL)
149                 err(1, "memalign");
150
151         fd = open(filename, open_flags, 0666);
152         if (fd == -1)
153                 err(1, "open: %s", filename);
154
155         memset(buffer, 'x', BUFFER_SIZE);
156         ret = write(fd, buffer, BUFFER_SIZE);
157         if (ret < 0)
158                 err(1, "write: %s", filename);
159         if (ret != BUFFER_SIZE) {
160                 fprintf(stderr, "%s: short write\n", filename);
161                 exit(1);
162         }
163
164         ret = lseek(fd, 0, SEEK_SET);
165         if (ret != 0)
166                 err(1, "lseek: %s", filename);
167
168         do_splice(fd, filename, BUFFER_SIZE);
169
170         if (unlink(filename) == -1)
171                 err(1, "unlink: %s", filename);
172
173         return 0;
174 }