netty5/transport-native-io_uring/src/main/c/netty_io_uring_native.c

297 lines
9.5 KiB
C

/*
* Copyright 2020 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#define _GNU_SOURCE // RTLD_DEFAULT
#include "io_uring.h"
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <jni.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "syscall.h"
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <syscall.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
static jmethodID ringBufferMethodId = NULL;
static jmethodID ioUringSubmissionQueueMethodId = NULL;
static jmethodID ioUringCommpletionQueueMethodId = NULL;
static jclass ringBufferClass = NULL;
static jclass ioUringCompletionQueueClass = NULL;
static jclass ioUringSubmissionQueueClass = NULL;
void io_uring_unmap_rings(struct io_uring_sq *sq, struct io_uring_cq *cq) {
munmap(sq->ring_ptr, sq->ring_sz);
if (cq->ring_ptr && cq->ring_ptr != sq->ring_ptr) {
munmap(cq->ring_ptr, cq->ring_sz);
}
}
static int io_uring_mmap(int fd, struct io_uring_params *p,
struct io_uring_sq *sq, struct io_uring_cq *cq)
{
size_t size;
int ret;
sq->ring_sz = p->sq_off.array + p->sq_entries * sizeof(unsigned);
cq->ring_sz = p->cq_off.cqes + p->cq_entries * sizeof(struct io_uring_cqe);
if (p->features & IORING_FEAT_SINGLE_MMAP) {
if (cq->ring_sz > sq->ring_sz)
sq->ring_sz = cq->ring_sz;
cq->ring_sz = sq->ring_sz;
}
sq->ring_ptr = mmap(0, sq->ring_sz, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_POPULATE, fd, IORING_OFF_SQ_RING);
if (sq->ring_ptr == MAP_FAILED)
return -errno;
if (p->features & IORING_FEAT_SINGLE_MMAP) {
cq->ring_ptr = sq->ring_ptr;
} else {
cq->ring_ptr = mmap(0, cq->ring_sz, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_POPULATE, fd, IORING_OFF_CQ_RING);
if (cq->ring_ptr == MAP_FAILED) {
cq->ring_ptr = NULL;
ret = -errno;
goto err;
}
}
sq->khead = sq->ring_ptr + p->sq_off.head;
sq->ktail = sq->ring_ptr + p->sq_off.tail;
sq->kring_mask = sq->ring_ptr + p->sq_off.ring_mask;
sq->kring_entries = sq->ring_ptr + p->sq_off.ring_entries;
sq->kflags = sq->ring_ptr + p->sq_off.flags;
sq->kdropped = sq->ring_ptr + p->sq_off.dropped;
sq->array = sq->ring_ptr + p->sq_off.array;
size = p->sq_entries * sizeof(struct io_uring_sqe);
sq->sqes = mmap(0, size, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_POPULATE, fd,
IORING_OFF_SQES);
if (sq->sqes == MAP_FAILED) {
ret = -errno;
goto err;
}
cq->khead = cq->ring_ptr + p->cq_off.head;
cq->ktail = cq->ring_ptr + p->cq_off.tail;
cq->kring_mask = cq->ring_ptr + p->cq_off.ring_mask;
cq->kring_entries = cq->ring_ptr + p->cq_off.ring_entries;
cq->koverflow = cq->ring_ptr + p->cq_off.overflow;
cq->cqes = cq->ring_ptr + p->cq_off.cqes;
return 0;
err:
io_uring_unmap_rings(sq, cq);
return ret;
}
void setup_io_uring(int ring_fd, struct io_uring *io_uring_ring,
struct io_uring_params *p) {
int ret;
ret = io_uring_mmap(ring_fd, p, &io_uring_ring->sq, &io_uring_ring->cq);
if (!ret) {
io_uring_ring->flags = p->flags;
io_uring_ring->ring_fd = ring_fd;
} else {
//Todo signal this back to EventLoop
perror("setup_io_uring error \n");
}
}
static jint netty_io_uring_enter(JNIEnv *env, jclass class1, jint ring_fd, jint to_submit,
jint min_complete, jint flags) {
return sys_io_uring_enter(ring_fd, to_submit, min_complete, flags, NULL);
}
static int nettyBlockingSocket(int domain, int type, int protocol) {
return socket(domain, type, protocol);
}
static jobject netty_io_uring_setup(JNIEnv *env, jclass class1, jint entries) {
struct io_uring_params p;
memset(&p, 0, sizeof(p));
int ring_fd = sys_io_uring_setup((int)entries, &p);
//Todo
if (ring_fd < -1) {
//throw Exception
return NULL;
}
struct io_uring io_uring_ring;
//Todo memset instead
io_uring_ring.flags = 0;
io_uring_ring.sq.sqe_tail = 0;
io_uring_ring.sq.sqe_head = 0;
setup_io_uring(ring_fd, &io_uring_ring, &p);
jobject ioUringSubmissionQueue = (*env)->NewObject(
env, ioUringSubmissionQueueClass, ioUringSubmissionQueueMethodId,
(jlong)io_uring_ring.sq.khead, (jlong)io_uring_ring.sq.ktail,
(jlong)io_uring_ring.sq.kring_mask,
(jlong)io_uring_ring.sq.kring_entries, (jlong)io_uring_ring.sq.kflags,
(jlong)io_uring_ring.sq.kdropped, (jlong)io_uring_ring.sq.array,
(jlong)io_uring_ring.sq.sqes, (jlong)io_uring_ring.sq.ring_sz,
(jlong)io_uring_ring.cq.ring_ptr, (jint)ring_fd);
jobject ioUringCompletionQueue = (*env)->NewObject(
env, ioUringCompletionQueueClass, ioUringCommpletionQueueMethodId,
(jlong)io_uring_ring.cq.khead, (jlong)io_uring_ring.cq.ktail,
(jlong)io_uring_ring.cq.kring_mask,
(jlong)io_uring_ring.cq.kring_entries,
(jlong)io_uring_ring.cq.koverflow, (jlong)io_uring_ring.cq.cqes,
(jlong)io_uring_ring.cq.ring_sz, (jlong)io_uring_ring.cq.ring_ptr,
(jint)ring_fd);
jobject ringBuffer =
(*env)->NewObject(env, ringBufferClass, ringBufferMethodId,
ioUringSubmissionQueue, ioUringCompletionQueue);
return ringBuffer;
}
static jint netty_create_file(JNIEnv *env, jclass class) {
return open("io-uring-test.txt", O_RDWR | O_TRUNC | O_CREAT, 0644);
}
static void netty_io_uring_native_JNI_OnUnLoad(JNIEnv *env) {
// Todo OnUnLoad
}
// JNI Method Registration Table Begin
static const JNINativeMethod method_table[] = {
{"ioUringSetup", "(I)Lio/netty/channel/uring/RingBuffer;", (void *)netty_io_uring_setup},
{"createFile", "()I", (void *)netty_create_file},
{"ioUringEnter", "(IIII)I", (void *)netty_io_uring_enter}};
static const jint method_table_size =
sizeof(method_table) / sizeof(method_table[0]);
// JNI Method Registration Table End
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env;
char *nettyClassName = NULL;
if ((*vm)->GetEnv(vm, (void **)&env, NETTY_JNI_VERSION) != JNI_OK) {
return JNI_ERR;
}
char *packagePrefix = NULL;
Dl_info dlinfo;
jint status = 0;
if (!dladdr((void *)netty_io_uring_native_JNI_OnUnLoad, &dlinfo)) {
fprintf(stderr,
"FATAL: transport-native-epoll JNI call to dladdr failed!\n");
return JNI_ERR;
}
packagePrefix = netty_unix_util_parse_package_prefix(
dlinfo.dli_fname, "netty_transport_native_io_uring", &status);
if (status == JNI_ERR) {
fprintf(stderr,
"FATAL: netty_transport_native_io_uring JNI encountered unexpected "
"dlinfo.dli_fname: %s\n",
dlinfo.dli_fname);
return JNI_ERR;
}
if (netty_unix_util_register_natives(env, packagePrefix,
"io/netty/channel/uring/Native",
method_table, method_table_size) != 0) {
printf("netty register natives error\n");
}
// Load all c modules that we depend upon
if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto done;
}
if (netty_unix_errors_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto done;
}
if (netty_unix_filedescriptor_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto done;
}
if (netty_unix_socket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto done;
}
if (netty_unix_buffer_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto done;
}
NETTY_PREPEND(packagePrefix, "io/netty/channel/uring/RingBuffer",
nettyClassName, done);
NETTY_LOAD_CLASS(env, ringBufferClass, nettyClassName, done);
NETTY_GET_METHOD(env, ringBufferClass, ringBufferMethodId, "<init>",
"(Lio/netty/channel/uring/IOUringSubmissionQueue;Lio/netty/"
"channel/uring/IOUringCompletionQueue;)V",
done);
NETTY_PREPEND(packagePrefix, "io/netty/channel/uring/IOUringSubmissionQueue",
nettyClassName, done);
NETTY_LOAD_CLASS(env, ioUringSubmissionQueueClass, nettyClassName, done);
NETTY_GET_METHOD(env, ioUringSubmissionQueueClass,
ioUringSubmissionQueueMethodId, "<init>", "(JJJJJJJJIJI)V",
done);
NETTY_PREPEND(packagePrefix, "io/netty/channel/uring/IOUringCompletionQueue",
nettyClassName, done);
NETTY_LOAD_CLASS(env, ioUringCompletionQueueClass, nettyClassName, done);
NETTY_GET_METHOD(env, ioUringCompletionQueueClass,
ioUringCommpletionQueueMethodId, "<init>", "(JJJJJJIJI)V",
done);
done:
//unload
return NETTY_JNI_VERSION;
}