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.c
29 *
30 * This is a test for the extended and old gem_create ioctl, that
31 * includes allocation of object from stolen memory and shmem.
32 *
33 * The goal is to simply ensure that basics work and invalid input
34 * combinations 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 #include <pthread.h>
48 #include <stdatomic.h>
49
50 #include <drm.h>
51
52 #include "ioctl_wrappers.h"
53 #include "intel_bufmgr.h"
54 #include "intel_batchbuffer.h"
55 #include "intel_io.h"
56 #include "intel_chipset.h"
57 #include "igt_aux.h"
58 #include "drmtest.h"
59 #include "drm.h"
60 #include "i915_drm.h"
61
62 IGT_TEST_DESCRIPTION("This is a test for the extended & old gem_create ioctl,"
63 " that includes allocation of object from stolen memory"
64 " and shmem.");
65
66 #define CLEAR(s) memset(&s, 0, sizeof(s))
67 #define PAGE_SIZE 4096
68
69 struct local_i915_gem_create_v2 {
70 uint64_t size;
71 uint32_t handle;
72 uint32_t pad;
73 #define I915_CREATE_PLACEMENT_STOLEN (1<<0)
74 uint32_t flags;
75 } create_v2;
76
77 #define LOCAL_IOCTL_I915_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_CREATE, struct local_i915_gem_create_v2)
78
invalid_flag_test(int fd)79 static void invalid_flag_test(int fd)
80 {
81 int ret;
82
83 gem_require_stolen_support(fd);
84
85 create_v2.handle = 0;
86 create_v2.size = PAGE_SIZE;
87 create_v2.flags = ~I915_CREATE_PLACEMENT_STOLEN;
88 ret = drmIoctl(fd, LOCAL_IOCTL_I915_GEM_CREATE, &create_v2);
89
90 igt_assert(ret <= 0);
91
92 create_v2.flags = ~0;
93 ret = drmIoctl(fd, LOCAL_IOCTL_I915_GEM_CREATE, &create_v2);
94
95 igt_assert(ret <= 0);
96 }
97
create_ioctl(int fd,struct drm_i915_gem_create * create)98 static int create_ioctl(int fd, struct drm_i915_gem_create *create)
99 {
100 int err = 0;
101
102 if (igt_ioctl(fd, DRM_IOCTL_I915_GEM_CREATE, create)) {
103 err = -errno;
104 igt_assume(err != 0);
105 }
106
107 errno = 0;
108 return err;
109 }
110
invalid_size_test(int fd)111 static void invalid_size_test(int fd)
112 {
113 struct drm_i915_gem_create create = {
114 .size = 0,
115 };
116
117 igt_assert_eq(create_ioctl(fd, &create), -EINVAL);
118 }
119
120 /*
121 * Creating an object with non-aligned size and trying to access it with an
122 * offset, which is greater than the requested size but smaller than the
123 * object's last page boundary. pwrite here must be successful.
124 */
valid_nonaligned_size(int fd)125 static void valid_nonaligned_size(int fd)
126 {
127 struct drm_i915_gem_create create = {
128 .size = PAGE_SIZE / 2,
129 };
130 char buf[PAGE_SIZE];
131
132 igt_assert_eq(create_ioctl(fd, &create), 0);
133
134 gem_write(fd, create.handle, PAGE_SIZE / 2, buf, PAGE_SIZE / 2);
135
136 gem_close(fd, create.handle);
137 }
138
139 /*
140 * Creating an object with non-aligned size and trying to access it with an
141 * offset, which is greater than the requested size and larger than the
142 * object's last page boundary. pwrite here must fail.
143 */
invalid_nonaligned_size(int fd)144 static void invalid_nonaligned_size(int fd)
145 {
146 struct drm_i915_gem_create create = {
147 .size = PAGE_SIZE / 2,
148 };
149 char buf[PAGE_SIZE];
150
151 igt_assert_eq(create_ioctl(fd, &create), 0);
152
153 /* This should fail. Hence cannot use gem_write. */
154 igt_assert(__gem_write(fd, create.handle,
155 PAGE_SIZE / 2, buf, PAGE_SIZE));
156
157 gem_close(fd, create.handle);
158 }
159
atomic_compare_swap_u64(_Atomic (uint64_t)* ptr,uint64_t oldval,uint64_t newval)160 static uint64_t atomic_compare_swap_u64(_Atomic(uint64_t) *ptr,
161 uint64_t oldval, uint64_t newval)
162 {
163 atomic_compare_exchange_strong(ptr, &oldval, newval);
164 return oldval;
165 }
166
get_npages(_Atomic (uint64_t)* global,uint64_t npages)167 static uint64_t get_npages(_Atomic(uint64_t) *global, uint64_t npages)
168 {
169 uint64_t try, old, max;
170
171 max = *global;
172 do {
173 old = max;
174 try = 1 + npages % (max / 2);
175 max -= try;
176 } while ((max = atomic_compare_swap_u64(global, old, max)) != old);
177
178 return try;
179 }
180
181 struct thread_clear {
182 _Atomic(uint64_t) max;
183 int timeout;
184 int i915;
185 };
186
thread_clear(void * data)187 static void *thread_clear(void *data)
188 {
189 struct thread_clear *arg = data;
190 unsigned long checked = 0;
191 int i915 = arg->i915;
192
193 igt_until_timeout(arg->timeout) {
194 struct drm_i915_gem_create create = {};
195 uint64_t npages;
196
197 npages = random();
198 npages <<= 32;
199 npages |= random();
200 npages = get_npages(&arg->max, npages);
201 create.size = npages << 12;
202
203 create_ioctl(i915, &create);
204 for (uint64_t page = 0; page < npages; page++) {
205 uint64_t x;
206
207 gem_read(i915, create.handle,
208 page * 4096 + (page % (4096 - sizeof(x))),
209 &x, sizeof(x));
210 igt_assert_eq_u64(x, 0);
211 }
212 gem_close(i915, create.handle);
213 checked += npages;
214
215 atomic_fetch_add(&arg->max, npages);
216 }
217
218 return (void *)(uintptr_t)checked;
219 }
220
always_clear(int i915,int timeout)221 static void always_clear(int i915, int timeout)
222 {
223 struct thread_clear arg = {
224 .i915 = i915,
225 .timeout = timeout,
226 .max = intel_get_avail_ram_mb() << (20 - 12), /* in pages */
227 };
228 const int ncpus = sysconf(_SC_NPROCESSORS_ONLN);
229 unsigned long checked;
230 pthread_t thread[ncpus];
231 void *result;
232
233 for (int i = 0; i < ncpus; i++)
234 pthread_create(&thread[i], NULL, thread_clear, &arg);
235
236 checked = 0;
237 for (int i = 0; i < ncpus; i++) {
238 pthread_join(thread[i], &result);
239 checked += (uintptr_t)result;
240 }
241 igt_info("Checked %'lu page allocations\n", checked);
242 }
243
size_update(int fd)244 static void size_update(int fd)
245 {
246 int size_initial_nonaligned = 15;
247
248 struct drm_i915_gem_create create = {
249 .size = size_initial_nonaligned,
250 };
251
252 igt_assert_eq(create_ioctl(fd, &create), 0);
253 igt_assert_neq(create.size, size_initial_nonaligned);
254 }
255
256 igt_main
257 {
258 int fd = -1;
259
260 igt_skip_on_simulation();
261
262 igt_fixture {
263 fd = drm_open_driver(DRIVER_INTEL);
264 }
265
266 igt_subtest("stolen-invalid-flag")
267 invalid_flag_test(fd);
268
269 igt_subtest("create-invalid-size")
270 invalid_size_test(fd);
271
272 igt_subtest("create-valid-nonaligned")
273 valid_nonaligned_size(fd);
274
275 igt_subtest("create-invalid-nonaligned")
276 invalid_nonaligned_size(fd);
277
278 igt_subtest("create-size-update")
279 size_update(fd);
280
281 igt_subtest("create-clear")
282 always_clear(fd, 30);
283 }
284