Unable to write file with io_uring in kernel 5.4 (always returns EINVAL)

243 views Asked by At

I am attempting to make a simple program to write to a file using kernel 5.4.0-150 and Ubuntu Bionic (18.04) on an x86_64 platform. No matter what I do, cqe->res always fails with EINVAL. I have no idea what parameter could possibly be wrong.

Do write SQE's work with kernel 5.4? (This machine is running 5.4 as it was advertised to be an LTS kernel).

#include <stdio.h>
#include <liburing.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

void dump_sqe(struct io_uring_sqe *sqe);

int main() {
    // Initialize the io_uring structure and create a ring buffer and completion events
    struct io_uring ring;
    void* buf;
    int fd;
    
    printf("Using liburing %d.%d\n",IO_URING_VERSION_MAJOR,IO_URING_VERSION_MINOR);
    
    int ret = io_uring_queue_init(16, &ring, 0);
    if (ret < 0) {
      printf("io_uring_queue_init: %d\n",ret);
    }

    if((fd = open("./temp.txt", O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0)
       perror("open");

    // Set up the write operation
    struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);

    posix_memalign(&buf,4096,4096);
    strcpy(buf,"Hello, world!\n");

    io_uring_prep_write(sqe, fd, buf, strlen(buf), 0);
    dump_sqe(sqe);
    
    // Submit the write request to the kernel
    ret = io_uring_submit(&ring);
    if (ret < 0) {
      printf("io_uring_submit: %d\n",ret);
    }

    // Wait for the completion of the write request
    struct io_uring_cqe *cqe;
    ret = io_uring_wait_cqe(&ring, &cqe);
    if (ret < 0) {
      printf("io_uring_wait_cqe: %d\n",ret);
    }

    // Process the completed write request
    if (cqe->res < 0) {
      printf("cqe->res = %d\n",cqe->res);
    } else {
      printf("Success\n");
    }

    // Clean up
    io_uring_cqe_seen(&ring, cqe);
    io_uring_queue_exit(&ring);
    return 0;
}

void dump_sqe(struct io_uring_sqe *sqe)
{
  printf("sqe->opcode = 0x%02x\n"
     "sqe->flags = 0x%08x\n"
     "sqe->ioprio = %d\n"
     "sqe->fd = %d\n"
     "sqe->off = %lld\n"
     "sqe->addr = 0x%016llu\n"
     "sqe->len = %d\n"
     "sqe->rw_flags = %d\n"
     "sqe->buf_index = %d\n"
     "sqe->personality = %d\n"
     "sqe->file_index = %d\n"
     "sqe->addr3 = %lld\n"
     "sqe->__pad2[0] = %lld\n\n",
     sqe->opcode,
     sqe->flags,
     sqe->ioprio,
     sqe->fd,
     sqe->off,
     sqe->addr,
     sqe->len,
     sqe->rw_flags,
     sqe->buf_index,
     sqe->personality,
     sqe->file_index,
     sqe->addr3,
     sqe->__pad2[0]);
}

Running the code above generates the following:

Using liburing 2.4
sqe->opcode = 0x17
sqe->flags = 0x00000000
sqe->ioprio = 0
sqe->fd = 4
sqe->off = 0
sqe->addr = 0x0094870066995200
sqe->len = 14
sqe->rw_flags = 0
sqe->buf_index = 0
sqe->personality = 0
sqe->file_index = 0
sqe->addr3 = 0
sqe->__pad2[0] = 0

cqe->res = -22

Have I done something wrong, or is there a specific kernel version where writes start to work? I used a page aligned buffer, thinking that might be the problem, but this did not make any difference.

Can anyone suggest something else to try? Unfortunately I can't upgrade the kernel on this machine due to organizational policy restrictions.

2

There are 2 answers

8
Craig Estey On

A quick look ...

You're using STDOUT_FILENO. This is shown by: sqe->fd = 1

From: https://man.archlinux.org/man/io_uring_setup.2.en

It [seems to] state that the fd must either be opened with O_DIRECT and/or must be a storage device.

So, I don't think you can use stdout.

For testing [at least], try opening a file or /dev device.

0
Chris Z On

I have found the answer to this problem:

io_uring opcode IORING_OP_READ (0x16) and IORING_OP_WRITE (0x17) did not appear in the Linux kernel until version 5.6.

To use io_uring in version 5.4, you may only use: io_uring_prep_readv/io_uring_prep_writev, and the length must be an integral multiple of 512 bytes.

Thank you everyone for your assistance.