| /* |
| * Copyright © 2016 Intel Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| */ |
| |
| #include "igt.h" |
| #include "igt_vgem.h" |
| |
| #include <sys/ioctl.h> |
| #include <sys/poll.h> |
| #include <signal.h> |
| #include <time.h> |
| |
| IGT_TEST_DESCRIPTION("Basic check of polling for prime/vgem fences."); |
| |
| static void test_read(int vgem, int i915) |
| { |
| struct vgem_bo scratch; |
| uint32_t handle; |
| uint32_t *ptr; |
| int dmabuf, i; |
| |
| scratch.width = 1024; |
| scratch.height = 1024; |
| scratch.bpp = 32; |
| vgem_create(vgem, &scratch); |
| |
| dmabuf = prime_handle_to_fd(vgem, scratch.handle); |
| handle = prime_fd_to_handle(i915, dmabuf); |
| close(dmabuf); |
| |
| ptr = vgem_mmap(vgem, &scratch, PROT_WRITE); |
| for (i = 0; i < 1024; i++) |
| ptr[1024*i] = i; |
| munmap(ptr, scratch.size); |
| gem_close(vgem, scratch.handle); |
| |
| for (i = 0; i < 1024; i++) { |
| uint32_t tmp; |
| gem_read(i915, handle, 4096*i, &tmp, sizeof(tmp)); |
| igt_assert_eq(tmp, i); |
| } |
| gem_close(i915, handle); |
| } |
| |
| static void test_fence_read(int i915, int vgem) |
| { |
| struct vgem_bo scratch; |
| uint32_t handle; |
| uint32_t *ptr; |
| uint32_t fence; |
| int dmabuf, i; |
| int master[2], slave[2]; |
| |
| igt_assert(pipe(master) == 0); |
| igt_assert(pipe(slave) == 0); |
| |
| scratch.width = 1024; |
| scratch.height = 1024; |
| scratch.bpp = 32; |
| vgem_create(vgem, &scratch); |
| |
| dmabuf = prime_handle_to_fd(vgem, scratch.handle); |
| handle = prime_fd_to_handle(i915, dmabuf); |
| close(dmabuf); |
| |
| igt_fork(child, 1) { |
| for (i = 0; i < 1024; i++) { |
| uint32_t tmp; |
| gem_read(i915, handle, 4096*i, &tmp, sizeof(tmp)); |
| igt_assert_eq(tmp, 0); |
| } |
| write(master[1], &child, sizeof(child)); |
| read(slave[0], &child, sizeof(child)); |
| for (i = 0; i < 1024; i++) { |
| uint32_t tmp; |
| gem_read(i915, handle, 4096*i, &tmp, sizeof(tmp)); |
| igt_assert_eq(tmp, i); |
| } |
| gem_close(i915, handle); |
| } |
| |
| read(master[0], &i, sizeof(i)); |
| fence = vgem_fence_attach(vgem, &scratch, VGEM_FENCE_WRITE); |
| write(slave[1], &i, sizeof(i)); |
| |
| ptr = vgem_mmap(vgem, &scratch, PROT_WRITE); |
| for (i = 0; i < 1024; i++) |
| ptr[1024*i] = i; |
| munmap(ptr, scratch.size); |
| vgem_fence_signal(vgem, fence); |
| gem_close(vgem, scratch.handle); |
| |
| igt_waitchildren(); |
| close(master[0]); |
| close(master[1]); |
| close(slave[0]); |
| close(slave[1]); |
| } |
| |
| static void test_fence_mmap(int i915, int vgem) |
| { |
| struct vgem_bo scratch; |
| uint32_t handle; |
| uint32_t *ptr; |
| uint32_t fence; |
| int dmabuf, i; |
| int master[2], slave[2]; |
| |
| igt_assert(pipe(master) == 0); |
| igt_assert(pipe(slave) == 0); |
| |
| scratch.width = 1024; |
| scratch.height = 1024; |
| scratch.bpp = 32; |
| vgem_create(vgem, &scratch); |
| |
| dmabuf = prime_handle_to_fd(vgem, scratch.handle); |
| handle = prime_fd_to_handle(i915, dmabuf); |
| close(dmabuf); |
| |
| igt_fork(child, 1) { |
| ptr = gem_mmap__gtt(i915, handle, 4096*1024, PROT_READ); |
| |
| gem_set_domain(i915, handle, I915_GEM_DOMAIN_GTT, 0); |
| for (i = 0; i < 1024; i++) |
| igt_assert_eq(ptr[1024*i], 0); |
| |
| write(master[1], &child, sizeof(child)); |
| read(slave[0], &child, sizeof(child)); |
| |
| gem_set_domain(i915, handle, I915_GEM_DOMAIN_GTT, 0); |
| for (i = 0; i < 1024; i++) |
| igt_assert_eq(ptr[1024*i], i); |
| |
| gem_close(i915, handle); |
| } |
| |
| read(master[0], &i, sizeof(i)); |
| fence = vgem_fence_attach(vgem, &scratch, VGEM_FENCE_WRITE); |
| write(slave[1], &i, sizeof(i)); |
| |
| ptr = vgem_mmap(vgem, &scratch, PROT_WRITE); |
| for (i = 0; i < 1024; i++) |
| ptr[1024*i] = i; |
| munmap(ptr, scratch.size); |
| vgem_fence_signal(vgem, fence); |
| gem_close(vgem, scratch.handle); |
| |
| igt_waitchildren(); |
| close(master[0]); |
| close(master[1]); |
| close(slave[0]); |
| close(slave[1]); |
| } |
| |
| static void test_write(int vgem, int i915) |
| { |
| struct vgem_bo scratch; |
| uint32_t handle; |
| uint32_t *ptr; |
| int dmabuf, i; |
| |
| scratch.width = 1024; |
| scratch.height = 1024; |
| scratch.bpp = 32; |
| vgem_create(vgem, &scratch); |
| |
| dmabuf = prime_handle_to_fd(vgem, scratch.handle); |
| handle = prime_fd_to_handle(i915, dmabuf); |
| close(dmabuf); |
| |
| ptr = vgem_mmap(vgem, &scratch, PROT_READ); |
| gem_close(vgem, scratch.handle); |
| |
| for (i = 0; i < 1024; i++) |
| gem_write(i915, handle, 4096*i, &i, sizeof(i)); |
| gem_close(i915, handle); |
| |
| for (i = 0; i < 1024; i++) |
| igt_assert_eq(ptr[1024*i], i); |
| munmap(ptr, scratch.size); |
| } |
| |
| static void test_gtt(int vgem, int i915) |
| { |
| struct vgem_bo scratch; |
| uint32_t handle; |
| uint32_t *ptr; |
| int dmabuf, i; |
| |
| scratch.width = 1024; |
| scratch.height = 1024; |
| scratch.bpp = 32; |
| vgem_create(vgem, &scratch); |
| |
| dmabuf = prime_handle_to_fd(vgem, scratch.handle); |
| handle = prime_fd_to_handle(i915, dmabuf); |
| close(dmabuf); |
| |
| ptr = gem_mmap__gtt(i915, handle, scratch.size, PROT_WRITE); |
| for (i = 0; i < 1024; i++) |
| ptr[1024*i] = i; |
| munmap(ptr, scratch.size); |
| |
| ptr = vgem_mmap(vgem, &scratch, PROT_READ | PROT_WRITE); |
| for (i = 0; i < 1024; i++) { |
| igt_assert_eq(ptr[1024*i], i); |
| ptr[1024*i] = ~i; |
| } |
| munmap(ptr, scratch.size); |
| |
| ptr = gem_mmap__gtt(i915, handle, scratch.size, PROT_READ); |
| for (i = 0; i < 1024; i++) |
| igt_assert_eq(ptr[1024*i], ~i); |
| munmap(ptr, scratch.size); |
| |
| gem_close(i915, handle); |
| gem_close(vgem, scratch.handle); |
| } |
| |
| static void test_shrink(int vgem, int i915) |
| { |
| struct vgem_bo scratch = { |
| .width = 1024, |
| .height = 1024, |
| .bpp = 32 |
| }; |
| int dmabuf; |
| |
| vgem_create(vgem, &scratch); |
| |
| dmabuf = prime_handle_to_fd(vgem, scratch.handle); |
| gem_close(vgem, scratch.handle); |
| |
| scratch.handle = prime_fd_to_handle(i915, dmabuf); |
| close(dmabuf); |
| |
| /* Populate the i915_bo->pages. */ |
| gem_set_domain(i915, scratch.handle, I915_GEM_DOMAIN_GTT, 0); |
| |
| /* Now evict them, establishing the link from i915:shrinker to vgem. */ |
| igt_drop_caches_set(i915, DROP_SHRINK_ALL); |
| |
| gem_close(i915, scratch.handle); |
| } |
| |
| static bool is_coherent(int i915) |
| { |
| int val = 1; /* by default, we assume GTT is coherent, hence the test */ |
| struct drm_i915_getparam gp = { |
| gp.param = 52, /* GTT_COHERENT */ |
| gp.value = &val, |
| }; |
| |
| ioctl(i915, DRM_IOCTL_I915_GETPARAM, &gp); |
| return val; |
| } |
| |
| static void test_gtt_interleaved(int vgem, int i915) |
| { |
| struct vgem_bo scratch; |
| uint32_t handle; |
| uint32_t *ptr, *gtt; |
| int dmabuf, i; |
| |
| igt_require(is_coherent(i915)); |
| |
| scratch.width = 1024; |
| scratch.height = 1024; |
| scratch.bpp = 32; |
| vgem_create(vgem, &scratch); |
| |
| dmabuf = prime_handle_to_fd(vgem, scratch.handle); |
| handle = prime_fd_to_handle(i915, dmabuf); |
| close(dmabuf); |
| |
| /* This assumes that GTT is perfectedly coherent. On certain machines, |
| * it is possible for a direct acces to bypass the GTT indirection. |
| * |
| * This test may fail. It tells us how far userspace can trust |
| * concurrent dmabuf/i915 access. In the future, we may have a kernel |
| * param to indicate whether or not this interleaving is possible. |
| * However, the mmaps may be passed around to third parties that do |
| * not know about the shortcommings... |
| */ |
| ptr = vgem_mmap(vgem, &scratch, PROT_WRITE); |
| gtt = gem_mmap__gtt(i915, handle, scratch.size, PROT_WRITE); |
| for (i = 0; i < 1024; i++) { |
| gtt[1024*i] = i; |
| /* The read from WC should act as a flush for the GTT wcb */ |
| igt_assert_eq(ptr[1024*i], i); |
| |
| ptr[1024*i] = ~i; |
| /* The read from GTT should act as a flush for the WC wcb */ |
| igt_assert_eq(gtt[1024*i], ~i); |
| } |
| munmap(gtt, scratch.size); |
| munmap(ptr, scratch.size); |
| |
| gem_close(i915, handle); |
| gem_close(vgem, scratch.handle); |
| } |
| |
| static bool prime_busy(int fd, bool excl) |
| { |
| struct pollfd pfd = { .fd = fd, .events = excl ? POLLOUT : POLLIN }; |
| return poll(&pfd, 1, 0) == 0; |
| } |
| |
| static void work(int i915, int dmabuf, unsigned ring, uint32_t flags) |
| { |
| const int SCRATCH = 0; |
| const int BATCH = 1; |
| const int gen = intel_gen(intel_get_drm_devid(i915)); |
| struct drm_i915_gem_exec_object2 obj[2]; |
| struct drm_i915_gem_relocation_entry store[1024+1]; |
| struct drm_i915_gem_execbuffer2 execbuf; |
| unsigned size = ALIGN(ARRAY_SIZE(store)*16 + 4, 4096); |
| bool read_busy, write_busy; |
| uint32_t *batch, *bbe; |
| int i, count; |
| |
| memset(&execbuf, 0, sizeof(execbuf)); |
| execbuf.buffers_ptr = (uintptr_t)obj; |
| execbuf.buffer_count = 2; |
| execbuf.flags = ring | flags; |
| if (gen < 6) |
| execbuf.flags |= I915_EXEC_SECURE; |
| |
| memset(obj, 0, sizeof(obj)); |
| obj[SCRATCH].handle = prime_fd_to_handle(i915, dmabuf); |
| |
| obj[BATCH].handle = gem_create(i915, size); |
| obj[BATCH].relocs_ptr = (uintptr_t)store; |
| obj[BATCH].relocation_count = ARRAY_SIZE(store); |
| memset(store, 0, sizeof(store)); |
| |
| batch = gem_mmap__wc(i915, obj[BATCH].handle, 0, size, PROT_WRITE); |
| gem_set_domain(i915, obj[BATCH].handle, |
| I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT); |
| |
| i = 0; |
| for (count = 0; count < 1024; count++) { |
| store[count].target_handle = obj[SCRATCH].handle; |
| store[count].presumed_offset = -1; |
| store[count].offset = sizeof(uint32_t) * (i + 1); |
| store[count].delta = sizeof(uint32_t) * count; |
| store[count].read_domains = I915_GEM_DOMAIN_INSTRUCTION; |
| store[count].write_domain = I915_GEM_DOMAIN_INSTRUCTION; |
| batch[i] = MI_STORE_DWORD_IMM | (gen < 6 ? 1 << 22 : 0); |
| if (gen >= 8) { |
| batch[++i] = 0; |
| batch[++i] = 0; |
| } else if (gen >= 4) { |
| batch[++i] = 0; |
| batch[++i] = 0; |
| store[count].offset += sizeof(uint32_t); |
| } else { |
| batch[i]--; |
| batch[++i] = 0; |
| } |
| batch[++i] = count; |
| i++; |
| } |
| |
| bbe = &batch[i]; |
| store[count].target_handle = obj[BATCH].handle; /* recurse */ |
| store[count].presumed_offset = 0; |
| store[count].offset = sizeof(uint32_t) * (i + 1); |
| store[count].delta = 0; |
| store[count].read_domains = I915_GEM_DOMAIN_COMMAND; |
| store[count].write_domain = 0; |
| batch[i] = MI_BATCH_BUFFER_START; |
| if (gen >= 8) { |
| batch[i] |= 1 << 8 | 1; |
| batch[++i] = 0; |
| batch[++i] = 0; |
| } else if (gen >= 6) { |
| batch[i] |= 1 << 8; |
| batch[++i] = 0; |
| } else { |
| batch[i] |= 2 << 6; |
| batch[++i] = 0; |
| if (gen < 4) { |
| batch[i] |= 1; |
| store[count].delta = 1; |
| } |
| } |
| i++; |
| igt_assert(i < size/sizeof(*batch)); |
| igt_require(__gem_execbuf(i915, &execbuf) == 0); |
| gem_close(i915, obj[BATCH].handle); |
| gem_close(i915, obj[SCRATCH].handle); |
| |
| write_busy = prime_busy(dmabuf, false); |
| read_busy = prime_busy(dmabuf, true); |
| |
| *bbe = MI_BATCH_BUFFER_END; |
| __sync_synchronize(); |
| munmap(batch, size); |
| |
| igt_assert(read_busy && write_busy); |
| } |
| |
| static void test_busy(int i915, int vgem, unsigned ring, uint32_t flags) |
| { |
| struct vgem_bo scratch; |
| struct timespec tv; |
| uint32_t *ptr; |
| int dmabuf; |
| int i; |
| |
| scratch.width = 1024; |
| scratch.height = 1; |
| scratch.bpp = 32; |
| vgem_create(vgem, &scratch); |
| dmabuf = prime_handle_to_fd(vgem, scratch.handle); |
| |
| work(i915, dmabuf, ring, flags); |
| |
| /* Calling busy in a loop should be enough to flush the rendering */ |
| memset(&tv, 0, sizeof(tv)); |
| while (prime_busy(dmabuf, false)) |
| igt_assert(igt_seconds_elapsed(&tv) < 10); |
| |
| ptr = vgem_mmap(vgem, &scratch, PROT_READ); |
| for (i = 0; i < 1024; i++) |
| igt_assert_eq_u32(ptr[i], i); |
| munmap(ptr, 4096); |
| |
| gem_close(vgem, scratch.handle); |
| close(dmabuf); |
| } |
| |
| static void test_wait(int i915, int vgem, unsigned ring, uint32_t flags) |
| { |
| struct vgem_bo scratch; |
| struct pollfd pfd; |
| uint32_t *ptr; |
| int i; |
| |
| scratch.width = 1024; |
| scratch.height = 1; |
| scratch.bpp = 32; |
| vgem_create(vgem, &scratch); |
| pfd.fd = prime_handle_to_fd(vgem, scratch.handle); |
| |
| work(i915, pfd.fd, ring, flags); |
| |
| pfd.events = POLLIN; |
| igt_assert_eq(poll(&pfd, 1, 10000), 1); |
| |
| ptr = vgem_mmap(vgem, &scratch, PROT_READ); |
| for (i = 0; i < 1024; i++) |
| igt_assert_eq_u32(ptr[i], i); |
| munmap(ptr, 4096); |
| |
| gem_close(vgem, scratch.handle); |
| close(pfd.fd); |
| } |
| |
| static void test_sync(int i915, int vgem, unsigned ring, uint32_t flags) |
| { |
| struct vgem_bo scratch; |
| uint32_t *ptr; |
| int dmabuf; |
| int i; |
| |
| scratch.width = 1024; |
| scratch.height = 1; |
| scratch.bpp = 32; |
| vgem_create(vgem, &scratch); |
| dmabuf = prime_handle_to_fd(vgem, scratch.handle); |
| |
| ptr = mmap(NULL, scratch.size, PROT_READ, MAP_SHARED, dmabuf, 0); |
| igt_assert(ptr != MAP_FAILED); |
| gem_close(vgem, scratch.handle); |
| |
| work(i915, dmabuf, ring, flags); |
| |
| prime_sync_start(dmabuf, false); |
| for (i = 0; i < 1024; i++) |
| igt_assert_eq_u32(ptr[i], i); |
| |
| prime_sync_end(dmabuf, false); |
| close(dmabuf); |
| |
| munmap(ptr, scratch.size); |
| } |
| |
| static void test_fence_wait(int i915, int vgem, unsigned ring, unsigned flags) |
| { |
| struct vgem_bo scratch; |
| uint32_t fence; |
| uint32_t *ptr; |
| int dmabuf; |
| |
| scratch.width = 1024; |
| scratch.height = 1; |
| scratch.bpp = 32; |
| vgem_create(vgem, &scratch); |
| |
| dmabuf = prime_handle_to_fd(vgem, scratch.handle); |
| fence = vgem_fence_attach(vgem, &scratch, VGEM_FENCE_WRITE); |
| igt_assert(prime_busy(dmabuf, false)); |
| gem_close(vgem, scratch.handle); |
| |
| ptr = mmap(NULL, scratch.size, PROT_READ, MAP_SHARED, dmabuf, 0); |
| igt_assert(ptr != MAP_FAILED); |
| |
| igt_fork(child, 1) |
| work(i915, dmabuf, ring, flags); |
| |
| sleep(1); |
| |
| /* Check for invalidly completing the task early */ |
| for (int i = 0; i < 1024; i++) |
| igt_assert_eq_u32(ptr[i], 0); |
| |
| igt_assert(prime_busy(dmabuf, false)); |
| vgem_fence_signal(vgem, fence); |
| igt_waitchildren(); |
| |
| /* But after signaling and waiting, it should be done */ |
| prime_sync_start(dmabuf, false); |
| for (int i = 0; i < 1024; i++) |
| igt_assert_eq_u32(ptr[i], i); |
| prime_sync_end(dmabuf, false); |
| |
| close(dmabuf); |
| |
| munmap(ptr, scratch.size); |
| } |
| |
| static void test_fence_hang(int i915, int vgem, unsigned flags) |
| { |
| struct vgem_bo scratch; |
| uint32_t *ptr; |
| int dmabuf; |
| int i; |
| |
| scratch.width = 1024; |
| scratch.height = 1; |
| scratch.bpp = 32; |
| vgem_create(vgem, &scratch); |
| dmabuf = prime_handle_to_fd(vgem, scratch.handle); |
| vgem_fence_attach(vgem, &scratch, flags | WIP_VGEM_FENCE_NOTIMEOUT); |
| |
| ptr = mmap(NULL, scratch.size, PROT_READ, MAP_SHARED, dmabuf, 0); |
| igt_assert(ptr != MAP_FAILED); |
| gem_close(vgem, scratch.handle); |
| |
| work(i915, dmabuf, I915_EXEC_DEFAULT, 0); |
| |
| /* The work should have been cancelled */ |
| |
| prime_sync_start(dmabuf, false); |
| for (i = 0; i < 1024; i++) |
| igt_assert_eq_u32(ptr[i], 0); |
| prime_sync_end(dmabuf, false); |
| close(dmabuf); |
| |
| munmap(ptr, scratch.size); |
| } |
| |
| static bool has_prime_export(int fd) |
| { |
| uint64_t value; |
| |
| if (drmGetCap(fd, DRM_CAP_PRIME, &value)) |
| return false; |
| |
| return value & DRM_PRIME_CAP_EXPORT; |
| } |
| |
| static bool has_prime_import(int fd) |
| { |
| uint64_t value; |
| |
| if (drmGetCap(fd, DRM_CAP_PRIME, &value)) |
| return false; |
| |
| return value & DRM_PRIME_CAP_IMPORT; |
| } |
| |
| static uint32_t set_fb_on_crtc(int fd, int pipe, struct vgem_bo *bo, uint32_t fb_id) |
| { |
| drmModeRes *resources = drmModeGetResources(fd); |
| struct drm_mode_modeinfo *modes = malloc(4096*sizeof(*modes)); |
| uint32_t encoders[32]; |
| |
| for (int o = 0; o < resources->count_connectors; o++) { |
| struct drm_mode_get_connector conn; |
| struct drm_mode_crtc set; |
| int e, m; |
| |
| memset(&conn, 0, sizeof(conn)); |
| conn.connector_id = resources->connectors[o]; |
| drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn); |
| if (!conn.count_modes) |
| continue; |
| |
| igt_assert(conn.count_modes <= 4096); |
| igt_assert(conn.count_encoders <= 32); |
| |
| conn.modes_ptr = (uintptr_t)modes; |
| conn.encoders_ptr = (uintptr_t)encoders; |
| conn.count_props = 0; |
| do_or_die(drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn)); |
| |
| for (e = 0; e < conn.count_encoders; e++) { |
| struct drm_mode_get_encoder enc; |
| |
| memset(&enc, 0, sizeof(enc)); |
| enc.encoder_id = encoders[e]; |
| drmIoctl(fd, DRM_IOCTL_MODE_GETENCODER, &enc); |
| if (enc.possible_crtcs & (1 << pipe)) |
| break; |
| } |
| if (e == conn.count_encoders) |
| continue; |
| |
| for (m = 0; m < conn.count_modes; m++) { |
| if (modes[m].hdisplay <= bo->width && |
| modes[m].vdisplay <= bo->height) |
| break; |
| } |
| if (m == conn.count_modes) |
| continue; |
| |
| memset(&set, 0, sizeof(set)); |
| set.crtc_id = resources->crtcs[pipe]; |
| set.fb_id = fb_id; |
| set.set_connectors_ptr = (uintptr_t)&conn.connector_id; |
| set.count_connectors = 1; |
| set.mode = modes[m]; |
| set.mode_valid = 1; |
| if (drmIoctl(fd, DRM_IOCTL_MODE_SETCRTC, &set) == 0) { |
| drmModeFreeResources(resources); |
| return set.crtc_id; |
| } |
| } |
| |
| drmModeFreeResources(resources); |
| return 0; |
| } |
| |
| static inline uint32_t pipe_select(int pipe) |
| { |
| if (pipe > 1) |
| return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT; |
| else if (pipe > 0) |
| return DRM_VBLANK_SECONDARY; |
| else |
| return 0; |
| } |
| |
| static unsigned get_vblank(int fd, int pipe, unsigned flags) |
| { |
| union drm_wait_vblank vbl; |
| |
| memset(&vbl, 0, sizeof(vbl)); |
| vbl.request.type = DRM_VBLANK_RELATIVE | pipe_select(pipe) | flags; |
| if (drmIoctl(fd, DRM_IOCTL_WAIT_VBLANK, &vbl)) |
| return 0; |
| |
| return vbl.reply.sequence; |
| } |
| |
| static void sighandler(int sig) |
| { |
| } |
| |
| static void flip_to_vgem(int i915, int vgem, |
| struct vgem_bo *bo, |
| uint32_t fb_id, |
| uint32_t crtc_id, |
| unsigned hang, |
| const char *name) |
| { |
| const struct timespec tv = { 1, 0 }; |
| struct pollfd pfd = { i915, POLLIN }; |
| struct drm_event_vblank vbl; |
| uint32_t fence; |
| |
| fence = vgem_fence_attach(vgem, bo, VGEM_FENCE_WRITE | hang); |
| |
| igt_fork(child, 1) { |
| do_or_die(drmModePageFlip(i915, crtc_id, fb_id, |
| DRM_MODE_PAGE_FLIP_EVENT, &fb_id)); |
| kill(getppid(), SIGHUP); |
| |
| /* Check we don't flip before the fence is ready */ |
| for (int n = 0; n < 5; n++) { |
| igt_assert_f(poll(&pfd, 1, 0) == 0, |
| "flip to %s completed whilst busy\n", |
| name); |
| get_vblank(i915, 0, DRM_VBLANK_NEXTONMISS); |
| } |
| } |
| |
| igt_assert_f(nanosleep(&tv, NULL) == -1, |
| "flip to busy %s blocked\n", name); |
| |
| /* And then the flip is completed as soon as it is ready */ |
| if (!hang) { |
| union drm_wait_vblank wait; |
| |
| memset(&wait, 0, sizeof(wait)); |
| wait.request.type = DRM_VBLANK_RELATIVE | pipe_select(0); |
| wait.request.sequence = 10; |
| do_or_die(drmIoctl(i915, DRM_IOCTL_WAIT_VBLANK, &wait)); |
| |
| vgem_fence_signal(vgem, fence); |
| get_vblank(i915, 0, DRM_VBLANK_NEXTONMISS); |
| igt_assert_eq(poll(&pfd, 1, 0), 1); |
| } |
| /* Even if hung, the flip must complete *eventually* */ |
| igt_set_timeout(20, "Ignored hang"); /* XXX lower fail threshold? */ |
| igt_assert_eq(read(i915, &vbl, sizeof(vbl)), sizeof(vbl)); |
| igt_reset_timeout(); |
| |
| igt_waitchildren(); |
| } |
| |
| static void test_flip(int i915, int vgem, unsigned hang) |
| { |
| struct vgem_bo bo[2]; |
| uint32_t fb_id[2], handle[2], crtc_id; |
| |
| signal(SIGHUP, sighandler); |
| |
| for (int i = 0; i < 2; i++) { |
| int fd; |
| |
| bo[i].width = 1024; |
| bo[i].height = 768; |
| bo[i].bpp = 32; |
| vgem_create(vgem, &bo[i]); |
| |
| fd = prime_handle_to_fd(vgem, bo[i].handle); |
| handle[i] = prime_fd_to_handle(i915, fd); |
| igt_assert(handle[i]); |
| close(fd); |
| |
| do_or_die(__kms_addfb(i915, handle[i], |
| bo[i].width, bo[i].height, bo[i].pitch, |
| DRM_FORMAT_XRGB8888, I915_TILING_NONE, NULL, |
| LOCAL_DRM_MODE_FB_MODIFIERS, &fb_id[i])); |
| igt_assert(fb_id[i]); |
| } |
| |
| igt_require((crtc_id = set_fb_on_crtc(i915, 0, &bo[0], fb_id[0]))); |
| |
| /* Bind both fb for use by flipping */ |
| for (int i = 1; i >= 0; i--) { |
| struct drm_event_vblank vbl; |
| |
| do_or_die(drmModePageFlip(i915, crtc_id, fb_id[i], |
| DRM_MODE_PAGE_FLIP_EVENT, &fb_id[i])); |
| igt_assert_eq(read(i915, &vbl, sizeof(vbl)), sizeof(vbl)); |
| } |
| |
| /* Schedule a flip to wait upon the frontbuffer vgem being written */ |
| flip_to_vgem(i915, vgem, &bo[0], fb_id[0], crtc_id, hang, "front"); |
| |
| /* Schedule a flip to wait upon the backbuffer vgem being written */ |
| flip_to_vgem(i915, vgem, &bo[1], fb_id[1], crtc_id, hang, "back"); |
| |
| for (int i = 0; i < 2; i++) { |
| do_or_die(drmModeRmFB(i915, fb_id[i])); |
| gem_close(i915, handle[i]); |
| gem_close(vgem, bo[i].handle); |
| } |
| |
| signal(SIGHUP, SIG_DFL); |
| } |
| |
| igt_main |
| { |
| const struct intel_execution_engine *e; |
| int i915 = -1; |
| int vgem = -1; |
| |
| igt_fixture { |
| vgem = drm_open_driver(DRIVER_VGEM); |
| igt_require(has_prime_export(vgem)); |
| |
| i915 = drm_open_driver_master(DRIVER_INTEL); |
| igt_require_gem(i915); |
| igt_require(has_prime_import(i915)); |
| gem_require_mmap_wc(i915); |
| } |
| |
| igt_subtest("basic-read") |
| test_read(vgem, i915); |
| |
| igt_subtest("basic-write") |
| test_write(vgem, i915); |
| |
| igt_subtest("basic-gtt") |
| test_gtt(vgem, i915); |
| |
| igt_subtest("shrink") |
| test_shrink(vgem, i915); |
| |
| igt_subtest("coherency-gtt") |
| test_gtt_interleaved(vgem, i915); |
| |
| for (e = intel_execution_engines; e->name; e++) { |
| igt_subtest_f("%ssync-%s", |
| e->exec_id == 0 ? "basic-" : "", |
| e->name) { |
| gem_require_ring(i915, e->exec_id | e->flags); |
| igt_require(gem_can_store_dword(i915, e->exec_id) | e->flags); |
| |
| gem_quiescent_gpu(i915); |
| test_sync(i915, vgem, e->exec_id, e->flags); |
| } |
| } |
| |
| for (e = intel_execution_engines; e->name; e++) { |
| igt_subtest_f("%sbusy-%s", |
| e->exec_id == 0 ? "basic-" : "", |
| e->name) { |
| gem_require_ring(i915, e->exec_id | e->flags); |
| igt_require(gem_can_store_dword(i915, e->exec_id) | e->flags); |
| |
| gem_quiescent_gpu(i915); |
| test_busy(i915, vgem, e->exec_id, e->flags); |
| } |
| } |
| |
| for (e = intel_execution_engines; e->name; e++) { |
| igt_subtest_f("%swait-%s", |
| e->exec_id == 0 ? "basic-" : "", |
| e->name) { |
| gem_require_ring(i915, e->exec_id | e->flags); |
| igt_require(gem_can_store_dword(i915, e->exec_id) | e->flags); |
| |
| gem_quiescent_gpu(i915); |
| test_wait(i915, vgem, e->exec_id, e->flags); |
| } |
| } |
| |
| /* Fence testing */ |
| igt_subtest_group { |
| igt_fixture { |
| igt_require(vgem_has_fences(vgem)); |
| } |
| |
| igt_subtest("basic-fence-read") |
| test_fence_read(i915, vgem); |
| igt_subtest("basic-fence-mmap") |
| test_fence_mmap(i915, vgem); |
| |
| for (e = intel_execution_engines; e->name; e++) { |
| igt_subtest_f("%sfence-wait-%s", |
| e->exec_id == 0 ? "basic-" : "", |
| e->name) { |
| gem_require_ring(i915, e->exec_id | e->flags); |
| igt_require(gem_can_store_dword(i915, e->exec_id) | e->flags); |
| |
| gem_quiescent_gpu(i915); |
| test_fence_wait(i915, vgem, e->exec_id, e->flags); |
| } |
| } |
| |
| igt_subtest("basic-fence-flip") |
| test_flip(i915, vgem, 0); |
| |
| igt_subtest_group { |
| igt_fixture { |
| igt_require(vgem_fence_has_flag(vgem, WIP_VGEM_FENCE_NOTIMEOUT)); |
| } |
| |
| igt_subtest("fence-read-hang") |
| test_fence_hang(i915, vgem, 0); |
| igt_subtest("fence-write-hang") |
| test_fence_hang(i915, vgem, VGEM_FENCE_WRITE); |
| |
| igt_subtest("fence-flip-hang") |
| test_flip(i915, vgem, WIP_VGEM_FENCE_NOTIMEOUT); |
| } |
| } |
| |
| igt_fixture { |
| close(i915); |
| close(vgem); |
| } |
| } |