1 /*
2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package sun.nio.ch.iouring;
27
28 import jdk.internal.ffi.generated.iouring.io_uring_rsrc_register;
29 import jdk.internal.ffi.generated.iouring.iovec;
30
31 import java.io.IOException;
32 import java.lang.foreign.Arena;
33 import java.lang.foreign.FunctionDescriptor;
34 import java.lang.foreign.MemorySegment;
35 import java.lang.foreign.ValueLayout;
36 import java.lang.invoke.MethodHandle;
37 import java.nio.ByteBuffer;
38 import java.util.IdentityHashMap;
39 import java.util.LinkedList;
40 import java.util.List;
41 import java.util.concurrent.ConcurrentLinkedQueue;
42
43 import static sun.nio.ch.iouring.Util.locateStdHandle;
44 import static jdk.internal.ffi.generated.iouring.iouring_h.IORING_REGISTER_BUFFERS2;
45
46 /**
47 * Kernel mapped direct ByteBuffers. Buffers are allocated by the constructor
48 * and later registered with an IOUringImpl. Users must obtain a buffer from
49 * this object to be used with any async/fixed I/O operation on the ring.
50 */
51 public class KMappedBuffers {
52 private final List<ByteBuffer> registeredBufferList;
53 private final ConcurrentLinkedQueue<ByteBuffer> registeredFreeList;
54
55 // Map which records the kernel index of each buffer
56 private final IdentityHashMap<ByteBuffer,Integer> registeredBuffers
57 = new IdentityHashMap<>();
58 private final Arena autoArena = Arena.ofAuto();
59
60 public KMappedBuffers(int num, int size) throws IOException {
61 this.registeredBufferList = List.copyOf(getDirectBuffers(num, size));
62 this.registeredFreeList = new ConcurrentLinkedQueue<>(registeredBufferList);
63 }
64
65 /**
66 * Registers this List of (direct) ByteBuffers with an IOUringImpl such
67 * that the buffers can be accessed directly by user code
68 * and by the kernel without copying, as part of
69 * fixed read and write operations.
70 *
71 * The buffer's position and limit are ignored for registration
72 * as the entire capacity is registered. Buffer's position and limit
73 * are however observed during readFixed and writeFixed calls.
74 *
75 * It probably makes sense to register the same number of buffers
76 * as there are entries in the Submission Queue.
77 *
78 * @param buffers
79 */
80 public void register(int ringfd) throws IOException {
81 int index = 0;
82 registeredBuffers.clear();
83 int size = registeredBufferList.size();
84 MemorySegment rsrc = autoArena.allocate(io_uring_rsrc_register.$LAYOUT());
85 MemorySegment iov = iovec.allocateArray(size, autoArena);
86 io_uring_rsrc_register.data(rsrc, iov.address());
87 io_uring_rsrc_register.tags(rsrc, 0); // Disable tagging for now
88 io_uring_rsrc_register.nr(rsrc, size);
89 for (ByteBuffer buffer : registeredBufferList) {
90 if (!buffer.isDirect()) {
91 throw new IllegalArgumentException("Buffers must be direct");
92 }
93 if (registeredBuffers.containsKey(buffer)) {
94 throw new IllegalArgumentException("Buffers must be unique");
95 }
96 registeredBuffers.put(buffer, index);
97 MemorySegment seg = iovec.asSlice(iov, index);
98 iovec.iov_base(seg, MemorySegment.ofBuffer(buffer));
99 iovec.iov_len(seg, buffer.capacity());
100 index++;
101 }
102 registerBufferSegment(ringfd, rsrc, (int)io_uring_rsrc_register.sizeof());
103 }
104
105 /**
106 * Returns a registered buffer.
107 * @return
108 */
109 public ByteBuffer getRegisteredBuffer() {
110 return registeredFreeList.poll();
111 }
112
113 public void returnRegisteredBuffer(ByteBuffer buf) {
114 checkAndGetIndexForBuffer(buf);
115 registeredFreeList.add(buf);
116 }
117
118 public int checkAndGetIndexForBuffer(ByteBuffer buf) {
119 int ret;
120 if ((ret = getRegisteredIndexFor(buf)) == -1) {
121 throw new IllegalArgumentException("Not a a registered buffer");
122 }
123 registeredFreeList.forEach((buf1) -> {
124 if (buf == buf1)
125 throw new IllegalArgumentException("buffer was not allocated");
126 });
127 return ret;
128 }
129
130 public int getRegisteredIndexFor(ByteBuffer buf) {
131 Integer ind = registeredBuffers.get(buf);
132 return ind == null ? -1 : ind.intValue();
133 }
134
135 private static List<ByteBuffer> getDirectBuffers(int n, int size) {
136 LinkedList<ByteBuffer> l = new LinkedList<>();
137 for (int i=0; i<n; i++)
138 l.add(ByteBuffer.allocateDirect(size));
139 return l;
140 }
141
142 private void registerBufferSegment(int ringfd, MemorySegment segment, int nentries) throws IOException {
143 int ret;
144 SystemCallContext ctx = SystemCallContext.get();
145 try {
146 ret = (int)register_fn
147 .invokeExact(
148 NR_io_uring_register,
149 ctx.errnoCaptureSegment(),
150 ringfd, IORING_REGISTER_BUFFERS2(),
151 segment.address(), nentries
152 );
153 } catch (Throwable e) {
154 throw new RuntimeException(e);
155 }
156 ctx.throwIOExceptionOnError(ret);
157 }
158
159 // syscall number
160 private static final int NR_io_uring_register = 427;
161
162 private static final MethodHandle register_fn = locateStdHandle(
163 "syscall",
164 FunctionDescriptor.of(ValueLayout.JAVA_INT, // result
165 ValueLayout.JAVA_INT, // syscall
166 ValueLayout.JAVA_INT, // ring fd
167 ValueLayout.JAVA_INT, // opcode
168 ValueLayout.JAVA_LONG, // iovec array
169 ValueLayout.JAVA_INT), // array length
170 SystemCallContext.errnoLinkerOption()
171 );
172 }