1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.channel.uring;
17
18 import io.netty.channel.unix.Buffer;
19 import io.netty.channel.unix.Errors;
20 import io.netty.util.internal.logging.InternalLogger;
21 import io.netty.util.internal.logging.InternalLoggerFactory;
22
23 import java.io.UncheckedIOException;
24 import java.lang.invoke.MethodHandles;
25 import java.lang.invoke.VarHandle;
26 import java.nio.ByteBuffer;
27 import java.nio.ByteOrder;
28 import java.util.StringJoiner;
29
30 final class SubmissionQueue {
31 private static final InternalLogger logger = InternalLoggerFactory.getInstance(SubmissionQueue.class);
32
33 static final int SQE_SIZE = 64;
34
35
36
37 private static final int SQE_OP_CODE_FIELD = 0;
38 private static final int SQE_FLAGS_FIELD = 1;
39 private static final int SQE_IOPRIO_FIELD = 2;
40 private static final int SQE_FD_FIELD = 4;
41 private static final int SQE_UNION1_FIELD = 8;
42 private static final int SQE_UNION2_FIELD = 16;
43 private static final int SQE_LEN_FIELD = 24;
44 private static final int SQE_UNION3_FIELD = 28;
45 private static final int SQE_USER_DATA_FIELD = 32;
46 private static final int SQE_UNION4_FIELD = 40;
47 private static final int SQE_PERSONALITY_FIELD = 42;
48 private static final int SQE_UNION5_FIELD = 44;
49 private static final int SQE_UNION6_FIELD = 48;
50
51
52
53 private static final VarHandle INT_HANDLE =
54 MethodHandles.byteBufferViewVarHandle(int[].class, ByteOrder.nativeOrder());
55 private final ByteBuffer kHead;
56 private final ByteBuffer kTail;
57 private final ByteBuffer kflags;
58 private final ByteBuffer submissionQueueArray;
59
60 final int ringEntries;
61 private final int ringMask;
62
63 final int ringSize;
64 final long ringAddress;
65 final int ringFd;
66 int enterRingFd;
67 private int enterFlags;
68 private int head;
69 private int tail;
70
71 private boolean closed;
72
73 SubmissionQueue(ByteBuffer khead, ByteBuffer ktail, int ringMask, int ringEntries, ByteBuffer kflags,
74 ByteBuffer submissionQueueArray,
75 int ringSize, long ringAddress,
76 int ringFd) {
77 this.kHead = khead;
78 this.kTail = ktail;
79 this.submissionQueueArray = submissionQueueArray;
80 this.ringSize = ringSize;
81 this.ringAddress = ringAddress;
82 this.ringFd = ringFd;
83 this.enterRingFd = ringFd;
84 this.ringEntries = ringEntries;
85 this.kflags = kflags;
86 this.ringMask = ringMask;
87 this.head = (int) INT_HANDLE.getVolatile(khead, 0);
88 this.tail = (int) INT_HANDLE.getVolatile(ktail, 0);
89 }
90
91 int flags() {
92 if (closed) {
93 return 0;
94 }
95
96 return (int) INT_HANDLE.getOpaque(kflags, 0);
97 }
98
99 long submissionQueueArrayAddress() {
100 return Buffer.memoryAddress(submissionQueueArray);
101 }
102
103 void close() {
104 closed = true;
105 }
106
107 private void checkClosed() {
108 if (closed) {
109 throw new IllegalStateException();
110 }
111 }
112
113 void tryRegisterRingFd() {
114 checkClosed();
115
116
117 int enterRingFd = Native.ioUringRegisterRingFds(ringFd);
118 int enterFlags;
119 if (enterRingFd < 0) {
120
121 enterRingFd = ringFd;
122 enterFlags = 0;
123 } else {
124 enterFlags = Native.IORING_ENTER_REGISTERED_RING;
125 }
126
127
128 if (IoUring.isIoringEnterNoIoWaitEnabled()) {
129 enterFlags |= Native.IORING_ENTER_NO_IOWAIT;
130 }
131 this.enterRingFd = enterRingFd;
132 this.enterFlags = enterFlags;
133 }
134
135 long enqueueSqe(byte opcode, byte flags, short ioPrio, int fd, long union1, long union2, int len,
136 int union3, long udata, short union4, short personality, int union5, long union6) {
137 checkClosed();
138 int pending = tail - head;
139 if (pending == ringEntries) {
140 int submitted = submit();
141 if (submitted == 0) {
142
143 throw new RuntimeException("SQ ring full and no submissions accepted");
144 }
145 }
146 int sqe = sqeIndex(tail++, ringMask);
147
148
149 submissionQueueArray.put(sqe + SQE_OP_CODE_FIELD, opcode);
150 submissionQueueArray.put(sqe + SQE_FLAGS_FIELD, flags);
151
152 submissionQueueArray.putShort(sqe + SQE_IOPRIO_FIELD, ioPrio);
153 submissionQueueArray.putInt(sqe + SQE_FD_FIELD, fd);
154 submissionQueueArray.putLong(sqe + SQE_UNION1_FIELD, union1);
155 submissionQueueArray.putLong(sqe + SQE_UNION2_FIELD, union2);
156 submissionQueueArray.putInt(sqe + SQE_LEN_FIELD, len);
157 submissionQueueArray.putInt(sqe + SQE_UNION3_FIELD, union3);
158 submissionQueueArray.putLong(sqe + SQE_USER_DATA_FIELD, udata);
159 submissionQueueArray.putShort(sqe + SQE_UNION4_FIELD, union4);
160 submissionQueueArray.putShort(sqe + SQE_PERSONALITY_FIELD, personality);
161 submissionQueueArray.putInt(sqe + SQE_UNION5_FIELD, union5);
162 submissionQueueArray.putLong(sqe + SQE_UNION6_FIELD, union6);
163
164 if (logger.isTraceEnabled()) {
165 if (opcode == Native.IORING_OP_WRITEV || opcode == Native.IORING_OP_READV) {
166 logger.trace("add(ring={}, enterRing:{} ): {}(fd={}, len={}, off={}, data={})",
167 ringFd, enterRingFd, Native.opToStr(opcode), fd, len, union1, udata);
168 } else {
169 logger.trace("add(ring={}, enterRing:{}): {}(fd={}, len={}, off={}, data={})",
170 ringFd, enterRingFd, Native.opToStr(opcode), fd, len, union1, udata);
171 }
172 }
173 return udata;
174 }
175
176 @Override
177 public String toString() {
178 StringJoiner sb = new StringJoiner(", ", "SubmissionQueue [", "]");
179 if (closed) {
180 sb.add("closed");
181 } else {
182 int pending = tail - head;
183 int idx = head;
184 for (int i = 0; i < pending; i++) {
185 int sqe = sqeIndex(idx++, ringMask);
186 sb.add(Native.opToStr(submissionQueueArray.get(sqe + SQE_OP_CODE_FIELD)) +
187 "(fd=" + submissionQueueArray.getInt(sqe + SQE_FD_FIELD) + ')');
188 }
189 }
190 return sb.toString();
191 }
192
193 private static int sqeIndex(int tail, int ringMask) {
194 return (tail & ringMask) * SQE_SIZE;
195 }
196
197 long addNop(byte flags, long udata) {
198 return addNop(flags, 0, udata);
199 }
200
201 long addNop(byte flags, int nopFlags, long udata) {
202
203
204 return enqueueSqe(Native.IORING_OP_NOP, flags, (short) 0, -1, 0, 0, 0, nopFlags, udata,
205 (short) 0, (short) 0, 0, 0);
206 }
207
208 long addTimeout(long timeoutMemoryAddress, long udata) {
209
210
211 return enqueueSqe(Native.IORING_OP_TIMEOUT, (byte) 0, (short) 0, -1, 1, timeoutMemoryAddress, 1,
212 0, udata, (short) 0, (short) 0, 0, 0);
213 }
214
215 long addLinkTimeout(long timeoutMemoryAddress, long extraData) {
216
217
218 return enqueueSqe(Native.IORING_OP_LINK_TIMEOUT, (byte) 0, (short) 0, -1, 1, timeoutMemoryAddress, 1,
219 0, extraData, (short) 0, (short) 0, 0, 0);
220 }
221
222 long addEventFdRead(int fd, long bufferAddress, int pos, int limit, long udata) {
223 return enqueueSqe(Native.IORING_OP_READ, (byte) 0, (short) 0, fd, 0, bufferAddress + pos, limit - pos,
224 0, udata, (short) 0, (short) 0, 0, 0);
225 }
226
227
228
229 long addCancel(long sqeToCancel, long udata) {
230 return enqueueSqe(Native.IORING_OP_ASYNC_CANCEL, (byte) 0, (short) 0, -1, 0, sqeToCancel, 0, 0,
231 udata, (short) 0, (short) 0, 0, 0);
232 }
233
234
235
236
237
238
239 int submit() {
240 checkClosed();
241 int submit = tail - head;
242 return submit > 0 ? submit(submit, 0, 0) : 0;
243 }
244
245
246
247
248
249
250
251 int submitAndGet() {
252 return submitAndGet0(1);
253 }
254
255
256
257
258
259
260 int submitAndGetNow() {
261 return submitAndGet0(0);
262 }
263
264 private int submitAndGet0(int minComplete) {
265 checkClosed();
266 int submit = tail - head;
267 if (submit > 0) {
268 return submit(submit, minComplete, Native.IORING_ENTER_GETEVENTS);
269 }
270 assert submit == 0;
271 int ret = ioUringEnter(0, minComplete, Native.IORING_ENTER_GETEVENTS);
272 if (ret < 0) {
273 throw new UncheckedIOException(Errors.newIOException("io_uring_enter", ret));
274 }
275 return ret;
276 }
277
278 private int submit(int toSubmit, int minComplete, int flags) {
279 INT_HANDLE.setRelease(kTail, 0, tail);
280 int ret = ioUringEnter(toSubmit, minComplete, flags);
281 head = (int) INT_HANDLE.getVolatile(kHead, 0);
282 if (ret != toSubmit) {
283 if (ret < 0) {
284 throw new UncheckedIOException(Errors.newIOException("io_uring_enter", ret));
285 }
286 }
287 return ret;
288 }
289
290 private int ioUringEnter(int toSubmit, int minComplete, int flags) {
291 int f = enterFlags | flags;
292
293 if (IoUring.isSetupSubmitAllSupported()) {
294 return ioUringEnter0(toSubmit, minComplete, f);
295 }
296
297
298 int submitted = 0;
299 for (;;) {
300 int ret = ioUringEnter0(toSubmit, minComplete, f);
301 if (ret < 0) {
302 return ret;
303 }
304 submitted += ret;
305 if (ret == toSubmit) {
306 return submitted;
307 }
308 if (logger.isTraceEnabled()) {
309
310 logger.trace("Not all submissions succeeded. Only {} of {} SQEs were submitted.", ret, toSubmit);
311 }
312 toSubmit -= ret;
313 }
314 }
315
316 private int ioUringEnter0(int toSubmit, int minComplete, int f) {
317 if (logger.isTraceEnabled()) {
318 logger.trace("io_uring_enter(ring={}, enterRing={}, toSubmit={}, minComplete={}, flags={}): {}",
319 ringFd, enterRingFd, toSubmit, minComplete, f, toString());
320 }
321 return Native.ioUringEnter(enterRingFd, toSubmit, minComplete, f);
322 }
323
324 public int count() {
325 return tail - head;
326 }
327
328 public int remaining() {
329 return ringEntries - count();
330 }
331 }