/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #include <limits.h> #include <unistd.h> #include <fcntl.h> #include <pthread.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <cutils/log.h> #include <cutils/ashmem.h> #include "gralloc_priv.h" #include "pmemalloc.h" #define BEGIN_FUNC LOGV("%s begin", __PRETTY_FUNCTION__) #define END_FUNC LOGV("%s end", __PRETTY_FUNCTION__) static int get_open_flags(int usage) { int openFlags = O_RDWR | O_SYNC; uint32_t uread = usage & GRALLOC_USAGE_SW_READ_MASK; uint32_t uwrite = usage & GRALLOC_USAGE_SW_WRITE_MASK; if (uread == GRALLOC_USAGE_SW_READ_OFTEN || uwrite == GRALLOC_USAGE_SW_WRITE_OFTEN) { openFlags &= ~O_SYNC; } return openFlags; } PmemAllocator::~PmemAllocator() { BEGIN_FUNC; END_FUNC; } PmemUserspaceAllocator::PmemUserspaceAllocator(Deps& deps, Deps::Allocator& allocator, const char* pmemdev): deps(deps), allocator(allocator), pmemdev(pmemdev), master_fd(MASTER_FD_INIT) { BEGIN_FUNC; pthread_mutex_init(&lock, NULL); END_FUNC; } PmemUserspaceAllocator::~PmemUserspaceAllocator() { BEGIN_FUNC; END_FUNC; } void* PmemUserspaceAllocator::get_base_address() { BEGIN_FUNC; END_FUNC; return master_base; } int PmemUserspaceAllocator::init_pmem_area_locked() { BEGIN_FUNC; int err = 0; int fd = deps.open(pmemdev, O_RDWR, 0); if (fd >= 0) { size_t size = 0; err = deps.getPmemTotalSize(fd, &size); if (err < 0) { LOGE("%s: PMEM_GET_TOTAL_SIZE failed (%d), limp mode", pmemdev, err); size = 8<<20; // 8 MiB } allocator.setSize(size); void* base = deps.mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (base == MAP_FAILED) { LOGE("%s: failed to map pmem master fd: %s", pmemdev, strerror(deps.getErrno())); err = -deps.getErrno(); base = 0; deps.close(fd); fd = -1; } else { master_fd = fd; master_base = base; } } else { LOGE("%s: failed to open pmem device: %s", pmemdev, strerror(deps.getErrno())); err = -deps.getErrno(); } END_FUNC; return err; } int PmemUserspaceAllocator::init_pmem_area() { BEGIN_FUNC; pthread_mutex_lock(&lock); int err = master_fd; if (err == MASTER_FD_INIT) { // first time, try to initialize pmem err = init_pmem_area_locked(); if (err) { LOGE("%s: failed to initialize pmem area", pmemdev); master_fd = err; } } else if (err < 0) { // pmem couldn't be initialized, never use it } else { // pmem OK err = 0; } pthread_mutex_unlock(&lock); END_FUNC; return err; } int PmemUserspaceAllocator::alloc_pmem_buffer(size_t size, int usage, void** pBase, int* pOffset, int* pFd) { BEGIN_FUNC; int err = init_pmem_area(); if (err == 0) { void* base = master_base; int offset = allocator.allocate(size); if (offset < 0) { // no more pmem memory LOGE("%s: no more pmem available", pmemdev); err = -ENOMEM; } else { int openFlags = get_open_flags(usage); //LOGD("%s: allocating pmem at offset 0x%p", pmemdev, offset); // now create the "sub-heap" int fd = deps.open(pmemdev, openFlags, 0); err = fd < 0 ? fd : 0; // and connect to it if (err == 0) err = deps.connectPmem(fd, master_fd); // and make it available to the client process if (err == 0) err = deps.mapPmem(fd, offset, size); if (err < 0) { LOGE("%s: failed to initialize pmem sub-heap: %d", pmemdev, err); err = -deps.getErrno(); deps.close(fd); allocator.deallocate(offset); fd = -1; } else { LOGV("%s: mapped fd %d at offset %d, size %d", pmemdev, fd, offset, size); memset((char*)base + offset, 0, size); *pBase = base; *pOffset = offset; *pFd = fd; } //LOGD_IF(!err, "%s: allocating pmem size=%d, offset=%d", pmemdev, size, offset); } } END_FUNC; return err; } int PmemUserspaceAllocator::free_pmem_buffer(size_t size, void* base, int offset, int fd) { BEGIN_FUNC; int err = 0; if (fd >= 0) { int err = deps.unmapPmem(fd, offset, size); LOGE_IF(err<0, "PMEM_UNMAP failed (%s), fd=%d, sub.offset=%u, " "sub.size=%u", strerror(deps.getErrno()), fd, offset, size); if (err == 0) { // we can't deallocate the memory in case of UNMAP failure // because it would give that process access to someone else's // surfaces, which would be a security breach. allocator.deallocate(offset); } } END_FUNC; return err; } PmemUserspaceAllocator::Deps::Allocator::~Allocator() { BEGIN_FUNC; END_FUNC; } PmemUserspaceAllocator::Deps::~Deps() { BEGIN_FUNC; END_FUNC; } PmemKernelAllocator::PmemKernelAllocator(Deps& deps, const char* pmemdev): deps(deps), pmemdev(pmemdev) { BEGIN_FUNC; END_FUNC; } PmemKernelAllocator::~PmemKernelAllocator() { BEGIN_FUNC; END_FUNC; } void* PmemKernelAllocator::get_base_address() { BEGIN_FUNC; END_FUNC; return 0; } static unsigned clp2(unsigned x) { x = x - 1; x = x | (x >> 1); x = x | (x >> 2); x = x | (x >> 4); x = x | (x >> 8); x = x | (x >>16); return x + 1; } int PmemKernelAllocator::alloc_pmem_buffer(size_t size, int usage, void** pBase,int* pOffset, int* pFd) { BEGIN_FUNC; *pBase = 0; *pOffset = 0; *pFd = -1; int err; int openFlags = get_open_flags(usage); int fd = deps.open(pmemdev, openFlags, 0); if (fd < 0) { err = -deps.getErrno(); END_FUNC; return err; } // The size should already be page aligned, now round it up to a power of 2. size = clp2(size); void* base = deps.mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (base == MAP_FAILED) { LOGE("%s: failed to map pmem fd: %s", pmemdev, strerror(deps.getErrno())); err = -deps.getErrno(); deps.close(fd); END_FUNC; return err; } memset(base, 0, size); *pBase = base; *pOffset = 0; *pFd = fd; END_FUNC; return 0; } int PmemKernelAllocator::free_pmem_buffer(size_t size, void* base, int offset, int fd) { BEGIN_FUNC; // The size should already be page aligned, now round it up to a power of 2 // like we did when allocating. size = clp2(size); int err = deps.munmap(base, size); if (err < 0) { err = deps.getErrno(); LOGW("%s: error unmapping pmem fd: %s", pmemdev, strerror(err)); return -err; } END_FUNC; return 0; } PmemKernelAllocator::Deps::~Deps() { BEGIN_FUNC; END_FUNC; }