Created
May 6, 2022 08:36
-
-
Save dv1/7c40329d1cf83529eb989344f046d899 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <assert.h> | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <inttypes.h> | |
#include <time.h> | |
#include <string.h> | |
#include <sys/ioctl.h> | |
#include <sys/mman.h> | |
#include <linux/ion.h> | |
#include <linux/dma-buf.h> | |
#include <linux/dma-heap.h> | |
#include <linux/version.h> | |
typedef struct | |
{ | |
int fd; | |
int size; | |
void *mapped_virtual_address; | |
int mmap_prot; | |
} | |
dmabuf_info; | |
typedef struct _allocator | |
{ | |
int fd; | |
int (*allocate_dmabuf)(dmabuf_info *dmabuf, struct _allocator *alloc, int size); | |
int (*sync_start)(dmabuf_info *dmabuf); | |
int (*sync_stop)(dmabuf_info *dmabuf); | |
} | |
allocator; | |
static unsigned int get_ion_heap_id_mask(int ion_fd, int *error) | |
{ | |
unsigned int heap_id_mask = 0; | |
/* Starting with kernel 4.14.34, we can iterate over the | |
* ION heaps and find those with type ION_HEAP_TYPE_DMA. */ | |
int i; | |
int heap_count; | |
struct ion_heap_query query = { 0 }; | |
struct ion_heap_data *heap_data = NULL; | |
if ((ioctl(ion_fd, ION_IOC_HEAP_QUERY, &query) < 0) || (query.cnt == 0)) | |
{ | |
if (error != NULL) | |
*error = errno; | |
return 0; | |
} | |
heap_count = query.cnt; | |
heap_data = calloc(heap_count, sizeof(struct ion_heap_data)); | |
query.cnt = heap_count; | |
query.heaps = (__u64)((uintptr_t)heap_data); | |
if (ioctl(ion_fd, ION_IOC_HEAP_QUERY, &query) < 0) | |
{ | |
if (error != NULL) | |
*error = errno; | |
free(heap_data); | |
return 0; | |
} | |
for (i = 0; i < heap_count; ++i) | |
{ | |
int is_dma_heap = (heap_data[i].type == ION_HEAP_TYPE_DMA); | |
if (is_dma_heap) | |
heap_id_mask |= 1u << heap_data[i].heap_id; | |
} | |
free(heap_data); | |
return heap_id_mask; | |
} | |
int ion_allocate_dmabuf(dmabuf_info *dmabuf, allocator *alloc, int size) | |
{ | |
int err = 0; | |
unsigned int ion_heap_id_mask = get_ion_heap_id_mask(alloc->fd, &err); | |
dmabuf->fd = -1; | |
if (err != 0) | |
{ | |
fprintf(stderr, "Could not get ION heap ID mask: %s (%d)\n", strerror(err), err); | |
return err; | |
} | |
struct ion_allocation_data allocation_data = | |
{ | |
.len = size, | |
.heap_id_mask = ion_heap_id_mask, | |
.flags = 0 | |
}; | |
if (ioctl(alloc->fd, ION_IOC_ALLOC, &allocation_data) < 0) | |
{ | |
fprintf(stderr, "Could not allocate DMA-BUF memory with ION: %s (%d)\n", strerror(errno), errno); | |
return errno; | |
} | |
dmabuf->fd = allocation_data.fd; | |
dmabuf->size = size; | |
dmabuf->mapped_virtual_address = NULL; | |
dmabuf->mmap_prot = 0; | |
return 0; | |
} | |
int ion_sync_start(dmabuf_info *dmabuf) | |
{ | |
/* no-op */ | |
return 0; | |
} | |
int ion_sync_stop(dmabuf_info *dmabuf) | |
{ | |
/* no-op */ | |
return 0; | |
} | |
allocator* create_ion_allocator(void) | |
{ | |
int ion_fd; | |
allocator *alloc; | |
ion_fd = open("/dev/ion", O_RDONLY); | |
if (ion_fd < 0) | |
{ | |
fprintf(stderr, "Could not create ION allocator: %s (%d)\n", strerror(errno), errno); | |
return NULL; | |
} | |
alloc = malloc(sizeof(allocator)); | |
alloc->fd = ion_fd; | |
alloc->allocate_dmabuf = ion_allocate_dmabuf; | |
alloc->sync_start = ion_sync_start; | |
alloc->sync_stop = ion_sync_stop; | |
return alloc; | |
} | |
int dma_heap_allocate_dmabuf(dmabuf_info *dmabuf, allocator *alloc, int size) | |
{ | |
struct dma_heap_allocation_data heap_alloc_data; | |
dmabuf->fd = -1; | |
memset(&heap_alloc_data, 0, sizeof(heap_alloc_data)); | |
heap_alloc_data.len = size; | |
heap_alloc_data.heap_flags = DMA_HEAP_VALID_HEAP_FLAGS; | |
heap_alloc_data.fd_flags = (O_RDWR | O_CLOEXEC); | |
if (ioctl(alloc->fd, DMA_HEAP_IOCTL_ALLOC, &heap_alloc_data) < 0) | |
{ | |
fprintf(stderr, "Could not allocate DMA-BUF memory with ION: %s (%d)\n", strerror(errno), errno); | |
return errno; | |
} | |
dmabuf->fd = heap_alloc_data.fd; | |
dmabuf->size = size; | |
dmabuf->mapped_virtual_address = NULL; | |
dmabuf->mmap_prot = 0; | |
return 0; | |
} | |
int dma_heap_phys_ioctl(int dmabuf_fd) | |
{ | |
struct dma_buf_phys dma_phys = { 0 }; | |
if (ioctl(dmabuf_fd, DMA_BUF_IOCTL_PHYS, &dma_phys) < 0) | |
return errno; | |
else | |
return 0; | |
} | |
int dma_heap_sync_start(dmabuf_info *dmabuf) | |
{ | |
if (dmabuf->mmap_prot & PROT_READ) | |
return dma_heap_phys_ioctl(dmabuf->fd); | |
else | |
return 0; | |
} | |
int dma_heap_sync_stop(dmabuf_info *dmabuf) | |
{ | |
if (dmabuf->mmap_prot & PROT_WRITE) | |
return dma_heap_phys_ioctl(dmabuf->fd); | |
else | |
return 0; | |
} | |
allocator* create_dma_heap_allocator(void) | |
{ | |
int dma_heap_fd = open("/dev/dma_heap/linux,cma", O_RDWR); | |
allocator *alloc = malloc(sizeof(allocator)); | |
alloc->fd = dma_heap_fd; | |
alloc->allocate_dmabuf = dma_heap_allocate_dmabuf; | |
alloc->sync_start = dma_heap_sync_start; | |
alloc->sync_stop = dma_heap_sync_stop; | |
return alloc; | |
} | |
void destroy_allocator(allocator *alloc) | |
{ | |
close(alloc->fd); | |
free(alloc); | |
} | |
uint8_t* map_dmabuf(dmabuf_info *dmabuf, int mmap_prot) | |
{ | |
dmabuf->mmap_prot = mmap_prot; | |
dmabuf->mapped_virtual_address = mmap(0, dmabuf->size, dmabuf->mmap_prot, MAP_SHARED, dmabuf->fd, 0); | |
if (dmabuf->mapped_virtual_address == MAP_FAILED) | |
return NULL; | |
else | |
return dmabuf->mapped_virtual_address; | |
} | |
void unmap_dmabuf(dmabuf_info *dmabuf) | |
{ | |
munmap(dmabuf->mapped_virtual_address, dmabuf->size); | |
} | |
void deallocate_dmabuf(dmabuf_info *dmabuf) | |
{ | |
close(dmabuf->fd); | |
dmabuf->fd = -1; | |
} | |
uint64_t get_current_time(void) | |
{ | |
struct timespec tp; | |
clock_gettime(CLOCK_MONOTONIC, &tp); | |
return ((uint64_t)(tp.tv_sec) * 1000000000ULL) + ((uint64_t)(tp.tv_nsec)); | |
} | |
void do_test_write(uint8_t *dest, int num_bytes) | |
{ | |
int i; | |
for (i = 0; i < num_bytes; ++i) | |
dest[i] = (uint8_t)(i & 255); | |
} | |
void do_test_readwrite(uint8_t *dest, int num_bytes) | |
{ | |
int i; | |
for (i = 0; i < num_bytes; ++i) | |
dest[i] ^= 255; | |
} | |
void do_test(allocator *alloc, char const *allocator_name) | |
{ | |
dmabuf_info dmabuf = { .fd = -1 }; | |
int const blocksize = 16 * 1048576; | |
uint64_t ts1, ts2, ts3, ts4, ts5; | |
int err; | |
if (alloc == NULL) | |
return; | |
fprintf(stderr, "Running test with %s allocator\n", allocator_name); | |
alloc->allocate_dmabuf(&dmabuf, alloc, blocksize); | |
if (dmabuf.fd < 0) | |
goto finish; | |
fprintf(stderr, "Allocated DMA-BUF memory, FD %d\n", dmabuf.fd); | |
fprintf(stderr, "\n"); | |
if (map_dmabuf(&dmabuf, PROT_READ|PROT_WRITE) == NULL) | |
{ | |
fprintf(stderr, "Could not mmap DMA-BUF memory: %s (%d)\n", strerror(errno), errno); | |
goto finish; | |
} | |
ts1 = get_current_time(); | |
if ((err = alloc->sync_start(&dmabuf)) != 0) | |
{ | |
fprintf(stderr, "sync-start failed: %s (%d)\n", strerror(errno), errno); | |
goto finish; | |
} | |
ts2 = get_current_time(); | |
do_test_write(dmabuf.mapped_virtual_address, blocksize); | |
ts3 = get_current_time(); | |
do_test_readwrite(dmabuf.mapped_virtual_address, blocksize); | |
ts4 = get_current_time(); | |
if ((err = alloc->sync_stop(&dmabuf)) != 0) | |
{ | |
fprintf(stderr, "sync-stop failed: %s (%d)\n", strerror(errno), errno); | |
goto finish; | |
} | |
ts5 = get_current_time(); | |
unmap_dmabuf(&dmabuf); | |
printf("sync_start: %" PRIu64 " us\n", (ts2 - ts1) / 1000); | |
printf("test write: %" PRIu64 " us\n", (ts3 - ts2) / 1000); | |
printf("test readwrite: %" PRIu64 " us\n", (ts4 - ts3) / 1000); | |
printf("sync_stop: %" PRIu64 " us\n", (ts5 - ts4) / 1000); | |
finish: | |
if (dmabuf.fd > 0) | |
deallocate_dmabuf(&dmabuf); | |
destroy_allocator(alloc); | |
} | |
int main(void) | |
{ | |
do_test(create_ion_allocator(), "ION"); | |
printf("\n"); | |
do_test(create_dma_heap_allocator(), "dma-heap"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment