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         e.flags = SubmissionEntryFlags.IO_DRAIN;
71     })(fd);
72 
73     auto ret = io.submit(NUM_WRITES + 1);
74     if (ret < 0)
75     {
76         if (ret == -EINVAL)
77         {
78             version (D_BetterC)
79             {
80                 errmsg = "Expected kernel to support barrier fsync";
81                 return;
82             }
83             else throw new Exception("Expected kernel to support barrier fsync");
84         }
85         assert(0, "submit failed");
86     }
87     else assert(ret == NUM_WRITES + 1);
88 
89     // assert(io.length == NUM_WRITES + 1); // TODO: fails on GH actions with 5.11
90     foreach (i; 0..NUM_WRITES + 1)
91     {
92         if (io.empty) io.wait(1); // TODO: workaround for above, similar is used here: https://github.com/axboe/liburing/blob/0b6b5bc79a85bc3a461c6f3ba9c0ce0dba696d4c/test/fsync.c#L111
93         if (io.front.res == -EINVAL)
94         {
95             version (D_BetterC)
96             {
97                 errmsg = "Expected kernel to support IOSQE_IO_DRAIN";
98                 break;
99             }
100             else throw new Exception("Expected kernel to support IOSQE_IO_DRAIN");
101         }
102         assert(io.front.res >= 0);
103         if (i < NUM_WRITES) assert(io.front.user_data == 1, "unexpected op completion");
104         else assert(io.front.user_data == 2, "unexpected op completion");
105 
106         io.popFront();
107     }
108 
109     close(fd);
110 }
111 
112 @("range")
113 unittest
114 {
115     if (!checkKernelVersion(5, 2)) return;
116 
117     enum NUM_WRITES = 4;
118     Uring io;
119     auto res = io.setup();
120     assert(res >= 0, "Error initializing IO");
121 
122     auto fname = getTestFileName!"fsync_range";
123     auto fd = openFile(fname, O_CREAT | O_WRONLY);
124     scope (exit) unlink(&fname[0]);
125 
126     iovec[NUM_WRITES] iovecs;
127     foreach (i; 0..NUM_WRITES)
128     {
129         iovecs[i].iov_base = malloc(4096);
130         iovecs[i].iov_len = 4096;
131     }
132 
133     int off;
134     foreach (i; 0..NUM_WRITES)
135     {
136         io.putWith!((ref SubmissionEntry e, int fd, ref iovec v, int off)
137         {
138             e.prepWritev(fd, v, off);
139             e.user_data = 1;
140             e.flags = SubmissionEntryFlags.IO_LINK;
141         })(fd, iovecs[i], off);
142         off += 4096;
143     }
144 
145     io.putWith!((ref SubmissionEntry e, int fd)
146     {
147         e.prepSyncFileRange(fd, (NUM_WRITES - 1) * 4096, 4096);
148         e.user_data = 2;
149         e.flags = SubmissionEntryFlags.IO_LINK;
150     })(fd);
151 
152     auto ret = io.submit(NUM_WRITES + 1);
153     if (ret < 0)
154     {
155         if (ret == -EINVAL)
156         {
157             version (D_BetterC)
158             {
159                 errmsg = "Expected kernel to support range file sync";
160                 return;
161             }
162             else throw new Exception("Expected kernel to support range file sync");
163         }
164         assert(0, "submit failed");
165     }
166     else assert(ret == NUM_WRITES + 1);
167 
168     assert(io.length == NUM_WRITES + 1);
169     foreach (i; 0..NUM_WRITES + 1)
170     {
171         assert(io.front.res >= 0);
172         if (i < NUM_WRITES) assert(io.front.user_data == 1, "unexpected op completion");
173         else assert(io.front.user_data == 2, "unexpected op completion");
174         io.popFront();
175     }
176 
177     close(fd);
178 }