1 // SPDX-License-Identifier: Apache-2.0
2
3 #define _LARGEFILE64_SOURCE
4
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <getopt.h>
8 #include <poll.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/mman.h>
13 #include <sys/prctl.h>
14 #include <unistd.h>
15 #include <iostream>
16
17 #define SECTOR_SIZE ((__u64)512)
18 #define BUFFER_BYTES 4096
19
20 #define MAX(a, b) ((a) > (b) ? (a) : (b))
21
22 /* This should be replaced with linux/dm-user.h. */
23 #ifndef _LINUX_DM_USER_H
24 #define _LINUX_DM_USER_H
25
26 #include <linux/types.h>
27
28 #define DM_USER_REQ_MAP_READ 0
29 #define DM_USER_REQ_MAP_WRITE 1
30 #define DM_USER_REQ_MAP_FLUSH 2
31 #define DM_USER_REQ_MAP_DISCARD 3
32 #define DM_USER_REQ_MAP_SECURE_ERASE 4
33 #define DM_USER_REQ_MAP_WRITE_SAME 5
34 #define DM_USER_REQ_MAP_WRITE_ZEROES 6
35 #define DM_USER_REQ_MAP_ZONE_OPEN 7
36 #define DM_USER_REQ_MAP_ZONE_CLOSE 8
37 #define DM_USER_REQ_MAP_ZONE_FINISH 9
38 #define DM_USER_REQ_MAP_ZONE_APPEND 10
39 #define DM_USER_REQ_MAP_ZONE_RESET 11
40 #define DM_USER_REQ_MAP_ZONE_RESET_ALL 12
41
42 #define DM_USER_REQ_MAP_FLAG_FAILFAST_DEV 0x00001
43 #define DM_USER_REQ_MAP_FLAG_FAILFAST_TRANSPORT 0x00002
44 #define DM_USER_REQ_MAP_FLAG_FAILFAST_DRIVER 0x00004
45 #define DM_USER_REQ_MAP_FLAG_SYNC 0x00008
46 #define DM_USER_REQ_MAP_FLAG_META 0x00010
47 #define DM_USER_REQ_MAP_FLAG_PRIO 0x00020
48 #define DM_USER_REQ_MAP_FLAG_NOMERGE 0x00040
49 #define DM_USER_REQ_MAP_FLAG_IDLE 0x00080
50 #define DM_USER_REQ_MAP_FLAG_INTEGRITY 0x00100
51 #define DM_USER_REQ_MAP_FLAG_FUA 0x00200
52 #define DM_USER_REQ_MAP_FLAG_PREFLUSH 0x00400
53 #define DM_USER_REQ_MAP_FLAG_RAHEAD 0x00800
54 #define DM_USER_REQ_MAP_FLAG_BACKGROUND 0x01000
55 #define DM_USER_REQ_MAP_FLAG_NOWAIT 0x02000
56 #define DM_USER_REQ_MAP_FLAG_CGROUP_PUNT 0x04000
57 #define DM_USER_REQ_MAP_FLAG_NOUNMAP 0x08000
58 #define DM_USER_REQ_MAP_FLAG_HIPRI 0x10000
59 #define DM_USER_REQ_MAP_FLAG_DRV 0x20000
60 #define DM_USER_REQ_MAP_FLAG_SWAP 0x40000
61
62 #define DM_USER_RESP_SUCCESS 0
63 #define DM_USER_RESP_ERROR 1
64 #define DM_USER_RESP_UNSUPPORTED 2
65
66 struct dm_user_message {
67 __u64 seq;
68 __u64 type;
69 __u64 flags;
70 __u64 sector;
71 __u64 len;
72 __u8 buf[];
73 };
74
75 #endif
76
77 static bool verbose = false;
78
write_all(int fd,void * buf,size_t len)79 ssize_t write_all(int fd, void* buf, size_t len) {
80 char* buf_c = (char*)buf;
81 ssize_t total = 0;
82 ssize_t once;
83
84 while (total < static_cast<ssize_t>(len)) {
85 once = write(fd, buf_c + total, len - total);
86 if (once < 0) return once;
87 if (once == 0) {
88 errno = ENOSPC;
89 return 0;
90 }
91 total += once;
92 }
93
94 return total;
95 }
96
read_all(int fd,void * buf,size_t len)97 ssize_t read_all(int fd, void* buf, size_t len) {
98 char* buf_c = (char*)buf;
99 ssize_t total = 0;
100 ssize_t once;
101
102 while (total < static_cast<ssize_t>(len)) {
103 once = read(fd, buf_c + total, len - total);
104 if (once < 0) return once;
105 if (once == 0) {
106 errno = ENOSPC;
107 return 0;
108 }
109 total += once;
110 }
111
112 return total;
113 }
114
not_splice(int from,int to,__u64 count)115 int not_splice(int from, int to, __u64 count) {
116 while (count > 0) {
117 char buf[BUFFER_BYTES];
118 __u64 max = count > BUFFER_BYTES ? BUFFER_BYTES : count;
119
120 if (read_all(from, buf, max) <= 0) {
121 perror("Unable to read");
122 return -EIO;
123 }
124
125 if (write_all(to, buf, max) <= 0) {
126 perror("Unable to write");
127 return -EIO;
128 }
129
130 count -= max;
131 }
132
133 return 0;
134 }
135
simple_daemon(char * control_path,char * backing_path)136 int simple_daemon(char* control_path, char* backing_path) {
137 int control_fd = open(control_path, O_RDWR);
138 if (control_fd < 0) {
139 fprintf(stderr, "Unable to open control device %s\n", control_path);
140 return -1;
141 }
142
143 int backing_fd = open(backing_path, O_RDWR);
144 if (backing_fd < 0) {
145 fprintf(stderr, "Unable to open backing device %s\n", backing_path);
146 return -1;
147 }
148
149 while (1) {
150 struct dm_user_message msg;
151 char* base;
152 __u64 type;
153
154 if (verbose) std::cerr << "dmuserd: Waiting for message...\n";
155
156 if (read_all(control_fd, &msg, sizeof(msg)) < 0) {
157 if (errno == ENOTBLK) return 0;
158
159 perror("unable to read msg");
160 return -1;
161 }
162
163 if (verbose) {
164 std::string type;
165 switch (msg.type) {
166 case DM_USER_REQ_MAP_WRITE:
167 type = "write";
168 break;
169 case DM_USER_REQ_MAP_READ:
170 type = "read";
171 break;
172 case DM_USER_REQ_MAP_FLUSH:
173 type = "flush";
174 break;
175 default:
176 /*
177 * FIXME: Can't I do "whatever"s here rather that
178 * std::string("whatever")?
179 */
180 type = std::string("(unknown, id=") + std::to_string(msg.type) + ")";
181 break;
182 }
183
184 std::string flags;
185 if (msg.flags & DM_USER_REQ_MAP_FLAG_SYNC) {
186 if (!flags.empty()) flags += "|";
187 flags += "S";
188 }
189 if (msg.flags & DM_USER_REQ_MAP_FLAG_META) {
190 if (!flags.empty()) flags += "|";
191 flags += "M";
192 }
193 if (msg.flags & DM_USER_REQ_MAP_FLAG_FUA) {
194 if (!flags.empty()) flags += "|";
195 flags += "FUA";
196 }
197 if (msg.flags & DM_USER_REQ_MAP_FLAG_PREFLUSH) {
198 if (!flags.empty()) flags += "|";
199 flags += "F";
200 }
201
202 std::cerr << "dmuserd: Got " << type << " request " << flags << " for sector "
203 << std::to_string(msg.sector) << " with length " << std::to_string(msg.len)
204 << "\n";
205 }
206
207 type = msg.type;
208 switch (type) {
209 case DM_USER_REQ_MAP_READ:
210 msg.type = DM_USER_RESP_SUCCESS;
211 break;
212 case DM_USER_REQ_MAP_WRITE:
213 if (msg.flags & DM_USER_REQ_MAP_FLAG_PREFLUSH ||
214 msg.flags & DM_USER_REQ_MAP_FLAG_FUA) {
215 if (fsync(backing_fd) < 0) {
216 perror("Unable to fsync(), just sync()ing instead");
217 sync();
218 }
219 }
220 msg.type = DM_USER_RESP_SUCCESS;
221 if (lseek64(backing_fd, msg.sector * SECTOR_SIZE, SEEK_SET) < 0) {
222 perror("Unable to seek");
223 return -1;
224 }
225 if (not_splice(control_fd, backing_fd, msg.len) < 0) {
226 if (errno == ENOTBLK) return 0;
227 std::cerr << "unable to handle write data\n";
228 return -1;
229 }
230 if (msg.flags & DM_USER_REQ_MAP_FLAG_FUA) {
231 if (fsync(backing_fd) < 0) {
232 perror("Unable to fsync(), just sync()ing instead");
233 sync();
234 }
235 }
236 break;
237 case DM_USER_REQ_MAP_FLUSH:
238 msg.type = DM_USER_RESP_SUCCESS;
239 if (fsync(backing_fd) < 0) {
240 perror("Unable to fsync(), just sync()ing instead");
241 sync();
242 }
243 break;
244 default:
245 std::cerr << "dmuserd: unsupported op " << std::to_string(msg.type) << "\n";
246 msg.type = DM_USER_RESP_UNSUPPORTED;
247 break;
248 }
249
250 if (verbose) std::cerr << "dmuserd: Responding to message\n";
251
252 if (write_all(control_fd, &msg, sizeof(msg)) < 0) {
253 if (errno == ENOTBLK) return 0;
254 perror("unable to write msg");
255 return -1;
256 }
257
258 switch (type) {
259 case DM_USER_REQ_MAP_READ:
260 if (verbose) std::cerr << "dmuserd: Sending read data\n";
261 if (lseek64(backing_fd, msg.sector * SECTOR_SIZE, SEEK_SET) < 0) {
262 perror("Unable to seek");
263 return -1;
264 }
265 if (not_splice(backing_fd, control_fd, msg.len) < 0) {
266 if (errno == ENOTBLK) return 0;
267 std::cerr << "unable to handle read data\n";
268 return -1;
269 }
270 break;
271 }
272 }
273
274 /* The daemon doesn't actully terminate for this test. */
275 perror("Unable to read from control device");
276 return -1;
277 }
278
usage(char * prog)279 void usage(char* prog) {
280 printf("Usage: %s\n", prog);
281 printf(" Handles block requests in userspace, backed by memory\n");
282 printf(" -h Display this help message\n");
283 printf(" -c <control dev> Control device to use for the test\n");
284 printf(" -b <store path> The file to use as a backing store, otherwise memory\n");
285 printf(" -v Enable verbose mode\n");
286 }
287
main(int argc,char * argv[])288 int main(int argc, char* argv[]) {
289 char* control_path = NULL;
290 char* backing_path = NULL;
291 char* store;
292 int c;
293
294 prctl(PR_SET_IO_FLUSHER, 0, 0, 0, 0);
295
296 while ((c = getopt(argc, argv, "h:c:s:b:v")) != -1) {
297 switch (c) {
298 case 'h':
299 usage(basename(argv[0]));
300 exit(0);
301 case 'c':
302 control_path = strdup(optarg);
303 break;
304 case 'b':
305 backing_path = strdup(optarg);
306 break;
307 case 'v':
308 verbose = true;
309 break;
310 default:
311 usage(basename(argv[0]));
312 exit(1);
313 }
314 }
315
316 int r = simple_daemon(control_path, backing_path);
317 if (r) fprintf(stderr, "simple_daemon() errored out\n");
318 return r;
319 }
320