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 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 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 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 ByteBuffer getRegisteredBuffer() { 110 return registeredFreeList.poll(); 111 } 112 113 void returnRegisteredBuffer(ByteBuffer buf) { 114 checkAndGetIndexForBuffer(buf); 115 registeredFreeList.add(buf); 116 } 117 118 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 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 private void registerBufferSegment(int ringfd, MemorySegment segment, int nentries) throws IOException { 142 int ret; 143 SystemCallContext ctx = SystemCallContext.get(); 144 try { 145 ret = (int)register_fn 146 .invokeExact( 147 NR_io_uring_register, 148 ctx.errnoCaptureSegment(), 149 ringfd, IORING_REGISTER_BUFFERS2(), 150 segment.address(), nentries 151 ); 152 } catch (Throwable e) { 153 throw new RuntimeException(e); 154 } 155 ctx.throwIOExceptionOnError(ret); 156 } 157 158 // syscall number 159 private static final int NR_io_uring_register = 427; 160 161 private static final MethodHandle register_fn = locateStdHandle( 162 "syscall", 163 FunctionDescriptor.of(ValueLayout.JAVA_INT, // result 164 ValueLayout.JAVA_INT, // syscall 165 ValueLayout.JAVA_INT, // ring fd 166 ValueLayout.JAVA_INT, // opcode 167 ValueLayout.JAVA_LONG, // iovec array 168 ValueLayout.JAVA_INT), // array length 169 SystemCallContext.errnoLinkerOption() 170 ); 171 }