Merge "Add MemoryDealer to libhidlmemory"
diff --git a/libhidlcache/Android.bp b/libhidlcache/Android.bp
index ea846d8..d28a4f1 100644
--- a/libhidlcache/Android.bp
+++ b/libhidlcache/Android.bp
@@ -38,16 +38,17 @@
"android.hidl.memory@1.0",
"android.hidl.memory.block@1.0",
"android.hidl.memory.token@1.0",
- "libhidlbase"
+ "libhidlbase",
],
srcs: [
"HidlMemoryCache.cpp",
- "mapping.cpp"
+ "MemoryDealer.cpp",
+ "mapping.cpp",
],
}
cc_test {
- name: "libhidlcache_test",
+ name: "libhidlcache_test",
defaults: ["hidl-gen-defaults"],
shared_libs: [
diff --git a/libhidlcache/MemoryDealer.cpp b/libhidlcache/MemoryDealer.cpp
new file mode 100644
index 0000000..e9196a1
--- /dev/null
+++ b/libhidlcache/MemoryDealer.cpp
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2007 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 "MemoryDealer"
+
+#include <hidlcache/MemoryDealer.h>
+#include <hidlmemory/HidlMemoryToken.h>
+#include <hidlmemory/mapping.h>
+
+#include <list>
+
+#include <log/log.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+using std::string;
+
+namespace android {
+namespace hardware {
+
+class SimpleBestFitAllocator {
+ enum { PAGE_ALIGNED = 0x00000001 };
+
+ public:
+ explicit SimpleBestFitAllocator(size_t size);
+ ~SimpleBestFitAllocator();
+
+ size_t allocate(size_t size, uint32_t flags = 0);
+ status_t deallocate(size_t offset);
+ size_t size() const;
+ void dump(const char* tag) const;
+ void dump(string& res, const char* tag) const;
+
+ static size_t getAllocationAlignment() { return kMemoryAlign; }
+
+ private:
+ struct chunk_t {
+ chunk_t(size_t start, size_t size) : start(start), size(size), free(1) {}
+ size_t start;
+ size_t size : 28;
+ int free : 4;
+ };
+ using List = std::list<chunk_t*>;
+ using Iterator = std::list<chunk_t*>::iterator;
+ using IteratorConst = std::list<chunk_t*>::const_iterator;
+ using Mutex = std::mutex;
+ using Lock = std::lock_guard<Mutex>;
+
+ ssize_t alloc(size_t size, uint32_t flags);
+ chunk_t* dealloc(size_t start);
+ void dump_l(const char* tag) const;
+ void dump_l(string& res, const char* tag) const;
+
+ static const int kMemoryAlign;
+ mutable Mutex mLock;
+ List mList;
+ size_t mHeapSize;
+};
+
+MemoryDealer::MemoryDealer(size_t size) : mAllocator(new SimpleBestFitAllocator(size)) {}
+
+MemoryDealer::~MemoryDealer() {
+ delete mAllocator;
+}
+
+ssize_t MemoryDealer::allocateOffset(size_t size) {
+ return mAllocator->allocate(size);
+}
+
+void MemoryDealer::deallocate(size_t offset) {
+ mAllocator->deallocate(offset);
+}
+
+void MemoryDealer::dump(const char* tag) const {
+ mAllocator->dump(tag);
+}
+
+size_t MemoryDealer::getAllocationAlignment() {
+ return SimpleBestFitAllocator::getAllocationAlignment();
+}
+
+// align all the memory blocks on a cache-line boundary
+const int SimpleBestFitAllocator::kMemoryAlign = 32;
+
+SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size) {
+ size_t pagesize = getpagesize();
+ mHeapSize = ((size + pagesize - 1) & ~(pagesize - 1));
+
+ chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign);
+ mList.push_front(node);
+}
+
+SimpleBestFitAllocator::~SimpleBestFitAllocator() {
+ while (mList.size() != 0) {
+ chunk_t* removed = mList.front();
+ mList.pop_front();
+#ifdef __clang_analyzer__
+ // Clang static analyzer gets confused in this loop
+ // and generates a false positive warning about accessing
+ // memory that is already freed.
+ // Add an "assert" to avoid the confusion.
+ LOG_ALWAYS_FATAL_IF(mList.head() == removed);
+#endif
+ delete removed;
+ }
+}
+
+size_t SimpleBestFitAllocator::size() const {
+ return mHeapSize;
+}
+
+size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags) {
+ Lock lock(mLock);
+ ssize_t offset = alloc(size, flags);
+ return offset;
+}
+
+status_t SimpleBestFitAllocator::deallocate(size_t offset) {
+ Lock lock(mLock);
+ chunk_t const* const freed = dealloc(offset);
+ if (freed) {
+ return NO_ERROR;
+ }
+ return NAME_NOT_FOUND;
+}
+
+ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags) {
+ if (size == 0) {
+ return 0;
+ }
+ size = (size + kMemoryAlign - 1) / kMemoryAlign;
+ size_t pagesize = getpagesize();
+
+ Iterator free_chunk_p = mList.end();
+ for (Iterator p = mList.begin(); p != mList.end(); p++) {
+ chunk_t* cur = *p;
+ int extra = 0;
+ if (flags & PAGE_ALIGNED) extra = (-cur->start & ((pagesize / kMemoryAlign) - 1));
+
+ // best fit
+ if (cur->free && (cur->size >= (size + extra))) {
+ if ((free_chunk_p == mList.end()) || (cur->size < (*free_chunk_p)->size)) {
+ free_chunk_p = p;
+ }
+ if (cur->size == size) {
+ break;
+ }
+ }
+ }
+ if (free_chunk_p != mList.end()) {
+ chunk_t* free_chunk = *free_chunk_p;
+ const size_t free_size = free_chunk->size;
+ free_chunk->free = 0;
+ free_chunk->size = size;
+ if (free_size > size) {
+ int extra = 0;
+ if (flags & PAGE_ALIGNED)
+ extra = (-free_chunk->start & ((pagesize / kMemoryAlign) - 1));
+ if (extra) {
+ chunk_t* split = new chunk_t(free_chunk->start, extra);
+ free_chunk->start += extra;
+ mList.insert(free_chunk_p, split);
+ }
+
+ ALOGE_IF(
+ (flags & PAGE_ALIGNED) && ((free_chunk->start * kMemoryAlign) & (pagesize - 1)),
+ "PAGE_ALIGNED requested, but page is not aligned!!!");
+
+ const ssize_t tail_free = free_size - (size + extra);
+ if (tail_free > 0) {
+ chunk_t* split = new chunk_t(free_chunk->start + free_chunk->size, tail_free);
+ mList.insert(++free_chunk_p, split);
+ }
+ }
+ return (free_chunk->start) * kMemoryAlign;
+ }
+ return NO_MEMORY;
+}
+
+SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) {
+ start = start / kMemoryAlign;
+
+ for (Iterator pos = mList.begin(); pos != mList.end(); pos++) {
+ chunk_t* cur = *pos;
+ if (cur->start == start) {
+ LOG_FATAL_IF(cur->free, "block at offset 0x%08lX of size 0x%08lX already freed",
+ cur->start * kMemoryAlign, cur->size * kMemoryAlign);
+
+ // merge freed blocks together
+ chunk_t* freed = cur;
+ cur->free = 1;
+ do {
+ if (pos != mList.begin()) {
+ pos--;
+ chunk_t* const p = *pos;
+ pos++;
+ if (p->free || !cur->size) {
+ freed = p;
+ p->size += cur->size;
+ mList.erase(pos);
+ delete cur;
+ }
+ }
+ if (++pos == mList.end()) break;
+ cur = *pos;
+ } while (cur && cur->free);
+
+#ifndef NDEBUG
+ if (!freed->free) {
+ dump_l("dealloc (!freed->free)");
+ }
+#endif
+ LOG_FATAL_IF(!freed->free, "freed block at offset 0x%08lX of size 0x%08lX is not free!",
+ freed->start * kMemoryAlign, freed->size * kMemoryAlign);
+
+ return freed;
+ }
+ }
+ return 0;
+}
+
+void SimpleBestFitAllocator::dump(const char* tag) const {
+ Lock lock(mLock);
+ dump_l(tag);
+}
+
+void SimpleBestFitAllocator::dump_l(const char* tag) const {
+ string result;
+ dump_l(result, tag);
+ ALOGD("%s", result.c_str());
+}
+
+void SimpleBestFitAllocator::dump(string& result, const char* tag) const {
+ Lock lock(mLock);
+ dump_l(result, tag);
+}
+
+void SimpleBestFitAllocator::dump_l(string& result, const char* tag) const {
+ size_t size = 0;
+ int32_t i = 0;
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ snprintf(buffer, SIZE, " %s (%p, size=%u)\n", tag, this, (unsigned int)mHeapSize);
+
+ result.append(buffer);
+
+ for (IteratorConst pos = mList.begin(); pos != mList.end(); pos++) {
+ chunk_t const* cur = *pos;
+
+ if (!cur->free) size += cur->size * kMemoryAlign;
+
+ i++;
+ }
+ snprintf(buffer, SIZE, " size allocated: %u (%u KB)\n", int(size), int(size / 1024));
+ result.append(buffer);
+}
+
+bool HidlMemoryDealer::isOk(const MemoryBlock& memblk) {
+ return memblk.token != nullptr;
+}
+
+sp<::android::hidl::memory::V1_0::IMemory> HidlMemoryDealer::heap() {
+ return mHeap;
+}
+
+// The required heap size alignment is 4096 bytes
+static const uint64_t kHeapSizeAlignment = (0x1ULL << 12);
+
+sp<HidlMemoryDealer> HidlMemoryDealer::getInstance(const hidl_memory& mem) {
+ uint64_t msk = (kHeapSizeAlignment - 1);
+ if (mem.size() & msk || !(mem.size() & ~msk)) {
+ ALOGE("size is not aligned to %x", static_cast<uint32_t>(kHeapSizeAlignment));
+ return nullptr;
+ }
+ sp<IMemory> heap = mapMemory(mem);
+ if (heap == nullptr) {
+ ALOGE("fail to mapMemory");
+ return nullptr;
+ }
+ return new HidlMemoryDealer(heap, mem);
+}
+
+HidlMemoryDealer::HidlMemoryDealer(sp<IMemory> heap, const hidl_memory& mem)
+ : MemoryDealer(heap->getSize()),
+ mHeap(heap),
+ mToken(new HidlMemoryToken(HidlMemory::getInstance(mem))) {}
+
+::android::hidl::memory::block::V1_0::MemoryBlock HidlMemoryDealer::allocate(size_t size) {
+ MemoryBlock memblk = {nullptr, 0xFFFFFFFFULL, 0xFFFFFFFFULL};
+ ssize_t offset = allocateOffset(size);
+ if (offset >= 0) {
+ memblk.token = mToken;
+ memblk.size = size;
+ memblk.offset = offset;
+ }
+ return memblk;
+}
+
+}; // namespace hardware
+}; // namespace android
diff --git a/libhidlcache/include/hidlcache/MemoryDealer.h b/libhidlcache/include/hidlcache/MemoryDealer.h
new file mode 100644
index 0000000..fceed83
--- /dev/null
+++ b/libhidlcache/include/hidlcache/MemoryDealer.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+#ifndef ANDROID_HIDL_MEMORY_DEALER_H
+#define ANDROID_HIDL_MEMORY_DEALER_H
+
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <android/hidl/memory/block/1.0/types.h>
+#include <stdint.h>
+#include <sys/types.h>
+namespace android {
+namespace hardware {
+
+class SimpleBestFitAllocator;
+
+// MemoryDealer allocates/deallocates blocks from a continuous memory region.
+// It operates on size and offset and does not depend on any specific data types.
+class MemoryDealer : public RefBase {
+ public:
+ /// Allocate a block with size. The allocated block is identified with an
+ /// offset. For example:
+ /// ssize_t K = dealer->allocateOffset(size);
+ /// On success, K is positive and points to a subregion K ~ (K+size-1) in the heap.
+ /// It's negative if the allocation fails.
+ virtual ssize_t allocateOffset(size_t size);
+ /// @param offset It points to the block that allocated with allocateOffset previously.
+ virtual void deallocate(size_t offset);
+ /// @param tag a string tag used to mark the dump message
+ virtual void dump(const char* tag) const;
+
+ // allocations are aligned to some value. return that value so clients can account for it.
+ static size_t getAllocationAlignment();
+
+ MemoryDealer(size_t size);
+ virtual ~MemoryDealer();
+
+ protected:
+ SimpleBestFitAllocator* mAllocator;
+};
+
+// It extends the generic MemoryDealer and uses
+// - sp<IMemory> to represent the main memory region.
+// - MemoryBlock to represent the the block to allocate/deallocate
+class HidlMemoryDealer : public MemoryDealer {
+ using IMemory = ::android::hidl::memory::V1_0::IMemory;
+ using IMemoryToken = ::android::hidl::memory::token::V1_0::IMemoryToken;
+ using MemoryBlock = ::android::hidl::memory::block::V1_0::MemoryBlock;
+
+ public:
+ static bool isOk(const MemoryBlock& memblk);
+ /// @param memory The memory size must align to 4096 bytes
+ static sp<HidlMemoryDealer> getInstance(const hidl_memory& memory);
+ virtual MemoryBlock allocate(size_t size);
+ virtual sp<IMemory> heap();
+
+ protected:
+ /// @param heap It must be acquired with mapMemory(memory) with its
+ /// argument corresponds to the 2nd argument passed to HidlMemoryDealer.
+ HidlMemoryDealer(sp<IMemory> heap, const hidl_memory& memory);
+ sp<IMemory> mHeap;
+ sp<IMemoryToken> mToken;
+};
+
+}; // namespace hardware
+}; // namespace android
+
+#endif // ANDROID_HIDL_MEMORY_DEALER_H