| /* |
| * 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 "SharedMemoryHost" |
| |
| #include <android-base/logging.h> |
| #include <android-base/mapped_file.h> |
| #include <cutils/ashmem.h> |
| #include <sys/mman.h> |
| |
| #include <limits> |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "Result.h" |
| #include "SharedMemory.h" |
| #include "TypeUtils.h" |
| #include "Types.h" |
| |
| namespace android::nn { |
| namespace { |
| |
| GeneralResult<Mapping> mapAshmem(const Memory& memory) { |
| CHECK_LE(memory.size, std::numeric_limits<uint32_t>::max()); |
| const auto size = memory.size; |
| |
| const int fd = std::get<Handle>(memory.handle).fds[0]; |
| std::shared_ptr<base::MappedFile> mapping = |
| base::MappedFile::FromFd(fd, /*offset=*/0, size, PROT_READ | PROT_WRITE); |
| if (mapping == nullptr) { |
| return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Can't mmap the file descriptor."; |
| } |
| void* data = mapping->data(); |
| |
| return Mapping{.pointer = data, .size = size, .context = std::move(mapping)}; |
| } |
| |
| struct MmapFdMappingContext { |
| int prot; |
| std::any context; |
| }; |
| |
| GeneralResult<Mapping> mapMemFd(const Memory& memory) { |
| const size_t size = memory.size; |
| const Handle& handle = std::get<Handle>(memory.handle); |
| const int fd = handle->fds[0]; |
| const int prot = handle->ints[0]; |
| const size_t offset = getOffsetFromInts(handle->ints[1], handle->ints[2]); |
| |
| std::shared_ptr<base::MappedFile> mapping = base::MappedFile::FromFd(fd, offset, size, prot); |
| if (mapping == nullptr) { |
| return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Can't mmap the file descriptor."; |
| } |
| void* data = mapping->data(); |
| |
| auto context = MmapFdMappingContext{.prot = prot, .context = std::move(mapping)}; |
| return Mapping{.pointer = data, .size = size, .context = std::move(context)}; |
| } |
| |
| void freeNoop(AHardwareBuffer* /*buffer*/) {} |
| |
| } // namespace |
| |
| HardwareBufferHandle::HardwareBufferHandle(AHardwareBuffer* handle, bool /*takeOwnership*/) |
| : mHandle(handle, freeNoop) { |
| CHECK(mHandle != nullptr); |
| } |
| |
| AHardwareBuffer* HardwareBufferHandle::get() const { |
| return mHandle.get(); |
| } |
| |
| GeneralResult<SharedMemory> createSharedMemory(size_t size) { |
| int fd = ashmem_create_region("NnapiAshmem", size); |
| if (fd < 0) { |
| return NN_ERROR(ErrorStatus::GENERAL_FAILURE) |
| << "ashmem_create_region(" << size << ") fails with " << fd; |
| } |
| |
| std::vector<base::unique_fd> fds; |
| fds.emplace_back(fd); |
| |
| auto handle = Handle{ |
| .fds = std::move(fds), |
| .ints = {}, |
| }); |
| return std::make_shared<const Memory>( |
| Memory{.handle = std::move(handle), .size = size, .name = "ashmem"}); |
| } |
| |
| GeneralResult<SharedMemory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t offset) { |
| if (size == 0 || fd < 0) { |
| return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Invalid size or fd"; |
| } |
| |
| // Duplicate the file descriptor so the resultant Memory owns its own version. |
| int dupFd = dup(fd); |
| if (dupFd == -1) { |
| // TODO(b/120417090): is ANEURALNETWORKS_UNEXPECTED_NULL the correct error to return here? |
| return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Failed to dup the fd"; |
| } |
| |
| std::vector<base::unique_fd> fds; |
| fds.emplace_back(dupFd); |
| |
| const auto [lowOffsetBits, highOffsetBits] = getIntsFromOffset(offset); |
| std::vector<int> ints = {prot, lowOffsetBits, highOffsetBits}; |
| |
| auto handle = Handle{ |
| .fds = std::move(fds), |
| .ints = std::move(ints), |
| }); |
| return SharedMemory{.handle = std::move(handle), .size = size, .name = "mmap_fd"}; |
| } |
| |
| GeneralResult<SharedMemory> createSharedMemoryFromAHWB(const AHardwareBuffer& /*ahwb*/) { |
| return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) |
| << "AHardwareBuffer memory not supported on host"; |
| } |
| |
| GeneralResult<Mapping> map(const SharedMemory& memory) { |
| CHECK(memory != nullptr); |
| if (memory->name == "ashmem") { |
| return mapAshmem(*memory); |
| } |
| if (memory->name == "mmap_fd") { |
| return mapMemFd(*memory); |
| } |
| return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Cannot map unknown memory " << memory->name; |
| } |
| |
| 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 |