bd4bfd035d
Change-Id: I7fae5cf67ed4a1f00012c64c384bb621ab8bb183
325 lines
7.7 KiB
C++
325 lines
7.7 KiB
C++
/*
|
|
* 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;
|
|
}
|