/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; If not, see .
*/
#include
#include
#include
#include "private.h"
#define DBGPRINTF(_m...) \
xtl_log(xcall->logger, XTL_DEBUG, -1, "xencall:buffer", _m)
#define ROUNDUP(_x,_w) (((unsigned long)(_x)+(1UL<<(_w))-1) & ~((1UL<<(_w))-1))
pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
static void cache_lock(xencall_handle *xcall)
{
int saved_errno = errno;
if ( xcall->flags & XENCALL_OPENFLAG_NON_REENTRANT )
return;
pthread_mutex_lock(&cache_mutex);
/* Ignore pthread errors. */
errno = saved_errno;
}
static void cache_unlock(xencall_handle *xcall)
{
int saved_errno = errno;
if ( xcall->flags & XENCALL_OPENFLAG_NON_REENTRANT )
return;
pthread_mutex_unlock(&cache_mutex);
/* Ignore pthread errors. */
errno = saved_errno;
}
static void *cache_alloc(xencall_handle *xcall, size_t nr_pages)
{
void *p = NULL;
cache_lock(xcall);
xcall->buffer_total_allocations++;
xcall->buffer_current_allocations++;
if ( xcall->buffer_current_allocations > xcall->buffer_maximum_allocations )
xcall->buffer_maximum_allocations = xcall->buffer_current_allocations;
if ( nr_pages > 1 )
{
xcall->buffer_cache_toobig++;
}
else if ( xcall->buffer_cache_nr > 0 )
{
p = xcall->buffer_cache[--xcall->buffer_cache_nr];
xcall->buffer_cache_hits++;
}
else
{
xcall->buffer_cache_misses++;
}
cache_unlock(xcall);
return p;
}
static int cache_free(xencall_handle *xcall, void *p, size_t nr_pages)
{
int rc = 0;
cache_lock(xcall);
xcall->buffer_total_releases++;
xcall->buffer_current_allocations--;
if ( nr_pages == 1 &&
xcall->buffer_cache_nr < BUFFER_CACHE_SIZE )
{
xcall->buffer_cache[xcall->buffer_cache_nr++] = p;
rc = 1;
}
cache_unlock(xcall);
return rc;
}
void buffer_release_cache(xencall_handle *xcall)
{
void *p;
cache_lock(xcall);
DBGPRINTF("total allocations:%d total releases:%d",
xcall->buffer_total_allocations,
xcall->buffer_total_releases);
DBGPRINTF("current allocations:%d maximum allocations:%d",
xcall->buffer_current_allocations,
xcall->buffer_maximum_allocations);
DBGPRINTF("cache current size:%d",
xcall->buffer_cache_nr);
DBGPRINTF("cache hits:%d misses:%d toobig:%d",
xcall->buffer_cache_hits,
xcall->buffer_cache_misses,
xcall->buffer_cache_toobig);
while ( xcall->buffer_cache_nr > 0 )
{
p = xcall->buffer_cache[--xcall->buffer_cache_nr];
osdep_free_pages(xcall, p, 1);
}
cache_unlock(xcall);
}
void *xencall_alloc_buffer_pages(xencall_handle *xcall, size_t nr_pages)
{
void *p = cache_alloc(xcall, nr_pages);
if ( !p )
p = osdep_alloc_pages(xcall, nr_pages);
if (!p)
return NULL;
memset(p, 0, nr_pages * PAGE_SIZE);
return p;
}
void xencall_free_buffer_pages(xencall_handle *xcall, void *p, size_t nr_pages)
{
if ( p == NULL )
return;
if ( !cache_free(xcall, p, nr_pages) )
osdep_free_pages(xcall, p, nr_pages);
}
struct allocation_header {
int nr_pages;
int pad[3];
};
void *xencall_alloc_buffer(xencall_handle *xcall, size_t size)
{
size_t actual_size = ROUNDUP(size + sizeof(struct allocation_header), PAGE_SHIFT);
int nr_pages = actual_size >> PAGE_SHIFT;
struct allocation_header *hdr;
hdr = xencall_alloc_buffer_pages(xcall, nr_pages);
if ( hdr == NULL )
return NULL;
hdr->nr_pages = nr_pages;
return (void *)(hdr+1);
}
void xencall_free_buffer(xencall_handle *xcall, void *p)
{
struct allocation_header *hdr;
if (p == NULL)
return;
hdr = p;
--hdr;
xencall_free_buffer_pages(xcall, hdr, hdr->nr_pages);
}
/*
* Local variables:
* mode: C
* c-file-style: "BSD"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/