summaryrefslogtreecommitdiff
path: root/src/uring_read_fault.c
blob: 9fb86c47b8b2304552c4c930a0c89b1f74574490 (plain)
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
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2022 SUSE Linux Products GmbH.  All Rights Reserved.
 */

/*
 * Create a file with 4 extents, each with a size matching the page size.
 * Then allocate a buffer to read all extents with io_uring, using O_DIRECT and
 * IOCB_NOWAIT. Before doing the read with io_uring, access the first page of
 * the read buffer to fault it in, so that during the read we only trigger page
 * faults when accessing the other pages (mapping to 2nd, 3rd and 4th extents).
 */

/* Get the O_DIRECT definition. */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

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

int main(int argc, char *argv[])
{
	struct io_uring ring;
	struct io_uring_sqe *sqe;
	struct io_uring_cqe *cqe;
	struct iovec iovec;
	int fd;
	long pagesize;
	void *write_buf;
	void *read_buf;
	ssize_t ret;
	int i;

	if (argc != 2) {
		fprintf(stderr, "Use: %s <file path>\n", argv[0]);
		return 1;
	}

	fd = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY | O_DIRECT, 0666);
	if (fd == -1) {
		fprintf(stderr, "Failed to create file %s: %s (errno %d)\n",
			argv[1], strerror(errno), errno);
		return 1;
	}

	pagesize = sysconf(_SC_PAGE_SIZE);
	if (pagesize == -1) {
		fprintf(stderr, "Failed to get page size: %s (errno %d)\n",
			strerror(errno), errno);
		return 1;
	}

	ret = posix_memalign(&write_buf, pagesize, 4 * pagesize);
	if (ret) {
		fprintf(stderr, "Failed to allocate write buffer\n");
		return 1;
	}

	memset(write_buf, 0xab, pagesize);
	memset(write_buf + pagesize, 0xcd, pagesize);
	memset(write_buf + 2 * pagesize, 0xef, pagesize);
	memset(write_buf + 3 * pagesize, 0x73, pagesize);

	/* Create the 4 extents, each with a size matching page size. */
	for (i = 0; i < 4; i++) {
		ret = pwrite(fd, write_buf + i * pagesize, pagesize,
			     i * pagesize);
		if (ret != pagesize) {
			fprintf(stderr,
				"Write failure, ret = %ld errno %d (%s)\n",
				ret, errno, strerror(errno));
			return 1;
		}
		ret = fsync(fd);
		if (ret != 0) {
			fprintf(stderr, "Fsync failure: %s (errno %d)\n",
				strerror(errno), errno);
			return 1;
		}
	}

	close(fd);
	fd = open(argv[1], O_RDONLY | O_DIRECT);
	if (fd == -1) {
		fprintf(stderr,
			"Failed to open file %s: %s (errno %d)",
			argv[1], strerror(errno), errno);
		return 1;
	}

	ret = posix_memalign(&read_buf, pagesize, 4 * pagesize);
	if (ret) {
		fprintf(stderr, "Failed to allocate read buffer\n");
		return 1;
	}

	/*
	 * Fault in only the first page of the read buffer.
	 * We want to trigger a page fault for the 2nd page of the read buffer
	 * during the read operation with io_uring (O_DIRECT and IOCB_NOWAIT).
	 */
	memset(read_buf, 0, 1);

	ret = io_uring_queue_init(1, &ring, 0);
	if (ret != 0) {
		fprintf(stderr, "Failed to creating io_uring queue\n");
		return 1;
	}

	sqe = io_uring_get_sqe(&ring);
	if (!sqe) {
		fprintf(stderr, "Failed to get io_uring sqe\n");
		return 1;
	}

	iovec.iov_base = read_buf;
	iovec.iov_len = 4 * pagesize;
	io_uring_prep_readv(sqe, fd, &iovec, 1, 0);

	ret = io_uring_submit_and_wait(&ring, 1);
	if (ret != 1) {
		fprintf(stderr, "Failed at io_uring_submit_and_wait()\n");
		return 1;
	}

	ret = io_uring_wait_cqe(&ring, &cqe);
	if (ret < 0) {
		fprintf(stderr, "Failed at io_uring_wait_cqe()\n");
		return 1;
	}

	if (cqe->res != (4 * pagesize))
		fprintf(stderr, "Unexpected cqe->res, got %d expected %ld\n",
			cqe->res, 4 * pagesize);

	if (memcmp(read_buf, write_buf, 4 * pagesize) != 0)
		fprintf(stderr,
			"Unexpected mismatch between read and write buffers\n");

	io_uring_cqe_seen(&ring, cqe);
	io_uring_queue_exit(&ring);

	return 0;
}