blob: 1f780cc2d3ecaa39bd3263442c36046d6e0a6ffb [file] [log] [blame]
/*
* memmgr.c
*
* Memory Allocator Interface functions for TI OMAP processors.
*
* Copyright (C) 2009-2011 Texas Instruments, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <pthread.h>
#include <errno.h>
#define BUF_ALLOCED 1
#define BUF_MAPPED 2
#define BUF_ANY ~0
#include <tiler.h>
typedef struct tiler_block_info tiler_block_info;
#define __DEBUG__
#undef __DEBUG_ENTRY__
#define __DEBUG_ASSERT__
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "utils.h"
#include "list_utils.h"
#include "debug_utils.h"
#include "tilermem.h"
#include "tilermem_utils.h"
#include "memmgr.h"
/* list of allocations */
struct _AllocData {
struct tiler_buf_info buf;
int buf_type;
struct _AllocList {
struct _AllocList *next, *last;
struct _AllocData *me;
} link;
};
static struct _AllocList bufs = {0};
static int bufs_inited = 0;
typedef struct _AllocList _AllocList;
typedef struct _AllocData _AllocData;
static int refCnt = 0;
static int td = -1;
static pthread_mutex_t ref_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t che_mutex = PTHREAD_MUTEX_INITIALIZER;
/**
* Initializes the static structures
*
* @author a0194118 (9/8/2009)
*/
static void init()
{
if (!bufs_inited)
{
DLIST_INIT(bufs);
bufs_inited = 1;
}
}
/**
* Increases the reference count. Initialized tiler if this was
* the first reference
*
* @author a0194118 (9/2/2009)
*
* @return 0 on success, non-0 error value on failure.
*/
static int inc_ref()
{
/* initialize tiler on first call */
pthread_mutex_lock(&ref_mutex);
int res = MEMMGR_ERR_NONE;
if (!refCnt++) {
/* initialize lists */
init();
#ifndef STUB_TILER
td = open("/dev/tiler", O_RDWR | O_SYNC);
if (NOT_I(td,>=,0)) res = MEMMGR_ERR_GENERIC;
#else
td = 2;
res = MEMMGR_ERR_NONE;
#endif
}
if (res)
{
refCnt--;
}
pthread_mutex_unlock(&ref_mutex);
return res;
}
/**
* Decreases the reference count. Deinitialized tiler if this
* was the last reference
*
* @author a0194118 (9/2/2009)
*
* @return 0 on success, non-0 error value on failure.
*/
static int dec_ref()
{
pthread_mutex_lock(&ref_mutex);
int res = MEMMGR_ERR_NONE;;
if (refCnt <= 0) res = MEMMGR_ERR_GENERIC;
else if (!--refCnt) {
#ifndef STUB_TILER
close(td);
td = -1;
#endif
}
pthread_mutex_unlock(&ref_mutex);
return res;
}
/**
* Returns the default page stride for this block
*
* @author a0194118 (9/4/2009)
*
* @param width Width of 2D container in bytes
*
* @return Stride
*/
static bytes_t def_stride(bytes_t width)
{
return ROUND_UP_TO2POW(width, PAGE_SIZE);
}
/**
* Returns the bytes per pixel for the pixel format.
*
* @author a0194118 (9/4/2009)
*
* @param pixelFormat Pixelformat
*
* @return Bytes per pixel
*/
static bytes_t def_bpp(pixel_fmt_t pixelFormat)
{
return (pixelFormat == PIXEL_FMT_32BIT ? 4 :
pixelFormat == PIXEL_FMT_16BIT ? 2 : 1);
}
/**
* Returns the size of the supplied block
*
* @author a0194118 (9/4/2009)
*
* @param blk Pointer to the tiler_block_info struct
*
* @return size of the block in bytes
*/
static bytes_t def_size(tiler_block_info *blk)
{
return (blk->fmt == PIXEL_FMT_PAGE ?
def_stride(blk->dim.len) :
blk->dim.area.height * def_stride(blk->dim.area.width * def_bpp(blk->fmt)));
}
/**
* Returns the map size based on an offset and buffer size
*
* @author a0194118 (7/8/2010)
*
* @param size Size of buffer
* @param offs Buffer offset
*
* @return (page aligned) size for mapping
*/
static bytes_t map_size(bytes_t size, bytes_t offs)
{
return def_stride(size + (offs & (PAGE_SIZE - 1)));
}
/**
* Records a buffer-pointer -- tiler-ID mapping for a specific
* buffer type.
*
* @author a0194118 (9/7/2009)
*
* @param bufPtr Buffer pointer
* @param tiler_id Tiler ID
* @param buf_type Buffer type: BUF_ALLOCED or BUF_MAPPED
*
* @return 0 on success, -ENOMEM on memory allocation failure
*/
static int buf_cache_add(struct tiler_buf_info *buf, int buf_type)
{
pthread_mutex_lock(&che_mutex);
_AllocData *ad = NEW(_AllocData);
if (ad)
{
memcpy(&ad->buf, buf, sizeof(ad->buf));
ad->buf_type = buf_type;
DLIST_MADD_BEFORE(bufs, ad, link);
}
pthread_mutex_unlock(&che_mutex);
return ad == NULL ? -ENOMEM : 0;
}
/**
* Retrieves the tiler ID for given pointer and buffer type from
* the records. If the pointer lies within a tracked buffer,
* the tiler ID is returned. Otherwise 0 is returned.
*
* @author a0194118 (9/7/2009)
*
* @param bufPtr Buffer pointer
* @param buf_type Buffer type: BUF_ALLOCED or BUF_MAPPED
*
* @return Tiler ID on success, 0 on failure.
*/
static void buf_cache_query(void *ptr, struct tiler_buf_info *buf,
int buf_type_mask)
{
IN;
if(0) DP("in(p=%p,t=%d,bp*=%p)", ptr, buf_type_mask, buf->blocks[0].ptr);
_AllocData *ad;
pthread_mutex_lock(&che_mutex);
DLIST_MLOOP(bufs, ad, link) {
if(0) {
DP("got(%p-%p,%d)", ad->buf.blocks->ptr, ad->buf.blocks->ptr + ad->buf.length, ad->buf_type);
CHK_P(ad->buf.blocks->ptr,<=,ptr);
CHK_P(ptr,<,ad->buf.blocks->ptr + ad->buf.length);
CHK_I(ad->buf_type & buf_type_mask,!=,0);
CHK_P(buf->blocks->ptr,!=,0);
}
if ((ad->buf_type & buf_type_mask) &&
ad->buf.blocks->ptr <= ptr && ptr < ad->buf.blocks->ptr + ad->buf.length) {
memcpy(buf, &ad->buf, sizeof(*buf));
pthread_mutex_unlock(&che_mutex);
return;
}
}
pthread_mutex_unlock(&che_mutex);
ZERO(*buf);
OUT;
}
/**
* Retrieves the tiler ID for given buffer pointer and buffer
* type from the records. If the tiler ID is found, it is
* removed from the records as well.
*
* @author a0194118 (9/7/2009)
*
* @param bufPtr Buffer pointer
* @param buf_type Buffer type: BUF_ALLOCED or BUF_MAPPED
*
* @return Tiler ID on success, 0 on failure.
*/
static void buf_cache_del(void *bufPtr, struct tiler_buf_info *buf,
int buf_type)
{
_AllocData *ad;
pthread_mutex_lock(&che_mutex);
DLIST_MLOOP(bufs, ad, link) {
if (ad->buf.blocks->ptr == bufPtr && ad->buf_type == buf_type) {
memcpy(buf, &ad->buf, sizeof(*buf));
DLIST_REMOVE(ad->link);
FREE(ad);
pthread_mutex_unlock(&che_mutex);
return;
}
}
pthread_mutex_unlock(&che_mutex);
OUT;
return;
}
/**
* Checks the consistency of the internal record cache. The
* number of elements in the cache should equal to the number of
* references.
*
* @author a0194118 (9/7/2009)
*
* @return 0 on success, non-0 error value on failure.
*/
static int cache_check()
{
int num_bufs = 0;
pthread_mutex_lock(&che_mutex);
init();
_AllocData *ad;
DLIST_MLOOP(bufs, ad, link) { num_bufs++; }
pthread_mutex_unlock(&che_mutex);
return (num_bufs == refCnt) ? MEMMGR_ERR_NONE : MEMMGR_ERR_GENERIC;
}
static void dump_block(struct tiler_block_info *blk, char *prefix, char *suffix)
{
#if 0
switch (blk->fmt)
{
case TILFMT_PAGE:
P("%s [%d:(%d,%08x), p=%p(0x%x),l=0x%x,s=%d,%d+%d]%s", prefix, blk->group_id, blk->key, blk->id, blk->ptr, blk->ssptr,
blk->dim.len, blk->stride, blk->align, blk->offs, suffix);
break;
case TILFMT_8BIT:
case TILFMT_16BIT:
case TILFMT_32BIT:
P("%s [%d:(%d,%08x), p=%p(0x%x),%d*%d*%d,s=%d,%d+%d]%s", prefix, blk->group_id, blk->key, blk->id, blk->ptr, blk->ssptr,
blk->dim.area.width, blk->dim.area.height, def_bpp(blk->fmt) * 8,
blk->stride, blk->align, blk->offs, suffix);
break;
default:
P("%s*[%d:(%d,%08x), p=%p(0x%x),l=0x%x,s=%d,%d+%d,fmt=0x%x]%s", prefix, blk->group_id, blk->key, blk->id, blk->ptr,
blk->ssptr, blk->dim.len, blk->stride, blk->align, blk->offs, blk->fmt, suffix);
}
#endif
}
static void dump_buf(struct tiler_buf_info* buf, char* prefix)
{
#if 0
P("%sbuf={n=%d,id=0x%x,len=0x%x", prefix, buf->num_blocks, buf->offset, buf->length);
int ix = 0;
for (ix = 0; ix < buf->num_blocks; ix++)
{
dump_block(buf->blocks + ix, "", ix + 1 == buf->num_blocks ? "}" : "");
}
#endif
}
/**
* Returns the tiler format for an address
*
* @author a0194118 (9/7/2009)
*
* @param ssptr Address
*
* @return The tiler format
*/
static enum tiler_fmt tiler_get_fmt(SSPtr ssptr)
{
#ifndef STUB_TILER
return (ssptr == 0 ? TILFMT_INVALID :
ssptr < TILER_MEM_8BIT ? TILFMT_NONE :
ssptr < TILER_MEM_16BIT ? TILFMT_8BIT :
ssptr < TILER_MEM_32BIT ? TILFMT_16BIT :
ssptr < TILER_MEM_PAGED ? TILFMT_32BIT :
ssptr < TILER_MEM_END ? TILFMT_PAGE : TILFMT_NONE);
#else
/* if emulating, we need to get through all allocated memory segments */
pthread_mutex_lock(&che_mutex);
init();
_AllocData *ad;
void *ptr = (void *) ssptr;
if (!ptr) return TILFMT_INVALID;
/* P("?%p", (void *)ssptr); */
DLIST_MLOOP(bufs, ad, link) {
int ix;
struct tiler_buf_info *buf = (struct tiler_buf_info *) ad->buf.offset;
/* P("buf[%d]", buf->num_blocks); */
for (ix = 0; ix < buf->num_blocks; ix++)
{
/* P("block[%p-%p]", buf->blocks[ix].ptr, buf->blocks[ix].ptr + def_size(buf->blocks + ix)); */
if (ptr >= buf->blocks[ix].ptr &&
ptr < buf->blocks[ix].ptr + def_size(buf->blocks + ix)) {
enum tiler_fmt fmt = buf->blocks[ix].fmt;
pthread_mutex_unlock(&che_mutex);
return fmt;
}
}
}
pthread_mutex_unlock(&che_mutex);
return TILFMT_NONE;
#endif
}
/**
* Allocates a memory block using tiler
*
* @author a0194118 (9/7/2009)
*
* @param blk Pointer to the block info
*
* @return ssptr of block allocated, or 0 on error
*/
static int tiler_alloc(struct tiler_block_info *blk)
{
dump_block(blk, "=(ta)=>", "");
blk->ptr = NULL;
int ret = A_S(ioctl(td, TILIOC_GBLK, blk),==,0);
dump_block(blk, "alloced: ", "");
return R_I(ret);
}
/**
* Frees a memory block using tiler
*
* @author a0194118 (9/7/2009)
*
* @param blk Pointer to the block info
*
* @return 0 on success, non-0 error value on failure.
*/
static int tiler_free(struct tiler_block_info *blk)
{
return R_I(ioctl(td, TILIOC_FBLK, blk));
}
/**
* Maps a memory block into tiler using tiler
*
* @author a0194118 (9/7/2009)
*
* @param blk Pointer to the block info
*
* @return ssptr of block mapped, or 0 on error
*/
static int tiler_map(struct tiler_block_info *blk)
{
dump_block(blk, "=(tm)=>", "");
int ret = A_S(ioctl(td, TILIOC_MBLK, blk),==,0);
dump_block(blk, "mapped: ", "");
return R_I(ret);
}
/**
* Unmaps a memory block from tiler using tiler
*
* @author a0194118 (9/7/2009)
*
* @param blk Pointer to the block info
*
* @return 0 on success, non-0 error value on failure.
*/
static int tiler_unmap(struct tiler_block_info *blk)
{
return ioctl(td, TILIOC_UMBLK, blk);
}
/**
* Registers a buffer structure with tiler, and maps the buffer
* into memory using tiler. On success, it writes the tiler ID
* of the buffer into the area pointed by tiler ID.
*
* @author a0194118 (9/7/2009)
*
* @param blks Pointer to array of block info structures
* @param num_blocks Number of blocks
* @param tiler_id Pointer to tiler ID.
*
* @return pointer to the mapped buffer.
*/
static void *tiler_mmap(struct tiler_block_info *blks, int num_blocks,
int buf_type)
{
IN;
/* get size */
int ix;
bytes_t size;
/* register buffer with tiler */
struct tiler_buf_info buf;
buf.num_blocks = num_blocks;
/* work on copy in buf */
memcpy(buf.blocks, blks, sizeof(tiler_block_info) * num_blocks);
#ifndef STUB_TILER
dump_buf(&buf, "==(RBUF)=>");
int ret = ioctl(td, TILIOC_RBUF, &buf);
dump_buf(&buf, "<=(RBUF)==");
if (NOT_I(ret,==,0)) return NULL;
size = buf.length;
#else
/* save buffer in stub */
struct tiler_buf_info *buf_c = NEWN(struct tiler_buf_info,2);
buf.offset = (uint32_t) buf_c;
#endif
if (NOT_P(buf.offset,!=,0)) return NULL;
/* map blocks to process space */
#ifndef STUB_TILER
void *bufPtr = mmap(0, map_size(size, buf.offset),
PROT_READ | PROT_WRITE, MAP_SHARED,
td, buf.offset & ~(PAGE_SIZE - 1));
if (bufPtr == MAP_FAILED){
bufPtr = NULL;
} else {
bufPtr += buf.offset & (PAGE_SIZE - 1);
}
if(0) DP("ptr=%p", bufPtr);
#else
void *bufPtr = malloc(size + PAGE_SIZE - 1);
buf_c[1].blocks[0].ptr = bufPtr;
bufPtr = ROUND_UP_TO2POW(bufPtr, PAGE_SIZE);
/* P("<= [0x%x]", size); */
/* fill out pointers - this is needed for caching 1D/2D type */
for (size = ix = 0; ix < num_blocks; ix++)
{
buf.blocks[ix].ptr = bufPtr ? bufPtr + size : bufPtr;
size += def_size(blks + ix);
}
memcpy(buf_c, &buf, sizeof(struct tiler_buf_info));
#endif
if (bufPtr != NULL)
{
/* fill out pointers */
for (size = ix = 0; ix < num_blocks; ix++)
{
buf.blocks[ix].ptr = bufPtr + size;
/* P(" [0x%p]", buf.blocks[ix].ptr); */
size += def_size(blks + ix);
#ifdef STUB_TILER
buf.blocks[ix].ssptr = (uint32_t) buf.blocks[ix].ptr;
#else
CHK_I((buf.blocks[ix].ssptr & (PAGE_SIZE - 1)),==,((uint32_t) buf.blocks[ix].ptr & (PAGE_SIZE - 1)));
#endif
}
}
/* if failed to map: unregister buffer */
if (NOT_P(bufPtr,!=,NULL) ||
/* or failed to cache tiler buffer */
NOT_I(buf_cache_add(&buf, buf_type),==,0))
{
#ifndef STUB_TILER
A_I(ioctl(td, TILIOC_URBUF, &buf),==,0);
#else
FREE(buf_c);
buf.offset = 0;
#endif
} else {
/* update original from local copy */
memcpy(blks, buf.blocks, sizeof(tiler_block_info) * num_blocks);
}
return R_P(bufPtr);
}
/**
* Checks whether the tiler_block_info is filled in correctly.
* Verifies the pixel format, correct length, width and or
* height, the length/stride relationship for 1D buffers, and
* the correct stride for 2D buffers. Also verifies block size
* to be page sized if desired.
*
* @author a0194118 (9/4/2009)
*
* @param blk Pointer to the tiler_block_info struct
* @param is_page_sized Whether the block needs to be page
* sized (fit on whole pages).
* @return 0 on success, non-0 error value on failure.
*/
static int check_block(tiler_block_info *blk, bool is_page_sized)
{
/* check pixelformat */
if (NOT_I(blk->fmt,>=,PIXEL_FMT_MIN) ||
NOT_I(blk->fmt,<=,PIXEL_FMT_MAX)) return MEMMGR_ERR_GENERIC;
if (blk->fmt == PIXEL_FMT_PAGE)
{ /* check 1D buffers */
/* length must be multiple of stride if stride > 0 */
if (NOT_I(blk->dim.len,>,0) ||
(blk->stride && NOT_I(blk->dim.len % blk->stride,==,0)))
return MEMMGR_ERR_GENERIC;
}
else
{ /* check 2D buffers */
/* check width, height and stride (must be the default stride or 0) */
bytes_t stride = def_stride(blk->dim.area.width * def_bpp(blk->fmt));
if (NOT_I(blk->dim.area.width,>,0) ||
NOT_I(blk->dim.area.height,>,0) ||
(blk->stride && NOT_I(blk->stride,==,stride)))
return MEMMGR_ERR_GENERIC;
}
if (is_page_sized && NOT_I(def_size(blk) & (PAGE_SIZE - 1),==,0))
return MEMMGR_ERR_GENERIC;
return MEMMGR_ERR_NONE;
}
/**
* Checks whether the block information is correct for map and
* alloc operations. Checks the number of blocks, and validity
* of each block. Warns if reserved member is not 0. Also
* checks if the alignment/offset requirements are consistent
* across the buffer
*
* @author a0194118 (9/7/2009)
*
* @param blks Pointer to the block info array.
* @param num_blocks Number of blocks.
* @param num_pagesize_blocks Number of blocks that must be
* page sized (these must be in
* front)
*
* @return 0 on success, non-0 error value on failure.
*/
static int check_blocks(struct tiler_block_info *blks, int num_blocks,
int num_pagesize_blocks)
{
/* check arguments */
if (NOT_I(num_blocks,>,0) ||
NOT_I(num_blocks,<=,TILER_MAX_NUM_BLOCKS)) return MEMMGR_ERR_GENERIC;
/* check block allocation params */
int ix;
for (ix = 0; ix < num_blocks; ix++)
{
struct tiler_block_info *blk = blks + ix;
CHK_I(blk->ssptr,==,0);
CHK_I(blk->id,==,0);
int ret = check_block(blk, ix < num_pagesize_blocks);
/* check alignment */
if (!ret)
{
}
if (ret)
{
DP("for block[%d]", ix);
return ret;
}
}
/* set alignment parameters */
return MEMMGR_ERR_NONE;
}
/**
* Resets the ptr and reserved fields of the block info
* structures.
*
* @author a0194118 (9/7/2009)
*
* @param blks Pointer to the block info array.
* @param num_blocks Number of blocks.
*/
static void reset_blocks(struct tiler_block_info *blks, int num_blocks)
{
int ix;
for (ix = 0; ix < num_blocks; ix++)
{
blks[ix].ssptr = 0;
blks[ix].id = 0;
blks[ix].ptr = NULL;
}
}
bytes_t MemMgr_PageSize()
{
return PAGE_SIZE;
}
void *MemMgr_Alloc(MemAllocBlock blocks[], int num_blocks)
{
IN;
void *bufPtr = NULL;
/* need to access ssptrs */
struct tiler_block_info *blks = (tiler_block_info *) blocks;
/* check block allocation params, and state */
if (NOT_I(check_blocks(blks, num_blocks, num_blocks - 1),==,0) ||
NOT_I(inc_ref(),==,0)) goto DONE;
/* ----- begin recoverable portion ----- */
int ix;
/* allocate each buffer using tiler driver and initialize block info */
for (ix = 0; ix < num_blocks; ix++)
{
if (ix)
{
/* continue offset between pages */
}
CHK_I(blks[ix].ptr,==,NULL);
if (NOT_I(tiler_alloc(blks + ix),==,0)) goto FAIL_ALLOC;
}
bufPtr = tiler_mmap(blks, num_blocks, BUF_ALLOCED);
if (A_P(bufPtr,!=,0)) goto DONE;
/* ------ error handling ------ */
FAIL_ALLOC:
while (ix)
{
tiler_free(blks + --ix);
}
/* clear ssptr and ptr fields for all blocks */
reset_blocks(blks, num_blocks);
A_I(dec_ref(),==,0);
DONE:
CHK_I(cache_check(),==,0);
return R_P(bufPtr);
}
int MemMgr_Free(void *bufPtr)
{
IN;
int ret = MEMMGR_ERR_GENERIC;
struct tiler_buf_info buf;
ZERO(buf);
/* retrieve registered buffers from vsptr */
/* :NOTE: if this succeeds, Memory Allocator stops tracking this buffer */
buf_cache_del(bufPtr, &buf, BUF_ALLOCED);
if (A_L(buf.offset,!=,0))
{
#ifndef STUB_TILER
dump_buf(&buf, "==(URBUF)=>");
ret = A_I(ioctl(td, TILIOC_URBUF, &buf),==,0);
dump_buf(&buf, "<=(URBUF)==");
/* free each block */
int ix;
for (ix = 0; ix < buf.num_blocks; ix++)
{
ERR_ADD(ret, tiler_free(buf.blocks + ix));
}
/* unmap buffer */
bufPtr = (void *)((uint32_t)bufPtr & ~(PAGE_SIZE - 1));
ERR_ADD(ret, munmap(bufPtr, map_size(buf.length, buf.offset)));
#else
void *ptr = (void *) buf.offset;
FREE(ptr);
ret = MEMMGR_ERR_NONE;
#endif
ERR_ADD(ret, dec_ref());
}
CHK_I(cache_check(),==,0);
return R_I(ret);
}
void *MemMgr_Map(MemAllocBlock blocks[], int num_blocks)
{
IN;
void *bufPtr = NULL;
/* need to access ssptrs */
struct tiler_block_info *blks = (tiler_block_info *) blocks;
/* check block params, and state */
if (check_blocks(blks, num_blocks, num_blocks) ||
NOT_I(inc_ref(),==,0)) goto DONE;
/* we only map 1 page aligned 1D buffer for now */
if (NOT_I(num_blocks,==,1) ||
NOT_I(blocks[0].pixelFormat,==,PIXEL_FMT_PAGE) ||
NOT_I(blocks[0].dim.len & (PAGE_SIZE - 1),==,0) ||
#ifdef STUB_TILER
NOT_I(MemMgr_IsMapped(blocks[0].ptr),==,0) ||
#endif
NOT_I((uint32_t)blocks[0].ptr & (PAGE_SIZE - 1),==,0))
goto FAIL;
/* ----- begin recoverable portion ----- */
int ix;
/* allocate each buffer using tiler driver */
for (ix = 0; ix < num_blocks; ix++)
{
if (ix)
{
/* continue offset between pages */
}
if (NOT_I(blks[ix].ptr,!=,NULL) ||
NOT_I(tiler_map(blks + ix),==,0)) goto FAIL_MAP;
}
/* map bufer into tiler space and register with tiler manager */
bufPtr = tiler_mmap(blks, num_blocks, BUF_MAPPED);
if (A_P(bufPtr,!=,0)) goto DONE;
/* ------ error handling ------ */
FAIL_MAP:
while (ix)
{
tiler_unmap(blks + --ix);
}
FAIL:
/* clear ssptr and ptr fields for all blocks */
reset_blocks(blks, num_blocks);
A_I(dec_ref(),==,0);
DONE:
CHK_I(cache_check(),==,0);
return R_P(bufPtr);
}
int MemMgr_UnMap(void *bufPtr)
{
IN;
int ret = MEMMGR_ERR_GENERIC;
struct tiler_buf_info buf;
ZERO(buf);
/* retrieve registered buffers from vsptr */
/* :NOTE: if this succeeds, Memory Allocator stops tracking this buffer */
buf_cache_del(bufPtr, &buf, BUF_MAPPED);
if (A_L(buf.offset,!=,0))
{
#ifndef STUB_TILER
dump_buf(&buf, "==(URBUF)=>");
ret = A_I(ioctl(td, TILIOC_URBUF, &buf),==,0);
dump_buf(&buf, "<=(URBUF)==");
/* unmap each block */
int ix;
for (ix = 0; ix < buf.num_blocks; ix++)
{
ERR_ADD(ret, tiler_unmap(buf.blocks + ix));
}
/* unmap buffer */
bufPtr = (void *)((uint32_t)bufPtr & ~(PAGE_SIZE - 1));
ERR_ADD(ret, munmap(bufPtr, map_size(buf.length, buf.offset)));
#else
struct tiler_buf_info *ptr = (struct tiler_buf_info *) buf.offset;
FREE(ptr[1].blocks[0].ptr);
FREE(ptr);
ret = MEMMGR_ERR_NONE;
#endif
ERR_ADD(ret, dec_ref());
}
CHK_I(cache_check(),==,0);
return R_I(ret);
}
bool MemMgr_Is1DBlock(void *ptr)
{
IN;
SSPtr ssptr = TilerMem_VirtToPhys(ptr);
enum tiler_fmt fmt = tiler_get_fmt(ssptr);
return R_I(fmt == TILFMT_PAGE);
}
bool MemMgr_Is2DBlock(void *ptr)
{
IN;
SSPtr ssptr = TilerMem_VirtToPhys(ptr);
enum tiler_fmt fmt = tiler_get_fmt(ssptr);
return R_I(fmt == TILFMT_8BIT || fmt == TILFMT_16BIT ||
fmt == TILFMT_32BIT);
}
bool MemMgr_IsMapped(void *ptr)
{
IN;
SSPtr ssptr = TilerMem_VirtToPhys(ptr);
enum tiler_fmt fmt = tiler_get_fmt(ssptr);
return R_I(fmt == TILFMT_8BIT || fmt == TILFMT_16BIT ||
fmt == TILFMT_32BIT || fmt == TILFMT_PAGE);
}
bytes_t MemMgr_GetStride(void *ptr)
{
IN;
#ifndef STUB_TILER
struct tiler_buf_info buf;
ZERO(buf);
/* find block that this buffer belongs to */
buf_cache_query(ptr, &buf, BUF_ALLOCED | BUF_MAPPED);
void *bufPtr = buf.blocks[0].ptr;
A_I(inc_ref(),==,0);
/* for tiler mapped buffers, get saved stride information */
if (buf.offset)
{
/* walk through block to determine which stride we need */
int ix;
for (ix = 0; ix < buf.num_blocks; ix++)
{
bytes_t size = def_size(buf.blocks + ix);
if (bufPtr <= ptr && ptr < bufPtr + size) {
A_I(dec_ref(),==,0);
return R_UP(buf.blocks[ix].stride);
}
bufPtr += size;
}
A_I(dec_ref(),==,0);
DP("assert: should not ever get here");
return R_UP(0);
}
/* see if pointer is valid */
else if (TilerMem_VirtToPhys(ptr) == 0)
{
A_I(dec_ref(),==,0);
return R_UP(0);
}
A_I(dec_ref(),==,0);
#else
/* if emulating, we need to get through all allocated memory segments */
pthread_mutex_lock(&che_mutex);
init();
_AllocData *ad;
if (!ptr) return R_UP(0);
DLIST_MLOOP(bufs, ad, link) {
int ix;
struct tiler_buf_info *buf = (struct tiler_buf_info *) ad->buf.offset;
for (ix = 0; ix < buf->num_blocks; ix++)
{
if (ptr >= buf->blocks[ix].ptr &&
ptr < buf->blocks[ix].ptr + def_size(buf->blocks + ix))
{
bytes_t stride = buf->blocks[ix].stride;
pthread_mutex_unlock(&che_mutex);
return R_UP(stride);
}
}
}
pthread_mutex_unlock(&che_mutex);
#endif
return R_UP(PAGE_SIZE);
}
bytes_t TilerMem_GetStride(SSPtr ssptr)
{
IN;
switch(tiler_get_fmt(ssptr))
{
case TILFMT_8BIT: return R_UP(TILER_STRIDE_8BIT);
case TILFMT_16BIT: return R_UP(TILER_STRIDE_16BIT);
case TILFMT_32BIT: return R_UP(TILER_STRIDE_32BIT);
case TILFMT_PAGE: return R_UP(PAGE_SIZE);
default: return R_UP(0);
}
}
SSPtr TilerMem_VirtToPhys(void *ptr)
{
#ifndef STUB_TILER
SSPtr ssptr = 0;
if(!NOT_I(inc_ref(),==,0))
{
ssptr = ioctl(td, TILIOC_GSSP, (unsigned long) ptr);
A_I(dec_ref(),==,0);
}
return (SSPtr)R_P(ssptr);
#else
return (SSPtr)ptr;
#endif
}
/**
* Internal Unit Test. Tests the static methods of this
* library. Assumes an unitialized state as well.
*
* @author a0194118 (9/4/2009)
*
* @return 0 for success, non-0 error value for failure.
*/
int __test__MemMgr()
{
int ret = 0;
/* state check */
ret |= NOT_I(TILER_PAGE_WIDTH * TILER_PAGE_HEIGHT,==,PAGE_SIZE);
ret |= NOT_I(refCnt,==,0);
ret |= NOT_I(inc_ref(),==,0);
ret |= NOT_I(refCnt,==,1);
ret |= NOT_I(dec_ref(),==,0);
ret |= NOT_I(refCnt,==,0);
/* enumeration check */
ret |= NOT_I(PIXEL_FMT_8BIT,==,TILFMT_8BIT);
ret |= NOT_I(PIXEL_FMT_16BIT,==,TILFMT_16BIT);
ret |= NOT_I(PIXEL_FMT_32BIT,==,TILFMT_32BIT);
ret |= NOT_I(PIXEL_FMT_PAGE,==,TILFMT_PAGE);
ret |= NOT_I(sizeof(MemAllocBlock),==,sizeof(struct tiler_block_info));
/* void * arithmetic */
void *a = (void *)1000, *b = a + 2000, *c = (void *)3000;
ret |= NOT_P(b,==,c);
/* def_stride */
ret |= NOT_I(def_stride(0),==,0);
ret |= NOT_I(def_stride(1),==,PAGE_SIZE);
ret |= NOT_I(def_stride(PAGE_SIZE),==,PAGE_SIZE);
ret |= NOT_I(def_stride(PAGE_SIZE + 1),==,2 * PAGE_SIZE);
/* def_bpp */
ret |= NOT_I(def_bpp(PIXEL_FMT_32BIT),==,4);
ret |= NOT_I(def_bpp(PIXEL_FMT_16BIT),==,2);
ret |= NOT_I(def_bpp(PIXEL_FMT_8BIT),==,1);
/* def_size */
tiler_block_info blk = {0};
blk.fmt = TILFMT_8BIT;
blk.dim.area.width = PAGE_SIZE * 8 / 10;
blk.dim.area.height = 10;
ret |= NOT_I(def_size(&blk),==,10 * PAGE_SIZE);
blk.fmt = TILFMT_16BIT;
blk.dim.area.width = PAGE_SIZE * 7 / 10;
ret |= NOT_I(def_size(&blk),==,20 * PAGE_SIZE);
blk.dim.area.width = PAGE_SIZE * 4 / 10;
ret |= NOT_I(def_size(&blk),==,10 * PAGE_SIZE);
blk.fmt = TILFMT_32BIT;
ret |= NOT_I(def_size(&blk),==,20 * PAGE_SIZE);
blk.dim.area.width = PAGE_SIZE * 6 / 10;
ret |= NOT_I(def_size(&blk),==,30 * PAGE_SIZE);
return ret;
}