Logo Search packages:      
Sourcecode: rageircd version File versions  Download package

blalloc.c

/*
 * RageIRCd: an advanced Internet Relay Chat daemon (ircd).
 * (C) 2000-2005 the RageIRCd Development Team, all rights reserved.
 *
 * This software is free, licensed under the General Public License.
 * Please refer to doc/LICENSE and doc/README for further details.
 *
 * $Id: blalloc.c,v 1.26.2.3 2005/07/07 00:02:56 amcwilliam Exp $
 */

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include "numeric.h"
#include "blalloc.h"
#include "setup.h"
#include "memory.h"

dlink_list heap_list = DLINK_LIST_INIT;

#ifdef HAVE_MMAP

#include "fd.h"
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

static int mmap_fd = -1;
static int mmap_flags = MAP_PRIVATE;

#ifndef MAP_ANON
#ifdef MAP_ANONYMOUS
#define MAP_ANON MAP_ANONYMOUS
#endif
#endif

#define alloc_block(sz)       mmap(NULL, sz, PROT_READ|PROT_WRITE, mmap_flags, mmap_fd, 0)
#define free_block(ptr, sz)   munmap(ptr, sz)
#else
#define alloc_block(sz)       MyMalloc(sz)
#define free_block(ptr, sz)   MyFree(ptr)
#endif

static Block *add_new_block(BlockHeap *heap)
{
      Block *block = NULL;
      int i;

      block = (Block *)MyMalloc(sizeof(Block));
      ASSERT(block != NULL);

      block->allocMap = (unsigned long *)MyMalloc(sizeof(unsigned long) * (heap->mapSize + 1));
      ASSERT(block->allocMap != NULL);

      block->elems = alloc_block(heap->elemSize * heap->elemsPerBlock);
#ifdef HAVE_MMAP
      ASSERT(block->elems != MAP_FAILED);
#else
      ASSERT(block->elems != NULL);
#endif
      block->endElem = (void *)((unsigned long)block->elems +
            (unsigned long)((heap->elemsPerBlock - 1) * heap->elemSize));
      block->freeElems += heap->elemsPerBlock;

      for (i = 0; i < heap->mapSize; i++) {
            block->allocMap[i] = 0L;
      }

      heap->freeElems += heap->elemsPerBlock;
      dlink_add_node(&heap->block_list, &block->self, block);

      return block;
}

BlockHeap *BlockHeapCreate(size_t elemsize, int elemsperblock)
{
      BlockHeap *heap;

      ASSERT(elemsize > 0);
      ASSERT(elemsperblock > 0);

      heap = (BlockHeap *)MyMalloc(sizeof(BlockHeap));
      ASSERT(heap != NULL);

      heap->elemSize = elemsize + (elemsize & (sizeof(void *) - 1));
      heap->elemsPerBlock = elemsperblock;
      heap->freeElems = 0;

      heap->mapSize = (heap->elemsPerBlock / (sizeof(unsigned long) * 8)) + 1;
      if (!(heap->elemsPerBlock % (sizeof(unsigned long) * 8))) {
            heap->mapSize--;
      }

      add_new_block(heap);
      dlink_add_node(&heap_list, &heap->node, heap);

      return heap;
}

static void DestroyBlock(BlockHeap *heap, Block *block)
{
      ASSERT(heap != NULL);
      ASSERT(block != NULL);

      dlink_del_nofree(&heap->block_list, NULL, &block->self);
      heap->freeElems -= heap->elemsPerBlock;

      free_block(block->elems, (heap->elemSize * (heap->elemsPerBlock + 1)));
      MyFree(block->allocMap);
      MyFree(block);
}


void BlockHeapDestroy(BlockHeap *heap)
{
      dlink_node *node, *next = NULL;
      Block *block;

      ASSERT(heap != NULL);

      DLINK_FOREACH_SAFE_DATA(heap->block_list.head, node, next, block, Block) {
            DestroyBlock(heap, block);
      }

      dlink_del_nofree(&heap_list, NULL, &heap->node);
      MyFree(heap);
}

void *BlockHeapALLOC(BlockHeap *heap)
{
      dlink_node *node;
      Block *block;
      int i;
      unsigned long mask, c;
      void *mem = NULL;

      ASSERT(heap != NULL);

      if (!heap->freeElems) {
            block = add_new_block(heap);
            block->allocMap[0] = 0x1L;
            block->freeElems--;
            heap->freeElems--;
            return block->elems;
      }

      DLINK_FOREACH_DATA(heap->block_list.head, node, block, Block) {
            if (!block->freeElems) {
                  continue;
            }

            mask = 0x1L;
            c = 0;
            i = 0;

            while (i < heap->mapSize) {
                  if (mask == 0x1L && (block->allocMap[i] == ~0)) {
                        i++;
                        c = 0;
                        continue;
                  }

                  if (!(block->allocMap[i] & mask)) {
                        mem = ((void *)((unsigned long)block->elems +
                              ((i * sizeof(unsigned long) * 8 + c) * (unsigned long)heap->elemSize)));
                        block->allocMap[i] |= mask;
                        memset(mem, '\0', heap->elemSize);

                        block->freeElems--;
                        heap->freeElems--;

                        return mem;
                  }

                  mask <<= 1;
                  c++;

                  if (!mask) {
                        mask = 0x1L;
                        c = 0;
                        i = 0;
                  }
            }
      }

      ASSERT(heap->freeElems == 0);
      return NULL;
}

void BlockHeapFREE(BlockHeap *heap, void *ptr)
{
      dlink_node *node;
      Block *block;
      unsigned long mask, c;

      ASSERT(heap != NULL);
      ASSERT(ptr != NULL);

      DLINK_FOREACH_DATA(heap->block_list.head, node, block, Block) {
            if (ptr < block->elems || (ptr > block->endElem)) {
                  continue;
            }

            c = ((unsigned long)ptr - (unsigned long)block->elems) / (unsigned long)heap->elemSize;
            mask = 1L << (c % (sizeof(unsigned long) * 8));
            c = c / (sizeof(unsigned long) * 8);

            ASSERT((block->allocMap[c] & mask) == mask);

            block->allocMap[c] = (block->allocMap[c] & ~mask);
            block->freeElems++;
            heap->freeElems++;
            break;
      }
}

void BlockHeapGarbageCollect(BlockHeap *heap)
{
      dlink_node *node, *next = NULL;
      Block *block;

      ASSERT(heap != NULL);

      if (heap->freeElems < heap->elemsPerBlock) {
            return;
      }
      DLINK_FOREACH_SAFE_DATA(heap->block_list.head, node, next, block, Block) {
            if (block->freeElems == heap->elemsPerBlock) {
                  DestroyBlock(heap, block);
            }
      }
}

void BlockHeapUsage(BlockHeap *heap, int *cnt, unsigned long *mem)
{
      ASSERT(heap != NULL);
      *cnt = (dlink_length(&heap->block_list) * heap->elemsPerBlock);
      *mem = (*cnt * heap->elemSize);
}

void blockheap_cleanup()
{
      dlink_node *node;

      DLINK_FOREACH(heap_list.head, node) {
            BlockHeapGarbageCollect(node->data);
      }
}

void init_blockheap(int stage)
{
      if (stage) {
            add_event("blockheap_cleanup", blockheap_cleanup, NULL, BLOCKHEAP_CLEANUP_FREQ, 1);
      }
#ifdef HAVE_MMAP
      else {
#ifndef MAP_ANON
            if ((mmap_fd = open("/dev/zero", O_RDWR)) < 0) {
                  ircdlog(LOG_ERROR, "FATAL: init_blockheap(%d) open(/dev/zero) failed: %s",
                        stage, strerror(errno));
                  abort();
            }

            fd_open(mmap_fd);
#else
            mmap_flags |= MAP_ANON;
#endif
      }
#endif
}

Generated by  Doxygen 1.6.0   Back to index