Path: blob/master/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
26285 views
// SPDX-License-Identifier: GPL-2.012#include <dirent.h>3#include <errno.h>4#include <fcntl.h>5#include <stdio.h>6#include <stdlib.h>7#include <stdint.h>8#include <string.h>9#include <unistd.h>10#include <sys/ioctl.h>11#include <sys/mman.h>12#include <sys/types.h>1314#include <linux/dma-buf.h>15#include <linux/dma-heap.h>16#include <drm/drm.h>17#include "../kselftest.h"1819#define DEVPATH "/dev/dma_heap"2021static int check_vgem(int fd)22{23drm_version_t version = { 0 };24char name[5];25int ret;2627version.name_len = 4;28version.name = name;2930ret = ioctl(fd, DRM_IOCTL_VERSION, &version);31if (ret || version.name_len != 4)32return 0;3334name[4] = '\0';3536return !strcmp(name, "vgem");37}3839static int open_vgem(void)40{41int i, fd;42const char *drmstr = "/dev/dri/card";4344fd = -1;45for (i = 0; i < 16; i++) {46char name[80];4748snprintf(name, 80, "%s%u", drmstr, i);4950fd = open(name, O_RDWR);51if (fd < 0)52continue;5354if (!check_vgem(fd)) {55close(fd);56fd = -1;57continue;58} else {59break;60}61}62return fd;63}6465static int import_vgem_fd(int vgem_fd, int dma_buf_fd, uint32_t *handle)66{67struct drm_prime_handle import_handle = {68.fd = dma_buf_fd,69.flags = 0,70.handle = 0,71};72int ret;7374ret = ioctl(vgem_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &import_handle);75if (ret == 0)76*handle = import_handle.handle;77return ret;78}7980static void close_handle(int vgem_fd, uint32_t handle)81{82struct drm_gem_close close = {83.handle = handle,84};8586ioctl(vgem_fd, DRM_IOCTL_GEM_CLOSE, &close);87}8889static int dmabuf_heap_open(char *name)90{91int ret, fd;92char buf[256];9394ret = snprintf(buf, 256, "%s/%s", DEVPATH, name);95if (ret < 0)96ksft_exit_fail_msg("snprintf failed! %d\n", ret);9798fd = open(buf, O_RDWR);99if (fd < 0)100ksft_exit_fail_msg("open %s failed: %s\n", buf, strerror(errno));101102return fd;103}104105static int dmabuf_heap_alloc_fdflags(int fd, size_t len, unsigned int fd_flags,106unsigned int heap_flags, int *dmabuf_fd)107{108struct dma_heap_allocation_data data = {109.len = len,110.fd = 0,111.fd_flags = fd_flags,112.heap_flags = heap_flags,113};114int ret;115116if (!dmabuf_fd)117return -EINVAL;118119ret = ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &data);120if (ret < 0)121return ret;122*dmabuf_fd = (int)data.fd;123return ret;124}125126static int dmabuf_heap_alloc(int fd, size_t len, unsigned int flags,127int *dmabuf_fd)128{129return dmabuf_heap_alloc_fdflags(fd, len, O_RDWR | O_CLOEXEC, flags,130dmabuf_fd);131}132133static int dmabuf_sync(int fd, int start_stop)134{135struct dma_buf_sync sync = {136.flags = start_stop | DMA_BUF_SYNC_RW,137};138139return ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync);140}141142#define ONE_MEG (1024 * 1024)143144static void test_alloc_and_import(char *heap_name)145{146int heap_fd = -1, dmabuf_fd = -1, importer_fd = -1;147uint32_t handle = 0;148void *p = NULL;149int ret;150151heap_fd = dmabuf_heap_open(heap_name);152153ksft_print_msg("Testing allocation and importing:\n");154ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0, &dmabuf_fd);155if (ret) {156ksft_test_result_fail("FAIL (Allocation Failed!) %d\n", ret);157return;158}159160/* mmap and write a simple pattern */161p = mmap(NULL, ONE_MEG, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf_fd, 0);162if (p == MAP_FAILED) {163ksft_test_result_fail("FAIL (mmap() failed): %s\n", strerror(errno));164goto close_and_return;165}166167dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START);168memset(p, 1, ONE_MEG / 2);169memset((char *)p + ONE_MEG / 2, 0, ONE_MEG / 2);170dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END);171172importer_fd = open_vgem();173if (importer_fd < 0) {174ksft_test_result_skip("Could not open vgem %d\n", importer_fd);175} else {176ret = import_vgem_fd(importer_fd, dmabuf_fd, &handle);177ksft_test_result(ret >= 0, "Import buffer %d\n", ret);178}179180ret = dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START);181if (ret < 0) {182ksft_print_msg("FAIL (DMA_BUF_SYNC_START failed!) %d\n", ret);183goto out;184}185186memset(p, 0xff, ONE_MEG);187ret = dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END);188if (ret < 0) {189ksft_print_msg("FAIL (DMA_BUF_SYNC_END failed!) %d\n", ret);190goto out;191}192193close_handle(importer_fd, handle);194ksft_test_result_pass("%s dmabuf sync succeeded\n", __func__);195return;196197out:198ksft_test_result_fail("%s dmabuf sync failed\n", __func__);199munmap(p, ONE_MEG);200close(importer_fd);201202close_and_return:203close(dmabuf_fd);204close(heap_fd);205}206207static void test_alloc_zeroed(char *heap_name, size_t size)208{209int heap_fd = -1, dmabuf_fd[32];210int i, j, k, ret;211void *p = NULL;212char *c;213214ksft_print_msg("Testing alloced %ldk buffers are zeroed:\n", size / 1024);215heap_fd = dmabuf_heap_open(heap_name);216217/* Allocate and fill a bunch of buffers */218for (i = 0; i < 32; i++) {219ret = dmabuf_heap_alloc(heap_fd, size, 0, &dmabuf_fd[i]);220if (ret) {221ksft_test_result_fail("FAIL (Allocation (%i) failed) %d\n", i, ret);222goto close_and_return;223}224225/* mmap and fill with simple pattern */226p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf_fd[i], 0);227if (p == MAP_FAILED) {228ksft_test_result_fail("FAIL (mmap() failed!): %s\n", strerror(errno));229goto close_and_return;230}231232dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_START);233memset(p, 0xff, size);234dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_END);235munmap(p, size);236}237/* close them all */238for (i = 0; i < 32; i++)239close(dmabuf_fd[i]);240ksft_test_result_pass("Allocate and fill a bunch of buffers\n");241242/* Allocate and validate all buffers are zeroed */243for (i = 0; i < 32; i++) {244ret = dmabuf_heap_alloc(heap_fd, size, 0, &dmabuf_fd[i]);245if (ret < 0) {246ksft_test_result_fail("FAIL (Allocation (%i) failed) %d\n", i, ret);247goto close_and_return;248}249250/* mmap and validate everything is zero */251p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf_fd[i], 0);252if (p == MAP_FAILED) {253ksft_test_result_fail("FAIL (mmap() failed!): %s\n", strerror(errno));254goto close_and_return;255}256257dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_START);258c = (char *)p;259for (j = 0; j < size; j++) {260if (c[j] != 0) {261ksft_print_msg("FAIL (Allocated buffer not zeroed @ %i)\n", j);262dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_END);263munmap(p, size);264goto out;265}266}267dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_END);268munmap(p, size);269}270271out:272ksft_test_result(i == 32, "Allocate and validate all buffers are zeroed\n");273274close_and_return:275/* close them all */276for (k = 0; k < i; k++)277close(dmabuf_fd[k]);278279close(heap_fd);280return;281}282283/* Test the ioctl version compatibility w/ a smaller structure then expected */284static int dmabuf_heap_alloc_older(int fd, size_t len, unsigned int flags,285int *dmabuf_fd)286{287int ret;288unsigned int older_alloc_ioctl;289struct dma_heap_allocation_data_smaller {290__u64 len;291__u32 fd;292__u32 fd_flags;293} data = {294.len = len,295.fd = 0,296.fd_flags = O_RDWR | O_CLOEXEC,297};298299older_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,300struct dma_heap_allocation_data_smaller);301if (!dmabuf_fd)302return -EINVAL;303304ret = ioctl(fd, older_alloc_ioctl, &data);305if (ret < 0)306return ret;307*dmabuf_fd = (int)data.fd;308return ret;309}310311/* Test the ioctl version compatibility w/ a larger structure then expected */312static int dmabuf_heap_alloc_newer(int fd, size_t len, unsigned int flags,313int *dmabuf_fd)314{315int ret;316unsigned int newer_alloc_ioctl;317struct dma_heap_allocation_data_bigger {318__u64 len;319__u32 fd;320__u32 fd_flags;321__u64 heap_flags;322__u64 garbage1;323__u64 garbage2;324__u64 garbage3;325} data = {326.len = len,327.fd = 0,328.fd_flags = O_RDWR | O_CLOEXEC,329.heap_flags = flags,330.garbage1 = 0xffffffff,331.garbage2 = 0x88888888,332.garbage3 = 0x11111111,333};334335newer_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,336struct dma_heap_allocation_data_bigger);337if (!dmabuf_fd)338return -EINVAL;339340ret = ioctl(fd, newer_alloc_ioctl, &data);341if (ret < 0)342return ret;343344*dmabuf_fd = (int)data.fd;345return ret;346}347348static void test_alloc_compat(char *heap_name)349{350int ret, heap_fd = -1, dmabuf_fd = -1;351352heap_fd = dmabuf_heap_open(heap_name);353354ksft_print_msg("Testing (theoretical) older alloc compat:\n");355ret = dmabuf_heap_alloc_older(heap_fd, ONE_MEG, 0, &dmabuf_fd);356if (dmabuf_fd >= 0)357close(dmabuf_fd);358ksft_test_result(!ret, "dmabuf_heap_alloc_older\n");359360ksft_print_msg("Testing (theoretical) newer alloc compat:\n");361ret = dmabuf_heap_alloc_newer(heap_fd, ONE_MEG, 0, &dmabuf_fd);362if (dmabuf_fd >= 0)363close(dmabuf_fd);364ksft_test_result(!ret, "dmabuf_heap_alloc_newer\n");365366close(heap_fd);367}368369static void test_alloc_errors(char *heap_name)370{371int heap_fd = -1, dmabuf_fd = -1;372int ret;373374heap_fd = dmabuf_heap_open(heap_name);375376ksft_print_msg("Testing expected error cases:\n");377ret = dmabuf_heap_alloc(0, ONE_MEG, 0x111111, &dmabuf_fd);378ksft_test_result(ret, "Error expected on invalid fd %d\n", ret);379380ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0x111111, &dmabuf_fd);381ksft_test_result(ret, "Error expected on invalid heap flags %d\n", ret);382383ret = dmabuf_heap_alloc_fdflags(heap_fd, ONE_MEG,384~(O_RDWR | O_CLOEXEC), 0, &dmabuf_fd);385ksft_test_result(ret, "Error expected on invalid heap flags %d\n", ret);386387if (dmabuf_fd >= 0)388close(dmabuf_fd);389close(heap_fd);390}391392static int numer_of_heaps(void)393{394DIR *d = opendir(DEVPATH);395struct dirent *dir;396int heaps = 0;397398while ((dir = readdir(d))) {399if (!strncmp(dir->d_name, ".", 2))400continue;401if (!strncmp(dir->d_name, "..", 3))402continue;403heaps++;404}405406return heaps;407}408409int main(void)410{411struct dirent *dir;412DIR *d;413414ksft_print_header();415416d = opendir(DEVPATH);417if (!d) {418ksft_print_msg("No %s directory?\n", DEVPATH);419return KSFT_SKIP;420}421422ksft_set_plan(11 * numer_of_heaps());423424while ((dir = readdir(d))) {425if (!strncmp(dir->d_name, ".", 2))426continue;427if (!strncmp(dir->d_name, "..", 3))428continue;429430ksft_print_msg("Testing heap: %s\n", dir->d_name);431ksft_print_msg("=======================================\n");432test_alloc_and_import(dir->d_name);433test_alloc_zeroed(dir->d_name, 4 * 1024);434test_alloc_zeroed(dir->d_name, ONE_MEG);435test_alloc_compat(dir->d_name);436test_alloc_errors(dir->d_name);437}438closedir(d);439440ksft_finished();441}442443444