Files
Ahmed Hesham 9ba6f062d4 Add support for allocating DMA buffers (#2170)
This adds support for allocating DMA buffers on systems that support it,
i.e. Linux and Android.

On mainline Linux, starting version 5.6 (equivalent to Android 12),
there is a new kernel module framework available called [DMA-BUF
Heaps](https://github.com/torvalds/linux/blob/master/drivers/dma-buf/dma-heap.c).
The goal of this framework is to provide a standardised way for user
applications to allocate and share memory buffers between different
devices, subsystems, etc. The main feature of interest is that the
framework provides device-agnostic allocation; it abstracts away the
underlying hardware, and provides a single IOCTL,
`DMA_HEAP_IOCTL_ALLOC`. Mainline implementation provides two heaps that
act as character devices that can allocate DMA buffers; system, which
uses the buddy allocator, and cma, which uses the
[CMA](https://developer.toradex.com/software/linux-resources/linux-features/contiguous-memory-allocator-cma-linux/)
(Contiguous Memory Allocator). Both of these are [kernel configuration
options](https://github.com/torvalds/linux/blob/master/drivers/dma-buf/heaps/Kconfig)
that need to be enabled when building the Linux kernel. Generally, any
kernel module implementing this framework is made available under
/dev/dma_heaps/<heap_name>, e.g. /dev/dma_heaps/system.

The implementation currently only supports one type of DMA heaps;
`system`, the default device path for which is `/dev/dma_heap/system`.
The path can be overridden at runtime using an environment variable,
`OCL_CTS_DMA_HEAP_PATH_SYSTEM`, if needed. Extending this in the future
should be trivial (subject to platform support), by adding an entry to
the enum `dma_buf_heap_type`, and an appropriate default path and
overriding environment variable name.

The proposed implementation will conditionally compile if the conditions
are met (i.e. building for Linux or Android, using kernel headers >=
5.6.0), and will provide a compile-time warning otherwise, and return
`-1` as the DMA handle in runtime if not.

To demonstrate the functionality, a new test is added for the
`cl_khr_external_memory_dma_buf` extension. If the extension is
supported by the device, a DMA buffer will be allocated and used to
create a CL buffer, that is then used by a simple kernel.

This should provide a way forward for adding more tests that depend on
DMA buffers.

---------

Signed-off-by: Gorazd Sumkovski <gorazd.sumkovski@arm.com>
Signed-off-by: Ahmed Hesham <ahmed.hesham@arm.com>
Co-authored-by: Gorazd Sumkovski <gorazd.sumkovski@arm.com>
2025-02-26 09:51:22 -08:00

107 lines
3.0 KiB
C

//
// Copyright (c) 2020 - 2024 The Khronos Group Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef HARNESS_ALLOC_H_
#define HARNESS_ALLOC_H_
#if defined(__linux__) || defined(linux) || defined(__APPLE__)
#if defined(__ANDROID__)
#include <malloc.h>
#else
#include <stdlib.h>
#endif
#endif
#include <stdint.h>
#if defined(__MINGW32__)
#include "mingw_compat.h"
#endif
#if defined(_WIN32)
#include <cstdlib>
#endif
inline void* align_malloc(size_t size, size_t alignment)
{
#if defined(_WIN32) && defined(_MSC_VER)
return _aligned_malloc(size, alignment);
#elif defined(__linux__) || defined(linux) || defined(__APPLE__)
void* ptr = NULL;
#if defined(__ANDROID__)
ptr = memalign(alignment, size);
if (ptr) return ptr;
#else
if (alignment < sizeof(void*))
{
alignment = sizeof(void*);
}
if (0 == posix_memalign(&ptr, alignment, size)) return ptr;
#endif
return NULL;
#elif defined(__MINGW32__)
return __mingw_aligned_malloc(size, alignment);
#else
#error "Please add support OS for aligned malloc"
#endif
}
inline void align_free(void* ptr)
{
#if defined(_WIN32) && defined(_MSC_VER)
_aligned_free(ptr);
#elif defined(__linux__) || defined(linux) || defined(__APPLE__)
return free(ptr);
#elif defined(__MINGW32__)
return __mingw_aligned_free(ptr);
#else
#error "Please add support OS for aligned free"
#endif
}
enum class dma_buf_heap_type
{
SYSTEM
};
/**
* @brief Allocate a DMA buffer.
*
* On systems that support it, use the DMA buffer heaps to allocate a DMA buffer
* of the requested size, using the requested heap type. The heap type defaults
* to using the system heap if no type is specified.
*
* A heap type will use a default path if one exists, and can be overriden using
* an environment variable for each type, as follows:
*
* SYSTEM:
* * Default path: /dev/dma_heap/system
* * Environment variable: OCL_CTS_DMA_HEAP_PATH_SYSTEM
*
* DMA buffer heaps require a minimum Linux kernel version 5.6. A compile-time
* warning is issued on older systems, as well as an error message at runtime.
*
* @param size [in] The requested buffer size in bytes.
* @param heap_type [in,opt] The heap type to use for the allocation.
*
* @retrun A file descriptor representing the allocated DMA buffer on success,
* -1 otherwise. Failure to open the DMA device returns TEST_SKIPPED_ITSELF so
* it can be handled separately to other failures.
*/
int allocate_dma_buf(uint64_t size,
dma_buf_heap_type heap_type = dma_buf_heap_type::SYSTEM);
#endif // #ifndef HARNESS_ALLOC_H_