1 module during.tests.fsync;
2 
3 import during;
4 import during.tests.base;
5 
6 import core.stdc.stdlib;
7 import core.sys.linux.errno;
8 import core.sys.linux.fcntl;
9 import core.sys.posix.sys.uio : iovec;
10 import core.sys.posix.unistd;
11 
12 @("single")
13 unittest
14 {
15     Uring io;
16     auto res = io.setup();
17     assert(res >= 0, "Error initializing IO");
18 
19     auto fname = getTestFileName!"fsync_single";
20     auto fd = openFile(fname, O_CREAT | O_WRONLY);
21     scope (exit) unlink(&fname[0]);
22 
23     auto ret = io
24         .putWith!((ref SubmissionEntry e, int fd) => e.prepFsync(fd))(fd)
25         .submit(1);
26     assert(ret == 1);
27     assert(io.length == 1);
28     assert(io.front.res == 0);
29     io.popFront();
30 
31     close(fd);
32 }
33 
34 @("barrier")
35 unittest
36 {
37     if (!checkKernelVersion(5, 3)) return;
38 
39     enum NUM_WRITES = 4;
40     Uring io;
41     auto res = io.setup();
42     assert(res >= 0, "Error initializing IO");
43 
44     auto fname = getTestFileName!"fsync_barier";
45     auto fd = openFile(fname, O_CREAT | O_WRONLY);
46     scope (exit) unlink(&fname[0]);
47 
48     iovec[NUM_WRITES] iovecs;
49     foreach (i; 0..NUM_WRITES)
50     {
51         iovecs[i].iov_base = malloc(4096);
52         iovecs[i].iov_len = 4096;
53     }
54 
55     int off;
56     foreach (i; 0..NUM_WRITES)
57     {
58         io.putWith!((ref SubmissionEntry e, int fd, ref iovec v, int off)
59         {
60             e.prepWritev(fd, v, off);
61             e.user_data = 1;
62         })(fd, iovecs[i], off);
63         off += 4096;
64     }
65 
66     io.putWith!((ref SubmissionEntry e, int fd)
67     {
68         e.prepFsync(fd, FsyncFlags.DATASYNC);
69         e.user_data = 2;
70         // TODO: Works with IO_LINK but not without it: See: https://github.com/axboe/liburing/issues/33
71         e.flags = cast(SubmissionEntryFlags)(SubmissionEntryFlags.IO_DRAIN | SubmissionEntryFlags.IO_LINK);
72         // e.flags = SubmissionEntryFlags.IO_DRAIN;
73     })(fd);
74 
75     auto ret = io.submit(NUM_WRITES + 1);
76     if (ret < 0)
77     {
78         if (ret == -EINVAL)
79         {
80             version (D_BetterC)
81             {
82                 errmsg = "Expected kernel to support barrier fsync";
83                 return;
84             }
85             else throw new Exception("Expected kernel to support barrier fsync");
86         }
87         assert(0, "submit failed");
88     }
89     else assert(ret == NUM_WRITES + 1);
90 
91     assert(io.length == NUM_WRITES + 1);
92     foreach (i; 0..NUM_WRITES + 1)
93     {
94         if (io.front.res == -EINVAL)
95         {
96             version (D_BetterC)
97             {
98                 errmsg = "Expected kernel to support IOSQE_IO_DRAIN";
99                 break;
100             }
101             else throw new Exception("Expected kernel to support IOSQE_IO_DRAIN");
102         }
103         assert(io.front.res >= 0);
104         if (i < NUM_WRITES) assert(io.front.user_data == 1, "unexpected op completion");
105         else assert(io.front.user_data == 2, "unexpected op completion");
106 
107         io.popFront();
108     }
109 
110     close(fd);
111 }
112 
113 @("range")
114 unittest
115 {
116     if (!checkKernelVersion(5, 2)) return;
117 
118     enum NUM_WRITES = 4;
119     Uring io;
120     auto res = io.setup();
121     assert(res >= 0, "Error initializing IO");
122 
123     auto fname = getTestFileName!"fsync_range";
124     auto fd = openFile(fname, O_CREAT | O_WRONLY);
125     scope (exit) unlink(&fname[0]);
126 
127     iovec[NUM_WRITES] iovecs;
128     foreach (i; 0..NUM_WRITES)
129     {
130         iovecs[i].iov_base = malloc(4096);
131         iovecs[i].iov_len = 4096;
132     }
133 
134     int off;
135     foreach (i; 0..NUM_WRITES)
136     {
137         io.putWith!((ref SubmissionEntry e, int fd, ref iovec v, int off)
138         {
139             e.prepWritev(fd, v, off);
140             e.user_data = 1;
141             e.flags = SubmissionEntryFlags.IO_LINK;
142         })(fd, iovecs[i], off);
143         off += 4096;
144     }
145 
146     io.putWith!((ref SubmissionEntry e, int fd)
147     {
148         e.prepSyncFileRange(fd, (NUM_WRITES - 1) * 4096, 4096);
149         e.user_data = 2;
150         e.flags = SubmissionEntryFlags.IO_LINK;
151     })(fd);
152 
153     auto ret = io.submit(NUM_WRITES + 1);
154     if (ret < 0)
155     {
156         if (ret == -EINVAL)
157         {
158             version (D_BetterC)
159             {
160                 errmsg = "Expected kernel to support range file sync";
161                 return;
162             }
163             else throw new Exception("Expected kernel to support range file sync");
164         }
165         assert(0, "submit failed");
166     }
167     else assert(ret == NUM_WRITES + 1);
168 
169     assert(io.length == NUM_WRITES + 1);
170     foreach (i; 0..NUM_WRITES + 1)
171     {
172         assert(io.front.res >= 0);
173         if (i < NUM_WRITES) assert(io.front.user_data == 1, "unexpected op completion");
174         else assert(io.front.user_data == 2, "unexpected op completion");
175         io.popFront();
176     }
177 
178     close(fd);
179 }