简体   繁体   English

如何使用 IORING_OP_READ_FIXED?

[英]How do I use IORING_OP_READ_FIXED?

On the following page https://lwn.net/Articles/810414/在下一页https://lwn.net/Articles/810414/

IORING_OP_READ_FIXED IORING_OP_WRITE_FIXED These opcodes also submit I/O operations, but they use "registered" buffers that are already mapped into the kernel, reducing the amount of total overhead . IORING_OP_READ_FIXED IORING_OP_WRITE_FIXED 这些操作码也提交 I/O 操作,但它们使用已映射到 kernel 的“注册”缓冲区,从而减少了总开销

However I could not find a single example online on how to use it.但是我在网上找不到一个关于如何使用它的例子。 In io_uring_enter it says在 io_uring_enter 它说

EFAULT IORING_OP_READ_FIXED or IORING_OP_WRITE_FIXED was specified in the opcode field of the submission queue entry, but either buffers were not registered for this io_uring instance, or the address range described by addr and len does not fit within the buffer registered at buf_index.在提交队列条目的操作码字段中指定了 EFAULT IORING_OP_READ_FIXED 或 IORING_OP_WRITE_FIXED,但要么没有为此 io_uring 实例注册缓冲区,要么 addr 和 len 描述的地址范围不适合在 buf_index 处注册的缓冲区。

It seems to me that I should pick a memory address and block for it to use but using an address like 0x555555500000 and len as 4096 gets me the same error.在我看来,我应该选择一个 memory 地址并阻止它使用,但使用像 0x555555500000 和 len 作为 4096 这样的地址会给我同样的错误。

How does IORING_OP_READ_FIXED work? IORING_OP_READ_FIXED 是如何工作的? Below is a working example of IORING_OP_READ下面是 IORING_OP_READ 的一个工作示例

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <linux/io_uring.h>

#define read_barrier()  __asm__ __volatile__("":::"memory")
#define write_barrier() __asm__ __volatile__("":::"memory")

int main(int argc, char *argv[])
{
    struct io_uring_params uring;
    memset(&uring, 0, sizeof(uring));
    auto queue_size = 5;
    auto ring_fd = syscall(__NR_io_uring_setup, queue_size, &uring);

    auto*uring_ptr = (char*)mmap(0, uring.sq_off.array + uring.sq_entries * 4, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, ring_fd, IORING_OFF_SQ_RING);
    auto*submit_entries = (io_uring_sqe*)mmap(0, uring.sq_entries * sizeof(struct io_uring_sqe), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, ring_fd, IORING_OFF_SQES);

    unsigned &sqHead = *(unsigned*)(uring_ptr + uring.sq_off.head);
    unsigned &sqTail = *(unsigned*)(uring_ptr + uring.sq_off.tail);
    unsigned &sqMask = *(unsigned*)(uring_ptr + uring.sq_off.ring_mask);
    unsigned &sqFlags = *(unsigned*)(uring_ptr + uring.sq_off.flags);
    unsigned *sqArray = (unsigned*)(uring_ptr + uring.sq_off.array);

    unsigned &cqHead = *(unsigned*)(uring_ptr + uring.cq_off.head);
    unsigned &cqTail = *(unsigned*)(uring_ptr + uring.cq_off.tail);
    unsigned &cqMask = *(unsigned*)(uring_ptr + uring.cq_off.ring_mask);
    io_uring_cqe *cqes = (io_uring_cqe*)(uring_ptr + uring.cq_off.cqes);


    int fd[2];
    fd[0] = open(argv[1], O_RDONLY);
    

    struct stat stat;
    if (fstat(fd[0], &stat) < 0) {
        perror("fstat");
        return -1;
    }

    int size_aligned = (stat.st_size & ~63) + (stat.st_size & 63 ? 64 : 0);

    auto*fileBuf = (unsigned char*)malloc(size_aligned*2);

    for(int i=0; i<1; i++)
    {
        io_uring_sqe&sqe = submit_entries[sqTail & sqMask];
        sqe.fd = fd[i];
        sqe.flags = 0;
        sqe.opcode = IORING_OP_READ;
        sqe.addr = (unsigned long long)fileBuf+i*size_aligned;
        sqe.len = size_aligned;
        sqe.user_data = (unsigned long long)fileBuf+i*size_aligned;
        sqArray[sqTail&sqMask] = sqTail&sqMask;
        sqTail++;
    }
    write_barrier();

    //int ret =  syscall(__NR_io_uring_enter, ring_fd, 2, 2, IORING_ENTER_GETEVENTS, 0);
    int ret =  syscall(__NR_io_uring_enter, ring_fd, 1, 1, IORING_ENTER_GETEVENTS, 0);
    //int ret =  syscall(__NR_io_uring_enter, ring_fd, 1, 0, IORING_ENTER_GETEVENTS, 0);
    //sleep(1);
    read_barrier();
    while (cqHead != cqTail)
    {
        unsigned long long a = cqHead;
        unsigned long long b = cqTail;
        unsigned long long c = cqMask;
        auto index=cqHead & cqMask;
        io_uring_cqe&cqe = cqes[index];
        auto u=cqe.user_data;
        auto f=cqe.flags;
        auto r=cqe.res;
        puts((const char*)u);
        cqHead++;
    }
    int a=0;
    return 0;
}

You need to register (and unregister) your buffers with the __NR_io_uring_register system call.您需要使用 __NR_io_uring_register 系统调用注册(和取消注册)缓冲区。 Using your example, you would apply your buffer to an iovec and the pass the iovec into the syscall:使用您的示例,您可以将缓冲区应用于 iovec 并将 iovec 传递给系统调用:

struct iovec iov = { .iov_base = (void *)fileBuf, .iov_len = (size_aligned*2) };

int rc = syscall(__NR_io_uring_register, ring_fd, IORING_REGISTER_BUFFERS, 
                 (void *)&iov, 1 /* number of iovs */);

To use the registered buffer you need to use provide the buffer's array offset in the sqe.要使用已注册的缓冲区,您需要使用在 sqe 中提供缓冲区的数组偏移量。 In this example, that value would always be '0' since only one iovec has been registered.在此示例中,该值将始终为“0”,因为仅注册了一个 iovec。 In your code, you would need to set sqe.opcode = IORING_OP_READ_FIXED and sqe.buf_index = 0 .在您的代码中,您需要设置sqe.opcode = IORING_OP_READ_FIXEDsqe.buf_index = 0

You may also want to consider using the initialization helper functions from liburing.h:您可能还需要考虑使用 liburing.h 中的初始化辅助函数:

static inline void io_uring_prep_read_fixed(struct io_uring_sqe *sqe, int fd,
                                            void *buf, unsigned nbytes,
                                            off_t offset, int buf_index)
{
        io_uring_prep_rw(IORING_OP_READ_FIXED, sqe, fd, buf, nbytes, offset);
        sqe->buf_index = buf_index;
}

I recommend looking at liburing - it aptly handles much of the tedious initialization minutiae as well providing wrappers for buffer [un]registration.我建议查看 liburing - 它恰当地处理了许多繁琐的初始化细节,并为缓冲区 [un] 注册提供了包装器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM