diff options
Diffstat (limited to 'tests/prime_self_import.c')
-rw-r--r-- | tests/prime_self_import.c | 362 |
1 files changed, 349 insertions, 13 deletions
diff --git a/tests/prime_self_import.c b/tests/prime_self_import.c index 111ed4da..67fd5217 100644 --- a/tests/prime_self_import.c +++ b/tests/prime_self_import.c @@ -1,5 +1,5 @@ /* - * Copyright © 2012 Intel Corporation + * Copyright © 2012-2013 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -31,51 +31,143 @@ * ... but with different fds, i.e. the wayland usecase. */ +#define _GNU_SOURCE #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> -#include <assert.h> #include <fcntl.h> #include <inttypes.h> #include <errno.h> #include <sys/stat.h> -#include <sys/mman.h> #include <sys/ioctl.h> +#include <pthread.h> + #include "drm.h" -#include "i915_drm.h" +#include "ioctl_wrappers.h" #include "drmtest.h" +#include "igt_debugfs.h" #define BO_SIZE (16*1024) +static char counter; +volatile int pls_die = 0; + static void check_bo(int fd1, uint32_t handle1, int fd2, uint32_t handle2) { char *ptr1, *ptr2; - static char counter = 0; int i; ptr1 = gem_mmap(fd1, handle1, BO_SIZE, PROT_READ | PROT_WRITE); ptr2 = gem_mmap(fd2, handle2, BO_SIZE, PROT_READ | PROT_WRITE); - assert(ptr1); + igt_assert(ptr1); /* check whether it's still our old object first. */ for (i = 0; i < BO_SIZE; i++) { - assert(ptr1[i] == counter); - assert(ptr2[i] == counter); + igt_assert(ptr1[i] == counter); + igt_assert(ptr2[i] == counter); } counter++; memset(ptr1, counter, BO_SIZE); - assert(memcmp(ptr1, ptr2, BO_SIZE) == 0); + igt_assert(memcmp(ptr1, ptr2, BO_SIZE) == 0); munmap(ptr1, BO_SIZE); munmap(ptr2, BO_SIZE); } -int main(int argc, char **argv) +static void test_with_fd_dup(void) +{ + int fd1, fd2; + uint32_t handle, handle_import; + int dma_buf_fd1, dma_buf_fd2; + + counter = 0; + + fd1 = drm_open_any(); + fd2 = drm_open_any(); + + handle = gem_create(fd1, BO_SIZE); + + dma_buf_fd1 = prime_handle_to_fd(fd1, handle); + gem_close(fd1, handle); + + dma_buf_fd2 = dup(dma_buf_fd1); + close(dma_buf_fd1); + handle_import = prime_fd_to_handle(fd2, dma_buf_fd2); + check_bo(fd2, handle_import, fd2, handle_import); + + close(dma_buf_fd2); + check_bo(fd2, handle_import, fd2, handle_import); + + close(fd1); + close(fd2); +} + +static void test_with_two_bos(void) +{ + int fd1, fd2; + uint32_t handle1, handle2, handle_import; + int dma_buf_fd; + + counter = 0; + + fd1 = drm_open_any(); + fd2 = drm_open_any(); + + handle1 = gem_create(fd1, BO_SIZE); + handle2 = gem_create(fd1, BO_SIZE); + + dma_buf_fd = prime_handle_to_fd(fd1, handle1); + handle_import = prime_fd_to_handle(fd2, dma_buf_fd); + + close(dma_buf_fd); + gem_close(fd1, handle1); + + dma_buf_fd = prime_handle_to_fd(fd1, handle2); + handle_import = prime_fd_to_handle(fd2, dma_buf_fd); + check_bo(fd1, handle2, fd2, handle_import); + + gem_close(fd1, handle2); + close(dma_buf_fd); + + check_bo(fd2, handle_import, fd2, handle_import); + + close(fd1); + close(fd2); +} + +static void test_with_one_bo_two_files(void) +{ + int fd1, fd2; + uint32_t handle_import, handle_open, handle_orig, flink_name; + int dma_buf_fd1, dma_buf_fd2; + + fd1 = drm_open_any(); + fd2 = drm_open_any(); + + handle_orig = gem_create(fd1, BO_SIZE); + dma_buf_fd1 = prime_handle_to_fd(fd1, handle_orig); + + flink_name = gem_flink(fd1, handle_orig); + handle_open = gem_open(fd2, flink_name); + + dma_buf_fd2 = prime_handle_to_fd(fd2, handle_open); + handle_import = prime_fd_to_handle(fd2, dma_buf_fd2); + + /* dma-buf selfimporting an flink bo should give the same handle */ + igt_assert(handle_import == handle_open); + + close(fd1); + close(fd2); + close(dma_buf_fd1); + close(dma_buf_fd2); +} + +static void test_with_one_bo(void) { int fd1, fd2; uint32_t handle, handle_import1, handle_import2, handle_selfimport; @@ -94,11 +186,11 @@ int main(int argc, char **argv) /* reimport should give us the same handle so that userspace can check * whether it has that bo already somewhere. */ handle_import2 = prime_fd_to_handle(fd2, dma_buf_fd); - assert(handle_import1 == handle_import2); + igt_assert(handle_import1 == handle_import2); /* Same for re-importing on the exporting fd. */ handle_selfimport = prime_fd_to_handle(fd1, dma_buf_fd); - assert(handle == handle_selfimport); + igt_assert(handle == handle_selfimport); /* close dma_buf, check whether nothing disappears. */ close(dma_buf_fd); @@ -118,6 +210,250 @@ int main(int argc, char **argv) /* Completely rip out exporting fd. */ close(fd1); check_bo(fd2, handle_import1, fd2, handle_import1); +} + +static int get_object_count(void) +{ + FILE *file; + int ret, scanned; + int device = drm_get_card(); + char *path; + + igt_drop_caches_set(DROP_RETIRE); + + ret = asprintf(&path, "/sys/kernel/debug/dri/%d/i915_gem_objects", device); + igt_assert(ret != -1); + + file = fopen(path, "r"); + + scanned = fscanf(file, "%i objects", &ret); + igt_assert(scanned == 1); + + return ret; +} + +static void *thread_fn_reimport_vs_close(void *p) +{ + struct drm_gem_close close_bo; + int *fds = p; + int fd = fds[0]; + int dma_buf_fd = fds[1]; + uint32_t handle; + + while (!pls_die) { + handle = prime_fd_to_handle(fd, dma_buf_fd); + + close_bo.handle = handle; + ioctl(fd, DRM_IOCTL_GEM_CLOSE, &close_bo); + } + + return (void *)0; +} + +static void test_reimport_close_race(void) +{ + pthread_t *threads; + int r, i, num_threads; + int fds[2]; + int obj_count; + void *status; + uint32_t handle; + int fake; + + /* Allocate exit handler fds in here so that we dont screw + * up the counts */ + fake = drm_open_any(); + + obj_count = get_object_count(); + + num_threads = sysconf(_SC_NPROCESSORS_ONLN); + + threads = calloc(num_threads, sizeof(pthread_t)); + + fds[0] = drm_open_any(); + igt_assert(fds[0] >= 0); - return 0; + handle = gem_create(fds[0], BO_SIZE); + + fds[1] = prime_handle_to_fd(fds[0], handle); + + for (i = 0; i < num_threads; i++) { + r = pthread_create(&threads[i], NULL, + thread_fn_reimport_vs_close, + (void *)(uintptr_t)fds); + igt_assert(r == 0); + } + + sleep(5); + + pls_die = 1; + + for (i = 0; i < num_threads; i++) { + pthread_join(threads[i], &status); + igt_assert(status == 0); + } + + close(fds[0]); + close(fds[1]); + + obj_count = get_object_count() - obj_count; + + igt_info("leaked %i objects\n", obj_count); + + close(fake); + + igt_assert(obj_count == 0); +} + +static void *thread_fn_export_vs_close(void *p) +{ + struct drm_prime_handle prime_h2f; + struct drm_gem_close close_bo; + int fd = (uintptr_t)p; + uint32_t handle; + + while (!pls_die) { + /* We want to race gem close against prime export on handle one.*/ + handle = gem_create(fd, 4096); + if (handle != 1) + gem_close(fd, handle); + + /* raw ioctl since we expect this to fail */ + + /* WTF: for gem_flink_race I've unconditionally used handle == 1 + * here, but with prime it seems to help a _lot_ to use + * something more random. */ + prime_h2f.handle = 1; + prime_h2f.flags = DRM_CLOEXEC; + prime_h2f.fd = -1; + + ioctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &prime_h2f); + + close_bo.handle = 1; + ioctl(fd, DRM_IOCTL_GEM_CLOSE, &close_bo); + + close(prime_h2f.fd); + } + + return (void *)0; +} + +static void test_export_close_race(void) +{ + pthread_t *threads; + int r, i, num_threads; + int fd; + int obj_count; + void *status; + + obj_count = get_object_count(); + + num_threads = sysconf(_SC_NPROCESSORS_ONLN); + + threads = calloc(num_threads, sizeof(pthread_t)); + + fd = drm_open_any(); + igt_assert(fd >= 0); + + for (i = 0; i < num_threads; i++) { + r = pthread_create(&threads[i], NULL, + thread_fn_export_vs_close, + (void *)(uintptr_t)fd); + igt_assert(r == 0); + } + + sleep(5); + + pls_die = 1; + + for (i = 0; i < num_threads; i++) { + pthread_join(threads[i], &status); + igt_assert(status == 0); + } + + close(fd); + + obj_count = get_object_count() - obj_count; + + igt_info("leaked %i objects\n", obj_count); + igt_assert(obj_count == 0); +} + +static void test_llseek_size(void) +{ + int fd, i; + uint32_t handle; + int dma_buf_fd; + + counter = 0; + + fd = drm_open_any(); + + + for (i = 0; i < 10; i++) { + int bufsz = 4096 << i; + + handle = gem_create(fd, bufsz); + dma_buf_fd = prime_handle_to_fd(fd, handle); + + gem_close(fd, handle); + + igt_assert(prime_get_size(dma_buf_fd) == bufsz); + + close(dma_buf_fd); + } + + close(fd); +} + +static void test_llseek_bad(void) +{ + int fd; + uint32_t handle; + int dma_buf_fd; + + counter = 0; + + fd = drm_open_any(); + + + handle = gem_create(fd, BO_SIZE); + dma_buf_fd = prime_handle_to_fd(fd, handle); + + gem_close(fd, handle); + + igt_require(lseek(dma_buf_fd, 0, SEEK_END) >= 0); + + igt_assert(lseek(dma_buf_fd, -1, SEEK_END) == -1 && errno == EINVAL); + igt_assert(lseek(dma_buf_fd, 1, SEEK_SET) == -1 && errno == EINVAL); + igt_assert(lseek(dma_buf_fd, BO_SIZE, SEEK_SET) == -1 && errno == EINVAL); + igt_assert(lseek(dma_buf_fd, BO_SIZE + 1, SEEK_SET) == -1 && errno == EINVAL); + igt_assert(lseek(dma_buf_fd, BO_SIZE - 1, SEEK_SET) == -1 && errno == EINVAL); + + close(dma_buf_fd); + + close(fd); +} + +igt_main +{ + struct { + const char *name; + void (*fn)(void); + } tests[] = { + { "with_one_bo", test_with_one_bo }, + { "with_one_bo_two_files", test_with_one_bo_two_files }, + { "with_two_bos", test_with_two_bos }, + { "with_fd_dup", test_with_fd_dup }, + { "export-vs-gem_close-race", test_export_close_race }, + { "reimport-vs-gem_close-race", test_reimport_close_race }, + { "llseek-size", test_llseek_size }, + { "llseek-bad", test_llseek_bad }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + igt_subtest(tests[i].name) + tests[i].fn(); + } } |