aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/liburing/test/read-mshot-empty.c
blob: 2fc5a63184b3a80d526b24eb0c488197fe117b75 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include "../config-host.h"
/* SPDX-License-Identifier: MIT */
/*
 * Description: test that multishot read correctly keeps reading until all
 *		data has been emptied. the original implementation failed
 *		to do so, if the available buffer size was less than what
 *		was available, hence requiring multiple reads to empty the
 *		file buffer.
 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <pthread.h>
#include <sys/time.h>

#include "liburing.h"
#include "helpers.h"

#define BGID		17
#define NR_BUFS		4
#define BR_MASK		(NR_BUFS - 1)
#define BUF_SIZE	32

static int do_write(int fd, void *buf, int buf_size)
{
	int ret;

	ret = write(fd, buf, buf_size);
	if (ret < 0) {
		perror("write");
		return 0;
	} else if (ret != buf_size) {
		fprintf(stderr, "bad write size %d\n", ret);
		return 0;
	}

	return 1;
}

static void *thread_fn(void *data)
{
	char w1[BUF_SIZE], w2[BUF_SIZE];
	int *fds = data;

	memset(w1, 0x11, BUF_SIZE);
	memset(w2, 0x22, BUF_SIZE);

	if (!do_write(fds[1], w1, BUF_SIZE))
		return NULL;
	if (!do_write(fds[1], w2, BUF_SIZE))
		return NULL;

	usleep(100000);

	if (!do_write(fds[1], w1, BUF_SIZE))
		return NULL;
	if (!do_write(fds[1], w2, BUF_SIZE))
		return NULL;

	return NULL;
}

int main(int argc, char *argv[])
{
	struct io_uring_buf_ring *br;
	struct io_uring_sqe *sqe;
	struct io_uring_cqe *cqe;
	struct io_uring ring;
	pthread_t thread;
	int i, ret, fds[2];
	void *buf, *tret;

	if (argc > 1)
		return T_EXIT_SKIP;

	if (pipe(fds) < 0) {
		perror("pipe");
		return T_EXIT_FAIL;
	}

	ret = io_uring_queue_init(8, &ring, 0);
	if (ret) {
		fprintf(stderr, "queue_init: %d\n", ret);
		return T_EXIT_FAIL;
	}

	br = io_uring_setup_buf_ring(&ring, NR_BUFS, BGID, 0, &ret);
	if (!br) {
		if (ret == -EINVAL)
			return T_EXIT_SKIP;
		fprintf(stderr, "failed buffer ring %d\n", ret);
		return T_EXIT_FAIL;
	}

	buf = malloc(NR_BUFS * BUF_SIZE);
	for (i = 0; i < NR_BUFS; i++) {
		void *this_buf = buf + i * BUF_SIZE;

		io_uring_buf_ring_add(br, this_buf, BUF_SIZE, i, BR_MASK, i);
	}
	io_uring_buf_ring_advance(br, NR_BUFS);

	sqe = io_uring_get_sqe(&ring);
	io_uring_prep_read_multishot(sqe, fds[0], 0, 0, BGID);

	ret = io_uring_submit(&ring);
	if (ret != 1) {
		fprintf(stderr, "bad submit %d\n", ret);
		return T_EXIT_FAIL;
	}

	/*
	 * read multishot not available would be ready as a cqe when
	 * submission returns, check and skip if not.
	 */
	ret = io_uring_peek_cqe(&ring, &cqe);
	if (!ret) {
		if (cqe->res == -EINVAL || cqe->res == -EBADF)
			return T_EXIT_SKIP;
	}

	pthread_create(&thread, NULL, thread_fn, fds);

	for (i = 0; i < 4; i++) {
		int buf_index;

		ret = io_uring_wait_cqe(&ring, &cqe);
		if (ret) {
			fprintf(stderr, "wait %d\n", ret);
			break;
		}

		if (cqe->res != BUF_SIZE) {
			fprintf(stderr, "size %d\n", cqe->res);
			return T_EXIT_FAIL;
		}
		if (!(cqe->flags & IORING_CQE_F_BUFFER)) {
			fprintf(stderr, "buffer not set\n");
			return T_EXIT_FAIL;
		}
		if (!(cqe->flags & IORING_CQE_F_MORE)) {
			fprintf(stderr, "more not set\n");
			return T_EXIT_FAIL;
		}
		buf_index = cqe->flags >> 16;
		assert(buf_index >= 0 && buf_index <= NR_BUFS);
		io_uring_cqe_seen(&ring, cqe);
	}

	pthread_join(thread, &tret);
	return T_EXIT_PASS;
}