1/*
2 * Copyright (C) 2010 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_NDEBUG 0
18
19#include <limits.h>
20#include <unistd.h>
21#include <fcntl.h>
22#include <pthread.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include <sys/mman.h>
27
28#include <cutils/log.h>
29#include <cutils/ashmem.h>
30
31#include "gralloc_priv.h"
32#include "pmemalloc.h"
33
34
35#define BEGIN_FUNC ALOGV("%s begin", __PRETTY_FUNCTION__)
36#define END_FUNC ALOGV("%s end", __PRETTY_FUNCTION__)
37
38
39static int get_open_flags(int usage) {
40    int openFlags = O_RDWR | O_SYNC;
41    uint32_t uread = usage & GRALLOC_USAGE_SW_READ_MASK;
42    uint32_t uwrite = usage & GRALLOC_USAGE_SW_WRITE_MASK;
43    if (uread == GRALLOC_USAGE_SW_READ_OFTEN ||
44        uwrite == GRALLOC_USAGE_SW_WRITE_OFTEN) {
45        openFlags &= ~O_SYNC;
46    }
47    return openFlags;
48}
49
50PmemAllocator::~PmemAllocator()
51{
52    BEGIN_FUNC;
53    END_FUNC;
54}
55
56
57PmemUserspaceAllocator::PmemUserspaceAllocator(Deps& deps, Deps::Allocator& allocator, const char* pmemdev):
58    deps(deps),
59    allocator(allocator),
60    pmemdev(pmemdev),
61    master_fd(MASTER_FD_INIT)
62{
63    BEGIN_FUNC;
64    pthread_mutex_init(&lock, NULL);
65    END_FUNC;
66}
67
68
69PmemUserspaceAllocator::~PmemUserspaceAllocator()
70{
71    BEGIN_FUNC;
72    END_FUNC;
73}
74
75
76void* PmemUserspaceAllocator::get_base_address() {
77    BEGIN_FUNC;
78    END_FUNC;
79    return master_base;
80}
81
82
83int PmemUserspaceAllocator::init_pmem_area_locked()
84{
85    BEGIN_FUNC;
86    int err = 0;
87    int fd = deps.open(pmemdev, O_RDWR, 0);
88    if (fd >= 0) {
89        size_t size = 0;
90        err = deps.getPmemTotalSize(fd, &size);
91        if (err < 0) {
92            ALOGE("%s: PMEM_GET_TOTAL_SIZE failed (%d), limp mode", pmemdev,
93                    err);
94            size = 8<<20;   // 8 MiB
95        }
96        allocator.setSize(size);
97
98        void* base = deps.mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd,
99                0);
100        if (base == MAP_FAILED) {
101            ALOGE("%s: failed to map pmem master fd: %s", pmemdev,
102                    strerror(deps.getErrno()));
103            err = -deps.getErrno();
104            base = 0;
105            deps.close(fd);
106            fd = -1;
107        } else {
108            master_fd = fd;
109            master_base = base;
110        }
111    } else {
112        ALOGE("%s: failed to open pmem device: %s", pmemdev,
113                strerror(deps.getErrno()));
114        err = -deps.getErrno();
115    }
116    END_FUNC;
117    return err;
118}
119
120
121int PmemUserspaceAllocator::init_pmem_area()
122{
123    BEGIN_FUNC;
124    pthread_mutex_lock(&lock);
125    int err = master_fd;
126    if (err == MASTER_FD_INIT) {
127        // first time, try to initialize pmem
128        err = init_pmem_area_locked();
129        if (err) {
130            ALOGE("%s: failed to initialize pmem area", pmemdev);
131            master_fd = err;
132        }
133    } else if (err < 0) {
134        // pmem couldn't be initialized, never use it
135    } else {
136        // pmem OK
137        err = 0;
138    }
139    pthread_mutex_unlock(&lock);
140    END_FUNC;
141    return err;
142}
143
144
145int PmemUserspaceAllocator::alloc_pmem_buffer(size_t size, int usage,
146        void** pBase, int* pOffset, int* pFd)
147{
148    BEGIN_FUNC;
149    int err = init_pmem_area();
150    if (err == 0) {
151        void* base = master_base;
152        int offset = allocator.allocate(size);
153        if (offset < 0) {
154            // no more pmem memory
155            ALOGE("%s: no more pmem available", pmemdev);
156            err = -ENOMEM;
157        } else {
158            int openFlags = get_open_flags(usage);
159
160            //ALOGD("%s: allocating pmem at offset 0x%p", pmemdev, offset);
161
162            // now create the "sub-heap"
163            int fd = deps.open(pmemdev, openFlags, 0);
164            err = fd < 0 ? fd : 0;
165
166            // and connect to it
167            if (err == 0)
168                err = deps.connectPmem(fd, master_fd);
169
170            // and make it available to the client process
171            if (err == 0)
172                err = deps.mapPmem(fd, offset, size);
173
174            if (err < 0) {
175                ALOGE("%s: failed to initialize pmem sub-heap: %d", pmemdev,
176                        err);
177                err = -deps.getErrno();
178                deps.close(fd);
179                allocator.deallocate(offset);
180                fd = -1;
181            } else {
182                ALOGV("%s: mapped fd %d at offset %d, size %d", pmemdev, fd, offset, size);
183                memset((char*)base + offset, 0, size);
184                *pBase = base;
185                *pOffset = offset;
186                *pFd = fd;
187            }
188            //ALOGD_IF(!err, "%s: allocating pmem size=%d, offset=%d", pmemdev, size, offset);
189        }
190    }
191    END_FUNC;
192    return err;
193}
194
195
196int PmemUserspaceAllocator::free_pmem_buffer(size_t size, void* base, int offset, int fd)
197{
198    BEGIN_FUNC;
199    int err = 0;
200    if (fd >= 0) {
201        int err = deps.unmapPmem(fd, offset, size);
202        ALOGE_IF(err<0, "PMEM_UNMAP failed (%s), fd=%d, sub.offset=%u, "
203                "sub.size=%u", strerror(deps.getErrno()), fd, offset, size);
204        if (err == 0) {
205            // we can't deallocate the memory in case of UNMAP failure
206            // because it would give that process access to someone else's
207            // surfaces, which would be a security breach.
208            allocator.deallocate(offset);
209        }
210    }
211    END_FUNC;
212    return err;
213}
214
215PmemUserspaceAllocator::Deps::Allocator::~Allocator()
216{
217    BEGIN_FUNC;
218    END_FUNC;
219}
220
221PmemUserspaceAllocator::Deps::~Deps()
222{
223    BEGIN_FUNC;
224    END_FUNC;
225}
226
227PmemKernelAllocator::PmemKernelAllocator(Deps& deps, const char* pmemdev):
228    deps(deps),
229    pmemdev(pmemdev)
230{
231    BEGIN_FUNC;
232    END_FUNC;
233}
234
235
236PmemKernelAllocator::~PmemKernelAllocator()
237{
238    BEGIN_FUNC;
239    END_FUNC;
240}
241
242
243void* PmemKernelAllocator::get_base_address() {
244    BEGIN_FUNC;
245    END_FUNC;
246    return 0;
247}
248
249
250static unsigned clp2(unsigned x) {
251    x = x - 1;
252    x = x | (x >> 1);
253    x = x | (x >> 2);
254    x = x | (x >> 4);
255    x = x | (x >> 8);
256    x = x | (x >>16);
257    return x + 1;
258}
259
260
261int PmemKernelAllocator::alloc_pmem_buffer(size_t size, int usage,
262        void** pBase,int* pOffset, int* pFd)
263{
264    BEGIN_FUNC;
265
266    *pBase = 0;
267    *pOffset = 0;
268    *pFd = -1;
269
270    int err;
271    int openFlags = get_open_flags(usage);
272    int fd = deps.open(pmemdev, openFlags, 0);
273    if (fd < 0) {
274        err = -deps.getErrno();
275        END_FUNC;
276        return err;
277    }
278
279    // The size should already be page aligned, now round it up to a power of 2.
280    size = clp2(size);
281
282    void* base = deps.mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
283    if (base == MAP_FAILED) {
284        ALOGE("%s: failed to map pmem fd: %s", pmemdev,
285             strerror(deps.getErrno()));
286        err = -deps.getErrno();
287        deps.close(fd);
288        END_FUNC;
289        return err;
290    }
291
292    memset(base, 0, size);
293
294    *pBase = base;
295    *pOffset = 0;
296    *pFd = fd;
297
298    END_FUNC;
299    return 0;
300}
301
302
303int PmemKernelAllocator::free_pmem_buffer(size_t size, void* base, int offset, int fd)
304{
305    BEGIN_FUNC;
306    // The size should already be page aligned, now round it up to a power of 2
307    // like we did when allocating.
308    size = clp2(size);
309
310    int err = deps.munmap(base, size);
311    if (err < 0) {
312        err = deps.getErrno();
313        ALOGW("%s: error unmapping pmem fd: %s", pmemdev, strerror(err));
314        return -err;
315    }
316    END_FUNC;
317    return 0;
318}
319
320PmemKernelAllocator::Deps::~Deps()
321{
322    BEGIN_FUNC;
323    END_FUNC;
324}
325