mirror of
				https://gitlab.labs.nic.cz/labs/bird.git
				synced 2024-05-11 16:54:54 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			274 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			274 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 *	BIRD Resource Manager -- Memory Pools
 | 
						|
 *
 | 
						|
 *	(c) 1998--2000 Martin Mares <mj@ucw.cz>
 | 
						|
 *
 | 
						|
 *	Can be freely distributed and used under the terms of the GNU GPL.
 | 
						|
 */
 | 
						|
 | 
						|
/**
 | 
						|
 * DOC: Linear memory pools
 | 
						|
 *
 | 
						|
 * Linear memory pools are collections of memory blocks which
 | 
						|
 * support very fast allocation of new blocks, but are able to free only
 | 
						|
 * the whole collection at once.
 | 
						|
 *
 | 
						|
 * Example: Each configuration is described by a complex system of structures,
 | 
						|
 * linked lists and function trees which are all allocated from a single linear
 | 
						|
 * pool, thus they can be freed at once when the configuration is no longer used.
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdint.h>
 | 
						|
 | 
						|
#include "nest/bird.h"
 | 
						|
#include "lib/resource.h"
 | 
						|
#include "lib/string.h"
 | 
						|
 | 
						|
struct lp_chunk {
 | 
						|
  struct lp_chunk *next;
 | 
						|
  uint size;
 | 
						|
  uintptr_t data_align[0];
 | 
						|
  byte data[0];
 | 
						|
};
 | 
						|
 | 
						|
const int lp_chunk_size = sizeof(struct lp_chunk);
 | 
						|
 | 
						|
struct linpool {
 | 
						|
  resource r;
 | 
						|
  byte *ptr, *end;
 | 
						|
  struct lp_chunk *first, *current, **plast;	/* Normal (reusable) chunks */
 | 
						|
  struct lp_chunk *first_large;			/* Large chunks */
 | 
						|
  uint chunk_size, threshold, total, total_large;
 | 
						|
};
 | 
						|
 | 
						|
static void lp_free(resource *);
 | 
						|
static void lp_dump(resource *);
 | 
						|
static resource *lp_lookup(resource *, unsigned long);
 | 
						|
static size_t lp_memsize(resource *r);
 | 
						|
 | 
						|
static struct resclass lp_class = {
 | 
						|
  "LinPool",
 | 
						|
  sizeof(struct linpool),
 | 
						|
  lp_free,
 | 
						|
  lp_dump,
 | 
						|
  lp_lookup,
 | 
						|
  lp_memsize
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * lp_new - create a new linear memory pool
 | 
						|
 * @p: pool
 | 
						|
 * @blk: block size
 | 
						|
 *
 | 
						|
 * lp_new() creates a new linear memory pool resource inside the pool @p.
 | 
						|
 * The linear pool consists of a list of memory chunks of size at least
 | 
						|
 * @blk.
 | 
						|
 */
 | 
						|
linpool
 | 
						|
*lp_new(pool *p, uint blk)
 | 
						|
{
 | 
						|
  linpool *m = ralloc(p, &lp_class);
 | 
						|
  m->plast = &m->first;
 | 
						|
  m->chunk_size = blk;
 | 
						|
  m->threshold = 3*blk/4;
 | 
						|
  return m;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * lp_alloc - allocate memory from a &linpool
 | 
						|
 * @m: linear memory pool
 | 
						|
 * @size: amount of memory
 | 
						|
 *
 | 
						|
 * lp_alloc() allocates @size bytes of memory from a &linpool @m
 | 
						|
 * and it returns a pointer to the allocated memory.
 | 
						|
 *
 | 
						|
 * It works by trying to find free space in the last memory chunk
 | 
						|
 * associated with the &linpool and creating a new chunk of the standard
 | 
						|
 * size (as specified during lp_new()) if the free space is too small
 | 
						|
 * to satisfy the allocation. If @size is too large to fit in a standard
 | 
						|
 * size chunk, an "overflow" chunk is created for it instead.
 | 
						|
 */
 | 
						|
void *
 | 
						|
lp_alloc(linpool *m, uint size)
 | 
						|
{
 | 
						|
  byte *a = (byte *) BIRD_ALIGN((unsigned long) m->ptr, CPU_STRUCT_ALIGN);
 | 
						|
  byte *e = a + size;
 | 
						|
 | 
						|
  if (e <= m->end)
 | 
						|
    {
 | 
						|
      m->ptr = e;
 | 
						|
      return a;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      struct lp_chunk *c;
 | 
						|
      if (size >= m->threshold)
 | 
						|
	{
 | 
						|
	  /* Too large => allocate large chunk */
 | 
						|
	  c = xmalloc(sizeof(struct lp_chunk) + size);
 | 
						|
	  m->total_large += size;
 | 
						|
	  c->next = m->first_large;
 | 
						|
	  m->first_large = c;
 | 
						|
	  c->size = size;
 | 
						|
	}
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  if (m->current)
 | 
						|
	    {
 | 
						|
	      /* Still have free chunks from previous incarnation (before lp_flush()) */
 | 
						|
	      c = m->current;
 | 
						|
	      m->current = c->next;
 | 
						|
	    }
 | 
						|
	  else
 | 
						|
	    {
 | 
						|
	      /* Need to allocate a new chunk */
 | 
						|
	      c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size);
 | 
						|
	      m->total += m->chunk_size;
 | 
						|
	      *m->plast = c;
 | 
						|
	      m->plast = &c->next;
 | 
						|
	      c->next = NULL;
 | 
						|
	      c->size = m->chunk_size;
 | 
						|
	    }
 | 
						|
	  m->ptr = c->data + size;
 | 
						|
	  m->end = c->data + m->chunk_size;
 | 
						|
	}
 | 
						|
      return c->data;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * lp_allocu - allocate unaligned memory from a &linpool
 | 
						|
 * @m: linear memory pool
 | 
						|
 * @size: amount of memory
 | 
						|
 *
 | 
						|
 * lp_allocu() allocates @size bytes of memory from a &linpool @m
 | 
						|
 * and it returns a pointer to the allocated memory. It doesn't
 | 
						|
 * attempt to align the memory block, giving a very efficient way
 | 
						|
 * how to allocate strings without any space overhead.
 | 
						|
 */
 | 
						|
void *
 | 
						|
lp_allocu(linpool *m, uint size)
 | 
						|
{
 | 
						|
  byte *a = m->ptr;
 | 
						|
  byte *e = a + size;
 | 
						|
 | 
						|
  if (e <= m->end)
 | 
						|
    {
 | 
						|
      m->ptr = e;
 | 
						|
      return a;
 | 
						|
    }
 | 
						|
  return lp_alloc(m, size);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * lp_allocz - allocate cleared memory from a &linpool
 | 
						|
 * @m: linear memory pool
 | 
						|
 * @size: amount of memory
 | 
						|
 *
 | 
						|
 * This function is identical to lp_alloc() except that it
 | 
						|
 * clears the allocated memory block.
 | 
						|
 */
 | 
						|
void *
 | 
						|
lp_allocz(linpool *m, uint size)
 | 
						|
{
 | 
						|
  void *z = lp_alloc(m, size);
 | 
						|
 | 
						|
  bzero(z, size);
 | 
						|
  return z;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * lp_flush - flush a linear memory pool
 | 
						|
 * @m: linear memory pool
 | 
						|
 *
 | 
						|
 * This function frees the whole contents of the given &linpool @m,
 | 
						|
 * but leaves the pool itself.
 | 
						|
 */
 | 
						|
void
 | 
						|
lp_flush(linpool *m)
 | 
						|
{
 | 
						|
  struct lp_chunk *c;
 | 
						|
 | 
						|
  /* Relink all normal chunks to free list and free all large chunks */
 | 
						|
  m->ptr = m->end = NULL;
 | 
						|
  m->current = m->first;
 | 
						|
  while (c = m->first_large)
 | 
						|
    {
 | 
						|
      m->first_large = c->next;
 | 
						|
      xfree(c);
 | 
						|
    }
 | 
						|
  m->total_large = 0;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
lp_free(resource *r)
 | 
						|
{
 | 
						|
  linpool *m = (linpool *) r;
 | 
						|
  struct lp_chunk *c, *d;
 | 
						|
 | 
						|
  for(d=m->first; d; d = c)
 | 
						|
    {
 | 
						|
      c = d->next;
 | 
						|
      xfree(d);
 | 
						|
    }
 | 
						|
  for(d=m->first_large; d; d = c)
 | 
						|
    {
 | 
						|
      c = d->next;
 | 
						|
      xfree(d);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
lp_dump(resource *r)
 | 
						|
{
 | 
						|
  linpool *m = (linpool *) r;
 | 
						|
  struct lp_chunk *c;
 | 
						|
  int cnt, cntl;
 | 
						|
 | 
						|
  for(cnt=0, c=m->first; c; c=c->next, cnt++)
 | 
						|
    ;
 | 
						|
  for(cntl=0, c=m->first_large; c; c=c->next, cntl++)
 | 
						|
    ;
 | 
						|
  debug("(chunk=%d threshold=%d count=%d+%d total=%d+%d)\n",
 | 
						|
	m->chunk_size,
 | 
						|
	m->threshold,
 | 
						|
	cnt,
 | 
						|
	cntl,
 | 
						|
	m->total,
 | 
						|
	m->total_large);
 | 
						|
}
 | 
						|
 | 
						|
static size_t
 | 
						|
lp_memsize(resource *r)
 | 
						|
{
 | 
						|
  linpool *m = (linpool *) r;
 | 
						|
  struct lp_chunk *c;
 | 
						|
  int cnt = 0;
 | 
						|
 | 
						|
  for(c=m->first; c; c=c->next)
 | 
						|
    cnt++;
 | 
						|
  for(c=m->first_large; c; c=c->next)
 | 
						|
    cnt++;
 | 
						|
 | 
						|
  return ALLOC_OVERHEAD + sizeof(struct linpool) +
 | 
						|
    cnt * (ALLOC_OVERHEAD + sizeof(struct lp_chunk)) +
 | 
						|
    m->total + m->total_large;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static resource *
 | 
						|
lp_lookup(resource *r, unsigned long a)
 | 
						|
{
 | 
						|
  linpool *m = (linpool *) r;
 | 
						|
  struct lp_chunk *c;
 | 
						|
 | 
						|
  for(c=m->first; c; c=c->next)
 | 
						|
    if ((unsigned long) c->data <= a && (unsigned long) c->data + c->size > a)
 | 
						|
      return r;
 | 
						|
  for(c=m->first_large; c; c=c->next)
 | 
						|
    if ((unsigned long) c->data <= a && (unsigned long) c->data + c->size > a)
 | 
						|
      return r;
 | 
						|
  return NULL;
 | 
						|
}
 |