blob: 2286fb0ffea0ee8e0c1b1283896a65971e188e4c [file] [log] [blame]
Iliyan Malchev202a77d2012-06-11 14:41:12 -07001/*
Naseer Ahmed29a26812012-06-14 00:56:20 -07002 * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Iliyan Malchev202a77d2012-06-11 14:41:12 -07003
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of Code Aurora Forum, Inc. nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29#include <unistd.h>
30#include <fcntl.h>
31#include <sys/mman.h>
32#include <stdlib.h>
33#include <cutils/log.h>
34#include <errno.h>
35#include <linux/android_pmem.h>
36#include "gralloc_priv.h"
37#include "pmemalloc.h"
38#include "pmem_bestfit_alloc.h"
39
40using namespace gralloc;
41using android::sp;
42
43// Common functions between userspace
44// and kernel allocators
45static int getPmemTotalSize(int fd, size_t* size)
46{
47 //XXX: 7x27
48 int err = 0;
49 pmem_region region;
50 if (ioctl(fd, PMEM_GET_TOTAL_SIZE, &region)) {
51 err = -errno;
52 } else {
53 *size = region.len;
54 }
55 return err;
56}
57
58static int getOpenFlags(bool uncached)
59{
60 if(uncached)
61 return O_RDWR | O_SYNC;
62 else
63 return O_RDWR;
64}
65
66static int connectPmem(int fd, int master_fd) {
67 if (ioctl(fd, PMEM_CONNECT, master_fd))
68 return -errno;
69 return 0;
70}
71
72static int mapSubRegion(int fd, int offset, size_t size) {
73 struct pmem_region sub = { offset, size };
74 if (ioctl(fd, PMEM_MAP, &sub))
75 return -errno;
76 return 0;
77}
78
79static int unmapSubRegion(int fd, int offset, size_t size) {
80 struct pmem_region sub = { offset, size };
81 if (ioctl(fd, PMEM_UNMAP, &sub))
82 return -errno;
83 return 0;
84}
85
86static int alignPmem(int fd, size_t size, int align) {
87 struct pmem_allocation allocation;
88 allocation.size = size;
89 allocation.align = align;
90 if (ioctl(fd, PMEM_ALLOCATE_ALIGNED, &allocation))
91 return -errno;
92 return 0;
93}
94
95static int cleanPmem(void *base, size_t size, int offset, int fd) {
96 struct pmem_addr pmem_addr;
97 pmem_addr.vaddr = (unsigned long) base;
98 pmem_addr.offset = offset;
99 pmem_addr.length = size;
100 if (ioctl(fd, PMEM_CLEAN_INV_CACHES, &pmem_addr))
101 return -errno;
102 return 0;
103}
104
105//-------------- PmemUserspaceAlloc-----------------------//
106PmemUserspaceAlloc::PmemUserspaceAlloc()
107{
108 mPmemDev = DEVICE_PMEM;
109 mMasterFd = FD_INIT;
110 mAllocator = new SimpleBestFitAllocator();
111 pthread_mutex_init(&mLock, NULL);
112}
113
114PmemUserspaceAlloc::~PmemUserspaceAlloc()
115{
116}
117
118int PmemUserspaceAlloc::init_pmem_area_locked()
119{
120 ALOGD("%s: Opening master pmem FD", __FUNCTION__);
121 int err = 0;
122 int fd = open(mPmemDev, O_RDWR, 0);
123 if (fd >= 0) {
124 size_t size = 0;
125 err = getPmemTotalSize(fd, &size);
126 ALOGD("%s: Total pmem size: %d", __FUNCTION__, size);
127 if (err < 0) {
128 ALOGE("%s: PMEM_GET_TOTAL_SIZE failed (%d), limp mode", mPmemDev,
Naseer Ahmed29a26812012-06-14 00:56:20 -0700129 err);
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700130 size = 8<<20; // 8 MiB
131 }
132 mAllocator->setSize(size);
133
134 void* base = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd,
Naseer Ahmed29a26812012-06-14 00:56:20 -0700135 0);
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700136 if (base == MAP_FAILED) {
137 err = -errno;
138 ALOGE("%s: Failed to map pmem master fd: %s", mPmemDev,
Naseer Ahmed29a26812012-06-14 00:56:20 -0700139 strerror(errno));
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700140 base = 0;
141 close(fd);
142 fd = -1;
143 } else {
144 mMasterFd = fd;
145 mMasterBase = base;
146 }
147 } else {
148 err = -errno;
149 ALOGE("%s: Failed to open pmem device: %s", mPmemDev,
Naseer Ahmed29a26812012-06-14 00:56:20 -0700150 strerror(errno));
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700151 }
152 return err;
153}
154
155int PmemUserspaceAlloc::init_pmem_area()
156{
157 pthread_mutex_lock(&mLock);
158 int err = mMasterFd;
159 if (err == FD_INIT) {
160 // first time, try to initialize pmem
161 ALOGD("%s: Initializing pmem area", __FUNCTION__);
162 err = init_pmem_area_locked();
163 if (err) {
164 ALOGE("%s: failed to initialize pmem area", mPmemDev);
165 mMasterFd = err;
166 }
167 } else if (err < 0) {
168 // pmem couldn't be initialized, never use it
169 } else {
170 // pmem OK
171 err = 0;
172 }
173 pthread_mutex_unlock(&mLock);
174 return err;
175
176}
177
178int PmemUserspaceAlloc::alloc_buffer(alloc_data& data)
179{
180 int err = init_pmem_area();
181 if (err == 0) {
182 void* base = mMasterBase;
183 size_t size = data.size;
184 int offset = mAllocator->allocate(size);
185 if (offset < 0) {
186 // no more pmem memory
187 ALOGE("%s: No more pmem available", mPmemDev);
188 err = -ENOMEM;
189 } else {
190 int openFlags = getOpenFlags(data.uncached);
191
192 // now create the "sub-heap"
193 int fd = open(mPmemDev, openFlags, 0);
194 err = fd < 0 ? fd : 0;
195
196 // and connect to it
197 if (err == 0)
198 err = connectPmem(fd, mMasterFd);
199
200 // and make it available to the client process
201 if (err == 0)
202 err = mapSubRegion(fd, offset, size);
203
204 if (err < 0) {
205 ALOGE("%s: Failed to initialize pmem sub-heap: %d", mPmemDev,
Naseer Ahmed29a26812012-06-14 00:56:20 -0700206 err);
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700207 close(fd);
208 mAllocator->deallocate(offset);
209 fd = -1;
210 } else {
211 ALOGD("%s: Allocated buffer base:%p size:%d offset:%d fd:%d",
Naseer Ahmed29a26812012-06-14 00:56:20 -0700212 mPmemDev, base, size, offset, fd);
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700213 memset((char*)base + offset, 0, size);
214 //Clean cache before flushing to ensure pmem is properly flushed
215 err = clean_buffer((void*)((intptr_t) base + offset), size, offset, fd);
216 if (err < 0) {
217 ALOGE("cleanPmem failed: (%s)", strerror(errno));
218 }
219 cacheflush(intptr_t(base) + offset, intptr_t(base) + offset + size, 0);
220 data.base = base;
221 data.offset = offset;
222 data.fd = fd;
223 }
224 }
225 }
226 return err;
227
228}
229
230int PmemUserspaceAlloc::free_buffer(void* base, size_t size, int offset, int fd)
231{
232 ALOGD("%s: Freeing buffer base:%p size:%d offset:%d fd:%d",
Naseer Ahmed29a26812012-06-14 00:56:20 -0700233 mPmemDev, base, size, offset, fd);
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700234 int err = 0;
235 if (fd >= 0) {
236 int err = unmapSubRegion(fd, offset, size);
237 ALOGE_IF(err<0, "PMEM_UNMAP failed (%s), fd=%d, sub.offset=%u, "
Naseer Ahmed29a26812012-06-14 00:56:20 -0700238 "sub.size=%u", strerror(errno), fd, offset, size);
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700239 if (err == 0) {
240 // we can't deallocate the memory in case of UNMAP failure
241 // because it would give that process access to someone else's
242 // surfaces, which would be a security breach.
243 mAllocator->deallocate(offset);
244 }
245 close(fd);
246 }
247 return err;
248}
249
250int PmemUserspaceAlloc::map_buffer(void **pBase, size_t size, int offset, int fd)
251{
252 int err = 0;
253 size += offset;
254 void *base = mmap(0, size, PROT_READ| PROT_WRITE,
Naseer Ahmed29a26812012-06-14 00:56:20 -0700255 MAP_SHARED, fd, 0);
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700256 *pBase = base;
257 if(base == MAP_FAILED) {
258 err = -errno;
259 ALOGE("%s: Failed to map buffer size:%d offset:%d fd:%d Error: %s",
Naseer Ahmed29a26812012-06-14 00:56:20 -0700260 mPmemDev, size, offset, fd, strerror(errno));
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700261 } else {
262 ALOGD("%s: Mapped buffer base:%p size:%d offset:%d fd:%d",
Naseer Ahmed29a26812012-06-14 00:56:20 -0700263 mPmemDev, base, size, offset, fd);
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700264 }
265 return err;
266
267}
268
269int PmemUserspaceAlloc::unmap_buffer(void *base, size_t size, int offset)
270{
271 int err = 0;
272 //pmem hack
273 base = (void*)(intptr_t(base) - offset);
274 size += offset;
275 ALOGD("%s: Unmapping buffer base:%p size:%d offset:%d",
Naseer Ahmed29a26812012-06-14 00:56:20 -0700276 mPmemDev , base, size, offset);
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700277 if (munmap(base, size) < 0) {
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700278 err = -errno;
279 ALOGE("%s: Failed to unmap memory at %p :%s",
Naseer Ahmed29a26812012-06-14 00:56:20 -0700280 mPmemDev, base, strerror(errno));
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700281
282 }
283
Naseer Ahmed29a26812012-06-14 00:56:20 -0700284 return err;
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700285}
286
287int PmemUserspaceAlloc::clean_buffer(void *base, size_t size, int offset, int fd)
288{
289 return cleanPmem(base, size, offset, fd);
290}
291
292
293//-------------- PmemKernelAlloc-----------------------//
294
295PmemKernelAlloc::PmemKernelAlloc(const char* pmemdev) :
296 mPmemDev(pmemdev)
297{
298}
299
300PmemKernelAlloc::~PmemKernelAlloc()
301{
302}
303
304int PmemKernelAlloc::alloc_buffer(alloc_data& data)
305{
306 int err, offset = 0;
307 int openFlags = getOpenFlags(data.uncached);
308 int size = data.size;
309
310 int fd = open(mPmemDev, openFlags, 0);
311 if (fd < 0) {
312 err = -errno;
313 ALOGE("%s: Error opening %s", __FUNCTION__, mPmemDev);
314 return err;
315 }
316
317 if (data.align == 8192) {
318 // Tile format buffers need physical alignment to 8K
319 // Default page size does not need this ioctl
320 err = alignPmem(fd, size, 8192);
321 if (err < 0) {
322 ALOGE("alignPmem failed");
323 }
324 }
325 void* base = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
326 if (base == MAP_FAILED) {
327 err = -errno;
328 ALOGE("%s: failed to map pmem fd: %s", mPmemDev,
Naseer Ahmed29a26812012-06-14 00:56:20 -0700329 strerror(errno));
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700330 close(fd);
331 return err;
332 }
333 memset(base, 0, size);
334 clean_buffer((void*)((intptr_t) base + offset), size, offset, fd);
335 data.base = base;
336 data.offset = 0;
337 data.fd = fd;
338 ALOGD("%s: Allocated buffer base:%p size:%d fd:%d",
Naseer Ahmed29a26812012-06-14 00:56:20 -0700339 mPmemDev, base, size, fd);
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700340 return 0;
341
342}
343
344int PmemKernelAlloc::free_buffer(void* base, size_t size, int offset, int fd)
345{
346 ALOGD("%s: Freeing buffer base:%p size:%d fd:%d",
Naseer Ahmed29a26812012-06-14 00:56:20 -0700347 mPmemDev, base, size, fd);
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700348
349 int err = unmap_buffer(base, size, offset);
350 close(fd);
351 return err;
352}
353
354int PmemKernelAlloc::map_buffer(void **pBase, size_t size, int offset, int fd)
355{
356 int err = 0;
357 void *base = mmap(0, size, PROT_READ| PROT_WRITE,
Naseer Ahmed29a26812012-06-14 00:56:20 -0700358 MAP_SHARED, fd, 0);
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700359 *pBase = base;
360 if(base == MAP_FAILED) {
361 err = -errno;
362 ALOGE("%s: Failed to map memory in the client: %s",
Naseer Ahmed29a26812012-06-14 00:56:20 -0700363 mPmemDev, strerror(errno));
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700364 } else {
365 ALOGD("%s: Mapped buffer base:%p size:%d, fd:%d",
Naseer Ahmed29a26812012-06-14 00:56:20 -0700366 mPmemDev, base, size, fd);
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700367 }
368 return err;
369
370}
371
372int PmemKernelAlloc::unmap_buffer(void *base, size_t size, int offset)
373{
374 int err = 0;
375 if (munmap(base, size)) {
376 err = -errno;
377 ALOGW("%s: Error unmapping memory at %p: %s",
Naseer Ahmed29a26812012-06-14 00:56:20 -0700378 mPmemDev, base, strerror(err));
Iliyan Malchev202a77d2012-06-11 14:41:12 -0700379 }
380 return err;
381
382}
383int PmemKernelAlloc::clean_buffer(void *base, size_t size, int offset, int fd)
384{
385 return cleanPmem(base, size, offset, fd);
386}
387