summaryrefslogtreecommitdiff
path: root/harness/cases/21.t
blob: ba988ed72320cb0cb8d9187977a1f2097ea5a104 (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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/*
 * Copyright 2017, Red Hat, Inc.
 *
 * Test RWF_NOWAIT.
 *
 * RWF_NOWAIT will cause -EAGAIN to be returned in the io_event for
 * any I/O that cannot be serviced without blocking the submission
 * thread.  Instances covered by the kernel at the time this test was
 * written include:
 * - O_DIRECT I/O to a file offset that has populated page cache pages
 * - the submission context cannot obtain the inode lock
 * - space allocation is necessary
 * - we need to wait for other I/O (e.g. in the misaligned I/O case)
 * - ...
 *

 * The easiest of these to test is that a direct I/O is writing to a
 * file offset with populated page cache.  We also test to ensure that
 * we can perform I/O in the absence of the above conditions.
 *
 * Author: Jeff Moyer <jmoyer@redhat.com>
 */
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <signal.h>
#include <sched.h>
#include <libaio.h>

#define TEMPLATE "21.XXXXXX"
#define BUFLEN 4096

#ifndef RWF_NOWAIT
#define RWF_NOWAIT	0x00000008
#endif

int
open_temp_file()
{
	int fd;
	char temp_file[sizeof(TEMPLATE)];

	strncpy(temp_file, TEMPLATE, sizeof(temp_file));
	fd = mkstemp(temp_file);
	if (fd < 0) {
		perror("mkstemp");
		return -1;
	}
	unlink(temp_file);
	return fd;
}

int
test_main()
{
	int fd, flags;
	int ret;
	io_context_t ctx;
	struct iocb iocb, *iocbp = &iocb;
	struct io_event event;
	char buf[BUFLEN] __attribute__((aligned (4096)));
	struct iovec iov;

	fd = open_temp_file();
	if (fd < 0)
		return 1;

	memset(&ctx, 0, sizeof(ctx));
	ret = io_setup(1, &ctx);
	if (ret != 0) {
		fprintf(stderr, "io_setup failed with %d\n", ret);
		return 1;
	}

	/*
	 * Perform a buffered write to a file.  This instantiates the
	 * block and adds the page to the page cache.
	 */
	memset(buf, 0xa, BUFLEN);
	ret = write(fd, buf, BUFLEN);
	if (ret != BUFLEN) {
		perror("write");
		return 1;
	}

	/*
	 * Now attempt an aio/dio pwritev2 with the RWF_NONBLOCK flag
	 * set.
	 */
	flags = fcntl(fd, F_GETFL);
	ret = fcntl(fd, F_SETFL, flags | O_DIRECT);
	if (ret < 0) {
		/* SKIP this test if O_DIRECT is not available on this fs */
		if (errno == EINVAL)
			return 3;
		perror("fcntl");
		return 1;
	}

	memset(buf, 0, BUFLEN);
	iov.iov_base = buf;
	iov.iov_len = BUFLEN;
	io_prep_preadv2(&iocb, fd, &iov, 1, 0, RWF_NOWAIT);

	ret = io_submit(ctx, 1, &iocbp);

	/*
	 * io_submit will return -EINVAL if RWF_NOWAIT is not supported.
	 */
	if (ret != 1) {
		if (ret == -EINVAL) {
			fprintf(stderr, "RWF_NOWAIT not supported by kernel.\n");
			/* just return success */
			return 0;
		}
		errno = -ret;
		perror("io_submit");
		return 1;
	}

	ret = io_getevents(ctx, 1, 1, &event, NULL);
	if (ret != 1) {
		errno = -ret;
		perror("io_getevents");
		return 1;
	}

	/*
	 * We expect -EAGAIN due to the existence of a page cache page
	 * for the file system block we are writing.
	 */
	if (event.res != -EAGAIN) {
		fprintf(stderr, "Expected -EAGAIN, got %lu\n", event.res);
		return 1;
	}

	/*
	 * An O_DIRECT write to the page will force the page out of the
	 * page cache, allowing the subsequent RWF_NOWAIT I/O to complete.
	 */
	ret = pwrite(fd, buf, BUFLEN, 0);
	if (ret != BUFLEN) {
		perror("write");
		return 1;
	}

	/*
	 * Now retry the RWF_NOWAIT I/O.  This should succeed.
	 */
	ret = io_submit(ctx, 1, &iocbp);
	if (ret != 1) {
		errno = -ret;
		perror("io_submit");
		return 1;
	}

	ret = io_getevents(ctx, 1, 1, &event, NULL);
	if (ret != 1) {
		errno = -ret;
		perror("io_getevents");
		return 1;
	}

	if (event.res != BUFLEN) {
		fprintf(stderr, "Expected %d, got %lu\n", BUFLEN, event.res);
		return 1;
	}

	return 0;
}
/*
 * Local variables:
 *  mode: c
 *  c-basic-offset: 8
 * End:
 */