|  | /* | 
|  | * 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> | 
|  |  | 
|  | #if 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 | 
|  |  | 
|  | #if 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. | 
|  |  | 
|  | #if 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(); | 
|  | #if 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() | 
|  | { | 
|  | #if 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() | 
|  | { | 
|  | #if 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 |