1 /*
2  * Copyright © 2015 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  *    Ankitprasad Sharma <ankitprasad.r.sharma at intel.com>
25  *
26  */
27 
28 /** @file gem_create_stolen.c
29  *
30  * This is a test for the extended gem_create ioctl, that includes allocation
31  * of object from stolen memory.
32  *
33  * The goal is to simply ensure the basics work, and invalid input combinations
34  * are rejected.
35  */
36 
37 #include <stdlib.h>
38 #include <sys/ioctl.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <fcntl.h>
42 #include <inttypes.h>
43 #include <errno.h>
44 #include <sys/stat.h>
45 #include <sys/time.h>
46 #include <getopt.h>
47 
48 #include <drm.h>
49 
50 #include "ioctl_wrappers.h"
51 #include "intel_bufmgr.h"
52 #include "intel_batchbuffer.h"
53 #include "intel_io.h"
54 #include "intel_chipset.h"
55 #include "igt_aux.h"
56 #include "drmtest.h"
57 #include "drm.h"
58 #include "i915_drm.h"
59 #include "i915/gem_mman.h"
60 
61 IGT_TEST_DESCRIPTION("This test verifies the exetended gem_create ioctl,"
62 		     " that includes allocation of obj from stolen region");
63 #define CLEAR(s) memset(&s, 0, sizeof(s))
64 #define SIZE 1024*1024
65 #define DWORD_SIZE 4
66 #define DATA 0xdead
67 #define LARGE_SIZE 0xffffffff
68 #define MAX_OBJECTS 100
69 
70 static drm_intel_bufmgr *bufmgr;
71 static struct intel_batchbuffer *batch;
72 
verify_copy_op(drm_intel_bo * src,drm_intel_bo * dest)73 static void verify_copy_op(drm_intel_bo *src, drm_intel_bo *dest)
74 {
75 	uint32_t *virt, i, ret;
76 	/* Fill the src BO with dwords */
77 	ret = drm_intel_gem_bo_map_gtt(src);
78 	igt_assert(!ret);
79 
80 	virt = src->virtual;
81 	for (i = 0; i < SIZE/DWORD_SIZE; i++)
82 		virt[i] = i;
83 
84 	intel_copy_bo(batch, dest, src, SIZE);
85 
86 	ret = drm_intel_gem_bo_map_gtt(dest);
87 	igt_assert(!ret);
88 
89 	virt = dest->virtual;
90 	/* verify */
91 	for (i = 0; i < SIZE/DWORD_SIZE; i++)
92 		igt_assert_eq(virt[i], i);
93 
94 	drm_intel_bo_unmap(src);
95 	drm_intel_bo_unmap(dest);
96 }
97 
stolen_pwrite(int fd)98 static void stolen_pwrite(int fd)
99 {
100 	drm_intel_bo *bo;
101 	uint32_t buf[SIZE/DWORD_SIZE];
102 	uint32_t handle = 0;
103 	uint32_t *virt;
104 	int i, ret = 0;
105 
106 	for (i = 0; i < SIZE/DWORD_SIZE; i++)
107 		buf[i] = DATA;
108 
109 	gem_require_stolen_support(fd);
110 
111 	handle = gem_create_stolen(fd, SIZE);
112 
113 	gem_write(fd, handle, 0, buf, SIZE);
114 	bo = gem_handle_to_libdrm_bo(bufmgr, fd, "bo", handle);
115 
116 	ret = drm_intel_gem_bo_map_gtt(bo);
117 	igt_assert(!ret);
118 
119 	virt = bo->virtual;
120 
121 	for (i = 0; i < SIZE/DWORD_SIZE; i++)
122 		igt_assert_eq(virt[i], DATA);
123 
124 	drm_intel_bo_unmap(bo);
125 	drm_intel_bo_unreference(bo);
126 	gem_close(fd, handle);
127 }
128 
stolen_pread(int fd)129 static void stolen_pread(int fd)
130 {
131 	drm_intel_bo *bo;
132 	uint32_t buf[SIZE/DWORD_SIZE];
133 	uint32_t handle = 0;
134 	uint32_t *virt;
135 	int i, ret = 0;
136 
137 	CLEAR(buf);
138 
139 	gem_require_stolen_support(fd);
140 
141 	handle = gem_create_stolen(fd, SIZE);
142 
143 	bo = gem_handle_to_libdrm_bo(bufmgr, fd, "bo", handle);
144 
145 	ret = drm_intel_gem_bo_map_gtt(bo);
146 	igt_assert(!ret);
147 
148 	virt = bo->virtual;
149 
150 	for (i = 0; i < SIZE/DWORD_SIZE; i++)
151 		virt[i] = DATA;
152 
153 	drm_intel_bo_unmap(bo);
154 	drm_intel_bo_unreference(bo);
155 
156 	gem_read(fd, handle, 0, buf, SIZE);
157 
158 	for (i = 0; i < SIZE/DWORD_SIZE; i++)
159 		igt_assert_eq(buf[i], DATA);
160 
161 	gem_close(fd, handle);
162 }
163 
copy_test(int fd)164 static void copy_test(int fd)
165 {
166 	drm_intel_bo *src, *dest;
167 	uint32_t src_handle = 0, dest_handle = 0;
168 
169 	gem_require_stolen_support(fd);
170 
171 	src_handle = gem_create_stolen(fd, SIZE);
172 	dest_handle = gem_create_stolen(fd, SIZE);
173 
174 	src = gem_handle_to_libdrm_bo(bufmgr, fd, "src_bo", src_handle);
175 	dest = gem_handle_to_libdrm_bo(bufmgr, fd, "dst_bo", dest_handle);
176 
177 	igt_assert(src != NULL);
178 	igt_assert(dest != NULL);
179 
180 	verify_copy_op(src, dest);
181 
182 	drm_intel_bo_unreference(src);
183 	drm_intel_bo_unreference(dest);
184 	gem_close(fd, src_handle);
185 	gem_close(fd, dest_handle);
186 }
187 
verify_object_clear(int fd)188 static void verify_object_clear(int fd)
189 {
190 	drm_intel_bo *bo;
191 	uint32_t handle = 0;
192 	uint32_t *virt;
193 	int i, ret;
194 
195 	gem_require_stolen_support(fd);
196 
197 	handle = gem_create_stolen(fd, SIZE);
198 
199 	bo = gem_handle_to_libdrm_bo(bufmgr, fd, "verify_bo", handle);
200 	igt_assert(bo != NULL);
201 
202 	ret = drm_intel_gem_bo_map_gtt(bo);
203 	igt_assert(!ret);
204 
205 	/* Verify if the BO is zeroed */
206 	virt = bo->virtual;
207 	for (i = 0; i < SIZE / DWORD_SIZE; i++)
208 		igt_assert(!virt[i]);
209 
210 	drm_intel_bo_unmap(bo);
211 	drm_intel_bo_unreference(bo);
212 	gem_close(fd, handle);
213 }
214 
stolen_large_obj_alloc(int fd)215 static void stolen_large_obj_alloc(int fd)
216 {
217 	uint32_t handle = 0;
218 
219 	gem_require_stolen_support(fd);
220 	handle = __gem_create_stolen(fd, (unsigned long long) LARGE_SIZE + 4096);
221 	igt_assert(!handle);
222 }
223 
stolen_fill_purge_test(int fd)224 static void stolen_fill_purge_test(int fd)
225 {
226 	drm_intel_bo *bo;
227 	int obj_count = 0, i = 0;
228 	int _ret = 0, j = 0;
229 	uint32_t handle[MAX_OBJECTS];
230 	uint32_t new_handle;
231 	uint32_t *virt;
232 	int retained;
233 
234 	gem_require_stolen_support(fd);
235 
236 	/* Exhaust Stolen space */
237 	do {
238 		handle[i] = __gem_create_stolen(fd, SIZE);
239 		if (handle[i] != 0) {
240 			bo = gem_handle_to_libdrm_bo(bufmgr, fd,
241 						     "verify_bo", handle[i]);
242 			igt_assert(bo != NULL);
243 
244 			_ret = drm_intel_gem_bo_map_gtt(bo);
245 			igt_assert(!_ret);
246 
247 			virt = bo->virtual;
248 			for (j = 0; j < SIZE/DWORD_SIZE; j++)
249 				virt[j] = DATA;
250 
251 			drm_intel_bo_unmap(bo);
252 			drm_intel_bo_unreference(bo);
253 
254 			obj_count++;
255 		}
256 
257 		i++;
258 	} while (handle[i-1] && i < MAX_OBJECTS);
259 
260 	igt_assert(obj_count > 0);
261 
262 	/* Mark all stolen objects purgeable */
263 	for (i = 0; i < obj_count; i++)
264 		retained = gem_madvise(fd, handle[i], I915_MADV_DONTNEED);
265 
266 	/* Try to allocate one more object */
267 	new_handle = gem_create_stolen(fd, SIZE);
268 
269 	/* Check if the retained object's memory contents are intact */
270 	for (i = 0; i < obj_count; i++) {
271 		retained = gem_madvise(fd, handle[i], I915_MADV_WILLNEED);
272 		if (retained) {
273 			bo = gem_handle_to_libdrm_bo(bufmgr, fd,
274 						     "verify_bo", handle[i]);
275 			igt_assert(bo != NULL);
276 
277 			_ret = drm_intel_gem_bo_map_gtt(bo);
278 			igt_assert(!_ret);
279 
280 			virt = bo->virtual;
281 			for (j = 0; j < SIZE/DWORD_SIZE; j++)
282 				igt_assert_eq(virt[j], DATA);
283 
284 			drm_intel_bo_unmap(bo);
285 			drm_intel_bo_unreference(bo);
286 		}
287 	}
288 
289 	gem_close(fd, new_handle);
290 	for (i = 0; i < obj_count; i++)
291 		gem_close(fd, handle[i]);
292 }
293 
stolen_hibernate(int fd)294 static void stolen_hibernate(int fd)
295 {
296 	drm_intel_bo *bo;
297 	drm_intel_bo *src, *dest;
298 	int obj_count = 0, i = 0;
299 	int ret, j;
300 	uint32_t handle[MAX_OBJECTS], src_handle;
301 	uint32_t *virt;
302 
303 	gem_require_stolen_support(fd);
304 
305 	src_handle = gem_create(fd, SIZE);
306 	src = gem_handle_to_libdrm_bo(bufmgr, fd,
307 				     "bo", src_handle);
308 	igt_assert(src != NULL);
309 
310 	ret = drm_intel_gem_bo_map_gtt(src);
311 	igt_assert_eq(ret, 0);
312 
313 	virt = src->virtual;
314 	for (j = 0; j < SIZE/DWORD_SIZE; j++) {
315 		igt_assert_eq(virt[j], 0);
316 		virt[j] = j;
317 	}
318 
319 	drm_intel_bo_unmap(src);
320 	/* Exhaust Stolen space */
321 	for (i = 0; i < MAX_OBJECTS; i++) {
322 		handle[i] = __gem_create_stolen(fd, SIZE);
323 		if (!handle[i])
324 			break;
325 
326 		bo = gem_handle_to_libdrm_bo(bufmgr, fd,
327 					     "verify_bo", handle[i]);
328 		igt_assert(bo != NULL);
329 		ret = drm_intel_gem_bo_map_gtt(bo);
330 		igt_assert_eq(ret, 0);
331 
332 		virt = bo->virtual;
333 		for (j = 0; j < SIZE/DWORD_SIZE; j++)
334 			igt_assert_eq(virt[j], 0);
335 
336 		drm_intel_bo_unmap(bo);
337 		drm_intel_bo_unreference(bo);
338 
339 		obj_count++;
340 	}
341 
342 	/* Assert if atleast one object is allocated from stolen, that
343 	 * is good enough to verify the content preservation across
344 	 * hibernation.
345 	 */
346 	igt_assert(obj_count > 0);
347 
348 	/* Copy data to all stolen backed objects */
349 	for (i = 0; i < obj_count; i++) {
350 		dest = gem_handle_to_libdrm_bo(bufmgr, fd,
351 					       "dst_bo", handle[i]);
352 		igt_assert(dest != NULL);
353 		/* Copy contents to stolen backed objects via blt and
354 		 * verify post-hibernation, this also helps in identifying
355 		 * that the operation was completed before going to
356 		 * hibernation.
357 		 */
358 		intel_copy_bo(batch, dest, src, SIZE);
359 	}
360 
361 	drm_intel_bo_unreference(src);
362 
363 	igt_system_suspend_autoresume(SUSPEND_STATE_DISK, SUSPEND_TEST_NONE);
364 	/* Check if the object's memory contents are intact
365 	 * across hibernation.
366 	 */
367 	for (i = 0; i < obj_count; i++) {
368 		bo = gem_handle_to_libdrm_bo(bufmgr, fd,
369 					     "verify_bo", handle[i]);
370 		igt_assert(bo != NULL);
371 		ret = drm_intel_gem_bo_map_gtt(bo);
372 		igt_assert_eq(ret, 0);
373 		virt = bo->virtual;
374 		for (j = 0; j < SIZE/DWORD_SIZE; j++)
375 			igt_assert_eq(virt[j], j);
376 
377 		drm_intel_bo_unmap(bo);
378 		drm_intel_bo_unreference(bo);
379 	}
380 
381 	gem_close(fd, src_handle);
382 	for (i = 0; i < obj_count; i++)
383 		gem_close(fd, handle[i]);
384 }
385 
386 static void
stolen_no_mmap(int fd)387 stolen_no_mmap(int fd)
388 {
389 	void *addr;
390 	uint32_t handle = 0;
391 
392 	gem_require_stolen_support(fd);
393 
394 	handle = gem_create_stolen(fd, SIZE);
395 
396 	addr = __gem_mmap__cpu(fd, handle, 0, SIZE, PROT_READ | PROT_WRITE);
397 	igt_assert(addr == NULL);
398 
399 	gem_close(fd, handle);
400 }
401 
402 igt_main
403 {
404 	int fd;
405 	uint32_t devid;
406 
407 	igt_skip_on_simulation();
408 
409 	igt_fixture {
410 		fd = drm_open_driver(DRIVER_INTEL);
411 		devid = intel_get_drm_devid(fd);
412 
413 		bufmgr = drm_intel_bufmgr_gem_init(fd, 4096);
414 		batch = intel_batchbuffer_alloc(bufmgr, devid);
415 	}
416 
417 	igt_subtest("stolen-clear")
418 		verify_object_clear(fd);
419 
420 	/*
421 	 * stolen mem special cases - checking for non cpu mappable
422 	 */
423 	igt_subtest("stolen-no-mmap")
424 		stolen_no_mmap(fd);
425 
426 	/* checking for pread/pwrite interfaces */
427 	igt_subtest("stolen-pwrite")
428 		stolen_pwrite(fd);
429 
430 	igt_subtest("stolen-pread")
431 		stolen_pread(fd);
432 
433 	/* Functional Test - blt copy */
434 	igt_subtest("stolen-copy")
435 		copy_test(fd);
436 
437 	igt_subtest("large-object-alloc")
438 		stolen_large_obj_alloc(fd);
439 
440 	/* Filling stolen completely and marking all the objects
441 	 * purgeable. Then trying to add one more object, to verify
442 	 * the purging logic.
443 	 * Again marking all objects WILLNEED and verifying the
444 	 * contents of the retained objects.
445 	 */
446 	igt_subtest("stolen-fill-purge")
447 		stolen_fill_purge_test(fd);
448 
449 	igt_subtest("stolen-hibernate")
450 		stolen_hibernate(fd);
451 
452 	igt_fixture {
453 		intel_batchbuffer_free(batch);
454 		drm_intel_bufmgr_destroy(bufmgr);
455 	}
456 }
457