1 /*
2 Copyright 2020 Boris-Barboris
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 
6 Permission is hereby granted, free of charge, to any person or organization
7 obtaining a copy of the software and accompanying documentation covered by
8 this license (the "Software") to use, reproduce, display, distribute,
9 execute, and transmit the Software, and to prepare derivative works of the
10 Software, and to permit third-parties to whom the Software is furnished to
11 do so, all subject to the following:
12 
13 The copyright notices in the Software and this entire statement, including
14 the above license grant, this restriction and the following disclaimer,
15 must be included in all copies of the Software, in whole or in part, and
16 all derivative works of the Software, unless such copies or derivative
17 works are solely in the form of machine-executable object code generated by
18 a source language processor.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 DEALINGS IN THE SOFTWARE.
27 */
28 module libaio;
29 
30 import core.stdc.config;
31 import core.sys.posix.time: timespec;
32 import core.sys.posix.signal: sigset_t;
33 import core.sys.posix.sys.uio: iovec;
34 import core.sys.linux.sys.socket: sockaddr;
35 
36 
37 // Online examples:
38 // https://www.fsl.cs.sunysb.edu/~vass/linux-aio.txt
39 // https://manpages.ubuntu.com/manpages/precise/man3/io.3.html
40 // https://pagure.io/libaio/blob/master/f/man/io.3
41 
42 
43 @nogc nothrow:
44 
45 alias io_context_t = void*;
46 
47 enum io_iocb_cmd: short
48 {
49     IO_CMD_PREAD = 0,
50     IO_CMD_PWRITE = 1,
51 
52     IO_CMD_FSYNC = 2,
53     IO_CMD_FDSYNC = 3,
54 
55     IO_CMD_POLL = 5,
56     IO_CMD_NOOP = 6,
57     IO_CMD_PREADV = 7,
58     IO_CMD_PWRITEV = 8,
59 };
60 
61 // from /usr/include/linux/aio_abi.h
62 enum io_iocb_flags: uint
63 {
64     IOCB_FLAG_RESFD = (1 << 0),
65     IOCB_FLAG_IOPRIO = (1 << 1)
66 }
67 
68 align(8) struct io_iocb_poll
69 {
70     int events;
71 };
72 
73 static assert (io_iocb_poll.sizeof == 8);
74 
75 struct io_iocb_sockaddr
76 {
77     sockaddr* addr;
78     int len;
79 };
80 
81 struct io_iocb_common
82 {
83     align(8):
84         void* buf;
85         c_ulong nbytes;
86         long offset;
87         long __pad3;
88     align(4):
89         uint flags;
90         uint resfd;
91 };
92 
93 static assert (io_iocb_common.sizeof == 40);
94 
95 struct io_iocb_vector
96 {
97     iovec* vec;
98     int nr;
99     long offset;
100 };
101 
102 static assert (io_iocb_vector.sizeof == 8 + 4 + 8 + 4);
103 
104 struct iocb
105 {
106     align(8):
107         void *data;
108         private uint key;   // used by kernel
109     align(4):
110         uint aio_rw_flags;  // RWF_ flags from /usr/include/linux/fs
111     align:
112         io_iocb_cmd aio_lio_opcode;
113         short aio_reqprio;
114         int aio_fildes;
115 
116         union {
117             io_iocb_common c;
118             io_iocb_vector v;
119             io_iocb_poll poll;
120             io_iocb_sockaddr saddr;
121         };
122 };
123 
124 static assert (iocb.sizeof == 8 + 4 + 4 + 2 * 2 + 4 + io_iocb_common.sizeof);
125 
126 struct io_event
127 {
128     align(8):
129         void* data;
130         iocb* obj;
131         c_ulong res;
132         c_ulong res2;
133 }
134 
135 static assert (io_event.sizeof == 32);
136 
137 
138 
139 alias io_callback_t = extern(C) nothrow @nogc void function(
140     io_context_t ctx, iocb* iocb, c_long res, c_long res2);
141 
142 alias da_io_queue_init = extern(C) nothrow @nogc int function(
143     int maxevents, io_context_t* ctxp);
144 
145 alias da_io_queue_release = extern(C) nothrow @nogc int function(
146     io_context_t ctx);
147 
148 alias da_io_queue_run = extern(C) nothrow @nogc int function(
149     io_context_t ctx);
150 
151 alias da_io_setup = extern(C) nothrow @nogc int function(
152     int maxevents, io_context_t* ctxp);
153 
154 alias da_io_destroy = extern(C) nothrow @nogc int function(
155     io_context_t ctx);
156 
157 alias da_io_submit = extern(C) nothrow @nogc int function(
158     io_context_t ctx, c_long nr, iocb** ios);
159 
160 alias da_io_cancel = extern(C) nothrow @nogc int function(
161     io_context_t ctx, iocb *iocb, io_event* evt);
162 
163 alias da_io_getevents = extern(C) nothrow @nogc int function(
164     io_context_t ctx_id, c_long min_nr, c_long nr, io_event* events,
165     timespec* timeout);
166 
167 alias da_io_pgetevents = extern(C) nothrow @nogc int function(
168     io_context_t ctx_id, c_long min_nr, c_long nr,
169     io_event* events, timespec* timeout, sigset_t* sigmask);
170 
171 
172 __gshared
173 {
174     da_io_queue_init io_queue_init;
175     da_io_queue_release io_queue_release;
176     da_io_queue_run io_queue_run;
177     da_io_setup io_setup;
178     da_io_destroy io_destroy;
179     da_io_submit io_submit;
180     da_io_cancel io_cancel;
181     da_io_getevents io_getevents;
182     da_io_pgetevents io_pgetevents;
183 }
184 
185 
186 // UFCS-optimized functions from libaio.h
187 
188 pragma(inline) void io_set_callback(ref iocb iocb, io_callback_t cb)
189 {
190     iocb.data = cast(void*) cb;
191 }
192 
193 pragma(inline) void io_prep_pread(
194     ref iocb iocb, int fd, void* buf, size_t count, long offset)
195 {
196     iocb = iocb.init;
197     iocb.aio_fildes = fd;
198     iocb.aio_lio_opcode = io_iocb_cmd.IO_CMD_PREAD;
199     iocb.aio_reqprio = 0;
200     iocb.c.buf = buf;
201     iocb.c.nbytes = count;
202     iocb.c.offset = offset;
203 }
204 
205 pragma(inline) void io_prep_pwrite(
206     ref iocb iocb, int fd, void* buf, size_t count, long offset)
207 {
208     iocb = iocb.init;
209     iocb.aio_fildes = fd;
210     iocb.aio_lio_opcode = io_iocb_cmd.IO_CMD_PWRITE;
211     iocb.aio_reqprio = 0;
212     iocb.c.buf = buf;
213     iocb.c.nbytes = count;
214     iocb.c.offset = offset;
215 }
216 
217 pragma(inline) void io_prep_preadv(
218     ref iocb iocb, int fd, iovec* iov, int iovcnt, long offset)
219 {
220     iocb = iocb.init;
221     iocb.aio_fildes = fd;
222     iocb.aio_lio_opcode = io_iocb_cmd.IO_CMD_PREADV;
223     iocb.aio_reqprio = 0;
224     iocb.c.buf = cast(void*) iov;
225     iocb.c.nbytes = iovcnt;
226     iocb.c.offset = offset;
227 }
228 
229 pragma(inline) void io_prep_pwritev(
230     ref iocb iocb, int fd, iovec* iov, int iovcnt, long offset)
231 {
232     iocb = iocb.init;
233     iocb.aio_fildes = fd;
234     iocb.aio_lio_opcode = io_iocb_cmd.IO_CMD_PWRITEV;
235     iocb.aio_reqprio = 0;
236     iocb.c.buf = cast(void*) iov;
237     iocb.c.nbytes = iovcnt;
238     iocb.c.offset = offset;
239 }
240 
241 pragma(inline) void io_prep_preadv2(
242     ref iocb iocb, int fd, iovec* iov, int iovcnt, long offset, int flags)
243 {
244     iocb = iocb.init;
245     iocb.aio_fildes = fd;
246     iocb.aio_lio_opcode = io_iocb_cmd.IO_CMD_PREADV;
247     iocb.aio_reqprio = 0;
248     iocb.aio_rw_flags = flags;
249     iocb.c.buf = cast(void*) iov;
250     iocb.c.nbytes = iovcnt;
251     iocb.c.offset = offset;
252 }
253 
254 pragma(inline) void io_prep_pwritev2(
255     ref iocb iocb, int fd, iovec* iov, int iovcnt, long offset, int flags)
256 {
257     iocb = iocb.init;
258     iocb.aio_fildes = fd;
259     iocb.aio_lio_opcode = io_iocb_cmd.IO_CMD_PWRITEV;
260     iocb.aio_reqprio = 0;
261     iocb.aio_rw_flags = flags;
262     iocb.c.buf = cast(void*) iov;
263     iocb.c.nbytes = iovcnt;
264     iocb.c.offset = offset;
265 }
266 
267 pragma(inline) void io_prep_poll(ref iocb iocb, int fd, int events)
268 {
269     iocb = iocb.init;
270     iocb.aio_fildes = fd;
271     iocb.aio_lio_opcode = io_iocb_cmd.IO_CMD_POLL;
272     iocb.aio_reqprio = 0;
273     iocb.poll.events = events;
274 }
275 
276 pragma(inline) int io_poll(
277     io_context_t ctx, iocb* iocb, io_callback_t cb, int fd, int events)
278 {
279     io_prep_poll(*iocb, fd, events);
280     io_set_callback(*iocb, cb);
281     return io_submit(ctx, 1, &iocb);
282 }
283 
284 pragma(inline) void io_prep_fsync(ref iocb iocb, int fd)
285 {
286     iocb = iocb.init;
287     iocb.aio_fildes = fd;
288     iocb.aio_lio_opcode = io_iocb_cmd.IO_CMD_FSYNC;
289     iocb.aio_reqprio = 0;
290 }
291 
292 pragma(inline) int io_fsync(
293     io_context_t ctx, iocb* iocb, io_callback_t cb, int fd)
294 {
295     io_prep_fsync(*iocb, fd);
296     io_set_callback(*iocb, cb);
297     return io_submit(ctx, 1, &iocb);
298 }
299 
300 pragma(inline) void io_prep_fdsync(ref iocb iocb, int fd)
301 {
302     iocb = iocb.init;
303     iocb.aio_fildes = fd;
304     iocb.aio_lio_opcode = io_iocb_cmd.IO_CMD_FDSYNC;
305     iocb.aio_reqprio = 0;
306 }
307 
308 pragma(inline) int io_fdsync(
309     io_context_t ctx, iocb* iocb, io_callback_t cb, int fd)
310 {
311     io_prep_fdsync(*iocb, fd);
312     io_set_callback(*iocb, cb);
313     return io_submit(ctx, 1, &iocb);
314 }
315 
316 pragma(inline) void io_set_eventfd(ref iocb iocb, int eventfd)
317 {
318 	iocb.c.flags |= io_iocb_flags.IOCB_FLAG_RESFD;
319 	iocb.c.resfd = eventfd;
320 }