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 }