Compare commits
4 Commits
81d47fb675
...
e117334dee
| Author | SHA1 | Date | |
|---|---|---|---|
| e117334dee | |||
| 0294498538 | |||
| 41ac164881 | |||
| d4ba121b56 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -3,5 +3,9 @@ file_hasher.ilk
|
||||
file_hasher.rdi
|
||||
file_hasher.exe
|
||||
file_hashes.txt
|
||||
Binaries/file_hashes.txt
|
||||
file_list.txt
|
||||
temp_code.c
|
||||
/.cache/clangd/index
|
||||
/file_hasher
|
||||
/io_uring_test
|
||||
|
||||
14
README.md
14
README.md
@@ -5,14 +5,16 @@ Collects some metadata and hashes files.
|
||||
## Building:
|
||||
### Windows:
|
||||
#### Release:
|
||||
clang-cl /O3 file_hasher.c xxh_x86dispatch.c advapi32.lib
|
||||
clang -O3 file_hasher.c xxh_x86dispatch.c -ladvapi32 -o file_hasher
|
||||
gcc -O3 file_hasher.c xxh_x86dispatch.c -ladvapi32 -o file_hasher
|
||||
clang-cl /O3 file_hasher.c xxh_x86dispatch.c
|
||||
|
||||
Note: MinGW does not provide IO Ring headers yet, to fix that include ioringapi.c, this will dynamically load all the functions and define all the symbols necessary to replace the official header.
|
||||
clang -O3 file_hasher.c xxh_x86dispatch.c -o file_hasher
|
||||
gcc -O3 file_hasher.c xxh_x86dispatch.c -o file_hasher
|
||||
|
||||
#### Debug:
|
||||
clang-cl /Zi /Od file_hasher.c xxh_x86dispatch.c advapi32.lib
|
||||
clang -g -O0 file_hasher.c xxh_x86dispatch.c -ladvapi32 -o file_hasher
|
||||
gcc -g -O0 file_hasher.c xxh_x86dispatch.c -ladvapi32 -o file_hasher
|
||||
clang-cl /Zi /Od file_hasher.c xxh_x86dispatch.c
|
||||
clang -g -O0 file_hasher.c xxh_x86dispatch.c -o file_hasher
|
||||
gcc -g -O0 file_hasher.c xxh_x86dispatch.c -o file_hasher
|
||||
|
||||
### Linux:
|
||||
#### Release:
|
||||
|
||||
56
base.h
56
base.h
@@ -1,34 +1,52 @@
|
||||
#pragma once
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma comment(lib, "advapi32.lib")
|
||||
#endif
|
||||
|
||||
#include <aclapi.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <ioringapi.h>
|
||||
#include <ntioring_x.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <windows.h>
|
||||
#include <winerror.h>
|
||||
|
||||
#elif defined(__linux__)
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <liburing.h>
|
||||
#include <pthread.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#include <sys/eventfd.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <immintrin.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define PLATFORM_WINDOWS 1
|
||||
#include <aclapi.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <windows.h>
|
||||
|
||||
#define strdup _strdup
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
Base types
|
||||
------------------------------------------------------------ */
|
||||
|
||||
@@ -49,3 +49,9 @@ Fixing user prompt parsing
|
||||
4.5: Porting to linux
|
||||
Reorganising the code
|
||||
Improving the scan function
|
||||
|
||||
5.0: Implementing the IO Ring instead of buffered hashing, huge performance gains. The IO Ring is event driven, thread local, uses DMA and direct disk I/O, bypassing the OS cash completely, it supports bashing multiple submissions and can handle multiple files at the same time.
|
||||
Hashing small files using XXH3_128bits() instead of the streaming pipeline(XXH3_128bits_reset(), XXH3_128bits_update(), XXH3_128bits_digest()), this reduses the overhead of creating a state and digest, coupled with the IO Ring it improves the hashing of small files whose size is inferior to the size of IO Ring buffers
|
||||
fixing the xxh_x86dispatch warnings
|
||||
Updating the progress printing function
|
||||
|
||||
|
||||
7
compile_commands.json
Normal file
7
compile_commands.json
Normal file
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"directory": "D:/Code/c/filehasher",
|
||||
"command": "clang-cl /O2 file_hasher.c xxh_x86dispatch.c",
|
||||
"file": "file_hasher.c"
|
||||
}
|
||||
]
|
||||
BIN
file_hasher
Normal file
BIN
file_hasher
Normal file
Binary file not shown.
@@ -74,7 +74,7 @@ int main(int argc, char **argv) {
|
||||
mem_arena *gp_arena = arena_create(¶ms);
|
||||
|
||||
// -------------------------------
|
||||
// Detect hardware threads
|
||||
// Detect hardware
|
||||
// -------------------------------
|
||||
// --- Windows: detect PHYSICAL cores (not logical threads) ---
|
||||
size_t hw_threads = platform_physical_cores();
|
||||
@@ -86,17 +86,41 @@ int main(int argc, char **argv) {
|
||||
hw_threads);
|
||||
printf(" Selected instruction set: %s\n", get_xxhash_instruction_set());
|
||||
|
||||
// Align IO Ring block size to the system page size
|
||||
g_ioring_buffer_size = ALIGN_UP_POW2(g_ioring_buffer_size, g_pagesize);
|
||||
// -------------------------------
|
||||
// Scanning and hashing
|
||||
// -------------------------------
|
||||
|
||||
// test_io_ring();
|
||||
MPMCQueue dir_queue;
|
||||
mpmc_init(&dir_queue, MiB(1));
|
||||
|
||||
MPMCQueue file_queue;
|
||||
mpmc_init(&file_queue, MiB(1));
|
||||
|
||||
// Starting hash threads
|
||||
// size_t num_hash_threads = num_threads;
|
||||
//
|
||||
// WorkerContext workers[num_hash_threads];
|
||||
// Thread *hash_threads =
|
||||
// arena_push(&gp_arena, sizeof(Thread) * num_hash_threads, true);
|
||||
//
|
||||
// for (size_t i = 0; i < num_hash_threads; ++i) {
|
||||
// workers[i].arena = arena_create(¶ms);
|
||||
// workers[i].file_queue = &file_queue;
|
||||
//
|
||||
// if (thread_create(&hash_threads[i], (ThreadFunc)hash_worker, &workers[i])
|
||||
// !=
|
||||
// 0) {
|
||||
// fprintf(stderr, "Failed to create hash thread %zu\n", i);
|
||||
// exit(1);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Starting hash threads
|
||||
size_t num_hash_threads = num_threads;
|
||||
// size_t num_hash_threads = 1;
|
||||
|
||||
WorkerContext workers[num_hash_threads];
|
||||
Thread *hash_threads =
|
||||
@@ -106,13 +130,45 @@ int main(int argc, char **argv) {
|
||||
workers[i].arena = arena_create(¶ms);
|
||||
workers[i].file_queue = &file_queue;
|
||||
|
||||
if (thread_create(&hash_threads[i], (ThreadFunc)hash_worker, &workers[i]) !=
|
||||
0) {
|
||||
if (thread_create(&hash_threads[i], (ThreadFunc)hash_worker_io_ring,
|
||||
&workers[i]) != 0) {
|
||||
fprintf(stderr, "Failed to create hash thread %zu\n", i);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Starting hash threads
|
||||
// size_t num_hash_threads = num_threads;
|
||||
//
|
||||
// WorkerContext workers[num_hash_threads];
|
||||
// Thread *hash_threads =
|
||||
// arena_push(&gp_arena, sizeof(Thread) * num_hash_threads, true);
|
||||
//
|
||||
// // Check if I/O Ring is available
|
||||
// bool io_ring_available = false;
|
||||
// HIORING test_ring = io_ring_init();
|
||||
// if (test_ring) {
|
||||
// io_ring_available = true;
|
||||
// io_ring_cleanup(test_ring);
|
||||
// // printf("I/O Ring is available, using high-performance async I/O\n");
|
||||
// } else {
|
||||
// printf("I/O Ring not available, using buffered I/O\n");
|
||||
// }
|
||||
//
|
||||
// for (size_t i = 0; i < num_hash_threads; ++i) {
|
||||
// workers[i].arena = arena_create(¶ms);
|
||||
// workers[i].file_queue = &file_queue;
|
||||
//
|
||||
// // Select the appropriate worker function
|
||||
// ThreadFunc fn = io_ring_available ? (ThreadFunc)hash_worker_io_ring
|
||||
// : (ThreadFunc)hash_worker;
|
||||
//
|
||||
// if (thread_create(&hash_threads[i], fn, &workers[i]) != 0) {
|
||||
// fprintf(stderr, "Failed to create hash thread %zu\n", i);
|
||||
// exit(1);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Starting progress printing thread
|
||||
Thread progress_thread_handle;
|
||||
if (thread_create(&progress_thread_handle, (ThreadFunc)progress_thread,
|
||||
@@ -197,7 +253,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
FILE *f = fopen(FILE_HASHES_TXT, "wb");
|
||||
|
||||
for (int i = 0; i < num_threads; i++) {
|
||||
for (int i = 0; i < num_hash_threads; i++) {
|
||||
mem_arena *arena = workers[i].arena;
|
||||
u8 *arena_base =
|
||||
(u8 *)arena + ALIGN_UP_POW2(sizeof(mem_arena), arena->align);
|
||||
@@ -209,6 +265,13 @@ int main(int argc, char **argv) {
|
||||
// -------------------------------
|
||||
// Print summary
|
||||
// -------------------------------
|
||||
uint64_t incomplete = atomic_load(&g_io_ring_fallbacks);
|
||||
if (incomplete > 0) {
|
||||
printf("\nWARNING: I/O Ring incomplete files: %llu (fallback to buffered "
|
||||
"I/O used)\n",
|
||||
(unsigned long long)incomplete);
|
||||
}
|
||||
|
||||
double total_seconds = timer_elapsed(&total_timer);
|
||||
|
||||
printf("Completed hashing %zu files\n", total_found);
|
||||
|
||||
147
io_ring_test.c
Normal file
147
io_ring_test.c
Normal file
@@ -0,0 +1,147 @@
|
||||
#pragma once
|
||||
|
||||
#include <ioringapi.h>
|
||||
#include <ntioring_x.h>
|
||||
// #include "ioringapi.c"
|
||||
#include <winerror.h>
|
||||
|
||||
// Initialize I/O Ring
|
||||
HIORING io_ring_init(void) {
|
||||
|
||||
// if (!io_ring_load_functions()) {
|
||||
// printf("[I/O Ring] Failed to load functions\n");
|
||||
// return NULL;
|
||||
// }
|
||||
|
||||
IORING_CAPABILITIES caps;
|
||||
ZeroMemory(&caps, sizeof(caps));
|
||||
|
||||
HRESULT hr = QueryIoRingCapabilities(&caps);
|
||||
if (FAILED(hr)) {
|
||||
printf("[I/O Ring] QueryIoRingCapabilities failed: 0x%08lx\n", hr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// printf("[I/O Ring] MaxVersion=%d, MaxSubmission=%u, MaxCompletion=%u\n",
|
||||
// (int)caps.MaxVersion, caps.MaxSubmissionQueueSize,
|
||||
// caps.MaxCompletionQueueSize);
|
||||
|
||||
if (caps.MaxVersion < IORING_VERSION_1) {
|
||||
printf("[I/O Ring] Version too old\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
IORING_CREATE_FLAGS flags = {0};
|
||||
HIORING ring = NULL;
|
||||
|
||||
// hr = CreateIoRing(IORING_VERSION_1, flags, 256, 512, &ring);
|
||||
hr = CreateIoRing(caps.MaxVersion, flags, 256, 512, &ring);
|
||||
if (FAILED(hr)) {
|
||||
printf("[I/O Ring] CreateIoRing failed: 0x%08lx\n", hr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// printf("[I/O Ring] Created successfully\n");
|
||||
|
||||
// Check if read operation is supported
|
||||
|
||||
// HRESULT io_ring_support = IsIoRingOpSupported(ring, IORING_OP_READ);
|
||||
// if (io_ring_support == S_FALSE) {
|
||||
// printf("[I/O Ring] Not supported, %ld /n", io_ring_support);
|
||||
// }
|
||||
|
||||
// Get ring info
|
||||
IORING_INFO info;
|
||||
ZeroMemory(&info, sizeof(info));
|
||||
GetIoRingInfo(ring, &info);
|
||||
// printf("[I/O Ring] Submission: %u, Completion: %u\n",
|
||||
// info.SubmissionQueueSize, info.CompletionQueueSize);
|
||||
|
||||
return ring;
|
||||
}
|
||||
|
||||
void io_ring_cleanup(HIORING ring) {
|
||||
if (ring) {
|
||||
CloseIoRing(ring);
|
||||
// printf("[I/O Ring] Closed\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Read file using I/O Ring
|
||||
int io_ring_read_file(HIORING ring, HANDLE hFile, void *buffer, DWORD size,
|
||||
UINT64 offset) {
|
||||
|
||||
IORING_HANDLE_REF file_ref = IoRingHandleRefFromHandle(hFile);
|
||||
IORING_BUFFER_REF buf_ref = IoRingBufferRefFromPointer(buffer);
|
||||
|
||||
HRESULT hr = BuildIoRingReadFile(ring, file_ref, buf_ref, size, offset,
|
||||
(UINT_PTR)buffer, IOSQE_FLAGS_NONE);
|
||||
|
||||
if (FAILED(hr))
|
||||
return -1;
|
||||
|
||||
UINT32 submitted = 0;
|
||||
hr = SubmitIoRing(ring, 1, INFINITE, &submitted);
|
||||
if (FAILED(hr) || submitted == 0)
|
||||
return -1;
|
||||
|
||||
for (;;) {
|
||||
IORING_CQE cqe;
|
||||
hr = PopIoRingCompletion(ring, &cqe);
|
||||
|
||||
if (FAILED(hr))
|
||||
continue;
|
||||
|
||||
if (cqe.UserData != (UINT_PTR)buffer)
|
||||
continue;
|
||||
|
||||
if (FAILED(cqe.ResultCode))
|
||||
return -1;
|
||||
|
||||
return (int)cqe.Information;
|
||||
}
|
||||
}
|
||||
|
||||
// Test function
|
||||
void test_io_ring(void) {
|
||||
printf("\n=== Testing I/O Ring ===\n");
|
||||
|
||||
HIORING ring = io_ring_init();
|
||||
if (!ring) {
|
||||
printf("I/O Ring not available\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create test file
|
||||
HANDLE hFile = CreateFileA("test.txt", GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile != INVALID_HANDLE_VALUE) {
|
||||
char test_data[] =
|
||||
"Hello, I/O Ring! This is a test of the Windows I/O Ring API.";
|
||||
DWORD written;
|
||||
WriteFile(hFile, test_data, sizeof(test_data), &written, NULL);
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
|
||||
// Read using I/O Ring
|
||||
hFile = CreateFileA("test.txt", GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
||||
if (hFile != INVALID_HANDLE_VALUE) {
|
||||
char buffer[512] = {0};
|
||||
int bytes = io_ring_read_file(ring, hFile, buffer, sizeof(buffer), 0);
|
||||
if (bytes > 0) {
|
||||
printf("Read %d bytes: %s\n", bytes, buffer);
|
||||
} else {
|
||||
printf("Failed to read file\n");
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
} else {
|
||||
printf("Failed to open test file\n");
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
DeleteFileA("test.txt");
|
||||
io_ring_cleanup(ring);
|
||||
|
||||
printf("=== Test complete ===\n\n");
|
||||
}
|
||||
BIN
io_uring_test
Normal file
BIN
io_uring_test
Normal file
Binary file not shown.
454
io_uring_test.c
Normal file
454
io_uring_test.c
Normal file
@@ -0,0 +1,454 @@
|
||||
/*
|
||||
# Compile
|
||||
gcc -o io_uring_test io_uring_test.c -luring
|
||||
|
||||
# Run
|
||||
./io_uring_test
|
||||
*/
|
||||
#include "base.h"
|
||||
#include <stdint.h>
|
||||
#define _GNU_SOURCE
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <liburing.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define TEST_FILE "test_io_uring.txt"
|
||||
#define BUFFER_SIZE 4096
|
||||
#define NUM_BUFFERS 4
|
||||
|
||||
// Colors for output
|
||||
#define COLOR_GREEN "\033[0;32m"
|
||||
#define COLOR_RED "\033[0;31m"
|
||||
#define COLOR_YELLOW "\033[0;33m"
|
||||
#define COLOR_BLUE "\033[0;34m"
|
||||
#define COLOR_RESET "\033[0m"
|
||||
|
||||
// Test result tracking
|
||||
typedef struct {
|
||||
int passed;
|
||||
int failed;
|
||||
} TestResults;
|
||||
|
||||
static void print_success(const char *step) {
|
||||
printf(COLOR_GREEN "[✓] SUCCESS: %s" COLOR_RESET "\n", step);
|
||||
}
|
||||
|
||||
static void print_failure(const char *step, const char *error) {
|
||||
printf(COLOR_RED "[✗] FAILED: %s - %s" COLOR_RESET "\n", step, error);
|
||||
}
|
||||
|
||||
static void print_info(const char *msg) {
|
||||
printf(COLOR_BLUE "[i] INFO: %s" COLOR_RESET "\n", msg);
|
||||
}
|
||||
|
||||
static void print_step(const char *step) {
|
||||
printf(COLOR_YELLOW "\n>>> Testing: %s" COLOR_RESET "\n", step);
|
||||
}
|
||||
|
||||
// Create a test file with known content
|
||||
static int create_test_file(void) {
|
||||
const char *test_content =
|
||||
"Hello, io_uring! This is a test file for async I/O operations.\n"
|
||||
"Line 2: Testing reads with registered buffers.\n"
|
||||
"Line 3: The quick brown fox jumps over the lazy dog.\n"
|
||||
"Line 4: ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"
|
||||
"Line 5: 0123456789\n";
|
||||
|
||||
FILE *f = fopen(TEST_FILE, "w");
|
||||
if (!f) {
|
||||
perror("Failed to create test file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(f, "%s", test_content);
|
||||
fclose(f);
|
||||
|
||||
print_info("Test file created successfully");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Test 1: Create io_uring instance
|
||||
static int test_io_uring_create(struct io_uring *ring, TestResults *results) {
|
||||
print_step("io_uring creation");
|
||||
|
||||
int ret = io_uring_queue_init(256, ring, 0);
|
||||
if (ret < 0) {
|
||||
print_failure("io_uring_queue_init", strerror(-ret));
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
print_success("io_uring instance created");
|
||||
results->passed++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Test 2: Register buffers
|
||||
static int test_register_buffers(struct io_uring *ring, void **buffers,
|
||||
struct iovec *iovs, TestResults *results) {
|
||||
print_step("Buffer registration");
|
||||
|
||||
// Allocate and prepare buffers
|
||||
size_t total_size = BUFFER_SIZE * NUM_BUFFERS;
|
||||
*buffers = aligned_alloc(4096, total_size); // Page-aligned for O_DIRECT
|
||||
if (!*buffers) {
|
||||
print_failure("Buffer allocation", strerror(errno));
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Initialize iovecs
|
||||
for (int i = 0; i < NUM_BUFFERS; i++) {
|
||||
iovs[i].iov_base = (char *)*buffers + (i * BUFFER_SIZE);
|
||||
iovs[i].iov_len = BUFFER_SIZE;
|
||||
memset(iovs[i].iov_base, 0, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
int ret = io_uring_register_buffers(ring, iovs, NUM_BUFFERS);
|
||||
if (ret < 0) {
|
||||
print_failure("io_uring_register_buffers", strerror(-ret));
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
print_success("Buffers registered successfully");
|
||||
results->passed++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Test 3: Open file
|
||||
// Modified test_open_file function
|
||||
static int test_open_file(int *fd, TestResults *results) {
|
||||
print_step("File opening");
|
||||
|
||||
// Get file size
|
||||
struct stat st;
|
||||
if (stat(TEST_FILE, &st) != 0) {
|
||||
print_failure("stat", strerror(errno));
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check if file size is page-aligned
|
||||
int page_size = plat_get_pagesize();
|
||||
size_t file_size = st.st_size;
|
||||
|
||||
printf(" File size: %zu bytes\n", file_size);
|
||||
printf(" Page size: %d bytes\n", page_size);
|
||||
|
||||
if (file_size % page_size != 0) {
|
||||
printf(" Extending read size from %zu to %zu bytes\n", file_size,
|
||||
ALIGN_UP_POW2(file_size, page_size));
|
||||
}
|
||||
|
||||
// Try O_DIRECT first
|
||||
*fd = open(TEST_FILE, O_RDONLY | O_DIRECT);
|
||||
if (*fd < 0) {
|
||||
print_info("O_DIRECT failed, trying without it");
|
||||
*fd = open(TEST_FILE, O_RDONLY);
|
||||
if (*fd < 0) {
|
||||
print_failure("open", strerror(errno));
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
print_info("Using buffered I/O (O_DIRECT not available)");
|
||||
} else {
|
||||
print_success("File opened with O_DIRECT");
|
||||
}
|
||||
|
||||
results->passed++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Test 4: Build and submit read operation
|
||||
static int test_submit_read(struct io_uring *ring, int fd, struct iovec *iovs,
|
||||
int buffer_id, uint64_t user_data,
|
||||
TestResults *results) {
|
||||
print_step("Building and submitting read operation");
|
||||
|
||||
// Get file size for proper alignment
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) != 0) {
|
||||
print_failure("fstat", strerror(errno));
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
u32 page_size = plat_get_pagesize();
|
||||
size_t file_size = st.st_size;
|
||||
size_t read_size = BUFFER_SIZE;
|
||||
|
||||
// For O_DIRECT, ensure read size is sector-aligned
|
||||
if (read_size > file_size) {
|
||||
read_size = ALIGN_UP_POW2(file_size, page_size);
|
||||
printf(" Adjusted read size to %zu bytes for O_DIRECT alignment\n",
|
||||
read_size);
|
||||
}
|
||||
|
||||
struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
|
||||
if (!sqe) {
|
||||
print_failure("io_uring_get_sqe", "No available SQE");
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Prepare read operation using registered buffer
|
||||
io_uring_prep_read_fixed(sqe, fd, iovs[buffer_id].iov_base, read_size, 0,
|
||||
buffer_id);
|
||||
io_uring_sqe_set_data64(sqe, user_data);
|
||||
|
||||
int ret = io_uring_submit(ring);
|
||||
if (ret < 0) {
|
||||
print_failure("io_uring_submit", strerror(-ret));
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
print_success("Read operation submitted successfully");
|
||||
results->passed++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Test 5: Wait for completion
|
||||
static int test_wait_completion(struct io_uring *ring,
|
||||
struct io_uring_cqe **cqe,
|
||||
TestResults *results) {
|
||||
print_step("Waiting for completion");
|
||||
|
||||
int ret = io_uring_wait_cqe(ring, cqe);
|
||||
if (ret < 0) {
|
||||
print_failure("io_uring_wait_cqe", strerror(-ret));
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
print_success("Completion received");
|
||||
results->passed++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Test 6: Process completion
|
||||
static int test_process_completion(struct io_uring_cqe *cqe,
|
||||
uint64_t expected_user_data,
|
||||
TestResults *results) {
|
||||
print_step("Processing completion");
|
||||
|
||||
uint64_t user_data = io_uring_cqe_get_data64(cqe);
|
||||
int res = cqe->res;
|
||||
|
||||
printf(" Completion data:\n");
|
||||
printf(" User data: %lu (expected: %lu)\n", user_data, expected_user_data);
|
||||
printf(" Result: %d bytes read\n", res);
|
||||
|
||||
if (user_data != expected_user_data) {
|
||||
print_failure("User data mismatch",
|
||||
"User data doesn't match expected value");
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
print_failure("Read operation", strerror(-res));
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
print_success("Completion processed successfully");
|
||||
results->passed++;
|
||||
return res; // Return number of bytes read
|
||||
}
|
||||
|
||||
// Test 7: Verify read data
|
||||
static int test_verify_data(struct iovec *iovs, int buffer_id, int bytes_read,
|
||||
TestResults *results) {
|
||||
print_step("Data verification");
|
||||
|
||||
char *data = (char *)iovs[buffer_id].iov_base;
|
||||
|
||||
printf(" Read data (first 200 chars):\n");
|
||||
printf(" ---\n");
|
||||
for (int i = 0; i < bytes_read && i < 200; i++) {
|
||||
putchar(data[i]);
|
||||
}
|
||||
if (bytes_read > 200)
|
||||
printf("...");
|
||||
printf("\n ---\n");
|
||||
|
||||
// Check if data is not empty
|
||||
if (bytes_read == 0) {
|
||||
print_failure("Data verification", "No data read");
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check if data contains expected content
|
||||
if (strstr(data, "io_uring") == NULL) {
|
||||
print_failure("Data verification", "Expected content not found");
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
print_success("Data verified successfully");
|
||||
results->passed++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Test 8: Test multiple concurrent reads
|
||||
static int test_concurrent_reads(struct io_uring *ring, int fd,
|
||||
struct iovec *iovs, TestResults *results) {
|
||||
print_step("Concurrent reads test");
|
||||
|
||||
int num_reads = 3;
|
||||
int submitted = 0;
|
||||
|
||||
// Submit multiple reads
|
||||
for (int i = 0; i < num_reads; i++) {
|
||||
struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
|
||||
if (!sqe) {
|
||||
print_failure("Getting SQE for concurrent read", "No available SQE");
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
off_t offset = i * 100; // Read from different offsets
|
||||
io_uring_prep_read_fixed(sqe, fd, iovs[i].iov_base, BUFFER_SIZE, offset, i);
|
||||
io_uring_sqe_set_data64(sqe, i);
|
||||
submitted++;
|
||||
}
|
||||
|
||||
int ret = io_uring_submit(ring);
|
||||
if (ret != submitted) {
|
||||
char msg[64];
|
||||
snprintf(msg, sizeof(msg), "Expected %d, got %d", submitted, ret);
|
||||
|
||||
print_failure("Submitting concurrent reads", msg);
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
print_success("Concurrent reads submitted");
|
||||
|
||||
// Wait for and process completions
|
||||
for (int i = 0; i < submitted; i++) {
|
||||
struct io_uring_cqe *cqe;
|
||||
ret = io_uring_wait_cqe(ring, &cqe);
|
||||
if (ret < 0) {
|
||||
print_failure("Waiting for concurrent read completion", strerror(-ret));
|
||||
results->failed++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t user_data = io_uring_cqe_get_data64(cqe);
|
||||
int res = cqe->res;
|
||||
|
||||
printf(" Concurrent read %lu completed: %d bytes read\n", user_data, res);
|
||||
io_uring_cqe_seen(ring, cqe);
|
||||
}
|
||||
|
||||
print_success("Concurrent reads completed successfully");
|
||||
results->passed++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Cleanup function
|
||||
static void cleanup(struct io_uring *ring, int fd, void *buffers) {
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
if (buffers) {
|
||||
io_uring_unregister_buffers(ring);
|
||||
free(buffers);
|
||||
}
|
||||
io_uring_queue_exit(ring);
|
||||
remove(TEST_FILE);
|
||||
}
|
||||
|
||||
int main() {
|
||||
TestResults results = {0, 0};
|
||||
struct io_uring ring;
|
||||
int fd = -1;
|
||||
void *buffers = NULL;
|
||||
struct iovec iovs[NUM_BUFFERS];
|
||||
|
||||
printf(COLOR_BLUE "\n========================================\n");
|
||||
printf(" io_uring Test Suite\n");
|
||||
printf("========================================\n" COLOR_RESET);
|
||||
|
||||
// Create test file
|
||||
if (create_test_file() != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Test 1: Create io_uring
|
||||
if (test_io_uring_create(&ring, &results) != 0) {
|
||||
cleanup(&ring, fd, buffers);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Test 2: Register buffers
|
||||
if (test_register_buffers(&ring, &buffers, iovs, &results) != 0) {
|
||||
cleanup(&ring, fd, buffers);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Test 3: Open file
|
||||
if (test_open_file(&fd, &results) != 0) {
|
||||
cleanup(&ring, fd, buffers);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Test 4: Submit read
|
||||
uint64_t test_user_data = 12345;
|
||||
if (test_submit_read(&ring, fd, iovs, 0, test_user_data, &results) != 0) {
|
||||
cleanup(&ring, fd, buffers);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Test 5: Wait for completion
|
||||
struct io_uring_cqe *cqe;
|
||||
if (test_wait_completion(&ring, &cqe, &results) != 0) {
|
||||
cleanup(&ring, fd, buffers);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Test 6: Process completion
|
||||
int bytes_read = test_process_completion(cqe, test_user_data, &results);
|
||||
if (bytes_read < 0) {
|
||||
cleanup(&ring, fd, buffers);
|
||||
return 1;
|
||||
}
|
||||
io_uring_cqe_seen(&ring, cqe);
|
||||
|
||||
// Test 7: Verify data
|
||||
if (test_verify_data(iovs, 0, bytes_read, &results) != 0) {
|
||||
cleanup(&ring, fd, buffers);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Test 8: Concurrent reads
|
||||
if (test_concurrent_reads(&ring, fd, iovs, &results) != 0) {
|
||||
cleanup(&ring, fd, buffers);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Print summary
|
||||
printf(COLOR_BLUE "\n========================================\n");
|
||||
printf(" TEST SUMMARY\n");
|
||||
printf("========================================\n" COLOR_RESET);
|
||||
printf(" Total tests: %d\n", results.passed + results.failed);
|
||||
printf(COLOR_GREEN " Passed: %d\n" COLOR_RESET, results.passed);
|
||||
if (results.failed > 0) {
|
||||
printf(COLOR_RED " Failed: %d\n" COLOR_RESET, results.failed);
|
||||
} else {
|
||||
printf(COLOR_GREEN " ✓ ALL TESTS PASSED!\n" COLOR_RESET);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
cleanup(&ring, fd, buffers);
|
||||
|
||||
return results.failed > 0 ? 1 : 0;
|
||||
}
|
||||
285
ioringapi.c
Normal file
285
ioringapi.c
Normal file
@@ -0,0 +1,285 @@
|
||||
#pragma once
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
#include <winnt.h>
|
||||
|
||||
// Forward declarations
|
||||
typedef struct IORING_HANDLE_REF IORING_HANDLE_REF;
|
||||
typedef struct IORING_BUFFER_REF IORING_BUFFER_REF;
|
||||
typedef void *HIORING;
|
||||
|
||||
/* --------------------- Types declaration --------------------- */
|
||||
typedef enum IORING_CREATE_ADVISORY_FLAGS {
|
||||
IORING_CREATE_ADVISORY_FLAGS_NONE,
|
||||
IORING_CREATE_SKIP_BUILDER_PARAM_CHECKS
|
||||
} IORING_CREATE_ADVISORY_FLAGS;
|
||||
// Specifies advisory flags for creating an I/O ring with a call to
|
||||
// CreateIoRing.
|
||||
|
||||
typedef enum IORING_CREATE_REQUIRED_FLAGS {
|
||||
IORING_CREATE_REQUIRED_FLAGS_NONE
|
||||
} IORING_CREATE_REQUIRED_FLAGS;
|
||||
// Specifies required flags for creating an I/O ring with a call to
|
||||
// CreateIoRing.
|
||||
|
||||
typedef enum IORING_REF_KIND {
|
||||
IORING_REF_RAW = 0,
|
||||
IORING_REF_REGISTERED = 1,
|
||||
} IORING_REF_KIND;
|
||||
// Specifies the type of an IORING_HANDLE_REF structure.
|
||||
|
||||
typedef enum IORING_SQE_FLAGS {
|
||||
IOSQE_FLAGS_NONE,
|
||||
IOSQE_FLAGS_DRAIN_PRECEDING_OPS
|
||||
} IORING_SQE_FLAGS;
|
||||
// Specifies kernel behavior options for I/O ring submission queue entries
|
||||
|
||||
// IORING_REGISTERED_BUFFER structure
|
||||
typedef struct IORING_REGISTERED_BUFFER {
|
||||
UINT32 Index;
|
||||
UINT32 Offset;
|
||||
} IORING_REGISTERED_BUFFER;
|
||||
|
||||
// IORING_HANDLE_REF
|
||||
struct IORING_HANDLE_REF {
|
||||
IORING_REF_KIND Kind;
|
||||
union {
|
||||
HANDLE Handle;
|
||||
UINT32 Index;
|
||||
} HandleUnion;
|
||||
};
|
||||
// Represents a reference to a file handle used in an I/O ring operation
|
||||
|
||||
// IORING_BUFFER_REF
|
||||
struct IORING_BUFFER_REF {
|
||||
IORING_REF_KIND Kind;
|
||||
union {
|
||||
void *Address;
|
||||
IORING_REGISTERED_BUFFER IndexAndOffset;
|
||||
} BufferUnion;
|
||||
};
|
||||
|
||||
typedef struct IORING_BUFFER_INFO {
|
||||
void *Address;
|
||||
UINT32 Length;
|
||||
} IORING_BUFFER_INFO;
|
||||
|
||||
// IORING_BUFFER_REF represents a reference to a buffer used in an I/O ring
|
||||
// operation
|
||||
|
||||
// IORING_VERSION enumeration
|
||||
typedef enum IORING_VERSION {
|
||||
IORING_VERSION_INVALID = 0,
|
||||
IORING_VERSION_1 = 1,
|
||||
IORING_VERSION_2 = 2,
|
||||
IORING_VERSION_3 = 3,
|
||||
IORING_VERSION_4 = 4,
|
||||
} IORING_VERSION;
|
||||
|
||||
typedef enum IORING_FEATURE_FLAGS {
|
||||
IORING_FEATURE_FLAGS_NONE = 0,
|
||||
IORING_FEATURE_UM_EMULATION = 1
|
||||
} IORING_FEATURE_FLAGS;
|
||||
|
||||
// IORING_CAPABILITIES structure
|
||||
typedef struct IORING_CAPABILITIES {
|
||||
IORING_VERSION MaxVersion;
|
||||
UINT32 MaxSubmissionQueueSize;
|
||||
UINT32 MaxCompletionQueueSize;
|
||||
IORING_FEATURE_FLAGS FeatureFlags;
|
||||
} IORING_CAPABILITIES;
|
||||
// Represents the IORING API capabilities.
|
||||
|
||||
// IORING_CQE structure
|
||||
typedef struct IORING_CQE {
|
||||
UINT_PTR UserData;
|
||||
HRESULT ResultCode;
|
||||
ULONG_PTR Information;
|
||||
} IORING_CQE;
|
||||
// Represents a completed I/O ring queue entry.
|
||||
|
||||
// IORING_CREATE_FLAGS structure
|
||||
typedef struct IORING_CREATE_FLAGS {
|
||||
IORING_CREATE_REQUIRED_FLAGS Required;
|
||||
IORING_CREATE_ADVISORY_FLAGS Advisory;
|
||||
} IORING_CREATE_FLAGS;
|
||||
// Specifies flags for creating an I/O ring with a call to CreateIoRing.
|
||||
|
||||
// IORING_INFO structure
|
||||
typedef struct IORING_INFO {
|
||||
IORING_VERSION IoRingVersion;
|
||||
IORING_CREATE_FLAGS Flags;
|
||||
UINT32 SubmissionQueueSize;
|
||||
UINT32 CompletionQueueSize;
|
||||
} IORING_INFO;
|
||||
// Represents the shape and version information for the specified I/O ring
|
||||
|
||||
// IORING_OP_CODE for IsIoRingOpSupported
|
||||
typedef enum IORING_OP_CODE {
|
||||
IORING_OP_NOP = 0,
|
||||
IORING_OP_READ = 1,
|
||||
IORING_OP_WRITE = 2,
|
||||
IORING_OP_FLUSH = 3,
|
||||
IORING_OP_REGISTER_BUFFERS = 4,
|
||||
IORING_OP_REGISTER_FILES = 5,
|
||||
IORING_OP_CANCEL = 6,
|
||||
} IORING_OP_CODE;
|
||||
|
||||
/* --------------------- Dynamic loader --------------------- */
|
||||
// Function pointer types
|
||||
typedef BOOL(WINAPI *IsIoRingOpSupported_t)(HIORING, IORING_OP_CODE);
|
||||
typedef HRESULT(WINAPI *QueryIoRingCapabilities_t)(IORING_CAPABILITIES *);
|
||||
typedef HRESULT(WINAPI *GetIoRingInfo_t)(HIORING, IORING_INFO *);
|
||||
typedef HRESULT(WINAPI *CreateIoRing_t)(IORING_VERSION, IORING_CREATE_FLAGS,
|
||||
UINT32, UINT32, HIORING *);
|
||||
typedef HRESULT(WINAPI *CloseIoRing_t)(HIORING);
|
||||
typedef HRESULT(WINAPI *SubmitIoRing_t)(HIORING, UINT32, UINT32, UINT32 *);
|
||||
typedef HRESULT(WINAPI *PopIoRingCompletion_t)(HIORING, IORING_CQE *);
|
||||
typedef HRESULT(WINAPI *SetIoRingCompletionEvent_t)(HIORING, HANDLE);
|
||||
typedef HRESULT(WINAPI *BuildIoRingCancelRequest_t)(HIORING, IORING_HANDLE_REF,
|
||||
UINT_PTR, UINT_PTR);
|
||||
typedef HRESULT(WINAPI *BuildIoRingReadFile_t)(HIORING, IORING_HANDLE_REF,
|
||||
IORING_BUFFER_REF, UINT32,
|
||||
UINT64, UINT_PTR,
|
||||
IORING_SQE_FLAGS);
|
||||
typedef HRESULT(WINAPI *BuildIoRingRegisterBuffers_t)(
|
||||
HIORING, UINT32, IORING_BUFFER_INFO const[], UINT_PTR);
|
||||
|
||||
typedef HRESULT(WINAPI *BuildIoRingRegisterFileHandles_t)(HIORING, UINT32,
|
||||
HANDLE const[],
|
||||
UINT_PTR);
|
||||
|
||||
// Core:
|
||||
// Queries the support of the specified operation for the specified I/O ring
|
||||
static IsIoRingOpSupported_t IsIoRingOpSupported = NULL;
|
||||
|
||||
// Queries the OS for the supported capabilities for IORINGs
|
||||
static QueryIoRingCapabilities_t QueryIoRingCapabilities = NULL;
|
||||
|
||||
// Gets information about the API version and queue sizes of an I/O ring
|
||||
static GetIoRingInfo_t GetIoRingInfo = NULL;
|
||||
|
||||
// Creates a new instance of an I/O ring submission/completion queue pair and
|
||||
// returns a handle for referencing the I/O ring
|
||||
static CreateIoRing_t CreateIoRing = NULL;
|
||||
|
||||
// Closes an HIORING handle that was previously opened with a call to
|
||||
// CreateIoRing
|
||||
static CloseIoRing_t CloseIoRing = NULL;
|
||||
|
||||
// Submission / completion:
|
||||
// Submits all constructed but not yet submitted entries to the kernel’s queue
|
||||
// and optionally waits for a set of operations to complete
|
||||
static SubmitIoRing_t SubmitIoRing = NULL;
|
||||
|
||||
// Pops a single entry from the completion queue, if one is available
|
||||
static PopIoRingCompletion_t PopIoRingCompletion = NULL;
|
||||
|
||||
// Registers a completion queue event with an IORING
|
||||
static SetIoRingCompletionEvent_t SetIoRingCompletionEvent = NULL;
|
||||
|
||||
// Operations:
|
||||
// Performs an asynchronous read from a file using an I/O ring
|
||||
static BuildIoRingReadFile_t BuildIoRingReadFile = NULL;
|
||||
|
||||
// Attempts to cancel a previously submitted I/O ring operation
|
||||
static BuildIoRingCancelRequest_t BuildIoRingCancelRequest = NULL;
|
||||
|
||||
// Registers an array of buffers with the system for future I/O ring operations
|
||||
static BuildIoRingRegisterBuffers_t BuildIoRingRegisterBuffers = NULL;
|
||||
|
||||
// Registers an array of file handles with the system for future I/O ring
|
||||
// operations
|
||||
static BuildIoRingRegisterFileHandles_t BuildIoRingRegisterFileHandles = NULL;
|
||||
|
||||
static int io_ring_loaded = 0;
|
||||
|
||||
static int io_ring_load_functions(void) {
|
||||
if (io_ring_loaded)
|
||||
return 1;
|
||||
|
||||
HMODULE hKernel = GetModuleHandleW(L"kernel32.dll");
|
||||
if (!hKernel)
|
||||
return 0;
|
||||
|
||||
IsIoRingOpSupported =
|
||||
(IsIoRingOpSupported_t)GetProcAddress(hKernel, "IsIoRingOpSupported");
|
||||
QueryIoRingCapabilities = (QueryIoRingCapabilities_t)GetProcAddress(
|
||||
hKernel, "QueryIoRingCapabilities");
|
||||
GetIoRingInfo = (GetIoRingInfo_t)GetProcAddress(hKernel, "GetIoRingInfo");
|
||||
CreateIoRing = (CreateIoRing_t)GetProcAddress(hKernel, "CreateIoRing");
|
||||
CloseIoRing = (CloseIoRing_t)GetProcAddress(hKernel, "CloseIoRing");
|
||||
SubmitIoRing = (SubmitIoRing_t)GetProcAddress(hKernel, "SubmitIoRing");
|
||||
PopIoRingCompletion =
|
||||
(PopIoRingCompletion_t)GetProcAddress(hKernel, "PopIoRingCompletion");
|
||||
SetIoRingCompletionEvent = (SetIoRingCompletionEvent_t)GetProcAddress(
|
||||
hKernel, "SetIoRingCompletionEvent");
|
||||
BuildIoRingReadFile =
|
||||
(BuildIoRingReadFile_t)GetProcAddress(hKernel, "BuildIoRingReadFile");
|
||||
BuildIoRingCancelRequest = (BuildIoRingCancelRequest_t)GetProcAddress(
|
||||
hKernel, "BuildIoRingCancelRequest");
|
||||
BuildIoRingRegisterBuffers = (BuildIoRingRegisterBuffers_t)GetProcAddress(
|
||||
hKernel, "BuildIoRingRegisterBuffers");
|
||||
BuildIoRingRegisterFileHandles =
|
||||
(BuildIoRingRegisterFileHandles_t)GetProcAddress(
|
||||
hKernel, "BuildIoRingRegisterFileHandles");
|
||||
|
||||
io_ring_loaded =
|
||||
(IsIoRingOpSupported && QueryIoRingCapabilities && CreateIoRing &&
|
||||
CloseIoRing && SubmitIoRing && PopIoRingCompletion &&
|
||||
SetIoRingCompletionEvent && BuildIoRingReadFile &&
|
||||
BuildIoRingCancelRequest && BuildIoRingRegisterBuffers &&
|
||||
BuildIoRingRegisterFileHandles);
|
||||
|
||||
if (io_ring_loaded)
|
||||
printf("[I/O Ring] Functions loaded\n");
|
||||
else
|
||||
printf("[I/O Ring] Some functions not available\n");
|
||||
|
||||
return io_ring_loaded;
|
||||
}
|
||||
|
||||
/* ------------- Standard helper functions definition ------------- */
|
||||
// Creates an instance of the IORING_BUFFER_REF structure with the provided
|
||||
// buffer index and offset
|
||||
static inline IORING_BUFFER_REF
|
||||
IoRingBufferRefFromIndexAndOffset(UINT32 index, UINT32 offset) {
|
||||
IORING_BUFFER_REF ref;
|
||||
ref.Kind = IORING_REF_REGISTERED;
|
||||
ref.BufferUnion.IndexAndOffset.Index = index;
|
||||
ref.BufferUnion.IndexAndOffset.Offset = offset;
|
||||
return ref;
|
||||
}
|
||||
|
||||
// Creates an instance of the IORING_BUFFER_REF structure from the provided
|
||||
// pointer
|
||||
static IORING_BUFFER_REF IoRingBufferRefFromPointer(void *addr) {
|
||||
IORING_BUFFER_REF ref;
|
||||
ref.Kind = IORING_REF_RAW;
|
||||
ref.BufferUnion.Address = addr;
|
||||
return ref;
|
||||
}
|
||||
|
||||
// Creates an instance of the IORING_HANDLE_REF structure from the provided file
|
||||
// handle
|
||||
static IORING_HANDLE_REF IoRingHandleRefFromHandle(HANDLE h) {
|
||||
IORING_HANDLE_REF ref;
|
||||
ref.Kind = IORING_REF_RAW;
|
||||
ref.HandleUnion.Handle = h;
|
||||
return ref;
|
||||
}
|
||||
|
||||
// Creates an instance of the IORING_HANDLE_REF structure from the provided
|
||||
// index
|
||||
static inline IORING_HANDLE_REF IoRingHandleRefFromIndex(UINT32 index) {
|
||||
IORING_HANDLE_REF ref;
|
||||
ref.Kind = IORING_REF_REGISTERED; // MUST be registered
|
||||
ref.HandleUnion.Index = index;
|
||||
return ref;
|
||||
}
|
||||
|
||||
// NOTE: If you are using index-based buffers or handles, make sure you have
|
||||
// successfully called BuildIoRingRegisterBuffers or
|
||||
// BuildIoRingRegisterFileHandles first so the kernel has a valid table to look
|
||||
// into, otherwise the kernel will treat the index as an invalid memory
|
||||
// address/handle.
|
||||
1197
platform.c
1197
platform.c
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user