blob: c361a1eb66526695b88c2e13de5609583290a2e6 [file] [log] [blame]
/*
* 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>
#ifdef __ANDROID__
#include <android/hardware_buffer.h>
#endif // __ANDROID__
#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 <cutils/ashmem.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
GeneralResult<SharedMemory> allocateSharedMemory(size_t size) {
CHECK_GT(size, 0u);
auto fd = base::unique_fd(ashmem_create_region("nnapi_ashmem", size));
if (!fd.ok()) {
return NN_ERROR() << "ashmem_create_region failed";
}
// TODO(b/205348471): verify size with ashmem_get_size_region
auto handle = Memory::Ashmem{
.fd = std::move(fd),
.size = size,
};
return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
}
GeneralResult<Mapping> map(const Memory::Ashmem& memory) {
constexpr off64_t offset = 0;
constexpr int prot = PROT_READ | PROT_WRITE;
std::shared_ptr<base::MappedFile> mapping =
base::MappedFile::FromFd(memory.fd, offset, memory.size, prot);
if (mapping == nullptr || mapping->data() == nullptr) {
return NN_ERROR() << "Can't mmap the file descriptor.";
}
return Mapping{
.pointer = mapping->data(),
.size = memory.size,
.context = std::move(mapping),
};
}
#else // NN_COMPATIBILITY_LIBRARY_BUILD
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) {
#ifdef __ANDROID__
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(memory.handle.get(), &desc);
return desc.format == AHARDWAREBUFFER_FORMAT_BLOB ? desc.width : 0;
#else // __ANDROID__
LOG(FATAL)
<< "size_t getSize(const Memory::HardwareBuffer& memory): Not Available on Host Build";
(void)memory;
return 0;
#endif // __ANDROID__
}
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) {
#ifdef __ANDROID__
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)};
#else // __ANDROID__
LOG(FATAL) << "GeneralResult<Mapping> map(const Memory::HardwareBuffer& memory): Not Available "
"on Host Build";
(void)memory;
return (NN_ERROR() << "map failed").operator nn::GeneralResult<Mapping>();
#endif // __ANDROID__
}
GeneralResult<Mapping> map(const Memory::Unknown& /*memory*/) {
return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Cannot map Unknown memory";
}
#ifdef __ANDROID__
void freeHardwareBuffer(AHardwareBuffer* buffer) {
if (buffer) {
AHardwareBuffer_release(buffer);
}
}
void freeNoop(AHardwareBuffer* /*buffer*/) {}
#endif // __ANDROID__
} // 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);
}
#ifdef __ANDROID__
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)});
}
#endif // __ANDROID__
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) {
#ifdef __ANDROID__
AHardwareBuffer* ahwb = memory.handle.get();
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(ahwb, &desc);
return desc.format == AHARDWAREBUFFER_FORMAT_BLOB;
#else // __ANDROID__
LOG(FATAL)
<< "bool isAhwbBlob(const Memory::HardwareBuffer& memory): Not Available on Host Build";
(void)memory;
return false;
#endif // __ANDROID__
}
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