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