1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "MemoryHeapPmem"
18
19#include <stdlib.h>
20#include <stdint.h>
21#include <unistd.h>
22#include <fcntl.h>
23#include <errno.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <sys/ioctl.h>
27
28#include <cutils/log.h>
29
30#include <binder/MemoryHeapPmem.h>
31#include <binder/MemoryHeapBase.h>
32
33#if HAVE_ANDROID_OS
34#include <linux/android_pmem.h>
35#endif
36
37namespace android {
38
39// ---------------------------------------------------------------------------
40
41MemoryHeapPmem::MemoryPmem::MemoryPmem(const sp<MemoryHeapPmem>& heap)
42    : BnMemory(), mClientHeap(heap)
43{
44}
45
46MemoryHeapPmem::MemoryPmem::~MemoryPmem() {
47    if (mClientHeap != NULL) {
48        mClientHeap->remove(this);
49    }
50}
51
52// ---------------------------------------------------------------------------
53
54class SubRegionMemory : public MemoryHeapPmem::MemoryPmem {
55public:
56    SubRegionMemory(const sp<MemoryHeapPmem>& heap, ssize_t offset, size_t size);
57    virtual ~SubRegionMemory();
58    virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const;
59private:
60    friend class MemoryHeapPmem;
61    void revoke();
62    size_t              mSize;
63    ssize_t             mOffset;
64};
65
66SubRegionMemory::SubRegionMemory(const sp<MemoryHeapPmem>& heap,
67        ssize_t offset, size_t size)
68    : MemoryHeapPmem::MemoryPmem(heap), mSize(size), mOffset(offset)
69{
70#ifndef NDEBUG
71    void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + offset);
72    memset(start_ptr, 0xda, size);
73#endif
74
75#if HAVE_ANDROID_OS
76    if (size > 0) {
77        const size_t pagesize = getpagesize();
78        size = (size + pagesize-1) & ~(pagesize-1);
79        int our_fd = heap->heapID();
80        struct pmem_region sub = { offset, size };
81        int err = ioctl(our_fd, PMEM_MAP, &sub);
82        LOGE_IF(err<0, "PMEM_MAP failed (%s), "
83                "mFD=%d, sub.offset=%lu, sub.size=%lu",
84                strerror(errno), our_fd, sub.offset, sub.len);
85}
86#endif
87}
88
89sp<IMemoryHeap> SubRegionMemory::getMemory(ssize_t* offset, size_t* size) const
90{
91    if (offset) *offset = mOffset;
92    if (size)   *size = mSize;
93    return getHeap();
94}
95
96SubRegionMemory::~SubRegionMemory()
97{
98    revoke();
99}
100
101
102void SubRegionMemory::revoke()
103{
104    // NOTE: revoke() doesn't need to be protected by a lock because it
105    // can only be called from MemoryHeapPmem::revoke(), which means
106    // that we can't be in ~SubRegionMemory(), or in ~SubRegionMemory(),
107    // which means MemoryHeapPmem::revoke() wouldn't have been able to
108    // promote() it.
109
110#if HAVE_ANDROID_OS
111    if (mSize != 0) {
112        const sp<MemoryHeapPmem>& heap(getHeap());
113        int our_fd = heap->heapID();
114        struct pmem_region sub;
115        sub.offset = mOffset;
116        sub.len = mSize;
117        int err = ioctl(our_fd, PMEM_UNMAP, &sub);
118        LOGE_IF(err<0, "PMEM_UNMAP failed (%s), "
119                "mFD=%d, sub.offset=%lu, sub.size=%lu",
120                strerror(errno), our_fd, sub.offset, sub.len);
121        mSize = 0;
122    }
123#endif
124}
125
126// ---------------------------------------------------------------------------
127
128MemoryHeapPmem::MemoryHeapPmem(const sp<MemoryHeapBase>& pmemHeap,
129        uint32_t flags)
130    : MemoryHeapBase()
131{
132    char const * const device = pmemHeap->getDevice();
133#if HAVE_ANDROID_OS
134    if (device) {
135        int fd = open(device, O_RDWR | (flags & NO_CACHING ? O_SYNC : 0));
136        LOGE_IF(fd<0, "couldn't open %s (%s)", device, strerror(errno));
137        if (fd >= 0) {
138            int err = ioctl(fd, PMEM_CONNECT, pmemHeap->heapID());
139            if (err < 0) {
140                LOGE("PMEM_CONNECT failed (%s), mFD=%d, sub-fd=%d",
141                        strerror(errno), fd, pmemHeap->heapID());
142                close(fd);
143            } else {
144                // everything went well...
145                mParentHeap = pmemHeap;
146                MemoryHeapBase::init(fd,
147                        pmemHeap->getBase(),
148                        pmemHeap->getSize(),
149                        pmemHeap->getFlags() | flags,
150                        device);
151            }
152        }
153    }
154#else
155    mParentHeap = pmemHeap;
156    MemoryHeapBase::init(
157            dup(pmemHeap->heapID()),
158            pmemHeap->getBase(),
159            pmemHeap->getSize(),
160            pmemHeap->getFlags() | flags,
161            device);
162#endif
163}
164
165MemoryHeapPmem::~MemoryHeapPmem()
166{
167}
168
169sp<IMemory> MemoryHeapPmem::mapMemory(size_t offset, size_t size)
170{
171    sp<MemoryPmem> memory = createMemory(offset, size);
172    if (memory != 0) {
173        Mutex::Autolock _l(mLock);
174        mAllocations.add(memory);
175    }
176    return memory;
177}
178
179sp<MemoryHeapPmem::MemoryPmem> MemoryHeapPmem::createMemory(
180        size_t offset, size_t size)
181{
182    sp<SubRegionMemory> memory;
183    if (heapID() > 0)
184        memory = new SubRegionMemory(this, offset, size);
185    return memory;
186}
187
188status_t MemoryHeapPmem::slap()
189{
190#if HAVE_ANDROID_OS
191    size_t size = getSize();
192    const size_t pagesize = getpagesize();
193    size = (size + pagesize-1) & ~(pagesize-1);
194    int our_fd = getHeapID();
195    struct pmem_region sub = { 0, size };
196    int err = ioctl(our_fd, PMEM_MAP, &sub);
197    LOGE_IF(err<0, "PMEM_MAP failed (%s), "
198            "mFD=%d, sub.offset=%lu, sub.size=%lu",
199            strerror(errno), our_fd, sub.offset, sub.len);
200    return -errno;
201#else
202    return NO_ERROR;
203#endif
204}
205
206status_t MemoryHeapPmem::unslap()
207{
208#if HAVE_ANDROID_OS
209    size_t size = getSize();
210    const size_t pagesize = getpagesize();
211    size = (size + pagesize-1) & ~(pagesize-1);
212    int our_fd = getHeapID();
213    struct pmem_region sub = { 0, size };
214    int err = ioctl(our_fd, PMEM_UNMAP, &sub);
215    LOGE_IF(err<0, "PMEM_UNMAP failed (%s), "
216            "mFD=%d, sub.offset=%lu, sub.size=%lu",
217            strerror(errno), our_fd, sub.offset, sub.len);
218    return -errno;
219#else
220    return NO_ERROR;
221#endif
222}
223
224void MemoryHeapPmem::revoke()
225{
226    SortedVector< wp<MemoryPmem> > allocations;
227
228    { // scope for lock
229        Mutex::Autolock _l(mLock);
230        allocations = mAllocations;
231    }
232
233    ssize_t count = allocations.size();
234    for (ssize_t i=0 ; i<count ; i++) {
235        sp<MemoryPmem> memory(allocations[i].promote());
236        if (memory != 0)
237            memory->revoke();
238    }
239}
240
241void MemoryHeapPmem::remove(const wp<MemoryPmem>& memory)
242{
243    Mutex::Autolock _l(mLock);
244    mAllocations.remove(memory);
245}
246
247// ---------------------------------------------------------------------------
248}; // namespace android
249