blob: 6d8552a62eaf1816af800c16094120aca109230b [file] [log] [blame]
John Stultz6a407882020-07-11 04:34:19 +00001/*
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_NDEBUG 0
18#define LOG_TAG "C2DmaBufAllocator"
Lajos Molnar4921c0b2021-03-18 10:35:19 -070019
John Stultz6a407882020-07-11 04:34:19 +000020#include <BufferAllocator/BufferAllocator.h>
21#include <C2Buffer.h>
22#include <C2Debug.h>
23#include <C2DmaBufAllocator.h>
24#include <C2ErrnoUtils.h>
Lajos Molnar4921c0b2021-03-18 10:35:19 -070025
John Stultz6a407882020-07-11 04:34:19 +000026#include <linux/ion.h>
27#include <sys/mman.h>
28#include <unistd.h> // getpagesize, size_t, close, dup
29#include <utils/Log.h>
30
31#include <list>
32
John Stultz6a407882020-07-11 04:34:19 +000033#include <android-base/properties.h>
John Stultz6a407882020-07-11 04:34:19 +000034
35namespace android {
36
37namespace {
Lajos Molnar4921c0b2021-03-18 10:35:19 -070038 constexpr size_t USAGE_LRU_CACHE_SIZE = 1024;
39
40 // max padding after ion/dmabuf allocations in bytes
41 constexpr uint32_t MAX_PADDING = 0x8000; // 32KB
John Stultz6a407882020-07-11 04:34:19 +000042}
43
44/* =========================== BUFFER HANDLE =========================== */
45/**
46 * Buffer handle
47 *
48 * Stores dmabuf fd & metadata
49 *
50 * This handle will not capture mapped fd-s as updating that would require a
51 * global mutex.
52 */
53
54struct C2HandleBuf : public C2Handle {
55 C2HandleBuf(int bufferFd, size_t size)
56 : C2Handle(cHeader),
57 mFds{bufferFd},
58 mInts{int(size & 0xFFFFFFFF), int((uint64_t(size) >> 32) & 0xFFFFFFFF), kMagic} {}
59
60 static bool IsValid(const C2Handle* const o);
61
62 int bufferFd() const { return mFds.mBuffer; }
63 size_t size() const {
64 return size_t(unsigned(mInts.mSizeLo)) | size_t(uint64_t(unsigned(mInts.mSizeHi)) << 32);
65 }
66
67 protected:
68 struct {
69 int mBuffer; // dmabuf fd
70 } mFds;
71 struct {
72 int mSizeLo; // low 32-bits of size
73 int mSizeHi; // high 32-bits of size
74 int mMagic;
75 } mInts;
76
77 private:
78 typedef C2HandleBuf _type;
79 enum {
80 kMagic = '\xc2io\x00',
81 numFds = sizeof(mFds) / sizeof(int),
82 numInts = sizeof(mInts) / sizeof(int),
83 version = sizeof(C2Handle)
84 };
85 // constexpr static C2Handle cHeader = { version, numFds, numInts, {} };
86 const static C2Handle cHeader;
87};
88
89const C2Handle C2HandleBuf::cHeader = {
90 C2HandleBuf::version, C2HandleBuf::numFds, C2HandleBuf::numInts, {}};
91
92// static
93bool C2HandleBuf::IsValid(const C2Handle* const o) {
94 if (!o || memcmp(o, &cHeader, sizeof(cHeader))) {
95 return false;
96 }
97 const C2HandleBuf* other = static_cast<const C2HandleBuf*>(o);
98 return other->mInts.mMagic == kMagic;
99}
100
101/* =========================== DMABUF ALLOCATION =========================== */
102class C2DmaBufAllocation : public C2LinearAllocation {
103 public:
104 /* Interface methods */
105 virtual c2_status_t map(size_t offset, size_t size, C2MemoryUsage usage, C2Fence* fence,
106 void** addr /* nonnull */) override;
107 virtual c2_status_t unmap(void* addr, size_t size, C2Fence* fenceFd) override;
108 virtual ~C2DmaBufAllocation() override;
109 virtual const C2Handle* handle() const override;
110 virtual id_t getAllocatorId() const override;
111 virtual bool equals(const std::shared_ptr<C2LinearAllocation>& other) const override;
112
113 // internal methods
114 C2DmaBufAllocation(BufferAllocator& alloc, size_t size, C2String heap_name, unsigned flags,
115 C2Allocator::id_t id);
116 C2DmaBufAllocation(size_t size, int shareFd, C2Allocator::id_t id);
117
118 c2_status_t status() const;
119
120 protected:
121 virtual c2_status_t mapInternal(size_t mapSize, size_t mapOffset, size_t alignmentBytes,
122 int prot, int flags, void** base, void** addr) {
123 c2_status_t err = C2_OK;
124 *base = mmap(nullptr, mapSize, prot, flags, mHandle.bufferFd(), mapOffset);
125 ALOGV("mmap(size = %zu, prot = %d, flags = %d, mapFd = %d, offset = %zu) "
126 "returned (%d)",
127 mapSize, prot, flags, mHandle.bufferFd(), mapOffset, errno);
128 if (*base == MAP_FAILED) {
129 *base = *addr = nullptr;
130 err = c2_map_errno<EINVAL>(errno);
131 } else {
132 *addr = (uint8_t*)*base + alignmentBytes;
133 }
134 return err;
135 }
136
137 C2Allocator::id_t mId;
138 C2HandleBuf mHandle;
139 c2_status_t mInit;
140 struct Mapping {
141 void* addr;
142 size_t alignmentBytes;
143 size_t size;
144 };
145 std::list<Mapping> mMappings;
146
147 // TODO: we could make this encapsulate shared_ptr and copiable
148 C2_DO_NOT_COPY(C2DmaBufAllocation);
149};
150
151c2_status_t C2DmaBufAllocation::map(size_t offset, size_t size, C2MemoryUsage usage, C2Fence* fence,
152 void** addr) {
153 (void)fence; // TODO: wait for fence
154 *addr = nullptr;
155 if (!mMappings.empty()) {
156 ALOGV("multiple map");
157 // TODO: technically we should return DUPLICATE here, but our block views
158 // don't actually unmap, so we end up remapping the buffer multiple times.
159 //
160 // return C2_DUPLICATE;
161 }
162 if (size == 0) {
163 return C2_BAD_VALUE;
164 }
165
166 int prot = PROT_NONE;
167 int flags = MAP_SHARED;
168 if (usage.expected & C2MemoryUsage::CPU_READ) {
169 prot |= PROT_READ;
170 }
171 if (usage.expected & C2MemoryUsage::CPU_WRITE) {
172 prot |= PROT_WRITE;
173 }
174
175 size_t alignmentBytes = offset % PAGE_SIZE;
176 size_t mapOffset = offset - alignmentBytes;
177 size_t mapSize = size + alignmentBytes;
178 Mapping map = {nullptr, alignmentBytes, mapSize};
179
180 c2_status_t err =
181 mapInternal(mapSize, mapOffset, alignmentBytes, prot, flags, &(map.addr), addr);
182 if (map.addr) {
183 mMappings.push_back(map);
184 }
185 return err;
186}
187
188c2_status_t C2DmaBufAllocation::unmap(void* addr, size_t size, C2Fence* fence) {
189 if (mMappings.empty()) {
190 ALOGD("tried to unmap unmapped buffer");
191 return C2_NOT_FOUND;
192 }
193 for (auto it = mMappings.begin(); it != mMappings.end(); ++it) {
194 if (addr != (uint8_t*)it->addr + it->alignmentBytes ||
195 size + it->alignmentBytes != it->size) {
196 continue;
197 }
198 int err = munmap(it->addr, it->size);
199 if (err != 0) {
200 ALOGD("munmap failed");
201 return c2_map_errno<EINVAL>(errno);
202 }
203 if (fence) {
204 *fence = C2Fence(); // not using fences
205 }
206 (void)mMappings.erase(it);
207 ALOGV("successfully unmapped: %d", mHandle.bufferFd());
208 return C2_OK;
209 }
210 ALOGD("unmap failed to find specified map");
211 return C2_BAD_VALUE;
212}
213
214c2_status_t C2DmaBufAllocation::status() const {
215 return mInit;
216}
217
218C2Allocator::id_t C2DmaBufAllocation::getAllocatorId() const {
219 return mId;
220}
221
222bool C2DmaBufAllocation::equals(const std::shared_ptr<C2LinearAllocation>& other) const {
223 if (!other || other->getAllocatorId() != getAllocatorId()) {
224 return false;
225 }
226 // get user handle to compare objects
227 std::shared_ptr<C2DmaBufAllocation> otherAsBuf =
228 std::static_pointer_cast<C2DmaBufAllocation>(other);
229 return mHandle.bufferFd() == otherAsBuf->mHandle.bufferFd();
230}
231
232const C2Handle* C2DmaBufAllocation::handle() const {
233 return &mHandle;
234}
235
236C2DmaBufAllocation::~C2DmaBufAllocation() {
237 if (!mMappings.empty()) {
238 ALOGD("Dangling mappings!");
239 for (const Mapping& map : mMappings) {
240 int err = munmap(map.addr, map.size);
241 if (err) ALOGD("munmap failed");
242 }
243 }
244 if (mInit == C2_OK) {
245 native_handle_close(&mHandle);
246 }
247}
248
249C2DmaBufAllocation::C2DmaBufAllocation(BufferAllocator& alloc, size_t size, C2String heap_name,
250 unsigned flags, C2Allocator::id_t id)
251 : C2LinearAllocation(size), mHandle(-1, 0) {
252 int bufferFd = -1;
253 int ret = 0;
254
Lajos Molnar83d0abe2021-03-25 17:44:17 +0000255 bufferFd = alloc.Alloc(heap_name, size, flags);
Lajos Molnar4921c0b2021-03-18 10:35:19 -0700256 if (bufferFd < 0) {
257 ret = bufferFd;
258 }
John Stultz6a407882020-07-11 04:34:19 +0000259
Lajos Molnar4921c0b2021-03-18 10:35:19 -0700260 // this may be a non-working handle if bufferFd is negative
John Stultz6a407882020-07-11 04:34:19 +0000261 mHandle = C2HandleBuf(bufferFd, size);
262 mId = id;
263 mInit = c2_status_t(c2_map_errno<ENOMEM, EACCES, EINVAL>(ret));
264}
265
266C2DmaBufAllocation::C2DmaBufAllocation(size_t size, int shareFd, C2Allocator::id_t id)
267 : C2LinearAllocation(size), mHandle(-1, 0) {
268 mHandle = C2HandleBuf(shareFd, size);
269 mId = id;
270 mInit = c2_status_t(c2_map_errno<ENOMEM, EACCES, EINVAL>(0));
271}
272
273/* =========================== DMABUF ALLOCATOR =========================== */
274C2DmaBufAllocator::C2DmaBufAllocator(id_t id) : mInit(C2_OK) {
275 C2MemoryUsage minUsage = {0, 0};
276 C2MemoryUsage maxUsage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
277 Traits traits = {"android.allocator.dmabuf", id, LINEAR, minUsage, maxUsage};
278 mTraits = std::make_shared<Traits>(traits);
279}
280
281C2Allocator::id_t C2DmaBufAllocator::getId() const {
282 std::lock_guard<std::mutex> lock(mUsageMapperLock);
283 return mTraits->id;
284}
285
286C2String C2DmaBufAllocator::getName() const {
287 std::lock_guard<std::mutex> lock(mUsageMapperLock);
288 return mTraits->name;
289}
290
291std::shared_ptr<const C2Allocator::Traits> C2DmaBufAllocator::getTraits() const {
292 std::lock_guard<std::mutex> lock(mUsageMapperLock);
293 return mTraits;
294}
295
296void C2DmaBufAllocator::setUsageMapper(const UsageMapperFn& mapper __unused, uint64_t minUsage,
297 uint64_t maxUsage, uint64_t blockSize) {
298 std::lock_guard<std::mutex> lock(mUsageMapperLock);
299 mUsageMapperCache.clear();
300 mUsageMapperLru.clear();
301 mUsageMapper = mapper;
302 Traits traits = {mTraits->name, mTraits->id, LINEAR, C2MemoryUsage(minUsage),
303 C2MemoryUsage(maxUsage)};
304 mTraits = std::make_shared<Traits>(traits);
305 mBlockSize = blockSize;
306}
307
308std::size_t C2DmaBufAllocator::MapperKeyHash::operator()(const MapperKey& k) const {
309 return std::hash<uint64_t>{}(k.first) ^ std::hash<size_t>{}(k.second);
310}
311
312c2_status_t C2DmaBufAllocator::mapUsage(C2MemoryUsage usage, size_t capacity, C2String* heap_name,
313 unsigned* flags) {
314 std::lock_guard<std::mutex> lock(mUsageMapperLock);
315 c2_status_t res = C2_OK;
316 // align capacity
317 capacity = (capacity + mBlockSize - 1) & ~(mBlockSize - 1);
318 MapperKey key = std::make_pair(usage.expected, capacity);
319 auto entry = mUsageMapperCache.find(key);
320 if (entry == mUsageMapperCache.end()) {
321 if (mUsageMapper) {
322 res = mUsageMapper(usage, capacity, heap_name, flags);
323 } else {
John Stultz9eda9bf2021-01-08 00:41:13 +0000324 if (C2DmaBufAllocator::system_uncached_supported() &&
325 !(usage.expected & (C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE)))
John Stultz6a407882020-07-11 04:34:19 +0000326 *heap_name = "system-uncached";
327 else
328 *heap_name = "system";
329 *flags = 0;
330 res = C2_NO_INIT;
331 }
332 // add usage to cache
333 MapperValue value = std::make_tuple(*heap_name, *flags, res);
334 mUsageMapperLru.emplace_front(key, value);
335 mUsageMapperCache.emplace(std::make_pair(key, mUsageMapperLru.begin()));
336 if (mUsageMapperCache.size() > USAGE_LRU_CACHE_SIZE) {
337 // remove LRU entry
338 MapperKey lruKey = mUsageMapperLru.front().first;
339 mUsageMapperCache.erase(lruKey);
340 mUsageMapperLru.pop_back();
341 }
342 } else {
343 // move entry to MRU
344 mUsageMapperLru.splice(mUsageMapperLru.begin(), mUsageMapperLru, entry->second);
345 const MapperValue& value = entry->second->second;
346 std::tie(*heap_name, *flags, res) = value;
347 }
348 return res;
349}
350
351c2_status_t C2DmaBufAllocator::newLinearAllocation(
352 uint32_t capacity, C2MemoryUsage usage, std::shared_ptr<C2LinearAllocation>* allocation) {
353 if (allocation == nullptr) {
354 return C2_BAD_VALUE;
355 }
356
357 allocation->reset();
358 if (mInit != C2_OK) {
359 return mInit;
360 }
361
362 C2String heap_name;
363 unsigned flags = 0;
364 c2_status_t ret = mapUsage(usage, capacity, &heap_name, &flags);
365 if (ret && ret != C2_NO_INIT) {
366 return ret;
367 }
368
Lajos Molnar4921c0b2021-03-18 10:35:19 -0700369 // TODO: should we pad before mapping usage?
370
371 // NOTE: read this property directly from the property as this code has to run on
372 // Android Q, but the sysprop was only introduced in Android S.
373 static size_t sPadding =
374 base::GetUintProperty("media.c2.dmabuf.padding", (uint32_t)0, MAX_PADDING);
375 if (sPadding > SIZE_MAX - capacity) {
376 // size would overflow
377 ALOGD("dmabuf_alloc: size #%x cannot accommodate padding #%zx", capacity, sPadding);
378 return C2_NO_MEMORY;
379 }
380
381 size_t allocSize = (size_t)capacity + sPadding;
382 // TODO: should we align allocation size to mBlockSize to reflect the true allocation size?
John Stultz6a407882020-07-11 04:34:19 +0000383 std::shared_ptr<C2DmaBufAllocation> alloc = std::make_shared<C2DmaBufAllocation>(
Lajos Molnar4921c0b2021-03-18 10:35:19 -0700384 mBufferAllocator, allocSize, heap_name, flags, getId());
John Stultz6a407882020-07-11 04:34:19 +0000385 ret = alloc->status();
386 if (ret == C2_OK) {
387 *allocation = alloc;
388 }
389 return ret;
390}
391
392c2_status_t C2DmaBufAllocator::priorLinearAllocation(
393 const C2Handle* handle, std::shared_ptr<C2LinearAllocation>* allocation) {
394 *allocation = nullptr;
395 if (mInit != C2_OK) {
396 return mInit;
397 }
398
399 if (!C2HandleBuf::IsValid(handle)) {
400 return C2_BAD_VALUE;
401 }
402
403 // TODO: get capacity and validate it
404 const C2HandleBuf* h = static_cast<const C2HandleBuf*>(handle);
405 std::shared_ptr<C2DmaBufAllocation> alloc =
406 std::make_shared<C2DmaBufAllocation>(h->size(), h->bufferFd(), getId());
407 c2_status_t ret = alloc->status();
408 if (ret == C2_OK) {
409 *allocation = alloc;
410 native_handle_delete(
411 const_cast<native_handle_t*>(reinterpret_cast<const native_handle_t*>(handle)));
412 }
413 return ret;
414}
415
416// static
417bool C2DmaBufAllocator::CheckHandle(const C2Handle* const o) {
418 return C2HandleBuf::IsValid(o);
419}
420
421} // namespace android