1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "SharedMemoryAndroid"
18
19 #include <android-base/logging.h>
20 #include <android-base/mapped_file.h>
21 #include <android-base/scopeguard.h>
22 #include <android/hardware_buffer.h>
23
24 #include <algorithm>
25 #include <any>
26 #include <iterator>
27 #include <limits>
28 #include <memory>
29 #include <string>
30 #include <utility>
31 #include <variant>
32 #include <vector>
33
34 #include "Result.h"
35 #include "SharedMemory.h"
36 #include "TypeUtils.h"
37 #include "Types.h"
38
39 #ifndef NN_COMPATIBILITY_LIBRARY_BUILD
40 #include <android/hidl/allocator/1.0/IAllocator.h>
41 #include <hidl/HidlSupport.h>
42 #include <hidlmemory/mapping.h>
43 #include <sys/mman.h>
44 #else
45 #include "DynamicCLDeps.h"
46 #endif // NN_COMPATIBILITY_LIBRARY_BUILD
47
48 namespace android::nn {
49 namespace {
50
createSharedMemoryFromUniqueFd(size_t size,int prot,base::unique_fd fd,size_t offset)51 GeneralResult<SharedMemory> createSharedMemoryFromUniqueFd(size_t size, int prot,
52 base::unique_fd fd, size_t offset) {
53 auto handle = Memory::Fd{
54 .size = size,
55 .prot = prot,
56 .fd = std::move(fd),
57 .offset = offset,
58 };
59 return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
60 }
61
62 #ifndef NN_COMPATIBILITY_LIBRARY_BUILD
63
64 using ::android::hardware::hidl_memory;
65 using ::android::hidl::allocator::V1_0::IAllocator;
66
67 const char* const kAllocatorService = "ashmem";
68
hidlHandleFromUniqueFd(base::unique_fd fd)69 GeneralResult<hardware::hidl_handle> hidlHandleFromUniqueFd(base::unique_fd fd) {
70 native_handle_t* nativeHandle = native_handle_create(1, 0);
71 if (nativeHandle == nullptr) {
72 return NN_ERROR() << "Failed to create native_handle";
73 }
74 nativeHandle->data[0] = fd.release();
75
76 hardware::hidl_handle hidlHandle;
77 hidlHandle.setTo(nativeHandle, /*shouldOwn=*/true);
78 return hidlHandle;
79 }
80
allocateSharedMemory(size_t size)81 GeneralResult<SharedMemory> allocateSharedMemory(size_t size) {
82 static const auto allocator = IAllocator::getService(kAllocatorService);
83 CHECK_GT(size, 0u);
84
85 hidl_memory maybeMemory;
86 auto fn = [&maybeMemory](bool success, const hidl_memory& memory) {
87 if (success) {
88 maybeMemory = memory;
89 }
90 };
91 allocator->allocate(size, fn);
92
93 if (!maybeMemory.valid()) {
94 return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
95 << "IAllocator::allocate returned an invalid (empty) memory object";
96 }
97 if (maybeMemory.handle()->numFds != 1) {
98 return NN_ERROR() << "IAllocator::allocate returned an invalid memory object with "
99 << maybeMemory.handle()->numFds << " numFds, but expected 1";
100 }
101 if (maybeMemory.handle()->numInts != 0) {
102 return NN_ERROR() << "IAllocator::allocate returned an invalid memory object with "
103 << maybeMemory.handle()->numInts << " numInts, but expected 0";
104 }
105
106 CHECK_LE(maybeMemory.size(), std::numeric_limits<size_t>::max());
107 const int fd = maybeMemory.handle()->data[0];
108
109 auto handle = Memory::Ashmem{
110 .fd = NN_TRY(dupFd(fd)),
111 .size = static_cast<size_t>(maybeMemory.size()),
112 };
113 return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
114 }
115
map(const Memory::Ashmem & memory)116 GeneralResult<Mapping> map(const Memory::Ashmem& memory) {
117 auto handle = NN_TRY(hidlHandleFromUniqueFd(NN_TRY(dupFd(memory.fd))));
118 const auto hidlMemory = hidl_memory("ashmem", std::move(handle), memory.size);
119
120 const auto mapping = mapMemory(hidlMemory);
121 if (mapping == nullptr) {
122 return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to map memory";
123 }
124
125 auto* const pointer = mapping->getPointer().withDefault(nullptr);
126 if (pointer == nullptr) {
127 return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to get the mapped pointer";
128 }
129
130 const auto fullSize = mapping->getSize().withDefault(0);
131 if (fullSize == 0 || fullSize > std::numeric_limits<size_t>::max()) {
132 return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to get the mapped size";
133 }
134
135 const size_t size = static_cast<size_t>(fullSize);
136
137 return Mapping{
138 .pointer = pointer,
139 .size = size,
140 .context = mapping,
141 };
142 }
143
144 #else
145
allocateSharedMemory(size_t size)146 GeneralResult<SharedMemory> allocateSharedMemory(size_t size) {
147 CHECK_GT(size, 0u);
148
149 const CompatibilityLayerMemory& memory = loadCompatibilityLayerMemory();
150 auto fd = base::unique_fd(memory.create(nullptr, size));
151 if (!fd.ok()) {
152 return NN_ERROR() << "ASharedMemory_create failed";
153 }
154
155 const size_t readSize = memory.getSize(fd.get());
156 CHECK_GE(readSize, size);
157
158 constexpr int prot = PROT_READ | PROT_WRITE;
159 constexpr size_t offset = 0;
160 return createSharedMemoryFromUniqueFd(size, prot, std::move(fd), offset);
161 }
162
map(const Memory::Ashmem &)163 GeneralResult<Mapping> map(const Memory::Ashmem& /*memory*/) {
164 return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Cannot map ashmem memory";
165 }
166
167 #endif // NN_COMPATIBILITY_LIBRARY_BUILD
168
getSize(const Memory::Ashmem & memory)169 size_t getSize(const Memory::Ashmem& memory) {
170 return memory.size;
171 }
172
getSize(const Memory::Fd & memory)173 size_t getSize(const Memory::Fd& memory) {
174 return memory.size;
175 }
176
getSize(const Memory::HardwareBuffer & memory)177 size_t getSize(const Memory::HardwareBuffer& memory) {
178 AHardwareBuffer_Desc desc;
179 AHardwareBuffer_describe(memory.handle.get(), &desc);
180 return desc.format == AHARDWAREBUFFER_FORMAT_BLOB ? desc.width : 0;
181 }
182
getSize(const Memory::Unknown & memory)183 size_t getSize(const Memory::Unknown& memory) {
184 return memory.size;
185 }
186
187 struct MmapFdMappingContext {
188 int prot;
189 std::any context;
190 };
191
map(const Memory::Fd & memory)192 GeneralResult<Mapping> map(const Memory::Fd& memory) {
193 std::shared_ptr<base::MappedFile> mapping =
194 base::MappedFile::FromFd(memory.fd, memory.offset, memory.size, memory.prot);
195 if (mapping == nullptr) {
196 return NN_ERROR() << "Can't mmap the file descriptor.";
197 }
198 char* data = mapping->data();
199
200 const bool writable = (memory.prot & PROT_WRITE) != 0;
201 std::variant<const void*, void*> pointer;
202 if (writable) {
203 pointer = static_cast<void*>(data);
204 } else {
205 pointer = static_cast<const void*>(data);
206 }
207
208 auto context = MmapFdMappingContext{.prot = memory.prot, .context = std::move(mapping)};
209 return Mapping{.pointer = pointer, .size = memory.size, .context = std::move(context)};
210 }
211
map(const Memory::HardwareBuffer & memory)212 GeneralResult<Mapping> map(const Memory::HardwareBuffer& memory) {
213 AHardwareBuffer_Desc desc;
214 AHardwareBuffer_describe(memory.handle.get(), &desc);
215
216 if (desc.format != AHARDWAREBUFFER_FORMAT_BLOB) {
217 return NN_ERROR() << "Unable to map non-blob AHardwareBuffer memory";
218 }
219 const uint32_t size = desc.width;
220
221 const uint64_t kCpuUsageMask =
222 AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK;
223 void* data = nullptr;
224 const auto status = AHardwareBuffer_lock(memory.handle.get(), desc.usage & kCpuUsageMask, -1,
225 nullptr, &data);
226 if (status != /*NO_ERROR*/ 0) {
227 return NN_ERROR() << "Can't lock the AHardwareBuffer. Error: " << status;
228 }
229
230 // Create shared scoped object to munmap.
231 auto scoped = base::make_scope_guard(
232 [ahwb = memory.handle.get()] { AHardwareBuffer_unlock(ahwb, nullptr); });
233 auto sharedScoped = std::make_shared<decltype(scoped)>(std::move(scoped));
234
235 return Mapping{.pointer = data, .size = size, .context = std::move(sharedScoped)};
236 }
237
map(const Memory::Unknown &)238 GeneralResult<Mapping> map(const Memory::Unknown& /*memory*/) {
239 return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Cannot map Unknown memory";
240 }
241
freeHardwareBuffer(AHardwareBuffer * buffer)242 void freeHardwareBuffer(AHardwareBuffer* buffer) {
243 if (buffer) {
244 AHardwareBuffer_release(buffer);
245 }
246 }
247
freeNoop(AHardwareBuffer *)248 void freeNoop(AHardwareBuffer* /*buffer*/) {}
249
250 } // namespace
251
dupFd(int fd)252 GeneralResult<base::unique_fd> dupFd(int fd) {
253 if (fd < 0) {
254 return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "dupFd was passed an invalid fd";
255 }
256 auto uniqueFd = base::unique_fd(dup(fd));
257 if (!uniqueFd.ok()) {
258 // TODO(b/120417090): is ANEURALNETWORKS_UNEXPECTED_NULL the correct error to return here?
259 return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to dup the fd";
260 }
261 return uniqueFd;
262 }
263
createSharedMemory(size_t size)264 GeneralResult<SharedMemory> createSharedMemory(size_t size) {
265 return allocateSharedMemory(size);
266 }
267
createSharedMemoryFromFd(size_t size,int prot,int fd,size_t offset)268 GeneralResult<SharedMemory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t offset) {
269 return createSharedMemoryFromUniqueFd(size, prot, NN_TRY(dupFd(fd)), offset);
270 }
271
createSharedMemoryFromAHWB(AHardwareBuffer * ahwb,bool takeOwnership)272 GeneralResult<SharedMemory> createSharedMemoryFromAHWB(AHardwareBuffer* ahwb, bool takeOwnership) {
273 CHECK(ahwb != nullptr);
274 const Memory::HardwareBuffer::Deleter deleter = (takeOwnership ? freeHardwareBuffer : freeNoop);
275 Memory::HardwareBuffer handle = {.handle = Memory::HardwareBuffer::Handle(ahwb, deleter)};
276 return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
277 }
278
getSize(const SharedMemory & memory)279 size_t getSize(const SharedMemory& memory) {
280 CHECK(memory != nullptr);
281 return std::visit([](const auto& x) { return getSize(x); }, memory->handle);
282 }
283
isAhwbBlob(const Memory::HardwareBuffer & memory)284 bool isAhwbBlob(const Memory::HardwareBuffer& memory) {
285 AHardwareBuffer* ahwb = memory.handle.get();
286 AHardwareBuffer_Desc desc;
287 AHardwareBuffer_describe(ahwb, &desc);
288 return desc.format == AHARDWAREBUFFER_FORMAT_BLOB;
289 }
290
isAhwbBlob(const SharedMemory & memory)291 bool isAhwbBlob(const SharedMemory& memory) {
292 CHECK(memory != nullptr);
293 if (!std::holds_alternative<Memory::HardwareBuffer>(memory->handle)) {
294 return false;
295 }
296 return isAhwbBlob(std::get<Memory::HardwareBuffer>(memory->handle));
297 }
298
map(const SharedMemory & memory)299 GeneralResult<Mapping> map(const SharedMemory& memory) {
300 if (memory == nullptr) {
301 return NN_ERROR() << "Unable to map nullptr SharedMemory object";
302 }
303 return std::visit([](const auto& x) { return map(x); }, memory->handle);
304 }
305
flush(const Mapping & mapping)306 bool flush(const Mapping& mapping) {
307 if (const auto* mmapFdMapping = std::any_cast<MmapFdMappingContext>(&mapping.context)) {
308 if (!std::holds_alternative<void*>(mapping.pointer)) {
309 return true;
310 }
311 void* data = std::get<void*>(mapping.pointer);
312 const int prot = mmapFdMapping->prot;
313 if (prot & PROT_WRITE) {
314 const size_t size = mapping.size;
315 return msync(data, size, MS_SYNC) == 0;
316 }
317 }
318 // No-op for other types of memory.
319 return true;
320 }
321
322 } // namespace android::nn
323