diff options
author | Sean Callanan <spyffe@google.com> | 2019-11-27 16:34:50 -0800 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-11-27 16:34:50 -0800 |
commit | 7553c470d4d3cf7568ca545096a355380ece9609 (patch) | |
tree | 850a6f81ff1d27569736452389a2e9a4fb1b9093 | |
parent | d0e779f49ce24dfd11f02da456f56c95cd730784 (diff) | |
parent | 2db11e1cbb37315375fd7cc70365f5297cd39580 (diff) | |
download | platform_external_igt-gpu-tools-7553c470d4d3cf7568ca545096a355380ece9609.tar.gz platform_external_igt-gpu-tools-7553c470d4d3cf7568ca545096a355380ece9609.tar.bz2 platform_external_igt-gpu-tools-7553c470d4d3cf7568ca545096a355380ece9609.zip |
igt-gpu-tools: add tests for ion/gem interaction
am: 2db11e1cbb
Change-Id: Ie6504fae45f3050bf600df8455ba3055920a9845
-rw-r--r-- | Android.bp | 13 | ||||
-rw-r--r-- | lib/gem.c | 119 | ||||
-rw-r--r-- | lib/gem.h | 85 | ||||
-rw-r--r-- | lib/gem_msm.c | 68 | ||||
-rw-r--r-- | lib/gem_msm.h | 8 | ||||
-rw-r--r-- | lib/ion.c | 180 | ||||
-rw-r--r-- | lib/ion.h | 108 | ||||
-rw-r--r-- | tests/ion_fb.c | 223 |
8 files changed, 802 insertions, 2 deletions
@@ -10,7 +10,7 @@ cc_defaults { "-DHAVE_LIBGEN_H", "-DHAVE_MEMFD_CREATE", ], - static_libs: ["libelf", "libkmod"], + static_libs: ["libelf", "libkmod", "libion", "liblog"], shared_libs: ["libdrm"], } @@ -19,6 +19,8 @@ cc_library_static { defaults: ["igt-gpu-tools-defaults"], srcs: [ "lib/drmtest.c", + "lib/gem.c", + "lib/gem_msm.c", "lib/igt_aux.c", "lib/igt_core.c", "lib/igt_debugfs.c", @@ -29,11 +31,13 @@ cc_library_static { "lib/igt_kms.c", "lib/igt_stats.c", "lib/igt_sysfs.c", + "lib/ion.c", "lib/ioctl_wrappers.c", "lib/i915/gem_mman.c", - "lib/uwildmat/uwildmat.c" + "lib/uwildmat/uwildmat.c", ], export_include_dirs: [ + "include", "lib", "lib/stubs/drm", "prebuilt-intermediates", @@ -58,3 +62,8 @@ cc_test { srcs: ["tests/kms_flip.c"], } +cc_test { + name: "ion_fb", + defaults: ["igt-gpu-tools-test-defaults"], + srcs: ["tests/ion_fb.c"], +} diff --git a/lib/gem.c b/lib/gem.c new file mode 100644 index 00000000..1f401929 --- /dev/null +++ b/lib/gem.c @@ -0,0 +1,119 @@ +#include "gem.h" + +#include "gem_msm.h" + +struct gem_driver_lookup +{ + const char name[16]; + struct gem_driver *driver; +}; + +static struct gem_driver_lookup drivers[] = { + { + .name = "msm_drm", + .driver = &gem_msm_driver + } +}; + +static inline size_t num_drivers() +{ + return ARRAY_SIZE(drivers); +} + +struct gem_driver *gem_get_driver(int drm_fd) +{ + char name[16] = {0}; + drm_version_t version = {}; + + version.name_len = sizeof(name); + version.name = name; + + if (drmIoctl(drm_fd, DRM_IOCTL_VERSION, &version)) + { + return NULL; + } + + for (struct gem_driver_lookup *di = drivers; + di - drivers < num_drivers(); + ++di) + { + if (!strncmp(name, di->name, sizeof(name))) + { + return di->driver; + } + } + + return NULL; +} + +int gem_size(int drm_fd, size_t *size, uint32_t gem_handle) +{ + struct drm_gem_flink drm_gem_flink_arg = { + .handle = gem_handle, + .name = 0 + }; + + if (drmIoctl(drm_fd, + DRM_IOCTL_GEM_FLINK, + &drm_gem_flink_arg)) + { + return -1; + } + + struct drm_gem_open drm_gem_open_arg = { + .name = drm_gem_flink_arg.name, + .handle = 0, + .size = 0 + }; + + if (drmIoctl(drm_fd, + DRM_IOCTL_GEM_OPEN, + &drm_gem_open_arg)) + { + return -1; + } + + *size = drm_gem_open_arg.size; + + if (drm_gem_open_arg.handle != gem_handle) + { + gem_release_handle(drm_fd, drm_gem_open_arg.handle); + } + + return 0; +} + +void gem_release_handle(int drm_fd, uint32_t gem_handle) +{ + struct drm_gem_close drm_gem_close_arg = { + .handle = gem_handle, + .pad = 0 + }; + + drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &drm_gem_close_arg); +} + +int drm_fb_for_gem_handle(int drm_fd, uint32_t *fb_id, uint32_t gem_handle, + const struct fb_configuration *fb_config) +{ + struct drm_mode_fb_cmd2 drm_mode_addfb2_arg = { + .fb_id = 0, + .width = fb_config->width, + .height = fb_config->height, + .pixel_format = fb_config->pixel_format, + .flags = DRM_MODE_FB_MODIFIERS, + .handles = { gem_handle, 0, 0, 0 }, + .pitches = { fb_config->width * fb_config->pixel_size, 0, 0, 0 }, + .offsets = { 0, 0, 0, 0 }, + .modifier = { 0 } + }; + + if (drmIoctl(drm_fd, DRM_IOCTL_MODE_ADDFB2, &drm_mode_addfb2_arg)) + { + return -1; + } + + *fb_id = drm_mode_addfb2_arg.fb_id; + + return 0; +} diff --git a/lib/gem.h b/lib/gem.h new file mode 100644 index 00000000..790d9adb --- /dev/null +++ b/lib/gem.h @@ -0,0 +1,85 @@ +#ifndef GEM_H +#define GEM_H + +#include "igt.h" + +struct gem_driver +{ + /** + * mmap: + * @ptr: (out) pointer to the buffer in the user process's memory + * @drm_fd: open DRM device fd + * @gem_handle: GEM handle + * @size: exact size of the GEM buffer + * + * Maps the buffer backing the GEM handle for reading and writing. + * + * Returns: 0 on success, -1 otherwise + **/ + int (*mmap)(void **ptr, int drm_fd, uint32_t gem_handle, size_t size); + + /** + * munmap: + * @drm_fd: open DRM device fd + * @gem_handle: GEM handle + * @ptr: pointer (see ptr argument to mmap) + * @size: exact size of the mapped area + * + * Unmaps a region previously mapped with mmap. + * + * Returns: 0 on success: -1 otherwise + **/ + int (*munmap)(int drm_fd, uint32_t gem_handle, void *ptr, size_t size); +}; + +/** + * gem_get_driver: + * @drm_fd: open DRM device fd + * + * Gets the driver-specific GEM APIs for a particular device. + * + * Returns: a struct with function pointers on success; NULL otherwise + **/ +struct gem_driver *gem_get_driver(int drm_fd); + +/** + * gem_size: + * @drm_fd: open DRM device fd + * @size: (out) size of the buffer + * @gem_handle: GEM handle + * Returns: size of the buffer backing the GEM handle. + **/ +int gem_size(int drm_fd, size_t *size, uint32_t gem_handle); + +/** + * gem_release_handle + * @drm_fd: open DRM device fd + * @gem_handle: GEM handle + * + * Releases a GEM handle. + **/ +void gem_release_handle(int drm_fd, uint32_t gem_handle); + +struct fb_configuration +{ + uint32_t width; + uint32_t height; + uint32_t pixel_format; + uint32_t pixel_size; +}; + +/** + * drm_fb_for_gem_handle + * @drm_fd: open DRM device fd + * @fb_id: (out) id of the DRM KMS fb + * @gem_handle: GEM handle + * @fb_config: metadata for the fb + * + * Converts a GEM buffer into a DRM KMS fb + * + * Returns: 0 if the buffer could be converted; -1 otherwise + **/ +int drm_fb_for_gem_handle(int drm_fd, uint32_t *fb_id, uint32_t gem_handle, + const struct fb_configuration *fb_config); + +#endif diff --git a/lib/gem_msm.c b/lib/gem_msm.c new file mode 100644 index 00000000..2160f443 --- /dev/null +++ b/lib/gem_msm.c @@ -0,0 +1,68 @@ +#include "gem_msm.h" +#include "drm-uapi/msm_drm.h" + +#include "drm.h" + +static int gem_msm_mmap(void **ptr, int drm_fd, uint32_t gem_handle, size_t size) +{ + struct drm_msm_gem_cpu_prep gem_prep = { + .handle = gem_handle, + .op = MSM_PREP_READ, + .timeout = { .tv_sec = 1, .tv_nsec = 0 } + }; + + if (drmIoctl(drm_fd, DRM_IOCTL_MSM_GEM_CPU_PREP, &gem_prep)) + { + return -1; + } + + struct drm_msm_gem_info gem_info = { + .handle = gem_handle, + .flags = 0, + .offset = 0 + }; + + if (drmIoctl(drm_fd, DRM_IOCTL_MSM_GEM_INFO, &gem_info)) + { + return -1; + } + + void *const k_addr = 0; + const int k_prot = PROT_READ | PROT_WRITE; + const int k_flags = MAP_SHARED; + + void *ret = mmap(k_addr, size, k_prot, k_flags, drm_fd, gem_info.offset); + + if (ret == MAP_FAILED) + { + return -1; + } + + *ptr = ret; + + return 0; +} + +static int gem_msm_munmap(int drm_fd, uint32_t gem_handle, void *ptr, size_t size) +{ + if (munmap(ptr, size)) + { + return -1; + } + + struct drm_msm_gem_cpu_fini gem_fini = { + .handle = gem_handle + }; + + if (drmIoctl(drm_fd, DRM_IOCTL_MSM_GEM_CPU_FINI, &gem_fini)) + { + return -1; + } + + return 0; +} + +struct gem_driver gem_msm_driver = { + .mmap = gem_msm_mmap, + .munmap = gem_msm_munmap +}; diff --git a/lib/gem_msm.h b/lib/gem_msm.h new file mode 100644 index 00000000..4b9724bc --- /dev/null +++ b/lib/gem_msm.h @@ -0,0 +1,8 @@ +#ifndef GEM_MSM_H +#define GEM_MSM_H + +#include "gem.h" + +extern struct gem_driver gem_msm_driver; + +#endif diff --git a/lib/ion.c b/lib/ion.c new file mode 100644 index 00000000..59f7b3b8 --- /dev/null +++ b/lib/ion.c @@ -0,0 +1,180 @@ +#include <ion/ion.h> +#include <linux/ion_4.12.h> + +#include "ion.h" + +int ion_get_heap_id(int ion_fd, uint32_t heap_type) +{ + int heap_count = 0; + int ret = -1; + + if (ion_query_heap_cnt(ion_fd, &heap_count)) + { + return -1; + } + + struct ion_heap_data *heap_data = + malloc(heap_count * sizeof(struct ion_heap_data)); + + if (ion_query_get_heaps(ion_fd, heap_count, heap_data)) + { + free(heap_data); + return -1; + } + + for (struct ion_heap_data *hi = heap_data; + (hi - heap_data) < heap_count; + ++hi) + { + if (hi->type == heap_type) + { + ret = hi->heap_id; + break; + } + } + + free(heap_data); + + return ret; +} + +static const int kBitsInAnInt = (sizeof(int) * 8); + +int ion_alloc_one_fd(int ion_fd, size_t size, int heap_id, int *ion_buffer_fd) +{ + if (heap_id < 0 || heap_id >= kBitsInAnInt) + { + return -1; + } + + const int align = 0; + const int heap_mask = 1 << heap_id; + const int flags = 0; + + return ion_alloc_fd(ion_fd, size, align, heap_mask, flags, ion_buffer_fd); +} + +int ion_mmap(void **ptr, int ion_buffer_fd, size_t size) +{ + void *const k_addr = 0; + const int k_prot = PROT_READ | PROT_WRITE; + const int k_flags = MAP_SHARED; + + void *ret = mmap(k_addr, size, k_prot, k_flags, ion_buffer_fd, 0); + + if (ret == MAP_FAILED) + { + return -1; + } + + *ptr = ret; + return 0; +} + +int ion_munmap(void *ptr, size_t size) +{ + if (munmap(ptr, size)) + { + return -1; + } + + return 0; +} + +int drm_check_prime_caps(int drm_fd) +{ + struct drm_get_cap drm_get_cap_arg = { + .capability = DRM_CAP_PRIME, + .value = 0 + }; + + if (drmIoctl(drm_fd, DRM_IOCTL_GET_CAP, &drm_get_cap_arg)) + { + return -1; + } + + if (!(drm_get_cap_arg.value & DRM_PRIME_CAP_IMPORT) || + !(drm_get_cap_arg.value & DRM_PRIME_CAP_EXPORT)) + { + return -1; + } + + return 0; +} + +int gem_handle_for_ion_buffer(int drm_fd, uint32_t *gem_handle, int ion_buffer_fd) +{ + struct drm_prime_handle drm_prime_fd_to_handle_arg = { + .handle = 0, + .flags = 0, + .fd = ion_buffer_fd + }; + + if (drmIoctl(drm_fd, + DRM_IOCTL_PRIME_FD_TO_HANDLE, + &drm_prime_fd_to_handle_arg)) + { + return -1; + } + + *gem_handle = drm_prime_fd_to_handle_arg.handle; + + return 0; +} + +int ion_fd_for_gem_handle(int drm_fd, int *ion_fd, uint32_t gem_handle) +{ + struct drm_prime_handle drm_prime_handle_to_fd_arg = { + .handle = gem_handle, + .flags = 0, + .fd = 0 + }; + + if (drmIoctl(drm_fd, + DRM_IOCTL_PRIME_HANDLE_TO_FD, + &drm_prime_handle_to_fd_arg)) + { + return -1; + } + + *ion_fd = drm_prime_handle_to_fd_arg.fd; + + return 0; +} + +int drm_fb_for_ion_buffer(int drm_fd, uint32_t *fb_id, int ion_buffer_fd, + const struct fb_configuration *fb_config) +{ + uint32_t gem_handle = 0; + + if (gem_handle_for_ion_buffer(drm_fd, &gem_handle, ion_buffer_fd)) + { + return -1; + } + + int ret = drm_fb_for_gem_handle(drm_fd, fb_id, gem_handle, fb_config); + + gem_release_handle(drm_fd, gem_handle); + return ret; +} + +void drm_release_fb(int drm_fd, uint32_t fb_id) +{ + (void)drmIoctl(drm_fd, DRM_IOCTL_MODE_RMFB, &fb_id); +} + +int ion_clone_fd_via_gem(int drm_fd, int *cloned_fd, int ion_buffer_fd) +{ + uint32_t gem_handle = 0; + if (gem_handle_for_ion_buffer(drm_fd, &gem_handle, ion_buffer_fd)) + { + return -1; + } + + int ret = ion_fd_for_gem_handle(drm_fd, cloned_fd, gem_handle); + gem_release_handle(drm_fd, gem_handle); + + return ret; +} + + diff --git a/lib/ion.h b/lib/ion.h new file mode 100644 index 00000000..c598b968 --- /dev/null +++ b/lib/ion.h @@ -0,0 +1,108 @@ +#ifndef ION_GEM_H +#define ION_GEM_H + +#include "igt.h" +#include "gem.h" + +/** + * ion_get_heap_id: + * @ion_fd: open ion device fd + * @heap_type: ION_HEAP_TYPE_* constant + * Returns: the index of the first heap with type matching heap_type, or -1 on + * failure + **/ +int ion_get_heap_id(int ion_fd, uint32_t heap_type); + +/** + * ion_alloc_one_fd + * @ion_fd: open ion device fd + * @size: size of the desired ion buffer + * @heap_id: index of the heap to allocate from + * @ion_buffer_fd: (out) ion buffer fd + * Returns: 0 on success; not 0 otherwise + **/ +int ion_alloc_one_fd(int ion_fd, size_t size, int heap_id, int *ion_buffer_fd); + +/** + * ion_mmap + * @ptr: (out) pointer to the buffer in the user process's memory + * @ion_buffer_fd: ion buffer fd + * @size: size of the desired mapping + * Returns: 0 on success; not 0 otherwise + **/ +int ion_mmap(void **ptr, int ion_buffer_fd, size_t size); + +/** + * ion_munmap + * @ptr: pointer to the buffer in the user process's memory + * @size: exact size of the mapping + * Returns: 0 on success; not 0 otherwise + **/ +int ion_munmap(void *ptr, size_t size); + +/** + * drm_check_prime_caps + * drm_fd: open DRM device fd + * Returns: 0 if the device supports Prime import/export; -1 otherwise + **/ +int drm_check_prime_caps(int drm_fd); + +/** + * gem_handle_for_ion_buffer + * drm_fd: open DRM device fd + * gem_handle: (out) GEM handle + * ion_fd: ion buffer fd + * + * Imports an ion buffer into GEM + * + * Returns: 0 if the ion buffer could be imported; -1 otherwise + **/ +int gem_handle_for_ion_buffer(int drm_fd, uint32_t *gem_handle, int ion_buffer_fd); + +/** + * ion_fd_for_gem_handle + * drm_fd: open DRM device fd + * ion_fd: ion buffer fd + * + * Exports a GEM buffer into ion + * + * Returns: 0 if the buffer could be exported; -1 otherwise + **/ +int ion_fd_for_gem_handle(int drm_fd, int *ion_bufferfd, uint32_t gem_handle); + +/** + * drm_fb_for_ion_buffer + * drm_fd: open DRM device fd + * fb_id: (out) id of the DRM KMS fb + * ion_fd: ion buffer fd + * fb_config: metadata for the fb + * + * Converts an ion buffer into a DRM KMS fb + * + * Returns: 0 if the buffer could be exported; -1 otherwise + **/ +int drm_fb_for_ion_buffer(int drm_fd, uint32_t *fb_id, int ion_buffer_fd, + const struct fb_configuration *fb_config); + +/** + * drm_release_fb + * drm_fd: open DRM device fd + * fb_id: id of the DRM KMS fb + * + * Releases the DRM KMS fb + **/ +void drm_release_fb(int drm_fd, uint32_t fb_id); + +/** + * ion_clone_fd_via_gem + * drm_fd: open DRM device fd + * cloned_fd: (out) cloned buffer fd + * ion_fd: ion buffer fd + * + * Uses GEM to clone an ion fd by importing and re-exporting it. + * + * Returns: 0 if the buffer could be cloned; -1 otherwise + **/ +int ion_clone_fd_via_gem(int drm_fd, int *cloned_fd, int ion_buffer_fd); + +#endif diff --git a/tests/ion_fb.c b/tests/ion_fb.c new file mode 100644 index 00000000..b8b86161 --- /dev/null +++ b/tests/ion_fb.c @@ -0,0 +1,223 @@ +#include "drm.h" +#include "gem.h" +#include "igt.h" +#include "ion.h" + +#include <ion/ion.h> + +size_t size_for_fb(const struct fb_configuration *config) +{ + return config->width * config->height * config->pixel_size; +} + +/** + * test_make_fb + * + * Tests that an ion buffer can be ingested into DRM to the point + * where it can be used for a framebuffer + **/ + +static void make_fb_with_buffer(int drm_fd, int ion_fd, + const struct fb_configuration *config, + int ion_buffer_fd) +{ + uint32_t fb_id = 0; + + igt_assert_eq(0, drm_check_prime_caps(drm_fd)); + igt_assert_eq(0, drm_fb_for_ion_buffer( + drm_fd, &fb_id, ion_buffer_fd, config)); + drm_release_fb(drm_fd, fb_id); +} + +static void make_fb_with_fds(int drm_fd, int ion_fd, + const struct fb_configuration *config) +{ + int ion_buffer_fd; + + const int heap_id = ion_get_heap_id(ion_fd, ION_HEAP_TYPE_SYSTEM); + igt_assert(heap_id != -1); + + igt_assert(!ion_alloc_one_fd( + ion_fd, + size_for_fb(config), + heap_id, + &ion_buffer_fd)); + + make_fb_with_buffer(drm_fd, ion_fd, config, ion_buffer_fd); + + close(ion_buffer_fd); +} + +static void test_make_fb(const struct fb_configuration *config) +{ + const int drm_fd = drm_open_driver(DRIVER_ANY); + igt_assert(drm_fd >= 0); + + const int ion_fd = ion_open(); + igt_assert(ion_fd >= 0); + + make_fb_with_fds(drm_fd, ion_fd, config); + + ion_close(ion_fd); + close(drm_fd); +} + +/** + * test_clone + * + * Tests that an ion buffer can be 'cloned' by making a GEM buffer out of + * it and then reversing the process + **/ + +static void clone_with_fds(int drm_fd, int ion_fd, + const struct fb_configuration *config) +{ + int ion_buffer_fd; + + const int heap_id = ion_get_heap_id(ion_fd, ION_HEAP_TYPE_SYSTEM); + igt_assert(heap_id != -1); + + igt_assert(!ion_alloc_one_fd( + ion_fd, + size_for_fb(config), + heap_id, + &ion_buffer_fd)); + + int clone_fd = 0; + + igt_assert(!ion_clone_fd_via_gem(drm_fd, &clone_fd, ion_buffer_fd)); + + igt_assert(clone_fd >= 0); + igt_assert(clone_fd != ion_buffer_fd); + + close(clone_fd); + close(ion_buffer_fd); +} + +static void test_clone(const struct fb_configuration *config) +{ + const int drm_fd = drm_open_driver(DRIVER_ANY); + igt_assert(drm_fd >= 0); + + const int ion_fd = ion_open(); + igt_assert(ion_fd >= 0); + + clone_with_fds(drm_fd, ion_fd, config); + + ion_close(ion_fd); + close(drm_fd); +} + +/** + * test_mmap + * + * Tests that the GEM version of an ion buffer contains the same data that + * the original ion buffer did + **/ + +static void mmap_with_buffer(int drm_fd, int ion_fd, + uint8_t *buffer, size_t size) +{ + int ion_buffer_fd; + + const int heap_id = ion_get_heap_id(ion_fd, ION_HEAP_TYPE_SYSTEM); + igt_assert(heap_id != -1); + + const struct gem_driver *gem = gem_get_driver(drm_fd); + igt_assert(gem != NULL); + + igt_assert(!ion_alloc_one_fd( + ion_fd, + size, + heap_id, + &ion_buffer_fd)); + + void *ion_ptr = NULL; + + igt_assert(!ion_mmap(&ion_ptr, ion_buffer_fd, size)); + + memcpy(buffer, ion_ptr, size); + + igt_assert(!ion_munmap(ion_ptr, size)); + + uint32_t gem_handle = 0; + + igt_assert(!gem_handle_for_ion_buffer( + drm_fd, + &gem_handle, + ion_buffer_fd)); + + close(ion_buffer_fd); + + size_t gem_buf_size = 0; + + igt_assert(!gem_size(drm_fd, &gem_buf_size, gem_handle)); + igt_assert_eq(gem_buf_size, size); + + void *gem_ptr = NULL; + igt_assert(!gem->mmap( + &gem_ptr, + drm_fd, + gem_handle, + size)); + + igt_assert(!memcmp(buffer, gem_ptr, size)); + + igt_assert(!gem->munmap( + drm_fd, + gem_handle, + gem_ptr, + size)); + + gem_release_handle(drm_fd, gem_handle); +} + +static void mmap_with_fds(int drm_fd, int ion_fd, + const struct fb_configuration *config) +{ + uint8_t *buffer = malloc(size_for_fb(config)); + igt_assert(buffer); + + mmap_with_buffer(drm_fd, ion_fd, buffer, size_for_fb(config)); + + free(buffer); +} + +static void test_mmap(const struct fb_configuration *config) +{ + const int drm_fd = drm_open_driver(DRIVER_ANY); + igt_assert(drm_fd >= 0); + + const int ion_fd = ion_open(); + igt_assert(ion_fd >= 0); + + mmap_with_fds(drm_fd, ion_fd, config); + + ion_close(ion_fd); + close(drm_fd); +} + +igt_main +{ + const struct fb_configuration config = { + .width = 1024, + .height = 1024, + .pixel_format = DRM_FORMAT_ABGR8888, + .pixel_size = 4 + }; + + igt_subtest("make-fb") + { + test_make_fb(&config); + } + + igt_subtest("clone") + { + test_clone(&config); + } + + igt_subtest("mmap") + { + test_mmap(&config); + } +} |