You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
362 lines
12 KiB
362 lines
12 KiB
/*
|
|
* Copyright (C) 2020 The Android Open Source Project
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#define LOG_TAG "DMABUFHEAPS"
|
|
|
|
#include <BufferAllocator/BufferAllocator.h>
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <ion/ion.h>
|
|
#include <linux/dma-buf.h>
|
|
#include <linux/dma-heap.h>
|
|
#include <linux/ion_4.12.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include <shared_mutex>
|
|
#include <string>
|
|
#include <unordered_set>
|
|
|
|
#include <android-base/logging.h>
|
|
#include <android-base/unique_fd.h>
|
|
|
|
static constexpr char kDmaHeapRoot[] = "/dev/dma_heap/";
|
|
static constexpr char kIonDevice[] = "/dev/ion";
|
|
static constexpr char kIonSystemHeapName[] = "ion_system_heap";
|
|
|
|
void BufferAllocator::LogInterface(const std::string& interface) {
|
|
if (!logged_interface_) {
|
|
LOG(INFO) << "Using : " << interface;
|
|
logged_interface_ = true;
|
|
}
|
|
}
|
|
|
|
int BufferAllocator::OpenDmabufHeap(const std::string& heap_name) {
|
|
std::shared_lock<std::shared_mutex> slock(dmabuf_heap_fd_mutex_);
|
|
|
|
/* Check if heap has already been opened. */
|
|
auto it = dmabuf_heap_fds_.find(heap_name);
|
|
if (it != dmabuf_heap_fds_.end())
|
|
return it->second;
|
|
|
|
slock.unlock();
|
|
|
|
/*
|
|
* Heap device needs to be opened, use a unique_lock since dmabuf_heap_fd_
|
|
* needs to be modified.
|
|
*/
|
|
std::unique_lock<std::shared_mutex> ulock(dmabuf_heap_fd_mutex_);
|
|
|
|
/*
|
|
* Check if we already opened this heap again to prevent racing threads from
|
|
* opening the heap device multiple times.
|
|
*/
|
|
it = dmabuf_heap_fds_.find(heap_name);
|
|
if (it != dmabuf_heap_fds_.end()) return it->second;
|
|
|
|
std::string heap_path = kDmaHeapRoot + heap_name;
|
|
int fd = TEMP_FAILURE_RETRY(open(heap_path.c_str(), O_RDONLY | O_CLOEXEC));
|
|
if (fd < 0) return -errno;
|
|
|
|
LOG(INFO) << "Using DMA-BUF heap named: " << heap_name;
|
|
|
|
auto ret = dmabuf_heap_fds_.insert({heap_name, android::base::unique_fd(fd)});
|
|
CHECK(ret.second);
|
|
return fd;
|
|
}
|
|
|
|
void BufferAllocator::QueryIonHeaps() {
|
|
uses_legacy_ion_iface_ = ion_is_legacy(ion_fd_);
|
|
if (uses_legacy_ion_iface_) {
|
|
LogInterface("Legacy ion heaps");
|
|
MapNameToIonMask(kDmabufSystemHeapName, ION_HEAP_SYSTEM_MASK, ION_FLAG_CACHED);
|
|
MapNameToIonMask(kDmabufSystemUncachedHeapName, ION_HEAP_SYSTEM_MASK);
|
|
return;
|
|
}
|
|
|
|
int heap_count;
|
|
int ret = ion_query_heap_cnt(ion_fd_, &heap_count);
|
|
if (ret == 0) {
|
|
ion_heap_info_.resize(heap_count, {});
|
|
ret = ion_query_get_heaps(ion_fd_, heap_count, ion_heap_info_.data());
|
|
}
|
|
|
|
// Abort if heap query fails
|
|
CHECK(ret == 0)
|
|
<< "Non-legacy ION implementation must support heap information queries";
|
|
LogInterface("Non-legacy ION heaps");
|
|
|
|
/*
|
|
* No error checking here, it is possible that devices may have used another name for
|
|
* the ion system heap.
|
|
*/
|
|
MapNameToIonName(kDmabufSystemHeapName, kIonSystemHeapName, ION_FLAG_CACHED);
|
|
MapNameToIonName(kDmabufSystemUncachedHeapName, kIonSystemHeapName);
|
|
}
|
|
|
|
BufferAllocator::BufferAllocator() {
|
|
ion_fd_.reset(TEMP_FAILURE_RETRY(open(kIonDevice, O_RDONLY| O_CLOEXEC)));
|
|
if (ion_fd_ >= 0)
|
|
QueryIonHeaps();
|
|
}
|
|
|
|
int BufferAllocator::MapNameToIonMask(const std::string& heap_name, unsigned int ion_heap_mask,
|
|
unsigned int ion_heap_flags) {
|
|
if (!ion_heap_mask)
|
|
return -EINVAL;
|
|
IonHeapConfig heap_config = { ion_heap_mask, ion_heap_flags };
|
|
|
|
std::unique_lock<std::shared_mutex> ulock(heap_name_to_config_mutex_);
|
|
heap_name_to_config_[heap_name] = heap_config;
|
|
return 0;
|
|
}
|
|
|
|
int BufferAllocator::GetIonHeapIdByName(const std::string& heap_name, unsigned int* heap_id) {
|
|
for (auto& it : ion_heap_info_) {
|
|
if (heap_name == it.name) {
|
|
*heap_id = it.heap_id;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
LOG(ERROR) << "No ion heap of name " << heap_name << " exists";
|
|
return -EINVAL;
|
|
}
|
|
|
|
int BufferAllocator::MapNameToIonName(const std::string& heap_name,
|
|
const std::string& ion_heap_name,
|
|
unsigned int ion_heap_flags) {
|
|
unsigned int ion_heap_id = 0;
|
|
auto ret = GetIonHeapIdByName(ion_heap_name, &ion_heap_id);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
unsigned int ion_heap_mask = 1 << ion_heap_id;
|
|
IonHeapConfig heap_config = { ion_heap_mask, ion_heap_flags };
|
|
|
|
std::unique_lock<std::shared_mutex> ulock(heap_name_to_config_mutex_);
|
|
heap_name_to_config_[heap_name] = heap_config;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int BufferAllocator::MapNameToIonHeap(const std::string& heap_name,
|
|
const std::string& ion_heap_name,
|
|
unsigned int ion_heap_flags,
|
|
unsigned int legacy_ion_heap_mask,
|
|
unsigned int legacy_ion_heap_flags) {
|
|
/* if the DMA-BUF Heap exists, we can ignore ion mappings */
|
|
int ret = OpenDmabufHeap(heap_name);
|
|
if (ret >= 0)
|
|
return 0;
|
|
|
|
if (uses_legacy_ion_iface_ || ion_heap_name == "") {
|
|
ret = MapNameToIonMask(heap_name, legacy_ion_heap_mask, legacy_ion_heap_flags);
|
|
} else if (!ion_heap_name.empty()) {
|
|
ret = MapNameToIonName(heap_name, ion_heap_name, ion_heap_flags);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int BufferAllocator::GetIonConfig(const std::string& heap_name, IonHeapConfig& heap_config) {
|
|
int ret = 0;
|
|
|
|
std::shared_lock<std::shared_mutex> slock(heap_name_to_config_mutex_);
|
|
|
|
auto it = heap_name_to_config_.find(heap_name);
|
|
if (it != heap_name_to_config_.end()) {
|
|
heap_config = it->second;
|
|
return ret;
|
|
}
|
|
|
|
slock.unlock();
|
|
|
|
if (uses_legacy_ion_iface_) {
|
|
ret = -EINVAL;
|
|
} else {
|
|
unsigned int heap_id;
|
|
ret = GetIonHeapIdByName(heap_name, &heap_id);
|
|
if (ret == 0) {
|
|
heap_config.mask = 1 << heap_id;
|
|
heap_config.flags = 0;
|
|
/* save it so that this lookup does not need to happen again */
|
|
std::unique_lock<std::shared_mutex> ulock(heap_name_to_config_mutex_);
|
|
heap_name_to_config_[heap_name] = heap_config;
|
|
}
|
|
}
|
|
|
|
if (ret)
|
|
LOG(ERROR) << "No ion heap of name " << heap_name << " exists";
|
|
return ret;
|
|
}
|
|
|
|
int BufferAllocator::DmabufAlloc(const std::string& heap_name, size_t len) {
|
|
int fd = OpenDmabufHeap(heap_name);
|
|
if (fd < 0) return fd;
|
|
|
|
struct dma_heap_allocation_data heap_data{
|
|
.len = len, // length of data to be allocated in bytes
|
|
.fd_flags = O_RDWR | O_CLOEXEC, // permissions for the memory to be allocated
|
|
};
|
|
|
|
auto ret = TEMP_FAILURE_RETRY(ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &heap_data));
|
|
if (ret < 0) {
|
|
PLOG(ERROR) << "Unable to allocate from DMA-BUF heap: " << heap_name;
|
|
return ret;
|
|
}
|
|
|
|
return heap_data.fd;
|
|
}
|
|
|
|
int BufferAllocator::IonAlloc(const std::string& heap_name, size_t len,
|
|
unsigned int heap_flags, size_t legacy_align) {
|
|
IonHeapConfig heap_config;
|
|
auto ret = GetIonConfig(heap_name, heap_config);
|
|
if (ret)
|
|
return ret;
|
|
|
|
int alloc_fd = -1;
|
|
unsigned int flags = heap_config.flags | heap_flags;
|
|
ret = ion_alloc_fd(ion_fd_, len, legacy_align, heap_config.mask, flags, &alloc_fd);
|
|
if (ret) {
|
|
PLOG(ERROR) << "allocation fails for ion heap with mask: " << heap_config.mask
|
|
<< " and flags: " << flags;
|
|
return ret;
|
|
}
|
|
return alloc_fd;
|
|
}
|
|
|
|
int BufferAllocator::Alloc(const std::string& heap_name, size_t len,
|
|
unsigned int heap_flags, size_t legacy_align) {
|
|
int fd = DmabufAlloc(heap_name, len);
|
|
|
|
if (fd < 0)
|
|
fd = IonAlloc(heap_name, len, heap_flags, legacy_align);
|
|
|
|
return fd;
|
|
}
|
|
|
|
int BufferAllocator::AllocSystem(bool cpu_access_needed, size_t len, unsigned int heap_flags,
|
|
size_t legacy_align) {
|
|
if (!cpu_access_needed) {
|
|
/*
|
|
* CPU does not need to access allocated buffer so we try to allocate in
|
|
* the 'system-uncached' heap after querying for its existence.
|
|
*/
|
|
static bool uncached_dmabuf_system_heap_support = [this]() -> bool {
|
|
auto dmabuf_heap_list = this->GetDmabufHeapList();
|
|
return (dmabuf_heap_list.find(kDmabufSystemUncachedHeapName) != dmabuf_heap_list.end());
|
|
}();
|
|
|
|
if (uncached_dmabuf_system_heap_support)
|
|
return DmabufAlloc(kDmabufSystemUncachedHeapName, len);
|
|
|
|
static bool uncached_ion_system_heap_support = [this]() -> bool {
|
|
IonHeapConfig heap_config;
|
|
auto ret = this->GetIonConfig(kDmabufSystemUncachedHeapName, heap_config);
|
|
return (ret == 0);
|
|
}();
|
|
|
|
if (uncached_ion_system_heap_support)
|
|
return IonAlloc(kDmabufSystemUncachedHeapName, len, heap_flags, legacy_align);
|
|
}
|
|
|
|
/*
|
|
* Either 1) CPU needs to access allocated buffer OR 2) CPU does not need to
|
|
* access allocated buffer but the "system-uncached" heap is unsupported.
|
|
*/
|
|
return Alloc(kDmabufSystemHeapName, len, heap_flags, legacy_align);
|
|
}
|
|
|
|
int BufferAllocator::LegacyIonCpuSync(unsigned int dmabuf_fd,
|
|
const CustomCpuSyncLegacyIon& legacy_ion_cpu_sync_custom,
|
|
void *legacy_ion_custom_data) {
|
|
if (!legacy_ion_cpu_sync_custom)
|
|
return ion_sync_fd(ion_fd_, dmabuf_fd);
|
|
|
|
// dup ion_fd_ so that we retain its ownership.
|
|
int new_ion_fd = TEMP_FAILURE_RETRY(dup(ion_fd_.get()));
|
|
if (new_ion_fd < 0) {
|
|
PLOG(ERROR) << "Unable to dup ion fd. error: " << new_ion_fd;
|
|
return new_ion_fd;
|
|
}
|
|
|
|
int ret = legacy_ion_cpu_sync_custom(new_ion_fd, dmabuf_fd, legacy_ion_custom_data);
|
|
|
|
close(new_ion_fd);
|
|
return ret;
|
|
}
|
|
|
|
int BufferAllocator::DoSync(unsigned int dmabuf_fd, bool start, SyncType sync_type,
|
|
const CustomCpuSyncLegacyIon& legacy_ion_cpu_sync_custom,
|
|
void *legacy_ion_custom_data) {
|
|
if (uses_legacy_ion_iface_) {
|
|
return LegacyIonCpuSync(dmabuf_fd, legacy_ion_cpu_sync_custom,
|
|
legacy_ion_custom_data);
|
|
}
|
|
|
|
struct dma_buf_sync sync = {
|
|
.flags = (start ? DMA_BUF_SYNC_START : DMA_BUF_SYNC_END) |
|
|
static_cast<uint64_t>(sync_type),
|
|
};
|
|
return TEMP_FAILURE_RETRY(ioctl(dmabuf_fd, DMA_BUF_IOCTL_SYNC, &sync));
|
|
}
|
|
|
|
int BufferAllocator::CpuSyncStart(unsigned int dmabuf_fd, SyncType sync_type,
|
|
const CustomCpuSyncLegacyIon& legacy_ion_cpu_sync_custom,
|
|
void *legacy_ion_custom_data) {
|
|
int ret = DoSync(dmabuf_fd, true /* start */, sync_type, legacy_ion_cpu_sync_custom,
|
|
legacy_ion_custom_data);
|
|
|
|
if (ret) PLOG(ERROR) << "CpuSyncStart() failure";
|
|
return ret;
|
|
}
|
|
|
|
int BufferAllocator::CpuSyncEnd(unsigned int dmabuf_fd, SyncType sync_type,
|
|
const CustomCpuSyncLegacyIon& legacy_ion_cpu_sync_custom,
|
|
void* legacy_ion_custom_data) {
|
|
int ret = DoSync(dmabuf_fd, false /* start */, sync_type, legacy_ion_cpu_sync_custom,
|
|
legacy_ion_custom_data);
|
|
if (ret) PLOG(ERROR) << "CpuSyncEnd() failure";
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::unordered_set<std::string> BufferAllocator::GetDmabufHeapList() {
|
|
std::unordered_set<std::string> heap_list;
|
|
std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(kDmaHeapRoot), closedir);
|
|
|
|
if (dir) {
|
|
struct dirent* dent;
|
|
while ((dent = readdir(dir.get()))) {
|
|
if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) continue;
|
|
|
|
heap_list.insert(dent->d_name);
|
|
}
|
|
}
|
|
|
|
return heap_list;
|
|
}
|
|
|
|
bool BufferAllocator::CheckIonSupport() {
|
|
static bool ion_support = (access(kIonDevice, R_OK) == 0);
|
|
|
|
return ion_support;
|
|
}
|