/* * static int split_block * * DESCRIPTION: * * When freeing space in a multi-block chunk we have to create new * blocks out of the upper areas being freed. * * RETURNS: * * Success - MPOOL_ERROR_NONE * * Failure - Mpool error code * * ARGUMENTS: * * mp_p <-> Pointer to the memory pool. * * free_addr -> Address that we are freeing. * * size -> Size of the space that we are taking from address. */ static int split_block(mpool_t * mp_p, void *free_addr, const unsigned long size) { mpool_block_t *block_p, *new_block_p; int ret, page_n; void *end_p; /* * 1st we find the block pointer from our free addr. At this point * the pointer must be the 1st one in the block if it is spans * multiple blocks. */ block_p = (mpool_block_t *) ((char *)free_addr - sizeof(mpool_block_t)); if (block_p->mb_magic != BLOCK_MAGIC || block_p->mb_magic2 != BLOCK_MAGIC) { return MPOOL_ERROR_POOL_OVER; } page_n = PAGES_IN_SIZE(mp_p, size); /* we are creating a new block structure for the 2nd ... */ new_block_p = (mpool_block_t *) ((char *)block_p + SIZE_OF_PAGES(mp_p, page_n)); new_block_p->mb_magic = BLOCK_MAGIC; /* New bounds is 1st block bounds. The 1st block's is reset below. */ new_block_p->mb_bounds_p = block_p->mb_bounds_p; /* Continue the linked list. The 1st block will point to us below. */ new_block_p->mb_next_p = block_p->mb_next_p; new_block_p->mb_magic2 = BLOCK_MAGIC; /* bounds for the 1st block are reset to the 1st page only */ block_p->mb_bounds_p = (char *)new_block_p; /* the next block pointer for the 1st block is now the new one */ block_p->mb_next_p = new_block_p; /* only free the space in the 1st block if it is only 1 block in size */ if (page_n == 1) { /* now free the rest of the 1st block block */ end_p = (char *)free_addr + size; ret = free_pointer(mp_p, end_p, (char *)block_p->mb_bounds_p - (char *)end_p); if (ret != MPOOL_ERROR_NONE) { return ret; } } /* now free the rest of the block */ ret = free_pointer(mp_p, FIRST_ADDR_IN_BLOCK(new_block_p), MEMORY_IN_BLOCK(new_block_p)); if (ret != MPOOL_ERROR_NONE) { return ret; } return MPOOL_ERROR_NONE; }
/* * mpool_t *mpool_open * * DESCRIPTION: * * Open/allocate a new memory pool. * * RETURNS: * * Success - Pool pointer which must be passed to mpool_close to * deallocate. * * Failure - NULL * * ARGUMENTS: * * flags -> Flags to set attributes of the memory pool. See the top * of mpool.h. * * page_size -> Set the internal memory page-size. This must be a * multiple of the getpagesize() value. Set to 0 for the default. * * start_addr -> Starting address to try and allocate memory pools. * This is ignored if the MPOOL_FLAG_USE_SBRK is enabled. * * error_p <- Pointer to integer which, if not NULL, will be set with * a mpool error code. */ mpool_t *mpool_open(const unsigned int flags, const unsigned int page_size, void *start_addr, int *error_p) { mpool_block_t *block_p; int page_n, ret; mpool_t mp, *mp_p; void *free_addr; if (! enabled_b) { startup(); } /* zero our temp struct */ memset(&mp, 0, sizeof(mp)); mp.mp_magic = MPOOL_MAGIC; mp.mp_flags = flags; mp.mp_alloc_c = 0; mp.mp_user_alloc = 0; mp.mp_max_alloc = 0; mp.mp_page_c = 0; /* mp.mp_page_size set below */ /* mp.mp_blocks_bit_n set below */ /* mp.mp_fd set below */ /* mp.mp_top set below */ /* mp.mp_addr set below */ mp.mp_log_func = NULL; mp.mp_min_p = NULL; mp.mp_bounds_p = NULL; mp.mp_first_p = NULL; mp.mp_last_p = NULL; mp.mp_magic2 = MPOOL_MAGIC; /* get and sanity check our page size */ if (page_size > 0) { mp.mp_page_size = page_size; if (mp.mp_page_size % getpagesize() != 0) { SET_POINTER(error_p, MPOOL_ERROR_ARG_INVALID); return NULL; } } else { mp.mp_page_size = getpagesize() * DEFAULT_PAGE_MULT; if (mp.mp_page_size % 1024 != 0) { SET_POINTER(error_p, MPOOL_ERROR_PAGE_SIZE); return NULL; } } if (BIT_IS_SET(flags, MPOOL_FLAG_USE_SBRK)) { mp.mp_fd = -1; mp.mp_addr = NULL; mp.mp_top = 0; } else { /* open dev-zero for our mmaping */ mp.mp_fd = open("/dev/zero", O_RDWR, 0); if (mp.mp_fd < 0) { SET_POINTER(error_p, MPOOL_ERROR_OPEN_ZERO); return NULL; } mp.mp_addr = start_addr; /* we start at the front of the file */ mp.mp_top = 0; } /* * Find out how many pages we need for our mpool structure. * * NOTE: this adds possibly unneeded space for mpool_block_t which * may not be in this block. */ page_n = PAGES_IN_SIZE(&mp, sizeof(mpool_t)); /* now allocate us space for the actual struct */ mp_p = alloc_pages(&mp, page_n, error_p); if (mp_p == NULL) { if (mp.mp_fd >= 0) { (void)close(mp.mp_fd); mp.mp_fd = -1; } return NULL; } /* * NOTE: we do not normally free the rest of the block here because * we want to lesson the chance of an allocation overwriting the * main structure. */ if (BIT_IS_SET(flags, MPOOL_FLAG_HEAVY_PACKING)) { /* we add a block header to the front of the block */ block_p = (mpool_block_t *)mp_p; /* init the block header */ block_p->mb_magic = BLOCK_MAGIC; block_p->mb_bounds_p = (char *)block_p + SIZE_OF_PAGES(&mp, page_n); block_p->mb_next_p = NULL; block_p->mb_magic2 = BLOCK_MAGIC; /* the mpool pointer is then the 2nd thing in the block */ mp_p = FIRST_ADDR_IN_BLOCK(block_p); free_addr = (char *)mp_p + sizeof(mpool_t); /* free the rest of the block */ ret = free_pointer(&mp, free_addr, (char *)block_p->mb_bounds_p - (char *)free_addr); if (ret != MPOOL_ERROR_NONE) { if (mp.mp_fd >= 0) { (void)close(mp.mp_fd); mp.mp_fd = -1; } /* NOTE: after this line mp_p will be invalid */ (void)free_pages(block_p, SIZE_OF_PAGES(&mp, page_n), BIT_IS_SET(flags, MPOOL_FLAG_USE_SBRK)); SET_POINTER(error_p, ret); return NULL; } /* * NOTE: if we are HEAVY_PACKING then the 1st block with the mpool * header is not on the block linked list. */ /* now copy our tmp structure into our new memory area */ memcpy(mp_p, &mp, sizeof(mpool_t)); /* we setup min/max to our current address which is as good as any */ mp_p->mp_min_p = block_p; mp_p->mp_bounds_p = block_p->mb_bounds_p; } else { /* now copy our tmp structure into our new memory area */ memcpy(mp_p, &mp, sizeof(mpool_t)); /* we setup min/max to our current address which is as good as any */ mp_p->mp_min_p = mp_p; mp_p->mp_bounds_p = (char *)mp_p + SIZE_OF_PAGES(mp_p, page_n); } SET_POINTER(error_p, MPOOL_ERROR_NONE); return mp_p; }
/* * static void *get_space * * DESCRIPTION: * * Moved a pointer into our free lists. * * RETURNS: * * Success - New address that we can use. * * Failure - NULL * * ARGUMENTS: * * mp_p <-> Pointer to the memory pool. * * byte_size -> Size of the address space that we need. * * error_p <- Pointer to integer which, if not NULL, will be set with * a mpool error code. */ static void *get_space(mpool_t *mp_p, const unsigned long byte_size, int *error_p) { mpool_block_t *block_p; mpool_free_t free_pnt; int ret; unsigned long size; unsigned int bit_c, page_n, left; void *free_addr = NULL, *free_end; size = byte_size; while ((size & (sizeof(void *) - 1)) > 0) { size++; } /* * First we check the free lists looking for something with enough * pages. Maybe we should only look X bits higher in the list. * * XXX: this is where we'd do the best fit. We'd look for the * closest match. We then could put the rest of the allocation that * we did not use in a lower free list. Have a define which states * how deep in the free list to go to find the closest match. */ for (bit_c = size_to_bits(size); bit_c <= MAX_BITS; bit_c++) { if (mp_p->mp_free[bit_c] != NULL) { free_addr = mp_p->mp_free[bit_c]; break; } } /* * If we haven't allocated any blocks or if the last block doesn't * have enough memory then we need a new block. */ if (bit_c > MAX_BITS) { /* we need to allocate more space */ page_n = PAGES_IN_SIZE(mp_p, size); /* now we try and get the pages we need/want */ block_p = alloc_pages(mp_p, page_n, error_p); if (block_p == NULL) { /* error_p set in alloc_pages */ return NULL; } /* init the block header */ block_p->mb_magic = BLOCK_MAGIC; block_p->mb_bounds_p = (char *)block_p + SIZE_OF_PAGES(mp_p, page_n); block_p->mb_next_p = mp_p->mp_first_p; block_p->mb_magic2 = BLOCK_MAGIC; /* * We insert it into the front of the queue. We could add it to * the end but there is not much use. */ mp_p->mp_first_p = block_p; if (mp_p->mp_last_p == NULL) { mp_p->mp_last_p = block_p; } free_addr = FIRST_ADDR_IN_BLOCK(block_p); #ifdef DEBUG (void)printf("had to allocate space for %lx of %lu bytes\n", (long)free_addr, size); #endif free_end = (char *)free_addr + size; left = (char *)block_p->mb_bounds_p - (char *)free_end; } else { if (bit_c < min_bit_free_next) { mp_p->mp_free[bit_c] = NULL; /* calculate the number of left over bytes */ left = bits_to_size(bit_c) - size; } else if (bit_c < min_bit_free_next) { /* grab the next pointer from the freed address into our list */ memcpy(mp_p->mp_free + bit_c, free_addr, sizeof(void *)); /* calculate the number of left over bytes */ left = bits_to_size(bit_c) - size; } else { /* grab the free structure from the address */ memcpy(&free_pnt, free_addr, sizeof(free_pnt)); mp_p->mp_free[bit_c] = free_pnt.mf_next_p; /* are we are splitting up a multiblock chunk into fewer blocks? */ if (PAGES_IN_SIZE(mp_p, free_pnt.mf_size) > PAGES_IN_SIZE(mp_p, size)) { ret = split_block(mp_p, free_addr, size); if (ret != MPOOL_ERROR_NONE) { SET_POINTER(error_p, ret); return NULL; } /* left over memory was taken care of in split_block */ left = 0; } else { /* calculate the number of left over bytes */ left = free_pnt.mf_size - size; } } #ifdef DEBUG (void)printf("found a free block at %lx of %lu bytes\n", (long)free_addr, left + size); #endif free_end = (char *)free_addr + size; } /* * If we have memory left over then we free it so someone else can * use it. We do not free the space if we just allocated a * multi-block chunk because we need to have every allocation easily * find the start of the block. Every user address % page-size * should take us to the start of the block. */ if (left > 0 && size <= MAX_BLOCK_USER_MEMORY(mp_p)) { /* free the rest of the block */ ret = free_pointer(mp_p, free_end, left); if (ret != MPOOL_ERROR_NONE) { SET_POINTER(error_p, ret); return NULL; } } /* update our bounds */ if (free_addr > mp_p->mp_bounds_p) { mp_p->mp_bounds_p = free_addr; } else if (free_addr < mp_p->mp_min_p) { mp_p->mp_min_p = free_addr; } return free_addr; }