1 /*
2 * Copyright © 2014 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 * Authors:
24 * Chris Wilson <chris@chris-wilson.co.uk>
25 *
26 */
27
28 /*
29 * Testcase: boundary testing of read(drm_fd)
30 */
31
32 #include "igt.h"
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <signal.h>
39 #include <fcntl.h>
40 #include <inttypes.h>
41 #include <errno.h>
42 #include <sys/stat.h>
43 #include <sys/ioctl.h>
44 #include <sys/time.h>
45 #include <sys/poll.h>
46 #include <pthread.h>
47 #include "drm.h"
48
49 IGT_TEST_DESCRIPTION("Call read(drm) and see if it behaves.");
50
sighandler(int sig)51 static void sighandler(int sig)
52 {
53 }
54
assert_empty(int fd)55 static void assert_empty(int fd)
56 {
57 struct pollfd pfd = {fd, POLLIN};
58 do_or_die(poll(&pfd, 1, 0));
59 }
60
generate_event(int fd,enum pipe pipe)61 static void generate_event(int fd, enum pipe pipe)
62 {
63 igt_assert(kmstest_get_vblank(fd, pipe, DRM_VBLANK_EVENT));
64 }
65
wait_for_event(int fd)66 static void wait_for_event(int fd)
67 {
68 struct pollfd pfd = {fd, POLLIN};
69 igt_assert(poll(&pfd, 1, -1) == 1);
70 }
71
setup(int in,int nonblock)72 static int setup(int in, int nonblock)
73 {
74 int fd;
75 int ret = -1;
76
77 alarm(0);
78
79 fd = dup(in);
80 if (fd != -1)
81 ret = fcntl(fd, F_GETFL);
82 if (ret != -1) {
83 if (nonblock)
84 ret |= O_NONBLOCK;
85 else
86 ret &= ~O_NONBLOCK;
87 ret = fcntl(fd, F_SETFL, ret);
88 }
89 igt_require(ret != -1);
90
91 assert_empty(fd);
92 return fd;
93 }
94
teardown(int fd)95 static void teardown(int fd)
96 {
97 alarm(0);
98 assert_empty(fd);
99 close(fd);
100 errno = 0;
101 }
102
test_invalid_buffer(int in)103 static void test_invalid_buffer(int in)
104 {
105 int fd = setup(in, 0);
106
107 alarm(1);
108
109 igt_assert_eq(read(fd, (void *)-1, 4096), -1);
110 igt_assert_eq(errno, EFAULT);
111
112 teardown(fd);
113 }
114
test_fault_buffer(int in,enum pipe pipe)115 static void test_fault_buffer(int in, enum pipe pipe)
116 {
117 int fd = setup(in, 0);
118 struct drm_mode_map_dumb arg;
119 char *buf;
120
121 memset(&arg, 0, sizeof(arg));
122 arg.handle = kmstest_dumb_create(fd, 32, 32, 32, NULL, NULL);
123
124 do_ioctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &arg);
125
126 buf = mmap(0, 4096, PROT_WRITE, MAP_SHARED, fd, arg.offset);
127 igt_assert(buf != MAP_FAILED);
128
129 generate_event(fd, pipe);
130
131 alarm(1);
132
133 igt_assert(read(fd, buf, 4096) > 0);
134
135 munmap(buf, 4096);
136 teardown(fd);
137 }
138
test_empty(int in,int nonblock,int expected)139 static void test_empty(int in, int nonblock, int expected)
140 {
141 char buffer[1024];
142 int fd = setup(in, nonblock);
143
144 alarm(1);
145 igt_assert_eq(read(fd, buffer, sizeof(buffer)), -1);
146 igt_assert_eq(errno, expected);
147
148 teardown(fd);
149 }
150
test_short_buffer(int in,int nonblock,enum pipe pipe)151 static void test_short_buffer(int in, int nonblock, enum pipe pipe)
152 {
153 char buffer[1024]; /* events are typically 32 bytes */
154 int fd = setup(in, nonblock);
155
156 generate_event(fd, pipe);
157 generate_event(fd, pipe);
158
159 wait_for_event(fd);
160
161 alarm(3);
162
163 igt_assert_eq(read(fd, buffer, 4), 0);
164 igt_assert(read(fd, buffer, 40) > 0);
165 igt_assert(read(fd, buffer, 40) > 0);
166
167 teardown(fd);
168 }
169
170 struct short_buffer_wakeup {
171 pthread_mutex_t mutex;
172 pthread_cond_t send, recv;
173 int counter;
174 int done;
175 int fd;
176 };
177
thread_short_buffer_wakeup(void * arg)178 static void *thread_short_buffer_wakeup(void *arg)
179 {
180 struct short_buffer_wakeup *w = arg;
181 char buffer; /* events are typically 32 bytes */
182
183 while (!w->done) {
184 /* Short read, does not consume the event. */
185 igt_assert_eq(read(w->fd, &buffer, sizeof(buffer)), 0);
186
187 pthread_mutex_lock(&w->mutex);
188 if (!--w->counter)
189 pthread_cond_signal(&w->send);
190 pthread_cond_wait(&w->recv, &w->mutex);
191 pthread_mutex_unlock(&w->mutex);
192 }
193
194 return NULL;
195 }
196
test_short_buffer_wakeup(int in,enum pipe pipe)197 static void test_short_buffer_wakeup(int in, enum pipe pipe)
198 {
199 const int nt = sysconf(_SC_NPROCESSORS_ONLN) + 1;
200 struct short_buffer_wakeup w = {
201 .fd = setup(in, 0),
202 };
203 pthread_t t[nt];
204 char buffer[1024]; /* events are typically 32 bytes */
205
206 pthread_mutex_init(&w.mutex, NULL);
207 pthread_cond_init(&w.send, NULL);
208 pthread_cond_init(&w.recv, NULL);
209
210 for (int n = 0; n < nt; n++)
211 pthread_create(&t[n], NULL, thread_short_buffer_wakeup, &w);
212
213 igt_until_timeout(30) {
214 struct timespec tv;
215 int err = 0;
216
217 pthread_mutex_lock(&w.mutex);
218 w.counter = nt;
219 pthread_cond_broadcast(&w.recv);
220 pthread_mutex_unlock(&w.mutex);
221
222 /* Give each thread a chance to sleep in drm_read() */
223 pthread_yield();
224
225 /* One event should wake all threads as none consume */
226 generate_event(w.fd, pipe);
227
228 clock_gettime(CLOCK_REALTIME, &tv);
229 tv.tv_sec += 5; /* Let's be very generous to the scheduler */
230
231 pthread_mutex_lock(&w.mutex);
232 while (w.counter && !err)
233 err = pthread_cond_timedwait(&w.send, &w.mutex, &tv);
234 pthread_mutex_unlock(&w.mutex);
235
236 igt_assert_f(err == 0,
237 "Timed out waiting for drm_read() to wakeup on an event\n");
238
239 /* No thread should consume the event */
240 igt_assert(read(w.fd, buffer, sizeof(buffer)) > 0);
241 }
242 pthread_mutex_lock(&w.mutex);
243 w.done = true;
244 pthread_cond_broadcast(&w.recv);
245 pthread_mutex_unlock(&w.mutex);
246
247 for (int n = 0; n < nt; n++)
248 pthread_join(t[n], NULL);
249
250 close(w.fd);
251 }
252
253 igt_main
254 {
255 int fd;
256 igt_display_t display;
257 struct igt_fb fb;
258 enum pipe pipe;
259
260 signal(SIGALRM, sighandler);
261 siginterrupt(SIGALRM, 1);
262
263 igt_fixture {
264 igt_output_t *output;
265
266 fd = drm_open_driver_master(DRIVER_ANY);
267 kmstest_set_vt_graphics_mode();
268
269 igt_display_require(&display, fd);
270 igt_display_require_output(&display);
271
272 for_each_pipe_with_valid_output(&display, pipe, output) {
273 drmModeModeInfo *mode = igt_output_get_mode(output);
274
275 igt_create_pattern_fb(fd, mode->hdisplay, mode->vdisplay,
276 DRM_FORMAT_XRGB8888,
277 LOCAL_DRM_FORMAT_MOD_NONE, &fb);
278
279 igt_output_set_pipe(output, pipe);
280 igt_plane_set_fb(igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY), &fb);
281 break;
282 }
283
284 igt_display_commit2(&display, display.is_atomic ? COMMIT_ATOMIC : COMMIT_LEGACY);
285 igt_require(kmstest_get_vblank(fd, pipe, 0));
286 }
287
288 igt_subtest("invalid-buffer")
289 test_invalid_buffer(fd);
290
291 igt_subtest("fault-buffer")
292 test_fault_buffer(fd, pipe);
293
294 igt_subtest("empty-block")
295 test_empty(fd, 0, EINTR);
296
297 igt_subtest("empty-nonblock")
298 test_empty(fd, 1, EAGAIN);
299
300 igt_subtest("short-buffer-block")
301 test_short_buffer(fd, 0, pipe);
302
303 igt_subtest("short-buffer-nonblock")
304 test_short_buffer(fd, 1, pipe);
305
306 igt_subtest("short-buffer-wakeup")
307 test_short_buffer_wakeup(fd, pipe);
308 }
309