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