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