From 5bc176b1a6fa7710845b23e38bc1c93c8345a3fc Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Wed, 21 Apr 2010 17:33:32 -0700 Subject: [PATCH] Refactor the qsd8k gralloc implementation. The purpose of this change is to add support for allocating gralloc buffers from either /dev/pmem or /dev/pmem_adsp depending on the usage flags. It does this by factoring out and abstracting the interactions with the pmem device. For /dev/pmem allocations, the kernel allocator is not used, so a single master fd is opened, and all the allocations are sub-allocated from that by gralloc. For /dev/pmem_adsp the kernel allocator is used, so it simply opens a new fd for each allocation. A very basic unit test that can be run on the host is included. It requires gtest, so to run it on a host system gtest must (currently) be compiled with BUILD_WITH_ASTL=true. Change-Id: If2ae0151698fad8107e18e808a3fa012a846263f --- libgralloc-qsd8k/Android.mk | 16 +- libgralloc-qsd8k/allocator.h | 13 +- libgralloc-qsd8k/gpu.cpp | 340 ++++++++++++ libgralloc-qsd8k/gpu.h | 76 +++ libgralloc-qsd8k/gralloc.cpp | 496 ++++-------------- libgralloc-qsd8k/gralloc_priv.h | 15 +- libgralloc-qsd8k/mapper.cpp | 3 +- libgralloc-qsd8k/pmemalloc.cpp | 324 ++++++++++++ libgralloc-qsd8k/pmemalloc.h | 161 ++++++ libgralloc-qsd8k/tests/Android.mk | 55 ++ libgralloc-qsd8k/tests/pmemalloc_test.cpp | 601 ++++++++++++++++++++++ 11 files changed, 1686 insertions(+), 414 deletions(-) create mode 100644 libgralloc-qsd8k/gpu.cpp create mode 100644 libgralloc-qsd8k/gpu.h create mode 100644 libgralloc-qsd8k/pmemalloc.cpp create mode 100644 libgralloc-qsd8k/pmemalloc.h create mode 100644 libgralloc-qsd8k/tests/Android.mk create mode 100644 libgralloc-qsd8k/tests/pmemalloc_test.cpp diff --git a/libgralloc-qsd8k/Android.mk b/libgralloc-qsd8k/Android.mk index d5ffa42..cc92d04 100644 --- a/libgralloc-qsd8k/Android.mk +++ b/libgralloc-qsd8k/Android.mk @@ -23,10 +23,22 @@ LOCAL_SHARED_LIBRARIES := liblog libcutils libGLESv1_CM LOCAL_SRC_FILES := \ allocator.cpp \ - gralloc.cpp \ framebuffer.cpp \ - mapper.cpp + gpu.cpp \ + gralloc.cpp \ + mapper.cpp \ + pmemalloc.cpp LOCAL_MODULE := gralloc.$(TARGET_BOARD_PLATFORM) LOCAL_CFLAGS:= -DLOG_TAG=\"$(TARGET_BOARD_PLATFORM).gralloc\" include $(BUILD_SHARED_LIBRARY) + +# Build a host library for testing +include $(CLEAR_VARS) +LOCAL_SRC_FILES := \ + gpu.cpp \ + pmemalloc.cpp + +LOCAL_MODULE := libgralloc_qsd8k_host +LOCAL_CFLAGS:= -DLOG_TAG=\"gralloc-qsd8k\" +include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libgralloc-qsd8k/allocator.h b/libgralloc-qsd8k/allocator.h index b0d89e9..dc81f51 100644 --- a/libgralloc-qsd8k/allocator.h +++ b/libgralloc-qsd8k/allocator.h @@ -22,6 +22,7 @@ #include #include "gr.h" +#include "pmemalloc.h" // ---------------------------------------------------------------------------- @@ -91,19 +92,19 @@ public: } }; -class SimpleBestFitAllocator +class SimpleBestFitAllocator : public PmemUserspaceAllocator::Deps::Allocator { public: SimpleBestFitAllocator(); SimpleBestFitAllocator(size_t size); - ~SimpleBestFitAllocator(); + virtual ~SimpleBestFitAllocator(); - ssize_t setSize(size_t size); + virtual ssize_t setSize(size_t size); - ssize_t allocate(size_t size, uint32_t flags = 0); - ssize_t deallocate(size_t offset); - size_t size() const; + virtual ssize_t allocate(size_t size, uint32_t flags = 0); + virtual ssize_t deallocate(size_t offset); + virtual size_t size() const; private: struct chunk_t { diff --git a/libgralloc-qsd8k/gpu.cpp b/libgralloc-qsd8k/gpu.cpp new file mode 100644 index 0000000..2a2d6d5 --- /dev/null +++ b/libgralloc-qsd8k/gpu.cpp @@ -0,0 +1,340 @@ +/* + * 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. + */ + +#include +#include +#include + +#include + +#include "gr.h" +#include "gpu.h" + +gpu_context_t::gpu_context_t(Deps& deps, PmemAllocator& pmemAllocator, + PmemAllocator& pmemAdspAllocator, const private_module_t* module) : + deps(deps), + pmemAllocator(pmemAllocator), + pmemAdspAllocator(pmemAdspAllocator) +{ + // Zero out the alloc_device_t + memset(static_cast(this), 0, sizeof(alloc_device_t)); + + // Initialize the procs + common.tag = HARDWARE_DEVICE_TAG; + common.version = 0; + common.module = const_cast(&module->base.common); + common.close = gralloc_close; + alloc = gralloc_alloc; + free = gralloc_free; +} + +int gpu_context_t::gralloc_alloc_framebuffer_locked(size_t size, int usage, + buffer_handle_t* pHandle) +{ + private_module_t* m = reinterpret_cast(common.module); + + // we don't support allocations with both the FB and PMEM_ADSP flags + if (usage & GRALLOC_USAGE_PRIVATE_PMEM_ADSP) { + return -EINVAL; + } + + // allocate the framebuffer + if (m->framebuffer == NULL) { + // initialize the framebuffer, the framebuffer is mapped once + // and forever. + int err = deps.mapFrameBufferLocked(m); + if (err < 0) { + return err; + } + } + + const uint32_t bufferMask = m->bufferMask; + const uint32_t numBuffers = m->numBuffers; + const size_t bufferSize = m->finfo.line_length * m->info.yres; + if (numBuffers == 1) { + // If we have only one buffer, we never use page-flipping. Instead, + // we return a regular buffer which will be memcpy'ed to the main + // screen when post is called. + int newUsage = (usage & ~GRALLOC_USAGE_HW_FB) | GRALLOC_USAGE_HW_2D; + return gralloc_alloc_buffer(bufferSize, newUsage, pHandle); + } + + if (bufferMask >= ((1LU<framebuffer->base); + private_handle_t* hnd = new private_handle_t(dup(m->framebuffer->fd), size, + private_handle_t::PRIV_FLAGS_USES_PMEM | + private_handle_t::PRIV_FLAGS_FRAMEBUFFER); + + // find a free slot + for (uint32_t i=0 ; ibufferMask |= (1LU<base = vaddr; + hnd->offset = vaddr - intptr_t(m->framebuffer->base); + *pHandle = hnd; + + return 0; +} + + +int gpu_context_t::gralloc_alloc_framebuffer(size_t size, int usage, + buffer_handle_t* pHandle) +{ + private_module_t* m = reinterpret_cast(common.module); + pthread_mutex_lock(&m->lock); + int err = gralloc_alloc_framebuffer_locked(size, usage, pHandle); + pthread_mutex_unlock(&m->lock); + return err; +} + + +int gpu_context_t::gralloc_alloc_buffer(size_t size, int usage, buffer_handle_t* pHandle) +{ + int err = 0; + int flags = 0; + + int fd = -1; + void* base = 0; // XXX JMG: This should change to just get an address from + // the PmemAllocator rather than getting the base & offset separately + int offset = 0; + int lockState = 0; + + size = roundUpToPageSize(size); + + if (usage & GRALLOC_USAGE_HW_TEXTURE) { + // enable pmem in that case, so our software GL can fallback to + // the copybit module. + flags |= private_handle_t::PRIV_FLAGS_USES_PMEM; + } + + if (usage & GRALLOC_USAGE_HW_2D) { + flags |= private_handle_t::PRIV_FLAGS_USES_PMEM; + } + + if (usage & GRALLOC_USAGE_PRIVATE_PMEM_ADSP) { + flags |= private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP; + flags &= ~private_handle_t::PRIV_FLAGS_USES_PMEM; + } + + private_module_t* m = reinterpret_cast(common.module); + + if ((flags & private_handle_t::PRIV_FLAGS_USES_PMEM) != 0 || + (flags & private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP) != 0) { + + PmemAllocator* pma = 0; + + if ((flags & private_handle_t::PRIV_FLAGS_USES_PMEM) != 0) { + if ((flags & private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP) != 0) { + LOGE("attempting to allocate a gralloc buffer with both the " + "USES_PMEM and USES_PMEM_ADSP flags. Unsetting the " + "USES_PMEM_ADSP flag."); + flags &= ~private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP; + } + pma = &pmemAllocator; + } else { // (flags & private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP) != 0 + pma = &pmemAdspAllocator; + } + + // PMEM buffers are always mmapped + lockState |= private_handle_t::LOCK_STATE_MAPPED; + + // Allocate the buffer from pmem + err = pma->alloc_pmem_buffer(size, usage, &base, &offset, &fd); + if (err < 0) { + if (((usage & GRALLOC_USAGE_HW_2D) == 0) && + ((usage & GRALLOC_USAGE_PRIVATE_PMEM_ADSP) == 0)) { + // the caller didn't request PMEM, so we can try something else + flags &= ~private_handle_t::PRIV_FLAGS_USES_PMEM; + err = 0; + goto try_ashmem; + } else { + LOGE("couldn't open pmem (%s)", strerror(errno)); + } + } + } else { +try_ashmem: + fd = deps.ashmem_create_region("gralloc-buffer", size); + if (fd < 0) { + LOGE("couldn't create ashmem (%s)", strerror(errno)); + err = -errno; + } + } + + if (err == 0) { + private_handle_t* hnd = new private_handle_t(fd, size, flags); + hnd->offset = offset; + hnd->base = int(base)+offset; + hnd->lockState = lockState; + *pHandle = hnd; + } + + LOGE_IF(err, "gralloc failed err=%s", strerror(-err)); + + return err; +} + +static inline size_t ALIGN(size_t x, size_t align) { + return (x + align-1) & ~(align-1); +} + +int gpu_context_t::alloc_impl(int w, int h, int format, int usage, + buffer_handle_t* pHandle, int* pStride) { + if (!pHandle || !pStride) + return -EINVAL; + + size_t size, alignedw, alignedh; + + alignedw = ALIGN(w, 32); + alignedh = ALIGN(h, 32); + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + size = alignedw * alignedh * 4; + break; + case HAL_PIXEL_FORMAT_RGB_888: + size = alignedw * alignedh * 3; + break; + case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_RGBA_5551: + case HAL_PIXEL_FORMAT_RGBA_4444: + size = alignedw * alignedh * 2; + break; + + // adreno formats + case HAL_PIXEL_FORMAT_YCrCb_420_SP: // NV21 + size = ALIGN(alignedw*alignedh, 4096); + size += ALIGN(2 * ALIGN(w/2, 32) * ALIGN(h/2, 32), 4096); + break; + case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: // NV12 + // The chroma plane is subsampled, + // but the pitch in bytes is unchanged + // The GPU needs 4K alignment, but the video decoder needs 8K + alignedw = ALIGN(w, 128); + size = ALIGN( alignedw * alignedh, 8192); + size += ALIGN( alignedw * ALIGN(h/2, 32), 4096); + break; + + case HAL_PIXEL_FORMAT_YV12: + alignedw = ALIGN(w, 16); + alignedh = ALIGN(h, 16); + size = alignedw * alignedh; + size += size / 2; + break; + + case HAL_PIXEL_FORMAT_YV16: + alignedh = ALIGN(h, 16); + size = alignedw * alignedh * 2; + break; + + default: + LOGE("unrecognized pixel format: %d", format); + return -EINVAL; + } + + if ((ssize_t)size <= 0) + return -EINVAL; + + int err; + if (usage & GRALLOC_USAGE_HW_FB) { + err = gralloc_alloc_framebuffer(size, usage, pHandle); + } else { + err = gralloc_alloc_buffer(size, usage, pHandle); + } + + if (err < 0) { + return err; + } + + *pStride = alignedw; + return 0; +} + +int gpu_context_t::free_impl(private_handle_t const* hnd) { + private_module_t* m = reinterpret_cast(common.module); + if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) { + // free this buffer + const size_t bufferSize = m->finfo.line_length * m->info.yres; + int index = (hnd->base - m->framebuffer->base) / bufferSize; + m->bufferMask &= ~(1<flags & private_handle_t::PRIV_FLAGS_USES_PMEM) { + pmem_allocator = &pmemAllocator; + } else if (hnd->flags & private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP) { + pmem_allocator = &pmemAdspAllocator; + } + pmem_allocator->free_pmem_buffer(hnd->size, (void*)hnd->base, + hnd->offset, hnd->fd); + deps.terminateBuffer(&m->base, const_cast(hnd)); + } + + deps.close(hnd->fd); + delete hnd; // XXX JMG: move this to the deps + return 0; +} + +/****************************************************************************** + * Static functions + *****************************************************************************/ + +int gpu_context_t::gralloc_alloc(alloc_device_t* dev, int w, int h, int format, + int usage, buffer_handle_t* pHandle, int* pStride) +{ + if (!dev) { + return -EINVAL; + } + gpu_context_t* gpu = reinterpret_cast(dev); + return gpu->alloc_impl(w, h, format, usage, pHandle, pStride); +} + +int gpu_context_t::gralloc_free(alloc_device_t* dev, + buffer_handle_t handle) +{ + if (private_handle_t::validate(handle) < 0) + return -EINVAL; + + private_handle_t const* hnd = reinterpret_cast(handle); + gpu_context_t* gpu = reinterpret_cast(dev); + return gpu->free_impl(hnd); +} + +/*****************************************************************************/ + +int gpu_context_t::gralloc_close(struct hw_device_t *dev) +{ + gpu_context_t* ctx = reinterpret_cast(dev); + if (ctx) { + /* TODO: keep a list of all buffer_handle_t created, and free them + * all here. + */ + delete ctx; + } + return 0; +} + + +gpu_context_t::Deps::~Deps() {} diff --git a/libgralloc-qsd8k/gpu.h b/libgralloc-qsd8k/gpu.h new file mode 100644 index 0000000..5da7b6a --- /dev/null +++ b/libgralloc-qsd8k/gpu.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef GRALLOC_QSD8K_GPU_H_ +#define GRALLOC_QSD8K_GPU_H_ + +#include +#include +#include +#include + +#include +#include + +#include "gralloc_priv.h" +#include "pmemalloc.h" + + +class gpu_context_t : public alloc_device_t { + public: + + class Deps { + public: + + virtual ~Deps(); + + // ashmem + virtual int ashmem_create_region(const char *name, size_t size) = 0; + + // POSIX + virtual int close(int fd) = 0; + + // Framebuffer (locally defined) + virtual int mapFrameBufferLocked(struct private_module_t* module) = 0; + virtual int terminateBuffer(gralloc_module_t const* module, + private_handle_t* hnd) = 0; + }; + + gpu_context_t(Deps& deps, PmemAllocator& pmemAllocator, + PmemAllocator& pmemAdspAllocator, const private_module_t* module); + + int gralloc_alloc_framebuffer_locked(size_t size, int usage, + buffer_handle_t* pHandle); + int gralloc_alloc_framebuffer(size_t size, int usage, + buffer_handle_t* pHandle); + int gralloc_alloc_buffer(size_t size, int usage, buffer_handle_t* pHandle); + int free_impl(private_handle_t const* hnd); + int alloc_impl(int w, int h, int format, int usage, + buffer_handle_t* pHandle, int* pStride); + + static int gralloc_alloc(alloc_device_t* dev, int w, int h, int format, + int usage, buffer_handle_t* pHandle, int* pStride); + static int gralloc_free(alloc_device_t* dev, buffer_handle_t handle); + static int gralloc_close(struct hw_device_t *dev); + + private: + + Deps& deps; + PmemAllocator& pmemAllocator; + PmemAllocator& pmemAdspAllocator; +}; + +#endif // GRALLOC_QSD8K_GPU_H diff --git a/libgralloc-qsd8k/gralloc.cpp b/libgralloc-qsd8k/gralloc.cpp index 3d08cfd..bf60324 100644 --- a/libgralloc-qsd8k/gralloc.cpp +++ b/libgralloc-qsd8k/gralloc.cpp @@ -14,44 +14,22 @@ * limitations under the License. */ -#include #include #include -#include -#include -#include -#include #include #include #include #include -#include -#include -#include - -#include -#include - -#include "gralloc_priv.h" -#include "allocator.h" - -#if HAVE_ANDROID_OS #include -#endif + +#include "allocator.h" +#include "gr.h" +#include "gpu.h" /*****************************************************************************/ -static SimpleBestFitAllocator sAllocator; - -/*****************************************************************************/ - -struct gralloc_context_t { - alloc_device_t device; - /* our private data here */ -}; - static int gralloc_alloc_buffer(alloc_device_t* dev, size_t size, int usage, buffer_handle_t* pHandle); @@ -82,8 +60,93 @@ extern int gralloc_perform(struct gralloc_module_t const* module, /*****************************************************************************/ +/* On-device dependency implementation */ +class PmemAllocatorDepsDeviceImpl : public PmemUserspaceAllocator::Deps, + public PmemKernelAllocator::Deps { + + virtual size_t getPmemTotalSize(int fd, size_t* size) { + pmem_region region; + int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®ion); + if (err == 0) { + *size = region.len; + } + return err; + } + + virtual int connectPmem(int fd, int master_fd) { + return ioctl(fd, PMEM_CONNECT, master_fd); + } + + virtual int mapPmem(int fd, int offset, size_t size) { + struct pmem_region sub = { offset, size }; + return ioctl(fd, PMEM_MAP, &sub); + } + + virtual int unmapPmem(int fd, int offset, size_t size) { + struct pmem_region sub = { offset, size }; + return ioctl(fd, PMEM_UNMAP, &sub); + } + + virtual int getErrno() { + return errno; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + return ::mmap(start, length, prot, flags, fd, offset); + } + + virtual int munmap(void* start, size_t length) { + return ::munmap(start, length); + } + + virtual int open(const char* pathname, int flags, int mode) { + return ::open(pathname, flags, mode); + } + + virtual int close(int fd) { + return ::close(fd); + } +}; + +class GpuContextDepsDeviceImpl : public gpu_context_t::Deps { + + public: + + virtual int ashmem_create_region(const char *name, size_t size) { + return ::ashmem_create_region(name, size); + } + + virtual int mapFrameBufferLocked(struct private_module_t* module) { + return ::mapFrameBufferLocked(module); + } + + virtual int terminateBuffer(gralloc_module_t const* module, + private_handle_t* hnd) { + return ::terminateBuffer(module, hnd); + } + + virtual int close(int fd) { + return ::close(fd); + } +}; + +static PmemAllocatorDepsDeviceImpl pmemAllocatorDeviceDepsImpl; +static GpuContextDepsDeviceImpl gpuContextDeviceDepsImpl; + +/*****************************************************************************/ + +static SimpleBestFitAllocator pmemAllocMgr; +static PmemUserspaceAllocator pmemAllocator(pmemAllocatorDeviceDepsImpl, pmemAllocMgr, + "/dev/pmem"); + +static PmemKernelAllocator pmemAdspAllocator(pmemAllocatorDeviceDepsImpl, + "/dev/pmem_adsp"); + +/*****************************************************************************/ + static struct hw_module_methods_t gralloc_module_methods = { - open: gralloc_device_open + open: gralloc_device_open }; struct private_module_t HAL_MODULE_INFO_SYM = { @@ -110,388 +173,21 @@ struct private_module_t HAL_MODULE_INFO_SYM = { bufferMask: 0, lock: PTHREAD_MUTEX_INITIALIZER, currentBuffer: 0, - pmem_master: -1, - pmem_master_base: 0, }; /*****************************************************************************/ -static int gralloc_alloc_framebuffer_locked(alloc_device_t* dev, - size_t size, int usage, buffer_handle_t* pHandle) -{ - private_module_t* m = reinterpret_cast( - dev->common.module); - - // allocate the framebuffer - if (m->framebuffer == NULL) { - // initialize the framebuffer, the framebuffer is mapped once - // and forever. - int err = mapFrameBufferLocked(m); - if (err < 0) { - return err; - } - } - - const uint32_t bufferMask = m->bufferMask; - const uint32_t numBuffers = m->numBuffers; - const size_t bufferSize = m->finfo.line_length * m->info.yres; - if (numBuffers == 1) { - // If we have only one buffer, we never use page-flipping. Instead, - // we return a regular buffer which will be memcpy'ed to the main - // screen when post is called. - int newUsage = (usage & ~GRALLOC_USAGE_HW_FB) | GRALLOC_USAGE_HW_2D; - return gralloc_alloc_buffer(dev, bufferSize, newUsage, pHandle); - } - - if (bufferMask >= ((1LU<framebuffer->base); - private_handle_t* hnd = new private_handle_t(dup(m->framebuffer->fd), size, - private_handle_t::PRIV_FLAGS_USES_PMEM | - private_handle_t::PRIV_FLAGS_FRAMEBUFFER); - - // find a free slot - for (uint32_t i=0 ; ibufferMask |= (1LU<base = vaddr; - hnd->offset = vaddr - intptr_t(m->framebuffer->base); - *pHandle = hnd; - - return 0; -} - -static int gralloc_alloc_framebuffer(alloc_device_t* dev, - size_t size, int usage, buffer_handle_t* pHandle) -{ - private_module_t* m = reinterpret_cast( - dev->common.module); - pthread_mutex_lock(&m->lock); - int err = gralloc_alloc_framebuffer_locked(dev, size, usage, pHandle); - pthread_mutex_unlock(&m->lock); - return err; -} - - -static int init_pmem_area_locked(private_module_t* m) -{ - int err = 0; - int master_fd = open("/dev/pmem", O_RDWR, 0); - if (master_fd >= 0) { - size_t size; - pmem_region region; - if (ioctl(master_fd, PMEM_GET_TOTAL_SIZE, ®ion) < 0) { - LOGE("PMEM_GET_TOTAL_SIZE failed, limp mode"); - size = 8<<20; // 8 MiB - } else { - size = region.len; - } - sAllocator.setSize(size); - - void* base = mmap(0, size, - PROT_READ|PROT_WRITE, MAP_SHARED, master_fd, 0); - if (base == MAP_FAILED) { - err = -errno; - base = 0; - close(master_fd); - master_fd = -1; - } - m->pmem_master = master_fd; - m->pmem_master_base = base; - } else { - err = -errno; - } - return err; -} - -static int init_pmem_area(private_module_t* m) -{ - pthread_mutex_lock(&m->lock); - int err = m->pmem_master; - if (err == -1) { - // first time, try to initialize pmem - err = init_pmem_area_locked(m); - if (err) { - m->pmem_master = err; - } - } else if (err < 0) { - // pmem couldn't be initialized, never use it - } else { - // pmem OK - err = 0; - } - pthread_mutex_unlock(&m->lock); - return err; -} - -static int gralloc_alloc_buffer(alloc_device_t* dev, - size_t size, int usage, buffer_handle_t* pHandle) -{ - int err = 0; - int flags = 0; - - int fd = -1; - void* base = 0; - int offset = 0; - int lockState = 0; - - size = roundUpToPageSize(size); - - if (usage & GRALLOC_USAGE_HW_TEXTURE) { - // enable pmem in that case, so our software GL can fallback to - // the copybit module. - flags |= private_handle_t::PRIV_FLAGS_USES_PMEM; - } - - if (usage & GRALLOC_USAGE_HW_2D) { - flags |= private_handle_t::PRIV_FLAGS_USES_PMEM; - } - - if ((flags & private_handle_t::PRIV_FLAGS_USES_PMEM) == 0) { -try_ashmem: - fd = ashmem_create_region("gralloc-buffer", size); - if (fd < 0) { - LOGE("couldn't create ashmem (%s)", strerror(errno)); - err = -errno; - } - } else { - private_module_t* m = reinterpret_cast( - dev->common.module); - - err = init_pmem_area(m); - if (err == 0) { - // PMEM buffers are always mmapped - base = m->pmem_master_base; - lockState |= private_handle_t::LOCK_STATE_MAPPED; - - offset = sAllocator.allocate(size); - if (offset < 0) { - // no more pmem memory - err = -ENOMEM; - } else { - struct pmem_region sub = { offset, size }; - 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; - } - - // now create the "sub-heap" - fd = open("/dev/pmem", openFlags, 0); - err = fd < 0 ? fd : 0; - - // and connect to it - if (err == 0) - err = ioctl(fd, PMEM_CONNECT, m->pmem_master); - - // and make it available to the client process - if (err == 0) - err = ioctl(fd, PMEM_MAP, &sub); - - if (err < 0) { - err = -errno; - close(fd); - sAllocator.deallocate(offset); - fd = -1; - } else { - memset((char*)base + offset, 0, size); - // clean and invalidate the new allocation - cacheflush(intptr_t(base) + offset, size, 0); - } - //LOGD_IF(!err, "allocating pmem size=%d, offset=%d", size, offset); - } - } else { - if ((usage & GRALLOC_USAGE_HW_2D) == 0) { - // the caller didn't request PMEM, so we can try something else - flags &= ~private_handle_t::PRIV_FLAGS_USES_PMEM; - err = 0; - goto try_ashmem; - } else { - LOGE("couldn't open pmem (%s)", strerror(errno)); - } - } - } - - if (err == 0) { - private_handle_t* hnd = new private_handle_t(fd, size, flags); - hnd->offset = offset; - hnd->base = int(base)+offset; - hnd->lockState = lockState; - *pHandle = hnd; - } - - LOGE_IF(err, "gralloc failed err=%s", strerror(-err)); - - return err; -} - -/*****************************************************************************/ - -static inline size_t ALIGN(size_t x, size_t align) { - return (x + align-1) & ~(align-1); -} - -static int gralloc_alloc(alloc_device_t* dev, - int w, int h, int format, int usage, - buffer_handle_t* pHandle, int* pStride) -{ - if (!pHandle || !pStride) - return -EINVAL; - - size_t size, alignedw, alignedh; - - alignedw = ALIGN(w, 32); - alignedh = ALIGN(h, 32); - switch (format) { - case HAL_PIXEL_FORMAT_RGBA_8888: - case HAL_PIXEL_FORMAT_RGBX_8888: - case HAL_PIXEL_FORMAT_BGRA_8888: - size = alignedw * alignedh * 4; - break; - case HAL_PIXEL_FORMAT_RGB_888: - size = alignedw * alignedh * 3; - break; - case HAL_PIXEL_FORMAT_RGB_565: - case HAL_PIXEL_FORMAT_RGBA_5551: - case HAL_PIXEL_FORMAT_RGBA_4444: - size = alignedw * alignedh * 2; - break; - - // adreno formats - case HAL_PIXEL_FORMAT_YCrCb_420_SP: // NV21 - size = ALIGN(alignedw*alignedh, 4096); - size += ALIGN(2 * ALIGN(w/2, 32) * ALIGN(h/2, 32), 4096); - break; - case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: // NV12 - // The chroma plane is subsampled, - // but the pitch in bytes is unchanged - // The GPU needs 4K alignment, but the video decoder needs 8K - alignedw = ALIGN(w, 128); - size = ALIGN( ALIGN(w, 128) * alignedh, 8192); - size += ALIGN( ALIGN(w, 128) * ALIGN(h/2, 32), 4096); - break; - - case HAL_PIXEL_FORMAT_YV12: - alignedw = ALIGN(w, 16); - alignedh = ALIGN(h, 16); - size = alignedw * alignedh; - size += size / 2; - break; - - case HAL_PIXEL_FORMAT_YV16: - alignedh = ALIGN(h, 16); - size = alignedw * alignedh * 2; - break; - - default: - return -EINVAL; - } - - if ((ssize_t)size <= 0) - return -EINVAL; - - int err; - if (usage & GRALLOC_USAGE_HW_FB) { - err = gralloc_alloc_framebuffer(dev, size, usage, pHandle); - } else { - err = gralloc_alloc_buffer(dev, size, usage, pHandle); - } - - if (err < 0) { - return err; - } - - *pStride = alignedw; - return 0; -} - -static int gralloc_free(alloc_device_t* dev, - buffer_handle_t handle) -{ - if (private_handle_t::validate(handle) < 0) - return -EINVAL; - - private_handle_t const* hnd = reinterpret_cast(handle); - if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) { - // free this buffer - private_module_t* m = reinterpret_cast( - dev->common.module); - const size_t bufferSize = m->finfo.line_length * m->info.yres; - int index = (hnd->base - m->framebuffer->base) / bufferSize; - m->bufferMask &= ~(1<flags & private_handle_t::PRIV_FLAGS_USES_PMEM) { - if (hnd->fd >= 0) { - struct pmem_region sub = { hnd->offset, hnd->size }; - int err = ioctl(hnd->fd, PMEM_UNMAP, &sub); - LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " - "fd=%d, sub.offset=%lu, sub.size=%lu", - strerror(errno), hnd->fd, hnd->offset, hnd->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. - sAllocator.deallocate(hnd->offset); - } - } - } - - gralloc_module_t* module = reinterpret_cast( - dev->common.module); - terminateBuffer(module, const_cast(hnd)); - } - - close(hnd->fd); - delete hnd; - return 0; -} - -/*****************************************************************************/ - -static int gralloc_close(struct hw_device_t *dev) -{ - gralloc_context_t* ctx = reinterpret_cast(dev); - if (ctx) { - /* TODO: keep a list of all buffer_handle_t created, and free them - * all here. - */ - free(ctx); - } - return 0; -} - int gralloc_device_open(const hw_module_t* module, const char* name, hw_device_t** device) { int status = -EINVAL; if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) { - gralloc_context_t *dev; - dev = (gralloc_context_t*)malloc(sizeof(*dev)); - - /* initialize our state here */ - memset(dev, 0, sizeof(*dev)); - - /* initialize the procs */ - dev->device.common.tag = HARDWARE_DEVICE_TAG; - dev->device.common.version = 0; - dev->device.common.module = const_cast(module); - dev->device.common.close = gralloc_close; - - dev->device.alloc = gralloc_alloc; - dev->device.free = gralloc_free; - - *device = &dev->device.common; + const private_module_t* m = reinterpret_cast( + module); + gpu_context_t *dev; + dev = new gpu_context_t(gpuContextDeviceDepsImpl, pmemAllocator, + pmemAdspAllocator, m); + *device = &dev->common; status = 0; } else { status = fb_device_open(module, name, device); diff --git a/libgralloc-qsd8k/gralloc_priv.h b/libgralloc-qsd8k/gralloc_priv.h index 28ea79b..36e6796 100644 --- a/libgralloc-qsd8k/gralloc_priv.h +++ b/libgralloc-qsd8k/gralloc_priv.h @@ -29,10 +29,16 @@ #include +enum { + /* gralloc usage bit indicating a pmem_adsp allocation should be used */ + GRALLOC_USAGE_PRIVATE_PMEM_ADSP = GRALLOC_USAGE_PRIVATE_0, +}; + /*****************************************************************************/ struct private_module_t; struct private_handle_t; +struct PmemAllocator; struct private_module_t { gralloc_module_t base; @@ -44,8 +50,6 @@ struct private_module_t { uint32_t bufferMask; pthread_mutex_t lock; buffer_handle_t currentBuffer; - int pmem_master; - void* pmem_master_base; struct fb_var_screeninfo info; struct fb_fix_screeninfo finfo; @@ -69,9 +73,10 @@ struct private_handle_t { #endif enum { - PRIV_FLAGS_FRAMEBUFFER = 0x00000001, - PRIV_FLAGS_USES_PMEM = 0x00000002, - PRIV_FLAGS_NEEDS_FLUSH = 0x00000004, + PRIV_FLAGS_FRAMEBUFFER = 0x00000001, + PRIV_FLAGS_USES_PMEM = 0x00000002, + PRIV_FLAGS_USES_PMEM_ADSP = 0x00000004, + PRIV_FLAGS_NEEDS_FLUSH = 0x00000008, }; enum { diff --git a/libgralloc-qsd8k/mapper.cpp b/libgralloc-qsd8k/mapper.cpp index 9e9fad0..e8be6f6 100644 --- a/libgralloc-qsd8k/mapper.cpp +++ b/libgralloc-qsd8k/mapper.cpp @@ -171,7 +171,8 @@ int terminateBuffer(gralloc_module_t const* module, if (hnd->lockState & private_handle_t::LOCK_STATE_MAPPED) { // this buffer was mapped, unmap it now - if (hnd->flags & private_handle_t::PRIV_FLAGS_USES_PMEM) { + if ((hnd->flags & private_handle_t::PRIV_FLAGS_USES_PMEM) || + (hnd->flags & private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP)) { if (hnd->pid != getpid()) { // ... unless it's a "master" pmem buffer, that is a buffer // mapped in the process it's been allocated. diff --git a/libgralloc-qsd8k/pmemalloc.cpp b/libgralloc-qsd8k/pmemalloc.cpp new file mode 100644 index 0000000..e42e898 --- /dev/null +++ b/libgralloc-qsd8k/pmemalloc.cpp @@ -0,0 +1,324 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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; +} diff --git a/libgralloc-qsd8k/pmemalloc.h b/libgralloc-qsd8k/pmemalloc.h new file mode 100644 index 0000000..b0f45c7 --- /dev/null +++ b/libgralloc-qsd8k/pmemalloc.h @@ -0,0 +1,161 @@ +/* + * 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. + */ + +#ifndef GRALLOC_QSD8K_PMEMALLOC_H +#define GRALLOC_QSD8K_PMEMALLOC_H + +#include +#include +#include +#include + + +/** + * An interface to the PMEM allocators. + */ +class PmemAllocator { + + public: + + virtual ~PmemAllocator(); + + // Only valid after init_pmem_area() has completed successfully. + virtual void* get_base_address() = 0; + + virtual int alloc_pmem_buffer(size_t size, int usage, void** pBase, + int* pOffset, int* pFd) = 0; + virtual int free_pmem_buffer(size_t size, void* base, int offset, int fd) = 0; +}; + + +/** + * A PMEM allocator that allocates the entire pmem memory from the kernel and + * then uses a user-space allocator to suballocate from that. This requires + * that the PMEM device driver have kernel allocation disabled. + */ +class PmemUserspaceAllocator: public PmemAllocator { + + public: + + class Deps { + public: + + class Allocator { + public: + virtual ~Allocator(); + virtual ssize_t setSize(size_t size) = 0; + virtual size_t size() const = 0; + virtual ssize_t allocate(size_t size, uint32_t flags = 0) = 0; + virtual ssize_t deallocate(size_t offset) = 0; + }; + + virtual ~Deps(); + + // pmem + virtual size_t getPmemTotalSize(int fd, size_t* size) = 0; + virtual int connectPmem(int fd, int master_fd) = 0; + virtual int mapPmem(int fd, int offset, size_t size) = 0; + virtual int unmapPmem(int fd, int offset, size_t size) = 0; + + // C99 + virtual int getErrno() = 0; + + // POSIX + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) = 0; + virtual int open(const char* pathname, int flags, int mode) = 0; + virtual int close(int fd) = 0; + }; + + PmemUserspaceAllocator(Deps& deps, Deps::Allocator& allocator, const char* pmemdev); + virtual ~PmemUserspaceAllocator(); + + // Only valid after init_pmem_area() has completed successfully. + virtual void* get_base_address(); + + virtual int init_pmem_area_locked(); + virtual int init_pmem_area(); + virtual int alloc_pmem_buffer(size_t size, int usage, void** pBase, + int* pOffset, int* pFd); + virtual int free_pmem_buffer(size_t size, void* base, int offset, int fd); + +#ifndef ANDROID_OS + // DO NOT USE: For testing purposes only. + void set_master_values(int fd, void* base) { + master_fd = fd; + master_base = base; + } +#endif // ANDROID_OS + + private: + + enum { + MASTER_FD_INIT = -1, + }; + + Deps& deps; + Deps::Allocator& allocator; + + pthread_mutex_t lock; + const char* pmemdev; + int master_fd; + void* master_base; +}; + + +/** + * A PMEM allocator that allocates each individual allocation from the kernel + * (using the kernel's allocator). This requires the kernel driver for the + * particular PMEM device being allocated from to support kernel allocation. + */ +class PmemKernelAllocator: public PmemAllocator { + + public: + + class Deps { + public: + + virtual ~Deps(); + + // C99 + virtual int getErrno() = 0; + + // POSIX + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) = 0; + virtual int munmap(void* start, size_t length) = 0; + virtual int open(const char* pathname, int flags, int mode) = 0; + virtual int close(int fd) = 0; + }; + + PmemKernelAllocator(Deps& deps, const char* pmemdev); + virtual ~PmemKernelAllocator(); + + // Only valid after init_pmem_area() has completed successfully. + virtual void* get_base_address(); + + virtual int alloc_pmem_buffer(size_t size, int usage, void** pBase, + int* pOffset, int* pFd); + virtual int free_pmem_buffer(size_t size, void* base, int offset, int fd); + + private: + + Deps& deps; + + const char* pmemdev; +}; + +#endif // GRALLOC_QSD8K_PMEMALLOC_H diff --git a/libgralloc-qsd8k/tests/Android.mk b/libgralloc-qsd8k/tests/Android.mk new file mode 100644 index 0000000..b9a7459 --- /dev/null +++ b/libgralloc-qsd8k/tests/Android.mk @@ -0,0 +1,55 @@ +# Copyright (C) 2008 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. + +LOCAL_PATH := $(call my-dir) + +# you can use EXTRA_CFLAGS to indicate additional CFLAGS to use +# in the build. The variables will be cleaned on exit +# +# + +libgralloc_test_includes:= \ + bionic/libstdc++/include \ + external/astl/include \ + external/gtest/include \ + $(LOCAL_PATH)/.. + +libgralloc_test_static_libs := \ + libgralloc_qsd8k_host \ + libgtest_main_host \ + libgtest_host \ + libastl_host \ + liblog + +define host-test + $(foreach file,$(1), \ + $(eval include $(CLEAR_VARS)) \ + $(eval LOCAL_CPP_EXTENSION := .cpp) \ + $(eval LOCAL_SRC_FILES := $(file)) \ + $(eval LOCAL_C_INCLUDES := $(libgralloc_test_includes)) \ + $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ + $(eval LOCAL_CFLAGS += $(EXTRA_CFLAGS)) \ + $(eval LOCAL_LDLIBS += $(EXTRA_LDLIBS)) \ + $(eval LOCAL_STATIC_LIBRARIES := $(libgralloc_test_static_libs)) \ + $(eval LOCAL_MODULE_TAGS := eng tests) \ + $(eval include $(BUILD_HOST_EXECUTABLE)) \ + ) \ + $(eval EXTRA_CFLAGS :=) \ + $(eval EXTRA_LDLIBS :=) +endef + +TEST_SRC_FILES := \ + pmemalloc_test.cpp + +$(call host-test, $(TEST_SRC_FILES)) diff --git a/libgralloc-qsd8k/tests/pmemalloc_test.cpp b/libgralloc-qsd8k/tests/pmemalloc_test.cpp new file mode 100644 index 0000000..94e86bf --- /dev/null +++ b/libgralloc-qsd8k/tests/pmemalloc_test.cpp @@ -0,0 +1,601 @@ +/* + * 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. + */ + +#include + +#include +#include +#include +#include + +#include "pmemalloc.h" + +class DepsStub : public PmemUserspaceAllocator::Deps, public PmemKernelAllocator::Deps { + + public: + + virtual size_t getPmemTotalSize(int fd, size_t* size) { + return 0; + } + + virtual int connectPmem(int fd, int master_fd) { + return 0; + } + + virtual int mapPmem(int fd, int offset, size_t size) { + return 0; + } + + virtual int unmapPmem(int fd, int offset, size_t size) { + return 0; + } + + virtual int getErrno() { + return 0; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + return 0; + } + + virtual int munmap(void* start, size_t length) { + return 0; + } + + virtual int open(const char* pathname, int flags, int mode) { + return 0; + } + + virtual int close(int fd) { + return 0; + } +}; + +/******************************************************************************/ + +class AllocatorStub : public PmemUserspaceAllocator::Deps::Allocator { + virtual ssize_t setSize(size_t size) { + return 0; + } + + virtual size_t size() const { + return 0; + } + + virtual ssize_t allocate(size_t size, uint32_t flags = 0) { + return 0; + } + + virtual ssize_t deallocate(size_t offset) { + return 0; + } +}; + +/******************************************************************************/ + +static const char* fakePmemDev = "/foo/bar"; + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithSuccessfulCompletion : public DepsStub { + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags); + EXPECT_EQ(0, mode); + return 1234; + } + + virtual size_t getPmemTotalSize(int fd, size_t* size) { + EXPECT_EQ(1234, fd); + *size = 16 << 20; + return 0; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + EXPECT_EQ(1234, fd); + return (void*)0x87654321; + } + +}; + +struct Allocator_InitPmemAreaLockedWithSuccessfulCompletion : public AllocatorStub { + + virtual ssize_t setSize(size_t size) { + EXPECT_EQ(size_t(16 << 20), size); + return 0; + } +}; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaLockedWithSuccessfulCompletion) { + Deps_InitPmemAreaLockedWithSuccessfulCompletion depsMock; + Allocator_InitPmemAreaLockedWithSuccessfulCompletion allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + int result = pma.init_pmem_area_locked(); + ASSERT_EQ(0, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEnomemOnMmap : public DepsStub { + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags); + EXPECT_EQ(0, mode); + return 1234; + } + + virtual size_t getPmemTotalSize(int fd, size_t* size) { + EXPECT_EQ(1234, fd); + *size = 16 << 20; + return 0; + } + + virtual int getErrno() { + return ENOMEM; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + return (void*)MAP_FAILED; + } + +}; + +struct Allocator_InitPmemAreaLockedWithEnomemOnMmap : public AllocatorStub { + + virtual ssize_t setSize(size_t size) { + EXPECT_EQ(size_t(16 << 20), size); + return 0; + } +}; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaLockedWthEnomemOnMmap) { + Deps_InitPmemAreaLockedWithEnomemOnMmap depsMock; + Allocator_InitPmemAreaLockedWithEnomemOnMmap allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + int result = pma.init_pmem_area_locked(); + ASSERT_EQ(-ENOMEM, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEaccesOnGetPmemTotalSize : public DepsStub { + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags); + EXPECT_EQ(0, mode); + return 1234; + } + + virtual size_t getPmemTotalSize(int fd, size_t* size) { + EXPECT_EQ(1234, fd); + return -EACCES; + } +}; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaLockedWthEaccesOnGetPmemTotalSize) { + Deps_InitPmemAreaLockedWithEaccesOnGetPmemTotalSize depsMock; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsMock, allocStub, fakePmemDev); + + int result = pma.init_pmem_area_locked(); + ASSERT_EQ(-EACCES, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEaccesOnOpen : public DepsStub { + + virtual int getErrno() { + return EACCES; + } + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags); + EXPECT_EQ(0, mode); + return -1; + } +}; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaLockedWithEaccesOnOpenMaster) { + Deps_InitPmemAreaLockedWithEaccesOnOpen depsMock; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsMock, allocStub, fakePmemDev); + + int result = pma.init_pmem_area_locked(); + ASSERT_EQ(-EACCES, result); +} + +/******************************************************************************/ + +typedef Deps_InitPmemAreaLockedWithSuccessfulCompletion Deps_InitPmemAreaWithSuccessfulInitialCompletion; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaWithSuccessfulInitialCompletion) { + Deps_InitPmemAreaWithSuccessfulInitialCompletion depsMock; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsMock, allocStub, fakePmemDev); + + int result = pma.init_pmem_area(); + ASSERT_EQ(0, result); +} + +/******************************************************************************/ + +typedef Deps_InitPmemAreaLockedWithEaccesOnOpen Deps_InitPmemAreaWithEaccesOnInitLocked; + +TEST(test_pmem_userspace_allocator, testInitPmemAreaWithEaccesOnInitLocked) { + Deps_InitPmemAreaWithEaccesOnInitLocked depsMock; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsMock, allocStub, fakePmemDev); + + int result = pma.init_pmem_area(); + ASSERT_EQ(-EACCES, result); +} + +/******************************************************************************/ + +TEST(test_pmem_userspace_allocator, testInitPmemAreaAfterSuccessfulInitialCompletion) { + DepsStub depsStub; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsStub, allocStub, fakePmemDev); + + pma.set_master_values(1234, 0); // Indicate that the pma has been successfully init'd + + int result = pma.init_pmem_area(); + ASSERT_EQ(0, result); + //XXX JMG: Add this back in maybe? ASSERT_EQ(1234, pmi.master); // Make sure the master fd wasn't changed +} + +/******************************************************************************/ + +TEST(test_pmem_userspace_allocator, testInitPmemAreaAfterFailedInit) { + DepsStub depsStub; + AllocatorStub allocStub; + PmemUserspaceAllocator pma(depsStub, allocStub, fakePmemDev); + + pma.set_master_values(-EACCES, 0); // Indicate that the pma has failed init + + int result = pma.init_pmem_area(); + ASSERT_EQ(-EACCES, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags : public DepsStub { + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags & O_RDWR); + EXPECT_EQ(0, mode); + return 5678; + } + + virtual int connectPmem(int fd, int master_fd) { + EXPECT_EQ(5678, fd); + EXPECT_EQ(1234, master_fd); + return 0; + } + + virtual int mapPmem(int fd, int offset, size_t size) { + EXPECT_EQ(5678, fd); + EXPECT_EQ(0x300, offset); + EXPECT_EQ(size_t(0x100), size); + return 0; + } +}; + + +struct Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags : public AllocatorStub { + + virtual ssize_t allocate(size_t size, uint32_t flags = 0) { + EXPECT_EQ(size_t(0x100), size); + EXPECT_EQ(uint32_t(0x0), flags); + return 0x300; + } +}; + +TEST(test_pmem_userspace_allocator, testAllocPmemBufferWithSuccessfulCompletionWithNoFlags) { + Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags depsMock; + Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + uint8_t buf[0x300 + 0x100]; // Create a buffer to get memzero'd + pma.set_master_values(1234, buf); // Indicate that the pma has been successfully init'd + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = 0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(0, result); + ASSERT_EQ(0x300, offset); + ASSERT_EQ(5678, fd); + for (int i = 0x300; i < 0x400; ++i) { + ASSERT_EQ(uint8_t(0), buf[i]); + } +} + +/******************************************************************************/ + +typedef Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags Deps_InitPmemAreaLockedWithSuccessfulCompletionWithAllFlags; + +typedef Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags Allocator_AllocPmemBufferWithSuccessfulCompletionWithAllFlags; + +TEST(test_pmem_userspace_allocator, testAllocPmemBufferWithSuccessfulCompletionWithAllFlags) { + Deps_InitPmemAreaLockedWithSuccessfulCompletionWithAllFlags depsMock; + Allocator_AllocPmemBufferWithSuccessfulCompletionWithAllFlags allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + uint8_t buf[0x300 + 0x100]; // Create a buffer to get memzero'd + pma.set_master_values(1234, buf); // Indicate that the pma has been successfully init'd + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(0, result); + ASSERT_EQ(0x300, offset); + ASSERT_EQ(5678, fd); + for (int i = 0x300; i < 0x400; ++i) { + ASSERT_EQ(0, buf[i]); + } +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEnodevOnOpen : public Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags { + + virtual int getErrno() { + return ENODEV; + } + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags & O_RDWR); + EXPECT_EQ(0, mode); + return -1; + } +}; + +typedef Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags Allocator_AllocPmemBufferWithEnodevOnOpen; + +TEST(test_pmem_userspace_allocator, testAllocPmemBufferWithSuccessfulCompletionWithEnodevOnOpen) { + Deps_InitPmemAreaLockedWithEnodevOnOpen depsMock; + Allocator_AllocPmemBufferWithEnodevOnOpen allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + uint8_t buf[0x300 + 0x100]; // Create a buffer to get memzero'd + pma.set_master_values(1234, buf); // Indicate that the pma has been successfully init'd + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(-ENODEV, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEnomemOnConnectPmem : public Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags { + + virtual int getErrno() { + return ENOMEM; + } + + virtual int connectPmem(int fd, int master_fd) { + EXPECT_EQ(5678, fd); + EXPECT_EQ(1234, master_fd); + return -1; + } +}; + +typedef Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags Allocator_AllocPmemBufferWithEnomemOnConnectPmem; + +TEST(test_pmem_userspace_allocator, testAllocPmemBufferWithSuccessfulCompletionWithEnomemOnConnectPmem) { + Deps_InitPmemAreaLockedWithEnomemOnConnectPmem depsMock; + Allocator_AllocPmemBufferWithEnomemOnConnectPmem allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + uint8_t buf[0x300 + 0x100]; // Create a buffer to get memzero'd + pma.set_master_values(1234, buf); // Indicate that the pma has been successfully init'd + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(-ENOMEM, result); +} + +/******************************************************************************/ + +struct Deps_InitPmemAreaLockedWithEnomemOnMapPmem : public Deps_InitPmemAreaLockedWithSuccessfulCompletionWithNoFlags { + + virtual int getErrno() { + return ENOMEM; + } + + virtual int mapPmem(int fd, int offset, size_t size) { + EXPECT_EQ(5678, fd); + EXPECT_EQ(0x300, offset); + EXPECT_EQ(size_t(0x100), size); + return -1; + } +}; + +typedef Allocator_AllocPmemBufferWithSuccessfulCompletionWithNoFlags Allocator_AllocPmemBufferWithEnomemOnMapPmem; + +TEST(test_pmem_userspace_allocator, testAllocPmemBufferWithEnomemOnMapPmem) { + Deps_InitPmemAreaLockedWithEnomemOnMapPmem depsMock; + Allocator_AllocPmemBufferWithEnomemOnMapPmem allocMock; + PmemUserspaceAllocator pma(depsMock, allocMock, fakePmemDev); + + uint8_t buf[0x300 + 0x100]; // Create a buffer to get memzero'd + pma.set_master_values(1234, buf); // Indicate that the pma has been successfully init'd + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(-ENOMEM, result); +} + +/******************************************************************************/ + +struct Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithNoFlags : public DepsStub { + + void* mmapResult; + + Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithNoFlags(void* mmapResult) : + mmapResult(mmapResult) {} + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags & O_RDWR); + EXPECT_EQ(0, mode); + return 5678; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + EXPECT_EQ(5678, fd); + return mmapResult; + } +}; + +TEST(test_pmem_kernel_allocator, testAllocPmemBufferWithSuccessfulCompletionWithNoFlags) { + uint8_t buf[0x100]; // Create a buffer to get memzero'd + Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithNoFlags depsMock(buf); + PmemKernelAllocator pma(depsMock, fakePmemDev); + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = 0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(0, result); + ASSERT_EQ(buf, base); + ASSERT_EQ(0, offset); + ASSERT_EQ(5678, fd); + for (int i = 0; i < 0x100; ++i) { + ASSERT_EQ(0, buf[i]); + } +} + +/******************************************************************************/ + +typedef Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithNoFlags Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithAllFlags; + +TEST(test_pmem_kernel_allocator, testAllocPmemBufferWithSuccessfulCompletionWithAllFlags) { + uint8_t buf[0x100]; // Create a buffer to get memzero'd + Deps_KernelAllocPmemBufferWithSuccessfulCompletionWithAllFlags depsMock(buf); + PmemKernelAllocator pma(depsMock, fakePmemDev); + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(0, result); + ASSERT_EQ(buf, base); + ASSERT_EQ(0, offset); + ASSERT_EQ(5678, fd); + for (int i = 0; i < 0x100; ++i) { + ASSERT_EQ(0, buf[i]); + } +} + +/******************************************************************************/ + +struct Deps_KernelAllocPmemBufferWithEpermOnOpen : public DepsStub { + + virtual int getErrno() { + return EPERM; + } + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags & O_RDWR); + EXPECT_EQ(0, mode); + return -1; + } +}; + + +TEST(test_pmem_kernel_allocator, testAllocPmemBufferWithEpermOnOpen) { + Deps_KernelAllocPmemBufferWithEpermOnOpen depsMock; + PmemKernelAllocator pma(depsMock, fakePmemDev); + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(-EPERM, result); + ASSERT_EQ(0, base); + ASSERT_EQ(0, offset); + ASSERT_EQ(-1, fd); +} + +/******************************************************************************/ + +struct Deps_KernelAllocPmemBufferWithEnomemOnMmap : DepsStub { + + virtual int open(const char* pathname, int flags, int mode) { + EXPECT_EQ(fakePmemDev, pathname); + EXPECT_EQ(O_RDWR, flags & O_RDWR); + EXPECT_EQ(0, mode); + return 5678; + } + + virtual void* mmap(void* start, size_t length, int prot, int flags, int fd, + off_t offset) { + return (void*)MAP_FAILED; + } + + virtual int getErrno() { + return ENOMEM; + } +}; + + +TEST(test_pmem_kernel_allocator, testAllocPmemBufferWithEnomemOnMmap) { + Deps_KernelAllocPmemBufferWithEnomemOnMmap depsMock; + PmemKernelAllocator pma(depsMock, fakePmemDev); + + void* base = 0; + int offset = -9182, fd = -9182; + int size = 0x100; + int flags = ~0; + int result = pma.alloc_pmem_buffer(size, flags, &base, &offset, &fd); + ASSERT_EQ(-ENOMEM, result); + ASSERT_EQ(0, base); + ASSERT_EQ(0, offset); + ASSERT_EQ(-1, fd); +} + +/******************************************************************************/