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 LOGV("%s begin", __PRETTY_FUNCTION__) 36 #define END_FUNC LOGV("%s end", __PRETTY_FUNCTION__) 37 38 39 static 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 50 PmemAllocator::~PmemAllocator() 51 { 52 BEGIN_FUNC; 53 END_FUNC; 54 } 55 56 57 PmemUserspaceAllocator::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 69 PmemUserspaceAllocator::~PmemUserspaceAllocator() 70 { 71 BEGIN_FUNC; 72 END_FUNC; 73 } 74 75 76 void* PmemUserspaceAllocator::get_base_address() { 77 BEGIN_FUNC; 78 END_FUNC; 79 return master_base; 80 } 81 82 83 int 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 LOGE("%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 LOGE("%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 LOGE("%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 121 int 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 LOGE("%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 145 int 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 LOGE("%s: no more pmem available", pmemdev); 156 err = -ENOMEM; 157 } else { 158 int openFlags = get_open_flags(usage); 159 160 //LOGD("%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 LOGE("%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 LOGV("%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 //LOGD_IF(!err, "%s: allocating pmem size=%d, offset=%d", pmemdev, size, offset); 189 } 190 } 191 END_FUNC; 192 return err; 193 } 194 195 196 int 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 LOGE_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 215 PmemUserspaceAllocator::Deps::Allocator::~Allocator() 216 { 217 BEGIN_FUNC; 218 END_FUNC; 219 } 220 221 PmemUserspaceAllocator::Deps::~Deps() 222 { 223 BEGIN_FUNC; 224 END_FUNC; 225 } 226 227 PmemKernelAllocator::PmemKernelAllocator(Deps& deps, const char* pmemdev): 228 deps(deps), 229 pmemdev(pmemdev) 230 { 231 BEGIN_FUNC; 232 END_FUNC; 233 } 234 235 236 PmemKernelAllocator::~PmemKernelAllocator() 237 { 238 BEGIN_FUNC; 239 END_FUNC; 240 } 241 242 243 void* PmemKernelAllocator::get_base_address() { 244 BEGIN_FUNC; 245 END_FUNC; 246 return 0; 247 } 248 249 250 static 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 261 int 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 LOGE("%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 303 int 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 LOGW("%s: error unmapping pmem fd: %s", pmemdev, strerror(err)); 314 return -err; 315 } 316 END_FUNC; 317 return 0; 318 } 319 320 PmemKernelAllocator::Deps::~Deps() 321 { 322 BEGIN_FUNC; 323 END_FUNC; 324 } 325