4989510ae14e348b948276e6e4b44e46b91500d2
[platform/upstream/libaio.git] / harness / cases / 19.t
1 /*
2  * Copyright 2015, Red Hat, Inc.
3  *
4  * This test remaps the aio ring buffer and ensures that I/O completions
5  * can still be reaped from userspace.
6  *
7  * Author: Jeff Moyer <jmoyer@redhat.com>
8  */
9 #define _GNU_SOURCE
10 #include <stdio.h>
11 #include <unistd.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/mman.h>
15 #include <signal.h>
16 #include <sched.h>
17 #include <libaio.h>
18
19 #define BUFLEN 4096
20 #define TEMPLATE "19.XXXXXX"
21
22 volatile sig_atomic_t timed_out = 0;
23
24 struct aio_ring {
25         unsigned        id;     /* kernel internal index number */
26         unsigned        nr;     /* number of io_events */
27         volatile unsigned       head;
28         volatile unsigned       tail;
29
30         unsigned        magic;
31         unsigned        compat_features;
32         unsigned        incompat_features;
33         unsigned        header_length;  /* size of aio_ring */
34
35         struct io_event io_events[0];
36 };
37
38 int
39 open_temp_file(void)
40 {
41         int fd;
42         char template[sizeof(TEMPLATE)];
43
44         strncpy(template, TEMPLATE, sizeof(TEMPLATE));
45         fd = mkostemp(template, O_DIRECT);
46         if (fd < 0) {
47                 perror("mkstemp");
48                 exit(1);
49         }
50         unlink(template);
51         return fd;
52 }
53
54 /*
55  * mmap will do the address space search for us.  when remapping the ring,
56  * the use of MREMAP_FIXED will cause this mapping to be unmapped.
57  *
58  * len - length in bytes
59  *
60  * Returns the available virtual address, or MAP_FAILED on error.
61  */
62 void *
63 find_unused_va(size_t len)
64 {
65         return mmap(0, len, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
66 }
67
68 void
69 alarm_handler(int __attribute__((unused))signo)
70 {
71         timed_out = 1;
72 }
73
74 int
75 user_getevents(io_context_t ctx, int nr_events, struct io_event *event)
76 {
77         struct aio_ring *ring = (void *)ctx;
78         int completed = 0;
79
80         timed_out = 0;
81         signal(SIGALRM, alarm_handler);
82         alarm(30);
83
84         while ((completed < nr_events) && !timed_out) {
85                 unsigned new_head;
86
87                 if (ring->head == ring->tail) {
88                         sched_yield();
89                         continue;
90                 }
91
92                 new_head = ring->head;
93                 *event = ring->io_events[new_head];
94                 new_head += 1;
95                 new_head %= ring->nr;
96                 ring->head = new_head;
97                 completed++;
98         }
99
100         alarm(0);
101         signal(SIGALRM, SIG_DFL);
102
103         return completed;
104 }
105
106 struct aio_ring *
107 remap_ring(struct aio_ring *ring)
108 {
109         struct aio_ring *new_ring;
110         size_t ring_size;
111
112         /*
113          * No need to round up to page size as ring->nr was adjusted
114          * already to fill the last page in the ring.
115          */
116         ring_size = sizeof(struct aio_ring) + ring->nr * sizeof(struct io_event);
117
118         /*
119          * Remap the ring.
120          */
121         new_ring = find_unused_va(ring_size);
122         if (new_ring == MAP_FAILED) {
123                 fprintf(stderr, "Unable to find suitable va for ring\n");
124                 return NULL;
125         }
126
127         new_ring = mremap(ring, ring_size, ring_size,
128                           MREMAP_FIXED|MREMAP_MAYMOVE, new_ring);
129         if (new_ring == MAP_FAILED || new_ring == ring) {
130                 perror("mremap");
131                 return NULL;
132         }
133
134         return new_ring;
135 }
136
137 io_context_t
138 remap_io_context(io_context_t ctx)
139 {
140         struct aio_ring *ring, *new_ring;
141
142         ring = (void *)ctx;
143         new_ring = remap_ring(ring);
144         if (!new_ring)
145                 return NULL;
146
147         ctx = (io_context_t)new_ring;
148         return ctx;
149 }
150
151 int
152 do_io(io_context_t ctx, struct iocb *iocbp, int fd)
153 {
154         int ret;
155         char buf[BUFLEN];
156
157         io_prep_pwrite(iocbp, fd, buf, BUFLEN, 0);
158         ret = io_submit(ctx, 1, &iocbp);
159         if (ret != 1) {
160                 fprintf(stderr, "io_submit failed with %d\n", ret);
161                 return 1;
162         }
163         return 0;
164 }
165
166 int
167 check_completion(io_context_t ctx, struct iocb *iocbp)
168 {
169         int ret;
170         struct io_event event;
171
172         ret = user_getevents(ctx, 1, &event);
173         if (ret != 1) {
174                 fprintf(stderr, "user_getevents timed out.\n");
175                 return 1;
176         }
177
178         if (event.obj != iocbp) {
179                 fprintf(stderr,
180                         "Error: event->opj (%p) does not match iocbp (%p)\n",
181                         event.obj, iocbp);
182                 return 1;
183         }
184
185         return 0;
186 }
187
188 int
189 test_main()
190 {
191         int fd;
192         int ret;
193         io_context_t ctx;
194         struct iocb iocb;
195
196         memset(&ctx, 0, sizeof(ctx));
197         fd = open_temp_file();
198
199         ret = io_setup(1, &ctx);
200         if (ret != 0) {
201                 fprintf(stderr, "io_setup failed with %d\n", ret);
202                 return 1;
203         }
204
205         /*
206          * First, try remapping the ring buffer in-between io_setup and
207          * io_submit.
208          */
209         ctx = remap_io_context(ctx);
210         if (ctx == NULL)
211                 return 1;
212
213         ret = do_io(ctx, &iocb, fd);
214         if (ret != 0)
215                 return 1;
216
217         ret = check_completion(ctx, &iocb);
218         if (ret != 0)
219                 return 1;
220
221         /*
222          * Now remap the ring in between io_submit and getevents.
223          */
224         ret = do_io(ctx, &iocb, fd);
225         if (ret != 0)
226                 return 1;
227
228         ctx = remap_io_context(ctx);
229         if (ctx == NULL)
230                 return 1;
231
232         ret = check_completion(ctx, &iocb);
233         if (ret != 0)
234                 return 1;
235
236         /*
237          * Success, clean up.
238          */
239         ret = io_destroy(ctx);
240         if (ret != 0) {
241                 fprintf(stderr, "io_destroy failed with %d\n", ret);
242                 return 1;
243         }
244         close(fd);
245
246         return 0;
247 }
248 /*
249  * Local variables:
250  *  mode: c
251  *  c-basic-offset: 8
252  * End:
253  */