Add HidlMemoryCache and mapMemory(memory_block) The HidlMemoryCache is a singleton class which provides cache for sp<IMemory>. It works as an abstraction layer on top of the IMapper. It supports, but is not limited to, the Ashmem type HidlMemory. Bug: 69640640 Test: hidl_test/internal master/sailfish Change-Id: I69cca18dbdda532eaae409ee9372b3080f7d58dd
diff --git a/libhidlcache/Android.bp b/libhidlcache/Android.bp new file mode 100644 index 0000000..7fd5aa7 --- /dev/null +++ b/libhidlcache/Android.bp
@@ -0,0 +1,47 @@ +// Copyright (C) 2016 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. + +cc_library { + name: "libhidlcache", + vendor_available: true, + vndk: { + enabled: true, + }, + defaults: ["libhidl-defaults"], + shared_libs: [ + "libbase", + "liblog", + "libutils", + "libcutils", + "libhidlbase", + "libhidlmemory", + "libhwbinder", + "libhidltransport", + "[email protected]", + "[email protected]", + "[email protected]", + ], + export_include_dirs: ["include"], + + export_shared_lib_headers: [ + "[email protected]", + "[email protected]", + "[email protected]", + "libhidlbase" + ], + srcs: [ + "HidlMemoryCache.cpp", + "mapping.cpp" + ], +}
diff --git a/libhidlcache/HidlCache.h b/libhidlcache/HidlCache.h new file mode 100644 index 0000000..db778d3 --- /dev/null +++ b/libhidlcache/HidlCache.h
@@ -0,0 +1,143 @@ +/* + * Copyright (C) 2016 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_HARDWARE_HIDL_CACHE_H +#define ANDROID_HARDWARE_HIDL_CACHE_H + +#include <utils/Log.h> + +namespace android { +namespace hardware { + +// A generic cache to map Key to sp<Value>. The cache records are kept with +// wp<Value>, so that it does not block the Value to be garbage collected +// when there's no other sp<> externally. +template <class Key, class Value, class Compare = std::less<Key>> +class HidlCache : public virtual RefBase { + using Mutex = std::mutex; + using Lock = std::lock_guard<Mutex>; + + public: + // A RAII class to manage lock/unlock HidlCache. + class HidlCacheLock : public virtual RefBase { + public: + HidlCacheLock(sp<HidlCache> cache, const Key& key) : mCache(cache), mKey(key) { + mCache->lock(mKey); + } + ~HidlCacheLock() { mCache->unlock(mKey); } + + private: + sp<HidlCache> mCache; + const Key mKey; + }; + // lock the IMemory refered by key and keep it alive even if there's no + // other memory block refers to. + virtual bool lock(const Key& key); + virtual sp<Value> unlock(const Key& key); + virtual bool flush(const Key& key); + // fetch the sp<Value> with key from cache, + // make a new instance with fill() if it does not present currently. + virtual sp<Value> fetch(const Key& key); + virtual sp<HidlCacheLock> lockGuard(const Key& key) { return new HidlCacheLock(this, key); } + + virtual ~HidlCache() {} + + protected: + friend void HidlCacheWhiteBoxTest(); + // This method shall be called with a lock held + virtual sp<Value> fillLocked(const Key& key) = 0; + + // @return nullptr if it does not present currently. + // @note This method shall be called with a lock held + virtual sp<Value> getCachedLocked(const Key& key); + bool cached(Key key) const { return mCached.count(key) > 0; } + bool locked(Key key) const { return mLocked.count(key) > 0; } + Mutex mMutex; + + std::map<Key, wp<Value>, Compare> mCached; + std::map<Key, sp<Value>, Compare> mLocked; +}; + +template <class Key, class Value, class Compare> +bool HidlCache<Key, Value, Compare>::lock(const Key& key) { + { + Lock lock(mMutex); + if (cached(key)) { + sp<Value> im = mCached[key].promote(); + if (im != nullptr) { + mLocked[key] = im; + return true; + } else { + mCached.erase(key); + } + } + } + sp<Value> value = fetch(key); + if (value == nullptr) { + return false; + } else { + Lock lock(mMutex); + mLocked[key] = value; + return true; + } +} + +template <class Key, class Value, class Compare> +sp<Value> HidlCache<Key, Value, Compare>::unlock(const Key& key) { + Lock lock(mMutex); + if (locked(key) > 0) { + sp<Value> v = mLocked[key]; + mLocked.erase(key); + return v; + } + return nullptr; +} + +template <class Key, class Value, class Compare> +bool HidlCache<Key, Value, Compare>::flush(const Key& key) { + Lock lock(mMutex); + bool contain = cached(key); + mCached.erase(key); + return contain; +} + +template <class Key, class Value, class Compare> +sp<Value> HidlCache<Key, Value, Compare>::getCachedLocked(const Key& key) { + if (cached(key)) { + wp<Value> cache = mCached[key]; + sp<Value> mem = cache.promote(); + if (mem != nullptr) { + return mem; + } else { + mCached.erase(key); + } + } + return nullptr; +} + +template <class Key, class Value, class Compare> +sp<Value> HidlCache<Key, Value, Compare>::fetch(const Key& key) { + Lock lock(mMutex); + sp<Value> value = getCachedLocked(key); + + if (value == nullptr) { + value = fillLocked(key); + } + return value; +} + +} // namespace hardware +} // namespace android +#endif
diff --git a/libhidlcache/HidlMemoryCache.cpp b/libhidlcache/HidlMemoryCache.cpp new file mode 100644 index 0000000..6f9c25c --- /dev/null +++ b/libhidlcache/HidlMemoryCache.cpp
@@ -0,0 +1,124 @@ +/* + * Copyright (C) 2016 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 "HidlMemoryCache" +#include "HidlMemoryCache.h" +#include <android/hidl/memory/1.0/IMemory.h> +#include <android/hidl/memory/token/1.0/IMemoryToken.h> +#include <hidlmemory/mapping.h> +#include <sys/mman.h> +#include <utils/Log.h> + +namespace android { +namespace hardware { + +using IMemoryToken = ::android::hidl::memory::token::V1_0::IMemoryToken; +using IMemory = ::android::hidl::memory::V1_0::IMemory; + +class IMemoryDecorator : public virtual IMemory { + public: + IMemoryDecorator(sp<IMemory> heap) : mHeap(heap) {} + virtual ~IMemoryDecorator(){}; + Return<void> update() override { return mHeap->update(); } + Return<void> read() override { return mHeap->read(); }; + Return<void> updateRange(uint64_t start, uint64_t length) override { + return mHeap->updateRange(start, length); + } + Return<void> readRange(uint64_t start, uint64_t length) override { + return mHeap->readRange(start, length); + } + Return<void> commit() override { return mHeap->commit(); } + + Return<void*> getPointer() override { return mHeap->getPointer(); } + Return<uint64_t> getSize() override { return mHeap->getSize(); } + + protected: + sp<IMemory> mHeap; +}; + +class IMemoryCacheable : public virtual IMemoryDecorator { + public: + IMemoryCacheable(sp<IMemory> heap, sp<IMemoryToken> key) : IMemoryDecorator(heap), mKey(key) {} + virtual ~IMemoryCacheable() { HidlMemoryCache::getInstance()->flush(mKey); } + + protected: + sp<IMemoryToken> mKey; +}; + +class IMemoryBlock : public virtual IMemoryDecorator { + public: + IMemoryBlock(sp<IMemory> heap, uint64_t size, uint64_t offset) + : IMemoryDecorator(heap), mSize(size), mOffset(offset), mHeapSize(heap->getSize()) {} + bool validRange(uint64_t start, uint64_t length) { + return (start + length < mSize) && (start + length >= start) && + (mOffset + mSize < mHeapSize); + } + Return<void> readRange(uint64_t start, uint64_t length) { + if (!validRange(start, length)) { + ALOGE("IMemoryBlock::readRange: out of range"); + Status status; + status.setException(Status::EX_ILLEGAL_ARGUMENT, "out of range"); + return Return<void>(status); + } + return mHeap->readRange(mOffset + start, length); + } + Return<void> updateRange(uint64_t start, uint64_t length) override { + if (!validRange(start, length)) { + ALOGE("IMemoryBlock::updateRange: out of range"); + return Void(); + } + return mHeap->updateRange(mOffset + start, length); + } + Return<uint64_t> getSize() override { return mSize; } + Return<void*> getPointer() override { + void* p = mHeap->getPointer(); + return (static_cast<char*>(p) + mOffset); + } + + protected: + uint64_t mSize; + uint64_t mOffset; + uint64_t mHeapSize; +}; + +sp<HidlMemoryCache> HidlMemoryCache::getInstance() { + static sp<HidlMemoryCache> instance = new HidlMemoryCache(); + return instance; +} + +sp<IMemory> HidlMemoryCache::fillLocked(const sp<IMemoryToken>& key) { + sp<IMemory> memory = nullptr; + Return<void> ret = key->get( + [&](const hidl_memory& mem) { memory = new IMemoryCacheable(mapMemory(mem), key); }); + if (!ret.isOk()) { + ALOGE("HidlMemoryCache::fill: cannot IMemoryToken::get."); + return nullptr; + } + mCached[key] = memory; + return memory; +} + +sp<IMemory> HidlMemoryCache::map(const MemoryBlock& memblk) { + sp<IMemoryToken> token = memblk.token; + sp<IMemory> heap = fetch(token); + if (heap == nullptr) { + return nullptr; + } + return new IMemoryBlock(heap, memblk.size, memblk.offset); +} + +} // namespace hardware +} // namespace android
diff --git a/libhidlcache/HidlMemoryCache.h b/libhidlcache/HidlMemoryCache.h new file mode 100644 index 0000000..c9a533b --- /dev/null +++ b/libhidlcache/HidlMemoryCache.h
@@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 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_HARDWARD_HIDLMEMORY_CACHE_H +#define ANDROID_HARDWARD_HIDLMEMORY_CACHE_H + +#include <android/hidl/memory/block/1.0/types.h> +#include <android/hidl/memory/token/1.0/IMemoryToken.h> +#include <hidl/HidlBinderSupport.h> +#include <hwbinder/IBinder.h> +#include <utils/RefBase.h> +#include "HidlCache.h" + +namespace android { +namespace hardware { + +struct IMemoryTokenCompare { + using IMemoryToken = ::android::hidl::memory::token::V1_0::IMemoryToken; + bool operator()(const sp<IMemoryToken>& lhs, const sp<IMemoryToken>& rhs) const { + sp<IBinder> lb = toBinder<IMemoryToken>(lhs); + sp<IBinder> rb = toBinder<IMemoryToken>(rhs); + return lb < rb; + } +}; + +// The HidlMemoryCache is a singleton class to provides cache for +// IMemoryToken => ::android::hidl::memory::V1_0::IMemory +// It's an abstraction layer on top of the IMapper and supports, but is +// not limited to, the Ashmem type HidlMemory. +class HidlMemoryCache + : public virtual HidlCache<sp<::android::hidl::memory::token::V1_0::IMemoryToken>, + ::android::hidl::memory::V1_0::IMemory, IMemoryTokenCompare> { + using IMemoryToken = ::android::hidl::memory::token::V1_0::IMemoryToken; + using IMemory = ::android::hidl::memory::V1_0::IMemory; + using MemoryBlock = ::android::hidl::memory::block::V1_0::MemoryBlock; + + public: + virtual sp<IMemory> map(const MemoryBlock& block); + // get the singleton + static sp<HidlMemoryCache> getInstance(); + + protected: + HidlMemoryCache() {} + virtual sp<IMemory> fillLocked(const sp<IMemoryToken>& key) override; +}; + +} // namespace hardware +} // namespace android + +#endif
diff --git a/libhidlcache/include/hidlcache/mapping.h b/libhidlcache/include/hidlcache/mapping.h new file mode 100644 index 0000000..972b7b5 --- /dev/null +++ b/libhidlcache/include/hidlcache/mapping.h
@@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 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_HARDWARE_CACHE_MAPPING_H +#define ANDROID_HARDWARE_CACHE_MAPPING_H + +#include <android/hidl/memory/1.0/IMemory.h> +#include <android/hidl/memory/block/1.0/types.h> +#include <android/hidl/memory/token/1.0/IMemoryToken.h> + +namespace android { +namespace hardware { + +/** + * Returns the IMemory instance corresponding to a MemoryBlock. The heap that + * a MemoryBlock belongs to is stored in an internal cache to reduce the number + * of invocations to the mapMemory(hidl_memory) + * + * Note, a cache entry is maintained by reference count and may be flushed when + * the count decrease to zero. Performance critical part that does not want its + * caches to be flushed can use HidlMemoryCacheLock. + */ +sp<::android::hidl::memory::V1_0::IMemory> mapMemory( + const ::android::hidl::memory::block::V1_0::MemoryBlock& block); + +/** + * Internally, there's a cache pool to keep IMemory instances for heap regions + * that are referred by the MemoryBlock. During development, this + * lockMemoryCache(...) method helps to diagnosis whether the cache is effective + * for a specific key. It returns a RAII object used to lock an IMemory instance + * referred by the key and keep it alive even if the instance is not referred by + * any MemoryBlock. If the cache in interest is already effective. It won't differ + * much in performance w/ wo/ the lockMemoryCache() + * + * @note An IMemory instance that is returned from the mapMemory() is + * initialized in an unlocked state. + */ +sp<RefBase> lockMemoryCache(const sp<::android::hidl::memory::token::V1_0::IMemoryToken> key); + +} // namespace hardware +} // namespace android +#endif
diff --git a/libhidlcache/mapping.cpp b/libhidlcache/mapping.cpp new file mode 100644 index 0000000..2a23e6f --- /dev/null +++ b/libhidlcache/mapping.cpp
@@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 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 "libhidlmemory" + +#include <map> +#include <mutex> +#include <string> + +#include <hidlmemory/mapping.h> + +#include <android-base/logging.h> +#include <hidl/HidlSupport.h> +#include "HidlMemoryCache.h" + +using android::hardware::HidlMemoryCache; +using android::hidl::memory::block::V1_0::MemoryBlock; +using android::hidl::memory::token::V1_0::IMemoryToken; +using android::hidl::memory::V1_0::IMemory; + +namespace android { +namespace hardware { + +sp<IMemory> mapMemory(const ::android::hidl::memory::block::V1_0::MemoryBlock& block) { + sp<HidlMemoryCache> c = HidlMemoryCache::getInstance(); + return c->map(block); +} + +sp<RefBase> lockMemoryCache(const sp<::android::hidl::memory::token::V1_0::IMemoryToken> key) { + sp<HidlMemoryCache> c = HidlMemoryCache::getInstance(); + return c->lockGuard(key); +} + +} // namespace hardware +} // namespace android