253 lines
5.3 KiB
C
253 lines
5.3 KiB
C
/* SPDX-License-Identifier: MIT */
|
|
/*
|
|
* Description: test io_uring fpos handling
|
|
*
|
|
*/
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <assert.h>
|
|
|
|
#include "helpers.h"
|
|
#include "liburing.h"
|
|
|
|
#define FILE_SIZE 5000
|
|
#define QUEUE_SIZE 2048
|
|
|
|
static void create_file(const char *file, size_t size)
|
|
{
|
|
ssize_t ret;
|
|
char *buf;
|
|
size_t idx;
|
|
int fd;
|
|
|
|
buf = t_malloc(size);
|
|
for (idx = 0; idx < size; ++idx) {
|
|
/* write 0 or 1 */
|
|
buf[idx] = (unsigned char)(idx & 0x01);
|
|
}
|
|
|
|
fd = open(file, O_WRONLY | O_CREAT, 0644);
|
|
assert(fd >= 0);
|
|
|
|
ret = write(fd, buf, size);
|
|
fsync(fd);
|
|
close(fd);
|
|
free(buf);
|
|
assert(ret == size);
|
|
}
|
|
|
|
static int test_read(struct io_uring *ring, bool async, int blocksize)
|
|
{
|
|
int ret, fd, i;
|
|
bool done = false;
|
|
struct io_uring_sqe *sqe;
|
|
struct io_uring_cqe *cqe;
|
|
loff_t current, expected = 0;
|
|
int count_ok;
|
|
int count_0 = 0, count_1 = 0;
|
|
unsigned char buff[QUEUE_SIZE * blocksize];
|
|
unsigned char reordered[QUEUE_SIZE * blocksize];
|
|
|
|
create_file(".test_fpos_read", FILE_SIZE);
|
|
fd = open(".test_fpos_read", O_RDONLY);
|
|
unlink(".test_fpos_read");
|
|
assert(fd >= 0);
|
|
|
|
while (!done) {
|
|
for (i = 0; i < QUEUE_SIZE; ++i) {
|
|
sqe = io_uring_get_sqe(ring);
|
|
if (!sqe) {
|
|
fprintf(stderr, "no sqe\n");
|
|
return -1;
|
|
}
|
|
io_uring_prep_read(sqe, fd,
|
|
buff + i * blocksize,
|
|
blocksize, -1);
|
|
sqe->user_data = i;
|
|
if (async)
|
|
sqe->flags |= IOSQE_ASYNC;
|
|
if (i != QUEUE_SIZE - 1)
|
|
sqe->flags |= IOSQE_IO_LINK;
|
|
}
|
|
ret = io_uring_submit_and_wait(ring, QUEUE_SIZE);
|
|
if (ret != QUEUE_SIZE) {
|
|
fprintf(stderr, "submit failed: %d\n", ret);
|
|
return 1;
|
|
}
|
|
count_ok = 0;
|
|
for (i = 0; i < QUEUE_SIZE; ++i) {
|
|
int res;
|
|
|
|
ret = io_uring_peek_cqe(ring, &cqe);
|
|
if (ret) {
|
|
fprintf(stderr, "peek failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
assert(cqe->user_data < QUEUE_SIZE);
|
|
memcpy(reordered + count_ok,
|
|
buff + cqe->user_data * blocksize, blocksize);
|
|
res = cqe->res;
|
|
io_uring_cqe_seen(ring, cqe);
|
|
if (res == 0) {
|
|
done = true;
|
|
} else if (res == -ECANCELED) {
|
|
/* cancelled, probably ok */
|
|
} else if (res < 0 || res > blocksize) {
|
|
fprintf(stderr, "bad read: %d\n", res);
|
|
return -1;
|
|
} else {
|
|
expected += res;
|
|
count_ok += res;
|
|
}
|
|
}
|
|
ret = 0;
|
|
for (i = 0; i < count_ok; i++) {
|
|
if (reordered[i] == 1) {
|
|
count_1++;
|
|
} else if (reordered[i] == 0) {
|
|
count_0++;
|
|
} else {
|
|
fprintf(stderr, "odd read %d\n",
|
|
(int)reordered[i]);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
}
|
|
if (labs(count_1 - count_0) > 1) {
|
|
fprintf(stderr, "inconsistent reads, got 0s:%d 1s:%d\n",
|
|
count_0, count_1);
|
|
ret = -1;
|
|
}
|
|
current = lseek(fd, 0, SEEK_CUR);
|
|
if (current != expected) {
|
|
fprintf(stderr, "f_pos incorrect, expected %ld have %ld\n",
|
|
(long) expected, (long) current);
|
|
ret = -1;
|
|
}
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int test_write(struct io_uring *ring, bool async, int blocksize)
|
|
{
|
|
int ret, fd, i;
|
|
struct io_uring_sqe *sqe;
|
|
struct io_uring_cqe *cqe;
|
|
bool fail = false;
|
|
loff_t current;
|
|
char data[blocksize+1];
|
|
char readbuff[QUEUE_SIZE*blocksize+1];
|
|
|
|
fd = open(".test_fpos_write", O_RDWR | O_CREAT, 0644);
|
|
unlink(".test_fpos_write");
|
|
assert(fd >= 0);
|
|
|
|
for (i = 0; i < blocksize; i++)
|
|
data[i] = 'A' + i;
|
|
|
|
data[blocksize] = '\0';
|
|
|
|
for (i = 0; i < QUEUE_SIZE; ++i) {
|
|
sqe = io_uring_get_sqe(ring);
|
|
if (!sqe) {
|
|
fprintf(stderr, "no sqe\n");
|
|
return -1;
|
|
}
|
|
io_uring_prep_write(sqe, fd, data + (i % blocksize), 1, -1);
|
|
sqe->user_data = 1;
|
|
if (async)
|
|
sqe->flags |= IOSQE_ASYNC;
|
|
if (i != QUEUE_SIZE - 1)
|
|
sqe->flags |= IOSQE_IO_LINK;
|
|
}
|
|
ret = io_uring_submit_and_wait(ring, QUEUE_SIZE);
|
|
if (ret != QUEUE_SIZE) {
|
|
fprintf(stderr, "submit failed: %d\n", ret);
|
|
return 1;
|
|
}
|
|
for (i = 0; i < QUEUE_SIZE; ++i) {
|
|
int res;
|
|
|
|
ret = io_uring_peek_cqe(ring, &cqe);
|
|
res = cqe->res;
|
|
if (ret) {
|
|
fprintf(stderr, "peek failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
io_uring_cqe_seen(ring, cqe);
|
|
if (!fail && res != 1) {
|
|
fprintf(stderr, "bad result %d\n", res);
|
|
fail = true;
|
|
}
|
|
}
|
|
current = lseek(fd, 0, SEEK_CUR);
|
|
if (current != QUEUE_SIZE) {
|
|
fprintf(stderr, "f_pos incorrect, expected %ld have %d\n",
|
|
(long) current, QUEUE_SIZE);
|
|
fail = true;
|
|
}
|
|
current = lseek(fd, 0, SEEK_SET);
|
|
if (current != 0) {
|
|
perror("seek to start");
|
|
return -1;
|
|
}
|
|
ret = read(fd, readbuff, QUEUE_SIZE);
|
|
if (ret != QUEUE_SIZE) {
|
|
fprintf(stderr, "did not write enough: %d\n", ret);
|
|
return -1;
|
|
}
|
|
i = 0;
|
|
while (i < QUEUE_SIZE - blocksize) {
|
|
if (strncmp(readbuff + i, data, blocksize)) {
|
|
char bad[QUEUE_SIZE+1];
|
|
|
|
memcpy(bad, readbuff + i, blocksize);
|
|
bad[blocksize] = '\0';
|
|
fprintf(stderr, "unexpected data %s\n", bad);
|
|
fail = true;
|
|
}
|
|
i += blocksize;
|
|
}
|
|
|
|
return fail ? -1 : 0;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
struct io_uring ring;
|
|
int ret;
|
|
|
|
if (argc > 1)
|
|
return 0;
|
|
|
|
ret = io_uring_queue_init(QUEUE_SIZE, &ring, 0);
|
|
if (ret) {
|
|
fprintf(stderr, "ring setup failed\n");
|
|
return 1;
|
|
}
|
|
|
|
for (int test = 0; test < 8; test++) {
|
|
int async = test & 0x01;
|
|
int write = test & 0x02;
|
|
int blocksize = test & 0x04 ? 1 : 7;
|
|
|
|
ret = write
|
|
? test_write(&ring, !!async, blocksize)
|
|
: test_read(&ring, !!async, blocksize);
|
|
if (ret) {
|
|
fprintf(stderr, "failed %s async=%d blocksize=%d\n",
|
|
write ? "write" : "read",
|
|
async, blocksize);
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|