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