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