/* * 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 "MemoryHeapPmem" #include <stdlib.h> #include <stdint.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <cutils/log.h> #include <binder/MemoryHeapPmem.h> #include <binder/MemoryHeapBase.h> #ifdef HAVE_ANDROID_OS #include <linux/android_pmem.h> #endif namespace android { // --------------------------------------------------------------------------- MemoryHeapPmem::MemoryPmem::MemoryPmem(const sp<MemoryHeapPmem>& heap) : BnMemory(), mClientHeap(heap) { } MemoryHeapPmem::MemoryPmem::~MemoryPmem() { if (mClientHeap != NULL) { mClientHeap->remove(this); } } // --------------------------------------------------------------------------- class SubRegionMemory : public MemoryHeapPmem::MemoryPmem { public: SubRegionMemory(const sp<MemoryHeapPmem>& heap, ssize_t offset, size_t size); virtual ~SubRegionMemory(); virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const; private: friend class MemoryHeapPmem; void revoke(); size_t mSize; ssize_t mOffset; }; SubRegionMemory::SubRegionMemory(const sp<MemoryHeapPmem>& heap, ssize_t offset, size_t size) : MemoryHeapPmem::MemoryPmem(heap), mSize(size), mOffset(offset) { #ifndef NDEBUG void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + offset); memset(start_ptr, 0xda, size); #endif #ifdef HAVE_ANDROID_OS if (size > 0) { const size_t pagesize = getpagesize(); size = (size + pagesize-1) & ~(pagesize-1); int our_fd = heap->heapID(); struct pmem_region sub = { offset, size }; int err = ioctl(our_fd, PMEM_MAP, &sub); LOGE_IF(err<0, "PMEM_MAP failed (%s), " "mFD=%d, sub.offset=%lu, sub.size=%lu", strerror(errno), our_fd, sub.offset, sub.len); } #endif } sp<IMemoryHeap> SubRegionMemory::getMemory(ssize_t* offset, size_t* size) const { if (offset) *offset = mOffset; if (size) *size = mSize; return getHeap(); } SubRegionMemory::~SubRegionMemory() { revoke(); } void SubRegionMemory::revoke() { // NOTE: revoke() doesn't need to be protected by a lock because it // can only be called from MemoryHeapPmem::revoke(), which means // that we can't be in ~SubRegionMemory(), or in ~SubRegionMemory(), // which means MemoryHeapPmem::revoke() wouldn't have been able to // promote() it. #ifdef HAVE_ANDROID_OS if (mSize != 0) { const sp<MemoryHeapPmem>& heap(getHeap()); int our_fd = heap->heapID(); struct pmem_region sub; sub.offset = mOffset; sub.len = mSize; int err = ioctl(our_fd, PMEM_UNMAP, &sub); LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " "mFD=%d, sub.offset=%lu, sub.size=%lu", strerror(errno), our_fd, sub.offset, sub.len); mSize = 0; } #endif } // --------------------------------------------------------------------------- MemoryHeapPmem::MemoryHeapPmem(const sp<MemoryHeapBase>& pmemHeap, uint32_t flags) : MemoryHeapBase() { char const * const device = pmemHeap->getDevice(); #ifdef HAVE_ANDROID_OS if (device) { int fd = open(device, O_RDWR | (flags & NO_CACHING ? O_SYNC : 0)); LOGE_IF(fd<0, "couldn't open %s (%s)", device, strerror(errno)); if (fd >= 0) { int err = ioctl(fd, PMEM_CONNECT, pmemHeap->heapID()); if (err < 0) { LOGE("PMEM_CONNECT failed (%s), mFD=%d, sub-fd=%d", strerror(errno), fd, pmemHeap->heapID()); close(fd); } else { // everything went well... mParentHeap = pmemHeap; MemoryHeapBase::init(fd, pmemHeap->getBase(), pmemHeap->getSize(), pmemHeap->getFlags() | flags, device); } } } #else mParentHeap = pmemHeap; MemoryHeapBase::init( dup(pmemHeap->heapID()), pmemHeap->getBase(), pmemHeap->getSize(), pmemHeap->getFlags() | flags, device); #endif } MemoryHeapPmem::~MemoryHeapPmem() { } sp<IMemory> MemoryHeapPmem::mapMemory(size_t offset, size_t size) { sp<MemoryPmem> memory = createMemory(offset, size); if (memory != 0) { Mutex::Autolock _l(mLock); mAllocations.add(memory); } return memory; } sp<MemoryHeapPmem::MemoryPmem> MemoryHeapPmem::createMemory( size_t offset, size_t size) { sp<SubRegionMemory> memory; if (heapID() > 0) memory = new SubRegionMemory(this, offset, size); return memory; } status_t MemoryHeapPmem::slap() { #ifdef HAVE_ANDROID_OS size_t size = getSize(); const size_t pagesize = getpagesize(); size = (size + pagesize-1) & ~(pagesize-1); int our_fd = getHeapID(); struct pmem_region sub = { 0, size }; int err = ioctl(our_fd, PMEM_MAP, &sub); LOGE_IF(err<0, "PMEM_MAP failed (%s), " "mFD=%d, sub.offset=%lu, sub.size=%lu", strerror(errno), our_fd, sub.offset, sub.len); return -errno; #else return NO_ERROR; #endif } status_t MemoryHeapPmem::unslap() { #ifdef HAVE_ANDROID_OS size_t size = getSize(); const size_t pagesize = getpagesize(); size = (size + pagesize-1) & ~(pagesize-1); int our_fd = getHeapID(); struct pmem_region sub = { 0, size }; int err = ioctl(our_fd, PMEM_UNMAP, &sub); LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " "mFD=%d, sub.offset=%lu, sub.size=%lu", strerror(errno), our_fd, sub.offset, sub.len); return -errno; #else return NO_ERROR; #endif } void MemoryHeapPmem::revoke() { SortedVector< wp<MemoryPmem> > allocations; { // scope for lock Mutex::Autolock _l(mLock); allocations = mAllocations; } ssize_t count = allocations.size(); for (ssize_t i=0 ; i<count ; i++) { sp<MemoryPmem> memory(allocations[i].promote()); if (memory != 0) memory->revoke(); } } void MemoryHeapPmem::remove(const wp<MemoryPmem>& memory) { Mutex::Autolock _l(mLock); mAllocations.remove(memory); } // --------------------------------------------------------------------------- }; // namespace android