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 * Tvrtko Ursulin <tvrtko.ursulin@intel.com>
25 *
26 */
27
28 /** @file gem_userptr_benchmark.c
29 *
30 * Benchmark the userptr code and impact of having userptr surfaces
31 * in process address space on some normal operations.
32 *
33 */
34
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <fcntl.h>
39 #include <inttypes.h>
40 #include <errno.h>
41 #include <assert.h>
42 #include <sys/stat.h>
43 #include <sys/time.h>
44 #include <sys/mman.h>
45 #include <signal.h>
46
47 #include "drm.h"
48 #include "i915_drm.h"
49
50 #include "drmtest.h"
51 #include "intel_bufmgr.h"
52 #include "intel_batchbuffer.h"
53 #include "intel_chipset.h"
54 #include "ioctl_wrappers.h"
55 #include "igt_aux.h"
56
57 #ifndef PAGE_SIZE
58 #define PAGE_SIZE 4096
59 #endif
60
61 static uint32_t userptr_flags = LOCAL_I915_USERPTR_UNSYNCHRONIZED;
62
63 #define BO_SIZE (65536)
64
gem_userptr_test_unsynchronized(void)65 static void gem_userptr_test_unsynchronized(void)
66 {
67 userptr_flags = LOCAL_I915_USERPTR_UNSYNCHRONIZED;
68 }
69
gem_userptr_test_synchronized(void)70 static void gem_userptr_test_synchronized(void)
71 {
72 userptr_flags = 0;
73 }
74
75 static void **handle_ptr_map;
76 static unsigned int num_handle_ptr_map;
77
add_handle_ptr(uint32_t handle,void * ptr)78 static void add_handle_ptr(uint32_t handle, void *ptr)
79 {
80 if (handle >= num_handle_ptr_map) {
81 handle_ptr_map = realloc(handle_ptr_map,
82 (handle + 1000) * sizeof(void*));
83 num_handle_ptr_map = handle + 1000;
84 }
85
86 handle_ptr_map[handle] = ptr;
87 }
88
get_handle_ptr(uint32_t handle)89 static void *get_handle_ptr(uint32_t handle)
90 {
91 return handle_ptr_map[handle];
92 }
93
free_handle_ptr(uint32_t handle)94 static void free_handle_ptr(uint32_t handle)
95 {
96 igt_assert(handle < num_handle_ptr_map);
97 igt_assert(handle_ptr_map[handle]);
98
99 free(handle_ptr_map[handle]);
100 handle_ptr_map[handle] = NULL;
101 }
102
create_userptr_bo(int fd,int size)103 static uint32_t create_userptr_bo(int fd, int size)
104 {
105 void *ptr;
106 uint32_t handle;
107 int ret;
108
109 ret = posix_memalign(&ptr, PAGE_SIZE, size);
110 igt_assert(ret == 0);
111
112 gem_userptr(fd, (uint32_t *)ptr, size, 0, userptr_flags, &handle);
113 add_handle_ptr(handle, ptr);
114
115 return handle;
116 }
117
free_userptr_bo(int fd,uint32_t handle)118 static void free_userptr_bo(int fd, uint32_t handle)
119 {
120 gem_close(fd, handle);
121 free_handle_ptr(handle);
122 }
123
has_userptr(int fd)124 static int has_userptr(int fd)
125 {
126 uint32_t handle = 0;
127 void *ptr;
128 uint32_t oldflags;
129 int ret;
130
131 assert(posix_memalign(&ptr, PAGE_SIZE, PAGE_SIZE) == 0);
132 oldflags = userptr_flags;
133 gem_userptr_test_unsynchronized();
134 ret = __gem_userptr(fd, ptr, PAGE_SIZE, 0, userptr_flags, &handle);
135 userptr_flags = oldflags;
136 if (ret != 0) {
137 free(ptr);
138 return 0;
139 }
140
141 gem_close(fd, handle);
142 free(ptr);
143
144 return handle != 0;
145 }
146
147 static const unsigned int nr_bos[] = {0, 1, 10, 100, 1000, 10000};
148 static const unsigned int test_duration_sec = 3;
149
150 static volatile unsigned int run_test;
151
alarm_handler(int sig)152 static void alarm_handler(int sig)
153 {
154 assert(run_test == 1);
155 run_test = 0;
156 }
157
start_test(unsigned int duration)158 static void start_test(unsigned int duration)
159 {
160 run_test = 1;
161 if (duration == 0)
162 duration = test_duration_sec;
163 signal(SIGALRM, alarm_handler);
164 alarm(duration);
165 }
166
exchange_ptr(void * array,unsigned i,unsigned j)167 static void exchange_ptr(void *array, unsigned i, unsigned j)
168 {
169 void **arr, *tmp;
170 arr = (void **)array;
171
172 tmp = arr[i];
173 arr[i] = arr[j];
174 arr[j] = tmp;
175 }
176
test_malloc_free(int random)177 static void test_malloc_free(int random)
178 {
179 unsigned long iter = 0;
180 unsigned int i, tot = 1000;
181 void *ptr[tot];
182
183 start_test(test_duration_sec);
184
185 while (run_test) {
186 for (i = 0; i < tot; i++) {
187 ptr[i] = malloc(1000);
188 assert(ptr[i]);
189 }
190 if (random)
191 igt_permute_array(ptr, tot, exchange_ptr);
192 for (i = 0; i < tot; i++)
193 free(ptr[i]);
194 iter++;
195 }
196
197 printf("%8lu iter/s\n", iter / test_duration_sec);
198 }
199
test_malloc_realloc_free(int random)200 static void test_malloc_realloc_free(int random)
201 {
202 unsigned long iter = 0;
203 unsigned int i, tot = 1000;
204 void *ptr[tot];
205
206 start_test(test_duration_sec);
207
208 while (run_test) {
209 for (i = 0; i < tot; i++) {
210 ptr[i] = malloc(1000);
211 assert(ptr[i]);
212 }
213 if (random)
214 igt_permute_array(ptr, tot, exchange_ptr);
215 for (i = 0; i < tot; i++) {
216 ptr[i] = realloc(ptr[i], 2000);
217 assert(ptr[i]);
218 }
219 if (random)
220 igt_permute_array(ptr, tot, exchange_ptr);
221 for (i = 0; i < tot; i++)
222 free(ptr[i]);
223 iter++;
224 }
225
226 printf("%8lu iter/s\n", iter / test_duration_sec);
227 }
228
test_mmap_unmap(int random)229 static void test_mmap_unmap(int random)
230 {
231 unsigned long iter = 0;
232 unsigned int i, tot = 1000;
233 void *ptr[tot];
234
235 start_test(test_duration_sec);
236
237 while (run_test) {
238 for (i = 0; i < tot; i++) {
239 ptr[i] = mmap(NULL, 1000, PROT_READ | PROT_WRITE,
240 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
241 assert(ptr[i] != MAP_FAILED);
242 }
243 if (random)
244 igt_permute_array(ptr, tot, exchange_ptr);
245 for (i = 0; i < tot; i++)
246 munmap(ptr[i], 1000);
247 iter++;
248 }
249
250 printf("%8lu iter/s\n", iter / test_duration_sec);
251 }
252
test_ptr_read(void * ptr)253 static void test_ptr_read(void *ptr)
254 {
255 unsigned long iter = 0;
256 volatile unsigned long *p;
257 unsigned long i, loops;
258
259 loops = BO_SIZE / sizeof(unsigned long) / 4;
260
261 start_test(test_duration_sec);
262
263 while (run_test) {
264 p = (unsigned long *)ptr;
265 for (i = 0; i < loops; i++) {
266 (void)*p++;
267 (void)*p++;
268 (void)*p++;
269 (void)*p++;
270 }
271 iter++;
272 }
273
274 printf("%8lu MB/s\n", iter / test_duration_sec * BO_SIZE / 1000000);
275 }
276
test_ptr_write(void * ptr)277 static void test_ptr_write(void *ptr)
278 {
279 unsigned long iter = 0;
280 volatile unsigned long *p;
281 register unsigned long i, loops;
282
283 loops = BO_SIZE / sizeof(unsigned long) / 4;
284
285 start_test(test_duration_sec);
286
287 while (run_test) {
288 p = (unsigned long *)ptr;
289 for (i = 0; i < loops; i++) {
290 *p++ = i;
291 *p++ = i;
292 *p++ = i;
293 *p++ = i;
294 }
295 iter++;
296 }
297
298 printf("%8lu MB/s\n", iter / test_duration_sec * BO_SIZE / 1000000);
299 }
300
do_impact_tests(unsigned int n,const char * pfix,const char * pfix2,void * ptr)301 static void do_impact_tests(unsigned int n, const char *pfix, const char *pfix2,
302 void *ptr)
303 {
304 printf("%s%sptr-read, %5u bos = ", pfix, pfix2, n);
305 test_ptr_read(ptr);
306
307 printf("%s%sptr-write %5u bos = ", pfix, pfix2, n);
308 test_ptr_write(ptr);
309
310 printf("%s%smalloc-free, %5u bos = ", pfix, pfix2, n);
311 test_malloc_free(0);
312 printf("%s%smalloc-free-random %5u bos = ", pfix, pfix2, n);
313 test_malloc_free(1);
314
315 printf("%s%smalloc-realloc-free, %5u bos = ", pfix, pfix2, n);
316 test_malloc_realloc_free(0);
317 printf("%s%smalloc-realloc-free-random, %5u bos = ", pfix, pfix2, n);
318 test_malloc_realloc_free(1);
319
320 printf("%s%smmap-unmap, %5u bos = ", pfix, pfix2, n);
321 test_mmap_unmap(0);
322 printf("%s%smmap-unmap-random, %5u bos = ", pfix, pfix2, n);
323 test_mmap_unmap(1);
324 }
325
test_impact_overlap(int fd,const char * prefix)326 static void test_impact_overlap(int fd, const char *prefix)
327 {
328 unsigned int total = sizeof(nr_bos) / sizeof(nr_bos[0]);
329 unsigned int subtest, i;
330 uint32_t handles[nr_bos[total-1]];
331 void *block = NULL;
332 void *ptr;
333 unsigned char *p;
334 char buffer[BO_SIZE];
335 int ret;
336
337 for (subtest = 0; subtest < total; subtest++) {
338 if (nr_bos[subtest] > 0) {
339 igt_assert(PAGE_SIZE < BO_SIZE);
340 ret = posix_memalign(&block, PAGE_SIZE,
341 PAGE_SIZE * nr_bos[subtest] + BO_SIZE);
342 igt_assert(ret == 0);
343
344 for (i = 0, p = block; i < nr_bos[subtest];
345 i++, p += PAGE_SIZE)
346 gem_userptr(fd, (uint32_t *)p, BO_SIZE, 0, userptr_flags, &handles[i]);
347 }
348
349 if (nr_bos[subtest] > 0)
350 ptr = block;
351 else
352 ptr = buffer;
353
354 do_impact_tests(nr_bos[subtest], prefix, "overlap-", ptr);
355
356 for (i = 0; i < nr_bos[subtest]; i++)
357 gem_close(fd, handles[i]);
358
359 free(block);
360 block = NULL;
361 }
362 }
363
test_impact(int fd,const char * prefix)364 static void test_impact(int fd, const char *prefix)
365 {
366 unsigned int total = sizeof(nr_bos) / sizeof(nr_bos[0]);
367 unsigned int subtest, i;
368 uint32_t handles[nr_bos[total-1]];
369 void *ptr;
370 char buffer[BO_SIZE];
371
372 for (subtest = 0; subtest < total; subtest++) {
373 for (i = 0; i < nr_bos[subtest]; i++)
374 handles[i] = create_userptr_bo(fd, BO_SIZE);
375
376 if (nr_bos[subtest] > 0)
377 ptr = get_handle_ptr(handles[0]);
378 else
379 ptr = buffer;
380
381 do_impact_tests(nr_bos[subtest], prefix, "no-overlap-", ptr);
382
383 for (i = 0; i < nr_bos[subtest]; i++)
384 free_userptr_bo(fd, handles[i]);
385 }
386 }
387
test_single(int fd)388 static void test_single(int fd)
389 {
390 char *ptr, *bo_ptr;
391 uint32_t handle = 0;
392 unsigned long iter = 0;
393 unsigned long map_size = BO_SIZE + PAGE_SIZE - 1;
394
395 ptr = mmap(NULL, map_size, PROT_READ | PROT_WRITE,
396 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
397 assert(ptr != MAP_FAILED);
398
399 bo_ptr = (char *)ALIGN((unsigned long)ptr, PAGE_SIZE);
400
401 start_test(test_duration_sec);
402
403 while (run_test) {
404 gem_userptr(fd, bo_ptr, BO_SIZE, 0, userptr_flags, &handle);
405 gem_close(fd, handle);
406 iter++;
407 }
408
409 munmap(ptr, map_size);
410
411 printf("%8lu iter/s\n", iter / test_duration_sec);
412 }
413
test_multiple(int fd,unsigned int batch,int random)414 static void test_multiple(int fd, unsigned int batch, int random)
415 {
416 char *ptr, *bo_ptr;
417 uint32_t handles[10000];
418 int map[10000];
419 unsigned long iter = 0;
420 int i;
421 unsigned long map_size = batch * BO_SIZE + PAGE_SIZE - 1;
422
423 assert(batch < (sizeof(handles) / sizeof(handles[0])));
424 assert(batch < (sizeof(map) / sizeof(map[0])));
425
426 ptr = mmap(NULL, map_size, PROT_READ | PROT_WRITE,
427 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
428 assert(ptr != MAP_FAILED);
429
430 bo_ptr = (char *)ALIGN((unsigned long)ptr, PAGE_SIZE);
431
432 for (i = 0; i < batch; i++)
433 map[i] = i;
434
435 start_test(test_duration_sec);
436
437 while (run_test) {
438 if (random)
439 igt_permute_array(map, batch, igt_exchange_int);
440 for (i = 0; i < batch; i++) {
441 gem_userptr(fd, bo_ptr + map[i] * BO_SIZE, BO_SIZE,
442 0, userptr_flags, &handles[i]);
443 }
444 if (random)
445 igt_permute_array(map, batch, igt_exchange_int);
446 for (i = 0; i < batch; i++)
447 gem_close(fd, handles[map[i]]);
448 iter++;
449 }
450
451 munmap(ptr, map_size);
452
453 printf("%8lu iter/s\n", iter * batch / test_duration_sec);
454 }
455
test_userptr(int fd)456 static void test_userptr(int fd)
457 {
458 printf("create-destroy = ");
459 test_single(fd);
460
461 printf("multi-create-destroy = ");
462 test_multiple(fd, 100, 0);
463
464 printf("multi-create-destroy-random = ");
465 test_multiple(fd, 100, 1);
466 }
467
main(int argc,char ** argv)468 int main(int argc, char **argv)
469 {
470 int fd = -1, ret;
471
472 igt_skip_on_simulation();
473
474 igt_subtest_init(argc, argv);
475
476 fd = drm_open_driver(DRIVER_INTEL);
477 igt_assert(fd >= 0);
478
479 ret = has_userptr(fd);
480 igt_skip_on_f(ret == 0, "No userptr support - %s (%d)\n",
481 strerror(errno), ret);
482
483
484 gem_userptr_test_unsynchronized();
485
486 igt_subtest("userptr-unsync")
487 test_userptr(fd);
488
489 igt_subtest("userptr-impact-unsync")
490 test_impact(fd, "unsync-");
491
492 igt_subtest("userptr-impact-unsync-overlap")
493 test_impact_overlap(fd, "unsync-");
494
495 gem_userptr_test_synchronized();
496
497 igt_subtest("userptr-sync")
498 test_userptr(fd);
499
500 igt_subtest("userptr-impact-sync")
501 test_impact(fd, "sync-");
502
503 igt_subtest("userptr-impact-sync-overlap")
504 test_impact_overlap(fd, "sync-");
505
506 igt_exit();
507
508 return 0;
509 }
510