+++ /dev/null
-/*
- +----------------------------------------------------------------------+
- | APC |
- +----------------------------------------------------------------------+
- | Copyright (c) 2008 The PHP Group |
- +----------------------------------------------------------------------+
- | This source file is subject to version 3.01 of the PHP license, |
- | that is bundled with this package in the file LICENSE, and is |
- | available through the world-wide-web at the following url: |
- | http://www.php.net/license/3_01.txt |
- | If you did not receive a copy of the PHP license and are unable to |
- | obtain it through the world-wide-web, please send a note to |
- | license@php.net so we can mail you a copy immediately. |
- +----------------------------------------------------------------------+
- | Authors: Daniel Cowgill <dcowgill@communityconnect.com> |
- | Rasmus Lerdorf <rasmus@php.net> |
- +----------------------------------------------------------------------+
-
- This software was contributed to PHP by Community Connect Inc. in 2002
- and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
- Future revisions and derivatives of this source code must acknowledge
- Community Connect Inc. as the original contributor of this module by
- leaving this note intact in the source code.
-
- All other licensing and usage conditions are those of the PHP Group.
-
- */
-
-/* $Id: apc_sma.c,v 1.69.2.4 2008/05/11 18:57:00 rasmus Exp $ */
-
-#include "apc_sma.h"
-#include "apc.h"
-#include "apc_globals.h"
-#include "apc_lock.h"
-#include "apc_shm.h"
-#include "apc_cache.h"
-#include <limits.h>
-#if APC_MMAP
-void *apc_mmap(char *file_mask, size_t size);
-void apc_unmap(void* shmaddr, size_t size);
-#endif
-
-#ifdef HAVE_VALGRIND_MEMCHECK_H
-#include <valgrind/memcheck.h>
-#endif
-
-/* {{{ locking macros */
-#define LOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_lock(c); }
-#define RDLOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_rdlock(c); }
-#define UNLOCK(c) { apc_lck_unlock(c); HANDLE_UNBLOCK_INTERRUPTIONS(); }
-/* }}} */
-
-enum { DEFAULT_NUMSEG=1, DEFAULT_SEGSIZE=30*1024*1024 };
-
-static int sma_initialized = 0; /* true if the sma has been initialized */
-static unsigned int sma_numseg; /* number of shm segments to allow */
-static size_t sma_segsize; /* size of each shm segment */
-static size_t* sma_segments; /* array of shm segment ids */
-static void** sma_shmaddrs; /* array of shm segment addresses */
-static int sma_lastseg = 0; /* index of MRU segment */
-
-typedef struct header_t header_t;
-struct header_t {
- apc_lck_t sma_lock; /* segment lock, MUST BE ALIGNED for futex locks */
- size_t segsize; /* size of entire segment */
- size_t avail; /* bytes available (not necessarily contiguous) */
- size_t nfoffset; /* start next fit search from this offset */
-#if ALLOC_DISTRIBUTION
- size_t adist[30];
-#endif
-};
-
-
-/* do not enable for threaded http servers */
-/* #define __APC_SMA_DEBUG__ 1 */
-
-#ifdef __APC_SMA_DEBUG__
-/* global counter for identifying blocks
- * Technically it is possible to do the same
- * using offsets, but double allocations of the
- * same offset can happen. */
-static volatile size_t block_id = 0;
-#endif
-
-#define APC_SMA_CANARIES 1
-
-typedef struct block_t block_t;
-struct block_t {
- size_t size; /* size of this block */
- size_t next; /* offset in segment of next free block */
-#ifdef APC_SMA_CANARIES
- size_t canary; /* canary to check for memory overwrites */
-#endif
-#ifdef __APC_SMA_DEBUG__
- size_t id; /* identifier for the memory block */
-#endif
-};
-
-/* The macros BLOCKAT and OFFSET are used for convenience throughout this
- * module. Both assume the presence of a variable shmaddr that points to the
- * beginning of the shared memory segment in question. */
-
-#define BLOCKAT(offset) ((block_t*)((char *)shmaddr + offset))
-#define OFFSET(block) ((size_t)(((char*)block) - (char*)shmaddr))
-
-/* Canary macros for setting, checking and resetting memory canaries */
-#ifdef APC_SMA_CANARIES
- #define SET_CANARY(v) (v)->canary = 0x42424242
- #define CHECK_CANARY(v) assert((v)->canary == 0x42424242)
- #define RESET_CANARY(v) (v)->canary = -42
-#else
- #define SET_CANARY(v)
- #define CHECK_CANARY(v)
- #define RESET_CANARY(v)
-#endif
-
-
-/* {{{ MINBLOCKSIZE */
-#define MINBLOCKSIZE (ALIGNWORD(1) + ALIGNWORD(sizeof(block_t)))
-/* }}} */
-
-/* {{{ sma_allocate: tries to allocate size bytes in a segment */
-static size_t sma_allocate(void* shmaddr, size_t size)
-{
- header_t* header; /* header of shared memory segment */
- block_t* prv; /* block prior to working block */
- block_t* cur; /* working block in list */
- block_t* prvnextfit; /* block before next fit */
- size_t realsize; /* actual size of block needed, including header */
- size_t last_offset; /* save the last search offset */
- int wrapped=0;
- const size_t block_size = ALIGNWORD(sizeof(struct block_t));
-
- realsize = ALIGNWORD(size + block_size);
-
- /*
- * First, insure that the segment contains at least realsize free bytes,
- * even if they are not contiguous.
- */
- header = (header_t*) shmaddr;
- if (header->avail < realsize) {
- return -1;
- }
-
- prvnextfit = 0; /* initially null (no fit) */
- last_offset = 0;
-
- /* If we have a next fit offset, start searching from there */
- if(header->nfoffset) {
- prv = BLOCKAT(header->nfoffset);
- /* if prv is the last block, jump to the beginning */
- if(prv->next == 0) {
- prv = BLOCKAT(ALIGNWORD(sizeof(header_t)));
- wrapped = 1;
- }
- } else {
- prv = BLOCKAT(ALIGNWORD(sizeof(header_t)));
- }
-
- CHECK_CANARY(prv);
-
- while (prv->next != header->nfoffset) {
- cur = BLOCKAT(prv->next);
-#ifdef __APC_SMA_DEBUG__
- CHECK_CANARY(cur);
-#endif
- /* If it can fit realiszie bytes in cur block, stop searching */
- if (cur->size >= realsize) {
- prvnextfit = prv;
- break;
- }
- last_offset = prv->next;
- prv = cur;
- if(wrapped && (prv->next >= header->nfoffset)) break;
-
- /* Check to see if we need to wrap around and search from the top */
- if(header->nfoffset && prv->next == 0) {
- prv = BLOCKAT(ALIGNWORD(sizeof(header_t)));
-#ifdef __APC_SMA_DEBUG__
- CHECK_CANARY(prv);
-#endif
- last_offset = 0;
- wrapped = 1;
- }
- }
-
- if (prvnextfit == 0) {
- header->nfoffset = 0;
- return -1;
- }
-
- prv = prvnextfit;
- cur = BLOCKAT(prv->next);
-
- CHECK_CANARY(prv);
- CHECK_CANARY(cur);
-
- if (cur->size == realsize || (cur->size > realsize && cur->size < (realsize + (MINBLOCKSIZE * 2)))) {
- /* cur is big enough for realsize, but too small to split - unlink it */
- prv->next = cur->next;
- }
- else {
- block_t* nxt; /* the new block (chopped part of cur) */
- size_t nxtoffset; /* offset of the block currently after cur */
- size_t oldsize; /* size of cur before split */
-
- /* nextfit is too big; split it into two smaller blocks */
- nxtoffset = cur->next;
- oldsize = cur->size;
- prv->next += realsize; /* skip over newly allocated block */
- cur->size = realsize; /* Set the size of this new block */
- nxt = BLOCKAT(prv->next);
- nxt->next = nxtoffset; /* Re-link the shortened block */
- nxt->size = oldsize - realsize; /* and fix the size */
- SET_CANARY(nxt);
-#ifdef __APC_SMA_DEBUG__
- nxt->id = -1;
-#endif
- }
-
- /* update the block header */
- header->avail -= cur->size;
-#if ALLOC_DISTRIBUTION
- header->adist[(int)(log(size)/log(2))]++;
-#endif
-
- header->nfoffset = last_offset;
-
- SET_CANARY(cur);
-#ifdef __APC_SMA_DEBUG__
- cur->id = ++block_id;
- fprintf(stderr, "allocate(realsize=%d,size=%d,id=%d)\n", (int)(size), (int)(cur->size), cur->id);
-#endif
-
- return OFFSET(cur) + block_size;
-}
-/* }}} */
-
-/* {{{ sma_deallocate: deallocates the block at the given offset */
-static size_t sma_deallocate(void* shmaddr, size_t offset)
-{
- header_t* header; /* header of shared memory segment */
- block_t* cur; /* the new block to insert */
- block_t* prv; /* the block before cur */
- block_t* nxt; /* the block after cur */
- size_t size; /* size of deallocated block */
-
- offset -= ALIGNWORD(sizeof(struct block_t));
- assert(offset >= 0);
-
- /* find position of new block in free list */
- cur = BLOCKAT(offset);
- prv = BLOCKAT(ALIGNWORD(sizeof(header_t)));
-
- CHECK_CANARY(cur);
-
-#ifdef __APC_SMA_DEBUG__
- CHECK_CANARY(prv);
- fprintf(stderr, "free(%p, size=%d,id=%d)\n", cur, (int)(cur->size), cur->id);
-#endif
- while (prv->next != 0 && prv->next < offset) {
- prv = BLOCKAT(prv->next);
-#ifdef __APC_SMA_DEBUG__
- CHECK_CANARY(prv);
-#endif
- }
-
- CHECK_CANARY(prv);
-
- /* insert new block after prv */
- cur->next = prv->next;
- prv->next = offset;
-
-#ifdef __APC_SMA_DEBUG__
- CHECK_CANARY(cur);
- cur->id = -1;
-#endif
-
- /* update the block header */
- header = (header_t*) shmaddr;
- header->avail += cur->size;
- size = cur->size;
-
- if (((char *)prv) + prv->size == (char *) cur) {
- /* cur and prv share an edge, combine them */
- prv->size += cur->size;
- prv->next = cur->next;
- RESET_CANARY(cur);
- cur = prv;
- }
-
- nxt = BLOCKAT(cur->next);
-
- if (((char *)cur) + cur->size == (char *) nxt) {
- /* cur and nxt shared an edge, combine them */
- cur->size += nxt->size;
- cur->next = nxt->next;
-#ifdef __APC_SMA_DEBUG__
- CHECK_CANARY(nxt);
- nxt->id = -1; /* assert this or set it ? */
-#endif
- RESET_CANARY(nxt);
- }
- header->nfoffset = 0; /* Reset the next fit search marker */
-
- return size;
-}
-/* }}} */
-
-/* {{{ apc_sma_init */
-
-void apc_sma_init(int numseg, size_t segsize, char *mmap_file_mask)
-{
- int i;
-
- if (sma_initialized) {
- return;
- }
- sma_initialized = 1;
-
-#if APC_MMAP
- /*
- * I don't think multiple anonymous mmaps makes any sense
- * so force sma_numseg to 1 in this case
- */
- if(!mmap_file_mask ||
- (mmap_file_mask && !strlen(mmap_file_mask)) ||
- (mmap_file_mask && !strcmp(mmap_file_mask, "/dev/zero"))) {
- sma_numseg = 1;
- } else {
- sma_numseg = numseg > 0 ? numseg : DEFAULT_NUMSEG;
- }
-#else
- sma_numseg = numseg > 0 ? numseg : DEFAULT_NUMSEG;
-#endif
-
- sma_segsize = segsize > 0 ? segsize : DEFAULT_SEGSIZE;
-
- sma_segments = (size_t*) apc_emalloc(sma_numseg*sizeof(size_t));
- sma_shmaddrs = (void**) apc_emalloc(sma_numseg*sizeof(void*));
-
- for (i = 0; i < sma_numseg; i++) {
- header_t* header;
- block_t* block;
- void* shmaddr;
-
-#if APC_MMAP
- sma_segments[i] = sma_segsize;
- sma_shmaddrs[i] = apc_mmap(mmap_file_mask, sma_segsize);
- if(sma_numseg != 1) memcpy(&mmap_file_mask[strlen(mmap_file_mask)-6], "XXXXXX", 6);
-#else
- sma_segments[i] = apc_shm_create(NULL, i, sma_segsize);
- sma_shmaddrs[i] = apc_shm_attach(sma_segments[i]);
-#endif
- shmaddr = sma_shmaddrs[i];
-
- header = (header_t*) shmaddr;
- apc_lck_create(NULL, 0, 1, header->sma_lock);
- header->segsize = sma_segsize;
- header->avail = sma_segsize - ALIGNWORD(sizeof(header_t)) - ALIGNWORD(sizeof(block_t));
- header->nfoffset = 0;
-#if ALLOC_DISTRIBUTION
- {
- int j;
- for(j=0; j<30; j++) header->adist[j] = 0;
- }
-#endif
- block = BLOCKAT(ALIGNWORD(sizeof(header_t)));
- block->size = 0;
- block->next = ALIGNWORD(sizeof(header_t)) + ALIGNWORD(sizeof(block_t));
- SET_CANARY(block);
-#ifdef __APC_SMA_DEBUG__
- block->id = -1;
-#endif
- block = BLOCKAT(block->next);
- block->size = header->avail;
- block->next = 0;
- SET_CANARY(block);
-#ifdef __APC_SMA_DEBUG__
- block->id = -1;
-#endif
- }
-}
-/* }}} */
-
-/* {{{ apc_sma_cleanup */
-void apc_sma_cleanup()
-{
- int i;
-
- assert(sma_initialized);
-
- for (i = 0; i < sma_numseg; i++) {
- apc_lck_destroy(((header_t*)sma_shmaddrs[i])->sma_lock);
-#if APC_MMAP
- apc_unmap(sma_shmaddrs[i], sma_segments[i]);
-#else
- apc_shm_detach(sma_shmaddrs[i]);
-#endif
- }
- sma_initialized = 0;
- apc_efree(sma_segments);
- apc_efree(sma_shmaddrs);
-}
-/* }}} */
-
-/* {{{ apc_sma_malloc */
-void* apc_sma_malloc(size_t n)
-{
- size_t off;
- int i;
-
- TSRMLS_FETCH();
- assert(sma_initialized);
- LOCK(((header_t*)sma_shmaddrs[sma_lastseg])->sma_lock);
-
- off = sma_allocate(sma_shmaddrs[sma_lastseg], n);
- if (off != -1) {
- void* p = (void *)(((char *)(sma_shmaddrs[sma_lastseg])) + off);
- if (APCG(mem_size_ptr) != NULL) { *(APCG(mem_size_ptr)) += n; }
- UNLOCK(((header_t*)sma_shmaddrs[sma_lastseg])->sma_lock);
-#ifdef VALGRIND_MALLOCLIKE_BLOCK
- VALGRIND_MALLOCLIKE_BLOCK(p, n, 0, 0);
-#endif
- return p;
- }
- UNLOCK(((header_t*)sma_shmaddrs[sma_lastseg])->sma_lock);
-
- for (i = 0; i < sma_numseg; i++) {
- if (i == sma_lastseg) {
- continue;
- }
- LOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
- off = sma_allocate(sma_shmaddrs[i], n);
- if (off != -1) {
- void* p = (void *)(((char *)(sma_shmaddrs[i])) + off);
- if (APCG(mem_size_ptr) != NULL) { *(APCG(mem_size_ptr)) += n; }
- UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
- sma_lastseg = i;
-#ifdef VALGRIND_MALLOCLIKE_BLOCK
- VALGRIND_MALLOCLIKE_BLOCK(p, n, 0, 0);
-#endif
- return p;
- }
- UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
- }
-
- return NULL;
-}
-/* }}} */
-
-/* {{{ apc_sma_realloc */
-void* apc_sma_realloc(void *p, size_t n)
-{
- apc_sma_free(p);
- return apc_sma_malloc(n);
-}
-/* }}} */
-
-/* {{{ apc_sma_strdup */
-char* apc_sma_strdup(const char* s)
-{
- void* q;
- int len;
-
- if(!s) return NULL;
-
- len = strlen(s)+1;
- q = apc_sma_malloc(len);
- if(!q) return NULL;
- memcpy(q, s, len);
- return q;
-}
-/* }}} */
-
-/* {{{ apc_sma_free */
-void apc_sma_free(void* p)
-{
- int i;
- size_t offset;
- size_t d_size;
- TSRMLS_FETCH();
-
- if (p == NULL) {
- return;
- }
-
- assert(sma_initialized);
-
- for (i = 0; i < sma_numseg; i++) {
- LOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
- offset = (size_t)((char *)p - (char *)(sma_shmaddrs[i]));
- if (p >= sma_shmaddrs[i] && offset < sma_segsize) {
- d_size = sma_deallocate(sma_shmaddrs[i], offset);
- if (APCG(mem_size_ptr) != NULL) { *(APCG(mem_size_ptr)) -= d_size; }
- UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
-#ifdef VALGRIND_FREELIKE_BLOCK
- VALGRIND_FREELIKE_BLOCK(p, 0);
-#endif
- return;
- }
- UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
- }
-
- apc_eprint("apc_sma_free: could not locate address %p", p);
-}
-/* }}} */
-
-/* {{{ apc_sma_info */
-apc_sma_info_t* apc_sma_info(zend_bool limited)
-{
- apc_sma_info_t* info;
- apc_sma_link_t** link;
- int i;
- char* shmaddr;
- block_t* prv;
-
- if (!sma_initialized) {
- return NULL;
- }
-
- info = (apc_sma_info_t*) apc_emalloc(sizeof(apc_sma_info_t));
- info->num_seg = sma_numseg;
- info->seg_size = sma_segsize - ALIGNWORD(sizeof(header_t)) - ALIGNWORD(sizeof(block_t));
-
- info->list = apc_emalloc(info->num_seg * sizeof(apc_sma_link_t*));
- for (i = 0; i < sma_numseg; i++) {
- info->list[i] = NULL;
- }
-
- if(limited) return info;
-
- /* For each segment */
- for (i = 0; i < sma_numseg; i++) {
- RDLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
- shmaddr = sma_shmaddrs[i];
- prv = BLOCKAT(ALIGNWORD(sizeof(header_t)));
-
- link = &info->list[i];
-
- /* For each block in this segment */
- while (prv->next != 0) {
- block_t* cur = BLOCKAT(prv->next);
-#ifdef __APC_SMA_DEBUG__
- CHECK_CANARY(cur);
-#endif
-
- *link = apc_emalloc(sizeof(apc_sma_link_t));
- (*link)->size = cur->size;
- (*link)->offset = prv->next;
- (*link)->next = NULL;
- link = &(*link)->next;
-
- prv = cur;
- }
- UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
- }
-
- return info;
-}
-/* }}} */
-
-/* {{{ apc_sma_free_info */
-void apc_sma_free_info(apc_sma_info_t* info)
-{
- int i;
-
- for (i = 0; i < info->num_seg; i++) {
- apc_sma_link_t* p = info->list[i];
- while (p) {
- apc_sma_link_t* q = p;
- p = p->next;
- apc_efree(q);
- }
- }
- apc_efree(info->list);
- apc_efree(info);
-}
-/* }}} */
-
-/* {{{ apc_sma_get_avail_mem */
-size_t apc_sma_get_avail_mem()
-{
- size_t avail_mem = 0;
- int i;
-
- for (i = 0; i < sma_numseg; i++) {
- header_t* header = (header_t*) sma_shmaddrs[i];
- avail_mem += header->avail;
- }
- return avail_mem;
-}
-/* }}} */
-
-#if ALLOC_DISTRIBUTION
-size_t *apc_sma_get_alloc_distribution(void) {
- header_t* header = (header_t*) sma_shmaddrs[0];
- return header->adist;
-}
-#endif
-
-#if 0
-/* {{{ apc_sma_check_integrity */
-void apc_sma_check_integrity()
-{
- int i;
-
- /* For each segment */
- for (i = 0; i < sma_numseg; i++) {
- char* shmaddr = sma_shmaddrs[i];
- header_t* header = (header_t*) shmaddr;
- block_t* prv = BLOCKAT(ALIGNWORD(sizeof(header_t)));
- int avail = 0;
-
- /* For each block in this segment */
- while (prv->next != 0) {
- block_t* cur = BLOCKAT(prv->next);
- avail += cur->size;
- prv = cur;
- }
-
- assert(avail == header->avail);
- }
-}
-/* }}} */
-#endif
-
-/*
- * Local variables:
- * tab-width: 4
- * c-basic-offset: 4
- * End:
- * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
- * vim<600: expandtab sw=4 ts=4 sts=4
- */