diff options
Diffstat (limited to 'libs/binder/IMemory.cpp')
-rw-r--r-- | libs/binder/IMemory.cpp | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp new file mode 100644 index 0000000..6c1d225 --- /dev/null +++ b/libs/binder/IMemory.cpp @@ -0,0 +1,492 @@ +/* + * Copyright (C) 2008 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 "IMemory" + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/mman.h> + +#include <binder/IMemory.h> +#include <utils/KeyedVector.h> +#include <utils/threads.h> +#include <utils/Atomic.h> +#include <binder/Parcel.h> +#include <utils/CallStack.h> + +#define VERBOSE 0 + +namespace android { +// --------------------------------------------------------------------------- + +class HeapCache : public IBinder::DeathRecipient +{ +public: + HeapCache(); + virtual ~HeapCache(); + + virtual void binderDied(const wp<IBinder>& who); + + sp<IMemoryHeap> find_heap(const sp<IBinder>& binder); + void pin_heap(const sp<IBinder>& binder); + void free_heap(const sp<IBinder>& binder); + sp<IMemoryHeap> get_heap(const sp<IBinder>& binder); + void dump_heaps(); + +private: + // For IMemory.cpp + struct heap_info_t { + sp<IMemoryHeap> heap; + int32_t count; + }; + + void free_heap(const wp<IBinder>& binder); + + Mutex mHeapCacheLock; + KeyedVector< wp<IBinder>, heap_info_t > mHeapCache; +}; + +static sp<HeapCache> gHeapCache = new HeapCache(); + +/******************************************************************************/ + +enum { + HEAP_ID = IBinder::FIRST_CALL_TRANSACTION +}; + +class BpMemoryHeap : public BpInterface<IMemoryHeap> +{ +public: + BpMemoryHeap(const sp<IBinder>& impl); + virtual ~BpMemoryHeap(); + + virtual int getHeapID() const; + virtual void* getBase() const; + virtual size_t getSize() const; + virtual uint32_t getFlags() const; + +private: + friend class IMemory; + friend class HeapCache; + + // for debugging in this module + static inline sp<IMemoryHeap> find_heap(const sp<IBinder>& binder) { + return gHeapCache->find_heap(binder); + } + static inline void free_heap(const sp<IBinder>& binder) { + gHeapCache->free_heap(binder); + } + static inline sp<IMemoryHeap> get_heap(const sp<IBinder>& binder) { + return gHeapCache->get_heap(binder); + } + static inline void dump_heaps() { + gHeapCache->dump_heaps(); + } + void inline pin_heap() const { + gHeapCache->pin_heap(const_cast<BpMemoryHeap*>(this)->asBinder()); + } + + void assertMapped() const; + void assertReallyMapped() const; + void pinHeap() const; + + mutable volatile int32_t mHeapId; + mutable void* mBase; + mutable size_t mSize; + mutable uint32_t mFlags; + mutable bool mRealHeap; + mutable Mutex mLock; +}; + +// ---------------------------------------------------------------------------- + +enum { + GET_MEMORY = IBinder::FIRST_CALL_TRANSACTION +}; + +class BpMemory : public BpInterface<IMemory> +{ +public: + BpMemory(const sp<IBinder>& impl); + virtual ~BpMemory(); + virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const; + +private: + mutable sp<IMemoryHeap> mHeap; + mutable ssize_t mOffset; + mutable size_t mSize; +}; + +/******************************************************************************/ + +void* IMemory::fastPointer(const sp<IBinder>& binder, ssize_t offset) const +{ + sp<IMemoryHeap> realHeap = BpMemoryHeap::get_heap(binder); + void* const base = realHeap->base(); + if (base == MAP_FAILED) + return 0; + return static_cast<char*>(base) + offset; +} + +void* IMemory::pointer() const { + ssize_t offset; + sp<IMemoryHeap> heap = getMemory(&offset); + void* const base = heap!=0 ? heap->base() : MAP_FAILED; + if (base == MAP_FAILED) + return 0; + return static_cast<char*>(base) + offset; +} + +size_t IMemory::size() const { + size_t size; + getMemory(NULL, &size); + return size; +} + +ssize_t IMemory::offset() const { + ssize_t offset; + getMemory(&offset); + return offset; +} + +/******************************************************************************/ + +BpMemory::BpMemory(const sp<IBinder>& impl) + : BpInterface<IMemory>(impl), mOffset(0), mSize(0) +{ +} + +BpMemory::~BpMemory() +{ +} + +sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const +{ + if (mHeap == 0) { + Parcel data, reply; + data.writeInterfaceToken(IMemory::getInterfaceDescriptor()); + if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) { + sp<IBinder> heap = reply.readStrongBinder(); + ssize_t o = reply.readInt32(); + size_t s = reply.readInt32(); + if (heap != 0) { + mHeap = interface_cast<IMemoryHeap>(heap); + if (mHeap != 0) { + mOffset = o; + mSize = s; + } + } + } + } + if (offset) *offset = mOffset; + if (size) *size = mSize; + return mHeap; +} + +// --------------------------------------------------------------------------- + +IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory"); + +BnMemory::BnMemory() { +} + +BnMemory::~BnMemory() { +} + +status_t BnMemory::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case GET_MEMORY: { + CHECK_INTERFACE(IMemory, data, reply); + ssize_t offset; + size_t size; + reply->writeStrongBinder( getMemory(&offset, &size)->asBinder() ); + reply->writeInt32(offset); + reply->writeInt32(size); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + + +/******************************************************************************/ + +BpMemoryHeap::BpMemoryHeap(const sp<IBinder>& impl) + : BpInterface<IMemoryHeap>(impl), + mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mRealHeap(false) +{ +} + +BpMemoryHeap::~BpMemoryHeap() { + if (mHeapId != -1) { + close(mHeapId); + if (mRealHeap) { + // by construction we're the last one + if (mBase != MAP_FAILED) { + sp<IBinder> binder = const_cast<BpMemoryHeap*>(this)->asBinder(); + + if (VERBOSE) { + LOGD("UNMAPPING binder=%p, heap=%p, size=%d, fd=%d", + binder.get(), this, mSize, mHeapId); + CallStack stack; + stack.update(); + stack.dump("callstack"); + } + + munmap(mBase, mSize); + } + } else { + // remove from list only if it was mapped before + sp<IBinder> binder = const_cast<BpMemoryHeap*>(this)->asBinder(); + free_heap(binder); + } + } +} + +void BpMemoryHeap::assertMapped() const +{ + if (mHeapId == -1) { + sp<IBinder> binder(const_cast<BpMemoryHeap*>(this)->asBinder()); + sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get())); + heap->assertReallyMapped(); + if (heap->mBase != MAP_FAILED) { + Mutex::Autolock _l(mLock); + if (mHeapId == -1) { + mBase = heap->mBase; + mSize = heap->mSize; + android_atomic_write( dup( heap->mHeapId ), &mHeapId ); + } + } else { + // something went wrong + free_heap(binder); + } + } +} + +void BpMemoryHeap::assertReallyMapped() const +{ + if (mHeapId == -1) { + + // remote call without mLock held, worse case scenario, we end up + // calling transact() from multiple threads, but that's not a problem, + // only mmap below must be in the critical section. + + Parcel data, reply; + data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor()); + status_t err = remote()->transact(HEAP_ID, data, &reply); + int parcel_fd = reply.readFileDescriptor(); + ssize_t size = reply.readInt32(); + uint32_t flags = reply.readInt32(); + + LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%ld, err=%d (%s)", + asBinder().get(), parcel_fd, size, err, strerror(-err)); + + int fd = dup( parcel_fd ); + LOGE_IF(fd==-1, "cannot dup fd=%d, size=%ld, err=%d (%s)", + parcel_fd, size, err, strerror(errno)); + + int access = PROT_READ; + if (!(flags & READ_ONLY)) { + access |= PROT_WRITE; + } + + Mutex::Autolock _l(mLock); + if (mHeapId == -1) { + mRealHeap = true; + mBase = mmap(0, size, access, MAP_SHARED, fd, 0); + if (mBase == MAP_FAILED) { + LOGE("cannot map BpMemoryHeap (binder=%p), size=%ld, fd=%d (%s)", + asBinder().get(), size, fd, strerror(errno)); + close(fd); + } else { + if (flags & MAP_ONCE) { + //LOGD("pinning heap (binder=%p, size=%d, fd=%d", + // asBinder().get(), size, fd); + pin_heap(); + } + mSize = size; + mFlags = flags; + android_atomic_write(fd, &mHeapId); + } + } + } +} + +int BpMemoryHeap::getHeapID() const { + assertMapped(); + return mHeapId; +} + +void* BpMemoryHeap::getBase() const { + assertMapped(); + return mBase; +} + +size_t BpMemoryHeap::getSize() const { + assertMapped(); + return mSize; +} + +uint32_t BpMemoryHeap::getFlags() const { + assertMapped(); + return mFlags; +} + +// --------------------------------------------------------------------------- + +IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap"); + +BnMemoryHeap::BnMemoryHeap() { +} + +BnMemoryHeap::~BnMemoryHeap() { +} + +status_t BnMemoryHeap::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case HEAP_ID: { + CHECK_INTERFACE(IMemoryHeap, data, reply); + reply->writeFileDescriptor(getHeapID()); + reply->writeInt32(getSize()); + reply->writeInt32(getFlags()); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +/*****************************************************************************/ + +HeapCache::HeapCache() + : DeathRecipient() +{ +} + +HeapCache::~HeapCache() +{ +} + +void HeapCache::binderDied(const wp<IBinder>& binder) +{ + //LOGD("binderDied binder=%p", binder.unsafe_get()); + free_heap(binder); +} + +sp<IMemoryHeap> HeapCache::find_heap(const sp<IBinder>& binder) +{ + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) { + heap_info_t& info = mHeapCache.editValueAt(i); + LOGD_IF(VERBOSE, + "found binder=%p, heap=%p, size=%d, fd=%d, count=%d", + binder.get(), info.heap.get(), + static_cast<BpMemoryHeap*>(info.heap.get())->mSize, + static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId, + info.count); + android_atomic_inc(&info.count); + return info.heap; + } else { + heap_info_t info; + info.heap = interface_cast<IMemoryHeap>(binder); + info.count = 1; + //LOGD("adding binder=%p, heap=%p, count=%d", + // binder.get(), info.heap.get(), info.count); + mHeapCache.add(binder, info); + return info.heap; + } +} + +void HeapCache::pin_heap(const sp<IBinder>& binder) +{ + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) { + heap_info_t& info(mHeapCache.editValueAt(i)); + android_atomic_inc(&info.count); + binder->linkToDeath(this); + } else { + LOGE("pin_heap binder=%p not found!!!", binder.get()); + } +} + +void HeapCache::free_heap(const sp<IBinder>& binder) { + free_heap( wp<IBinder>(binder) ); +} + +void HeapCache::free_heap(const wp<IBinder>& binder) +{ + sp<IMemoryHeap> rel; + { + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) { + heap_info_t& info(mHeapCache.editValueAt(i)); + int32_t c = android_atomic_dec(&info.count); + if (c == 1) { + LOGD_IF(VERBOSE, + "removing binder=%p, heap=%p, size=%d, fd=%d, count=%d", + binder.unsafe_get(), info.heap.get(), + static_cast<BpMemoryHeap*>(info.heap.get())->mSize, + static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId, + info.count); + rel = mHeapCache.valueAt(i).heap; + mHeapCache.removeItemsAt(i); + } + } else { + LOGE("free_heap binder=%p not found!!!", binder.unsafe_get()); + } + } +} + +sp<IMemoryHeap> HeapCache::get_heap(const sp<IBinder>& binder) +{ + sp<IMemoryHeap> realHeap; + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) realHeap = mHeapCache.valueAt(i).heap; + else realHeap = interface_cast<IMemoryHeap>(binder); + return realHeap; +} + +void HeapCache::dump_heaps() +{ + Mutex::Autolock _l(mHeapCacheLock); + int c = mHeapCache.size(); + for (int i=0 ; i<c ; i++) { + const heap_info_t& info = mHeapCache.valueAt(i); + BpMemoryHeap const* h(static_cast<BpMemoryHeap const *>(info.heap.get())); + LOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%d)", + mHeapCache.keyAt(i).unsafe_get(), + info.heap.get(), info.count, + h->mHeapId, h->mBase, h->mSize); + } +} + + +// --------------------------------------------------------------------------- +}; // namespace android |