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
23 #ifdef __ANDROID__
24 #include <android/hardware_buffer.h>
25 #endif // __ANDROID__
26
27 #include <algorithm>
28 #include <any>
29 #include <iterator>
30 #include <limits>
31 #include <memory>
32 #include <string>
33 #include <utility>
34 #include <variant>
35 #include <vector>
36
37 #include "Result.h"
38 #include "SharedMemory.h"
39 #include "TypeUtils.h"
40 #include "Types.h"
41
42 #ifndef NN_COMPATIBILITY_LIBRARY_BUILD
43 #include <cutils/ashmem.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
allocateSharedMemory(size_t size)64 GeneralResult<SharedMemory> allocateSharedMemory(size_t size) {
65 CHECK_GT(size, 0u);
66
67 auto fd = base::unique_fd(ashmem_create_region("nnapi_ashmem", size));
68 if (!fd.ok()) {
69 return NN_ERROR() << "ashmem_create_region failed";
70 }
71
72 // TODO(b/205348471): verify size with ashmem_get_size_region
73
74 auto handle = Memory::Ashmem{
75 .fd = std::move(fd),
76 .size = size,
77 };
78 return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
79 }
80
map(const Memory::Ashmem & memory)81 GeneralResult<Mapping> map(const Memory::Ashmem& memory) {
82 constexpr off64_t offset = 0;
83 constexpr int prot = PROT_READ | PROT_WRITE;
84
85 std::shared_ptr<base::MappedFile> mapping =
86 base::MappedFile::FromFd(memory.fd, offset, memory.size, prot);
87
88 if (mapping == nullptr || mapping->data() == nullptr) {
89 return NN_ERROR() << "Can't mmap the file descriptor.";
90 }
91
92 return Mapping{
93 .pointer = mapping->data(),
94 .size = memory.size,
95 .context = std::move(mapping),
96 };
97 }
98
99 #else // NN_COMPATIBILITY_LIBRARY_BUILD
100
allocateSharedMemory(size_t size)101 GeneralResult<SharedMemory> allocateSharedMemory(size_t size) {
102 CHECK_GT(size, 0u);
103
104 const CompatibilityLayerMemory& memory = loadCompatibilityLayerMemory();
105 auto fd = base::unique_fd(memory.create(nullptr, size));
106 if (!fd.ok()) {
107 return NN_ERROR() << "ASharedMemory_create failed";
108 }
109
110 const size_t readSize = memory.getSize(fd.get());
111 CHECK_GE(readSize, size);
112
113 constexpr int prot = PROT_READ | PROT_WRITE;
114 constexpr size_t offset = 0;
115 return createSharedMemoryFromUniqueFd(size, prot, std::move(fd), offset);
116 }
117
map(const Memory::Ashmem &)118 GeneralResult<Mapping> map(const Memory::Ashmem& /*memory*/) {
119 return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Cannot map ashmem memory";
120 }
121
122 #endif // NN_COMPATIBILITY_LIBRARY_BUILD
123
getSize(const Memory::Ashmem & memory)124 size_t getSize(const Memory::Ashmem& memory) {
125 return memory.size;
126 }
127
getSize(const Memory::Fd & memory)128 size_t getSize(const Memory::Fd& memory) {
129 return memory.size;
130 }
131
getSize(const Memory::HardwareBuffer & memory)132 size_t getSize(const Memory::HardwareBuffer& memory) {
133 #ifdef __ANDROID__
134 AHardwareBuffer_Desc desc;
135 AHardwareBuffer_describe(memory.handle.get(), &desc);
136 return desc.format == AHARDWAREBUFFER_FORMAT_BLOB ? desc.width : 0;
137 #else // __ANDROID__
138 LOG(FATAL)
139 << "size_t getSize(const Memory::HardwareBuffer& memory): Not Available on Host Build";
140 (void)memory;
141 return 0;
142 #endif // __ANDROID__
143 }
144
getSize(const Memory::Unknown & memory)145 size_t getSize(const Memory::Unknown& memory) {
146 return memory.size;
147 }
148
149 struct MmapFdMappingContext {
150 int prot;
151 std::any context;
152 };
153
map(const Memory::Fd & memory)154 GeneralResult<Mapping> map(const Memory::Fd& memory) {
155 std::shared_ptr<base::MappedFile> mapping =
156 base::MappedFile::FromFd(memory.fd, memory.offset, memory.size, memory.prot);
157 if (mapping == nullptr) {
158 return NN_ERROR() << "Can't mmap the file descriptor.";
159 }
160 char* data = mapping->data();
161
162 const bool writable = (memory.prot & PROT_WRITE) != 0;
163 std::variant<const void*, void*> pointer;
164 if (writable) {
165 pointer = static_cast<void*>(data);
166 } else {
167 pointer = static_cast<const void*>(data);
168 }
169
170 auto context = MmapFdMappingContext{.prot = memory.prot, .context = std::move(mapping)};
171 return Mapping{.pointer = pointer, .size = memory.size, .context = std::move(context)};
172 }
173
map(const Memory::HardwareBuffer & memory)174 GeneralResult<Mapping> map(const Memory::HardwareBuffer& memory) {
175 #ifdef __ANDROID__
176 AHardwareBuffer_Desc desc;
177 AHardwareBuffer_describe(memory.handle.get(), &desc);
178
179 if (desc.format != AHARDWAREBUFFER_FORMAT_BLOB) {
180 return NN_ERROR() << "Unable to map non-blob AHardwareBuffer memory";
181 }
182 const uint32_t size = desc.width;
183
184 const uint64_t kCpuUsageMask =
185 AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK;
186 void* data = nullptr;
187 const auto status = AHardwareBuffer_lock(memory.handle.get(), desc.usage & kCpuUsageMask, -1,
188 nullptr, &data);
189 if (status != /*NO_ERROR*/ 0) {
190 return NN_ERROR() << "Can't lock the AHardwareBuffer. Error: " << status;
191 }
192
193 // Create shared scoped object to munmap.
194 auto scoped = base::make_scope_guard(
195 [ahwb = memory.handle.get()] { AHardwareBuffer_unlock(ahwb, nullptr); });
196 auto sharedScoped = std::make_shared<decltype(scoped)>(std::move(scoped));
197
198 return Mapping{.pointer = data, .size = size, .context = std::move(sharedScoped)};
199 #else // __ANDROID__
200 LOG(FATAL) << "GeneralResult<Mapping> map(const Memory::HardwareBuffer& memory): Not Available "
201 "on Host Build";
202 (void)memory;
203 return (NN_ERROR() << "map failed").operator nn::GeneralResult<Mapping>();
204 #endif // __ANDROID__
205 }
206
map(const Memory::Unknown &)207 GeneralResult<Mapping> map(const Memory::Unknown& /*memory*/) {
208 return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Cannot map Unknown memory";
209 }
210
211 #ifdef __ANDROID__
freeHardwareBuffer(AHardwareBuffer * buffer)212 void freeHardwareBuffer(AHardwareBuffer* buffer) {
213 if (buffer) {
214 AHardwareBuffer_release(buffer);
215 }
216 }
217
freeNoop(AHardwareBuffer *)218 void freeNoop(AHardwareBuffer* /*buffer*/) {}
219 #endif // __ANDROID__
220
221 } // namespace
222
dupFd(int fd)223 GeneralResult<base::unique_fd> dupFd(int fd) {
224 if (fd < 0) {
225 return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "dupFd was passed an invalid fd";
226 }
227 auto uniqueFd = base::unique_fd(dup(fd));
228 if (!uniqueFd.ok()) {
229 // TODO(b/120417090): is ANEURALNETWORKS_UNEXPECTED_NULL the correct error to return here?
230 return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to dup the fd";
231 }
232 return uniqueFd;
233 }
234
createSharedMemory(size_t size)235 GeneralResult<SharedMemory> createSharedMemory(size_t size) {
236 return allocateSharedMemory(size);
237 }
238
createSharedMemoryFromFd(size_t size,int prot,int fd,size_t offset)239 GeneralResult<SharedMemory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t offset) {
240 return createSharedMemoryFromUniqueFd(size, prot, NN_TRY(dupFd(fd)), offset);
241 }
242
243 #ifdef __ANDROID__
createSharedMemoryFromAHWB(AHardwareBuffer * ahwb,bool takeOwnership)244 GeneralResult<SharedMemory> createSharedMemoryFromAHWB(AHardwareBuffer* ahwb, bool takeOwnership) {
245 CHECK(ahwb != nullptr);
246 const Memory::HardwareBuffer::Deleter deleter = (takeOwnership ? freeHardwareBuffer : freeNoop);
247 Memory::HardwareBuffer handle = {.handle = Memory::HardwareBuffer::Handle(ahwb, deleter)};
248 return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
249 }
250 #endif // __ANDROID__
251
getSize(const SharedMemory & memory)252 size_t getSize(const SharedMemory& memory) {
253 CHECK(memory != nullptr);
254 return std::visit([](const auto& x) { return getSize(x); }, memory->handle);
255 }
256
isAhwbBlob(const Memory::HardwareBuffer & memory)257 bool isAhwbBlob(const Memory::HardwareBuffer& memory) {
258 #ifdef __ANDROID__
259 AHardwareBuffer* ahwb = memory.handle.get();
260 AHardwareBuffer_Desc desc;
261 AHardwareBuffer_describe(ahwb, &desc);
262 return desc.format == AHARDWAREBUFFER_FORMAT_BLOB;
263 #else // __ANDROID__
264 LOG(FATAL)
265 << "bool isAhwbBlob(const Memory::HardwareBuffer& memory): Not Available on Host Build";
266 (void)memory;
267 return false;
268 #endif // __ANDROID__
269 }
270
isAhwbBlob(const SharedMemory & memory)271 bool isAhwbBlob(const SharedMemory& memory) {
272 CHECK(memory != nullptr);
273 if (!std::holds_alternative<Memory::HardwareBuffer>(memory->handle)) {
274 return false;
275 }
276 return isAhwbBlob(std::get<Memory::HardwareBuffer>(memory->handle));
277 }
278
map(const SharedMemory & memory)279 GeneralResult<Mapping> map(const SharedMemory& memory) {
280 if (memory == nullptr) {
281 return NN_ERROR() << "Unable to map nullptr SharedMemory object";
282 }
283 return std::visit([](const auto& x) { return map(x); }, memory->handle);
284 }
285
flush(const Mapping & mapping)286 bool flush(const Mapping& mapping) {
287 if (const auto* mmapFdMapping = std::any_cast<MmapFdMappingContext>(&mapping.context)) {
288 if (!std::holds_alternative<void*>(mapping.pointer)) {
289 return true;
290 }
291 void* data = std::get<void*>(mapping.pointer);
292 const int prot = mmapFdMapping->prot;
293 if (prot & PROT_WRITE) {
294 const size_t size = mapping.size;
295 return msync(data, size, MS_SYNC) == 0;
296 }
297 }
298 // No-op for other types of memory.
299 return true;
300 }
301
302 } // namespace android::nn
303