| /* |
| * 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 |