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.
323 lines
10 KiB
323 lines
10 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 "SharedMemoryAndroid"
|
|
|
|
#include <android-base/logging.h>
|
|
#include <android-base/mapped_file.h>
|
|
#include <android-base/scopeguard.h>
|
|
#include <android/hardware_buffer.h>
|
|
|
|
#include <algorithm>
|
|
#include <any>
|
|
#include <iterator>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <variant>
|
|
#include <vector>
|
|
|
|
#include "Result.h"
|
|
#include "SharedMemory.h"
|
|
#include "TypeUtils.h"
|
|
#include "Types.h"
|
|
|
|
#ifndef NN_COMPATIBILITY_LIBRARY_BUILD
|
|
#include <android/hidl/allocator/1.0/IAllocator.h>
|
|
#include <hidl/HidlSupport.h>
|
|
#include <hidlmemory/mapping.h>
|
|
#include <sys/mman.h>
|
|
#else
|
|
#include "DynamicCLDeps.h"
|
|
#endif // NN_COMPATIBILITY_LIBRARY_BUILD
|
|
|
|
namespace android::nn {
|
|
namespace {
|
|
|
|
GeneralResult<SharedMemory> createSharedMemoryFromUniqueFd(size_t size, int prot,
|
|
base::unique_fd fd, size_t offset) {
|
|
auto handle = Memory::Fd{
|
|
.size = size,
|
|
.prot = prot,
|
|
.fd = std::move(fd),
|
|
.offset = offset,
|
|
};
|
|
return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
|
|
}
|
|
|
|
#ifndef NN_COMPATIBILITY_LIBRARY_BUILD
|
|
|
|
using ::android::hardware::hidl_memory;
|
|
using ::android::hidl::allocator::V1_0::IAllocator;
|
|
|
|
const char* const kAllocatorService = "ashmem";
|
|
|
|
GeneralResult<hardware::hidl_handle> hidlHandleFromUniqueFd(base::unique_fd fd) {
|
|
native_handle_t* nativeHandle = native_handle_create(1, 0);
|
|
if (nativeHandle == nullptr) {
|
|
return NN_ERROR() << "Failed to create native_handle";
|
|
}
|
|
nativeHandle->data[0] = fd.release();
|
|
|
|
hardware::hidl_handle hidlHandle;
|
|
hidlHandle.setTo(nativeHandle, /*shouldOwn=*/true);
|
|
return hidlHandle;
|
|
}
|
|
|
|
GeneralResult<SharedMemory> allocateSharedMemory(size_t size) {
|
|
static const auto allocator = IAllocator::getService(kAllocatorService);
|
|
CHECK_GT(size, 0u);
|
|
|
|
hidl_memory maybeMemory;
|
|
auto fn = [&maybeMemory](bool success, const hidl_memory& memory) {
|
|
if (success) {
|
|
maybeMemory = memory;
|
|
}
|
|
};
|
|
allocator->allocate(size, fn);
|
|
|
|
if (!maybeMemory.valid()) {
|
|
return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
|
|
<< "IAllocator::allocate returned an invalid (empty) memory object";
|
|
}
|
|
if (maybeMemory.handle()->numFds != 1) {
|
|
return NN_ERROR() << "IAllocator::allocate returned an invalid memory object with "
|
|
<< maybeMemory.handle()->numFds << " numFds, but expected 1";
|
|
}
|
|
if (maybeMemory.handle()->numInts != 0) {
|
|
return NN_ERROR() << "IAllocator::allocate returned an invalid memory object with "
|
|
<< maybeMemory.handle()->numInts << " numInts, but expected 0";
|
|
}
|
|
|
|
CHECK_LE(maybeMemory.size(), std::numeric_limits<size_t>::max());
|
|
const int fd = maybeMemory.handle()->data[0];
|
|
|
|
auto handle = Memory::Ashmem{
|
|
.fd = NN_TRY(dupFd(fd)),
|
|
.size = static_cast<size_t>(maybeMemory.size()),
|
|
};
|
|
return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
|
|
}
|
|
|
|
GeneralResult<Mapping> map(const Memory::Ashmem& memory) {
|
|
auto handle = NN_TRY(hidlHandleFromUniqueFd(NN_TRY(dupFd(memory.fd))));
|
|
const auto hidlMemory = hidl_memory("ashmem", std::move(handle), memory.size);
|
|
|
|
const auto mapping = mapMemory(hidlMemory);
|
|
if (mapping == nullptr) {
|
|
return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to map memory";
|
|
}
|
|
|
|
auto* const pointer = mapping->getPointer().withDefault(nullptr);
|
|
if (pointer == nullptr) {
|
|
return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to get the mapped pointer";
|
|
}
|
|
|
|
const auto fullSize = mapping->getSize().withDefault(0);
|
|
if (fullSize == 0 || fullSize > std::numeric_limits<size_t>::max()) {
|
|
return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to get the mapped size";
|
|
}
|
|
|
|
const size_t size = static_cast<size_t>(fullSize);
|
|
|
|
return Mapping{
|
|
.pointer = pointer,
|
|
.size = size,
|
|
.context = mapping,
|
|
};
|
|
}
|
|
|
|
#else
|
|
|
|
GeneralResult<SharedMemory> allocateSharedMemory(size_t size) {
|
|
CHECK_GT(size, 0u);
|
|
|
|
const CompatibilityLayerMemory& memory = loadCompatibilityLayerMemory();
|
|
auto fd = base::unique_fd(memory.create(nullptr, size));
|
|
if (!fd.ok()) {
|
|
return NN_ERROR() << "ASharedMemory_create failed";
|
|
}
|
|
|
|
const size_t readSize = memory.getSize(fd.get());
|
|
CHECK_GE(readSize, size);
|
|
|
|
constexpr int prot = PROT_READ | PROT_WRITE;
|
|
constexpr size_t offset = 0;
|
|
return createSharedMemoryFromUniqueFd(size, prot, std::move(fd), offset);
|
|
}
|
|
|
|
GeneralResult<Mapping> map(const Memory::Ashmem& /*memory*/) {
|
|
return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Cannot map ashmem memory";
|
|
}
|
|
|
|
#endif // NN_COMPATIBILITY_LIBRARY_BUILD
|
|
|
|
size_t getSize(const Memory::Ashmem& memory) {
|
|
return memory.size;
|
|
}
|
|
|
|
size_t getSize(const Memory::Fd& memory) {
|
|
return memory.size;
|
|
}
|
|
|
|
size_t getSize(const Memory::HardwareBuffer& memory) {
|
|
AHardwareBuffer_Desc desc;
|
|
AHardwareBuffer_describe(memory.handle.get(), &desc);
|
|
return desc.format == AHARDWAREBUFFER_FORMAT_BLOB ? desc.width : 0;
|
|
}
|
|
|
|
size_t getSize(const Memory::Unknown& memory) {
|
|
return memory.size;
|
|
}
|
|
|
|
struct MmapFdMappingContext {
|
|
int prot;
|
|
std::any context;
|
|
};
|
|
|
|
GeneralResult<Mapping> map(const Memory::Fd& memory) {
|
|
std::shared_ptr<base::MappedFile> mapping =
|
|
base::MappedFile::FromFd(memory.fd, memory.offset, memory.size, memory.prot);
|
|
if (mapping == nullptr) {
|
|
return NN_ERROR() << "Can't mmap the file descriptor.";
|
|
}
|
|
char* data = mapping->data();
|
|
|
|
const bool writable = (memory.prot & PROT_WRITE) != 0;
|
|
std::variant<const void*, void*> pointer;
|
|
if (writable) {
|
|
pointer = static_cast<void*>(data);
|
|
} else {
|
|
pointer = static_cast<const void*>(data);
|
|
}
|
|
|
|
auto context = MmapFdMappingContext{.prot = memory.prot, .context = std::move(mapping)};
|
|
return Mapping{.pointer = pointer, .size = memory.size, .context = std::move(context)};
|
|
}
|
|
|
|
GeneralResult<Mapping> map(const Memory::HardwareBuffer& memory) {
|
|
AHardwareBuffer_Desc desc;
|
|
AHardwareBuffer_describe(memory.handle.get(), &desc);
|
|
|
|
if (desc.format != AHARDWAREBUFFER_FORMAT_BLOB) {
|
|
return NN_ERROR() << "Unable to map non-blob AHardwareBuffer memory";
|
|
}
|
|
const uint32_t size = desc.width;
|
|
|
|
const uint64_t kCpuUsageMask =
|
|
AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK;
|
|
void* data = nullptr;
|
|
const auto status = AHardwareBuffer_lock(memory.handle.get(), desc.usage & kCpuUsageMask, -1,
|
|
nullptr, &data);
|
|
if (status != /*NO_ERROR*/ 0) {
|
|
return NN_ERROR() << "Can't lock the AHardwareBuffer. Error: " << status;
|
|
}
|
|
|
|
// Create shared scoped object to munmap.
|
|
auto scoped = base::make_scope_guard(
|
|
[ahwb = memory.handle.get()] { AHardwareBuffer_unlock(ahwb, nullptr); });
|
|
auto sharedScoped = std::make_shared<decltype(scoped)>(std::move(scoped));
|
|
|
|
return Mapping{.pointer = data, .size = size, .context = std::move(sharedScoped)};
|
|
}
|
|
|
|
GeneralResult<Mapping> map(const Memory::Unknown& /*memory*/) {
|
|
return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Cannot map Unknown memory";
|
|
}
|
|
|
|
void freeHardwareBuffer(AHardwareBuffer* buffer) {
|
|
if (buffer) {
|
|
AHardwareBuffer_release(buffer);
|
|
}
|
|
}
|
|
|
|
void freeNoop(AHardwareBuffer* /*buffer*/) {}
|
|
|
|
} // namespace
|
|
|
|
GeneralResult<base::unique_fd> dupFd(int fd) {
|
|
if (fd < 0) {
|
|
return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "dupFd was passed an invalid fd";
|
|
}
|
|
auto uniqueFd = base::unique_fd(dup(fd));
|
|
if (!uniqueFd.ok()) {
|
|
// TODO(b/120417090): is ANEURALNETWORKS_UNEXPECTED_NULL the correct error to return here?
|
|
return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to dup the fd";
|
|
}
|
|
return uniqueFd;
|
|
}
|
|
|
|
GeneralResult<SharedMemory> createSharedMemory(size_t size) {
|
|
return allocateSharedMemory(size);
|
|
}
|
|
|
|
GeneralResult<SharedMemory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t offset) {
|
|
return createSharedMemoryFromUniqueFd(size, prot, NN_TRY(dupFd(fd)), offset);
|
|
}
|
|
|
|
GeneralResult<SharedMemory> createSharedMemoryFromAHWB(AHardwareBuffer* ahwb, bool takeOwnership) {
|
|
CHECK(ahwb != nullptr);
|
|
const Memory::HardwareBuffer::Deleter deleter = (takeOwnership ? freeHardwareBuffer : freeNoop);
|
|
Memory::HardwareBuffer handle = {.handle = Memory::HardwareBuffer::Handle(ahwb, deleter)};
|
|
return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
|
|
}
|
|
|
|
size_t getSize(const SharedMemory& memory) {
|
|
CHECK(memory != nullptr);
|
|
return std::visit([](const auto& x) { return getSize(x); }, memory->handle);
|
|
}
|
|
|
|
bool isAhwbBlob(const Memory::HardwareBuffer& memory) {
|
|
AHardwareBuffer* ahwb = memory.handle.get();
|
|
AHardwareBuffer_Desc desc;
|
|
AHardwareBuffer_describe(ahwb, &desc);
|
|
return desc.format == AHARDWAREBUFFER_FORMAT_BLOB;
|
|
}
|
|
|
|
bool isAhwbBlob(const SharedMemory& memory) {
|
|
CHECK(memory != nullptr);
|
|
if (!std::holds_alternative<Memory::HardwareBuffer>(memory->handle)) {
|
|
return false;
|
|
}
|
|
return isAhwbBlob(std::get<Memory::HardwareBuffer>(memory->handle));
|
|
}
|
|
|
|
GeneralResult<Mapping> map(const SharedMemory& memory) {
|
|
if (memory == nullptr) {
|
|
return NN_ERROR() << "Unable to map nullptr SharedMemory object";
|
|
}
|
|
return std::visit([](const auto& x) { return map(x); }, memory->handle);
|
|
}
|
|
|
|
bool flush(const Mapping& mapping) {
|
|
if (const auto* mmapFdMapping = std::any_cast<MmapFdMappingContext>(&mapping.context)) {
|
|
if (!std::holds_alternative<void*>(mapping.pointer)) {
|
|
return true;
|
|
}
|
|
void* data = std::get<void*>(mapping.pointer);
|
|
const int prot = mmapFdMapping->prot;
|
|
if (prot & PROT_WRITE) {
|
|
const size_t size = mapping.size;
|
|
return msync(data, size, MS_SYNC) == 0;
|
|
}
|
|
}
|
|
// No-op for other types of memory.
|
|
return true;
|
|
}
|
|
|
|
} // namespace android::nn
|