diff options
Diffstat (limited to 'mysys/my_alloc.c')
-rw-r--r-- | mysys/my_alloc.c | 268 |
1 files changed, 193 insertions, 75 deletions
diff --git a/mysys/my_alloc.c b/mysys/my_alloc.c index 3e0b774b0c7..9f230b6bbcb 100644 --- a/mysys/my_alloc.c +++ b/mysys/my_alloc.c @@ -20,15 +20,101 @@ #include <my_global.h> #include <my_sys.h> #include <m_string.h> +#include <my_bit.h> +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif + #undef EXTRA_DEBUG #define EXTRA_DEBUG /* data packed in MEM_ROOT -> min_malloc */ -#define MALLOC_FLAG(A) ((A & 1) ? MY_THREAD_SPECIFIC : 0) +/* Don't allocate too small blocks */ +#define ROOT_MIN_BLOCK_SIZE 256 + +/* bits in MEM_ROOT->flags */ +#define ROOT_FLAG_THREAD_SPECIFIC 1 +#define ROOT_FLAG_MPROTECT 2 + +#define MALLOC_FLAG(R) MYF((R)->flags & ROOT_FLAG_THREAD_SPECIFIC ? THREAD_SPECIFIC : 0) #define TRASH_MEM(X) TRASH_FREE(((char*)(X) + ((X)->size-(X)->left)), (X)->left) + +/* + Alloc memory through either my_malloc or mmap() +*/ + +static void *root_alloc(MEM_ROOT *root, size_t size, size_t *alloced_size, + myf my_flags) +{ + *alloced_size= size; +#if defined(HAVE_MMAP) && defined(HAVE_MPROTECT) && defined(MAP_ANONYMOUS) + if (root->flags & ROOT_FLAG_MPROTECT) + { + void *res; + *alloced_size= MY_ALIGN(size, my_system_page_size); + res= my_mmap(0, *alloced_size, PROT_READ | PROT_WRITE, + MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (res == MAP_FAILED) + res= 0; + return res; + } +#endif /* HAVE_MMAP */ + + return my_malloc(root->psi_key, size, + my_flags | MYF(root->flags & ROOT_FLAG_THREAD_SPECIFIC ? + MY_THREAD_SPECIFIC : 0)); +} + +static void root_free(MEM_ROOT *root, void *ptr, size_t size) +{ +#if defined(HAVE_MMAP) && defined(HAVE_MPROTECT) && defined(MAP_ANONYMOUS) + if (root->flags & ROOT_FLAG_MPROTECT) + my_munmap(ptr, size); + else +#endif + my_free(ptr); +} + + +/* + Calculate block sizes to use + + Sizes will be updated to next power of 2, minus operating system + memory management size. + + The idea is to reduce memory fragmentation as most system memory + allocators are using power of 2 block size internally. +*/ + +static void calculate_block_sizes(MEM_ROOT *mem_root, size_t block_size, + size_t *pre_alloc_size) +{ + size_t pre_alloc= *pre_alloc_size; + + if (mem_root->flags&= ROOT_FLAG_MPROTECT) + { + mem_root->block_size= MY_ALIGN(block_size, my_system_page_size); + if (pre_alloc) + pre_alloc= MY_ALIGN(pre_alloc, my_system_page_size); + } + else + { + DBUG_ASSERT(block_size <= UINT_MAX32); + mem_root->block_size= (my_round_up_to_next_power((uint32) block_size - + MALLOC_OVERHEAD)- + MALLOC_OVERHEAD); + if (pre_alloc) + pre_alloc= (my_round_up_to_next_power((uint32) pre_alloc - + MALLOC_OVERHEAD)- + MALLOC_OVERHEAD); + } + *pre_alloc_size= pre_alloc; +} + + /* Initialize memory root @@ -36,13 +122,18 @@ init_alloc_root() mem_root - memory root to initialize name - name of memroot (for debugging) - block_size - size of chunks (blocks) used for memory allocation + block_size - size of chunks (blocks) used for memory allocation. + Will be updated to next power of 2, minus + internal and system memory management size. This is + will reduce memory fragmentation as most system memory + allocators are using power of 2 block size internally. (It is external size of chunk i.e. it should include memory required for internal structures, thus it - should be no less than ALLOC_ROOT_MIN_BLOCK_SIZE) + should be no less than ROOT_MIN_BLOCK_SIZE). pre_alloc_size - if non-0, then size of block that should be pre-allocated during memory root initialization. my_flags MY_THREAD_SPECIFIC flag for my_malloc + MY_RROOT_USE_MPROTECT for read only protected memory DESCRIPTION This function prepares memory root for further use, sets initial size of @@ -50,9 +141,6 @@ Although error can happen during execution of this function if pre_alloc_size is non-0 it won't be reported. Instead it will be reported as error in first alloc_root() on this memory root. - - We don't want to change the structure size for MEM_ROOT. - Because of this, we store in MY_THREAD_SPECIFIC as bit 1 in block_size */ void init_alloc_root(PSI_memory_key key, MEM_ROOT *mem_root, size_t block_size, @@ -63,25 +151,31 @@ void init_alloc_root(PSI_memory_key key, MEM_ROOT *mem_root, size_t block_size, DBUG_PRINT("enter",("root: %p prealloc: %zu", mem_root, pre_alloc_size)); mem_root->free= mem_root->used= mem_root->pre_alloc= 0; - mem_root->min_malloc= 32; - mem_root->block_size= (block_size - ALLOC_ROOT_MIN_BLOCK_SIZE) & ~1; + mem_root->min_malloc= 32 + REDZONE_SIZE; + mem_root->block_size= MY_MAX(block_size, ROOT_MIN_BLOCK_SIZE); + mem_root->flags= 0; if (my_flags & MY_THREAD_SPECIFIC) - mem_root->block_size|= 1; + mem_root->flags|= ROOT_FLAG_THREAD_SPECIFIC; + if (my_flags & MY_ROOT_USE_MPROTECT) + mem_root->flags|= ROOT_FLAG_MPROTECT; + + calculate_block_sizes(mem_root, block_size, &pre_alloc_size); mem_root->error_handler= 0; mem_root->block_num= 4; /* We shift this with >>2 */ mem_root->first_block_usage= 0; - mem_root->m_psi_key= key; + mem_root->psi_key= key; #if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG)) if (pre_alloc_size) { - size_t size= pre_alloc_size + ALIGN_SIZE(sizeof(USED_MEM)); + size_t alloced_size; if ((mem_root->free= mem_root->pre_alloc= - (USED_MEM*) my_malloc(key, size, MYF(my_flags)))) + (USED_MEM*) root_alloc(mem_root, pre_alloc_size, &alloced_size, + MYF(0)))) { - mem_root->free->size= size; - mem_root->free->left= pre_alloc_size; + mem_root->free->size= alloced_size; + mem_root->free->left= alloced_size - ALIGN_SIZE(sizeof(USED_MEM)); mem_root->free->next= 0; TRASH_MEM(mem_root->free); } @@ -113,13 +207,14 @@ void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size, DBUG_ENTER("reset_root_defaults"); DBUG_ASSERT(alloc_root_inited(mem_root)); - mem_root->block_size= (((block_size - ALLOC_ROOT_MIN_BLOCK_SIZE) & ~1) | - (mem_root->block_size & 1)); + calculate_block_sizes(mem_root, block_size, &pre_alloc_size); + #if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG)) if (pre_alloc_size) { - size_t size= pre_alloc_size + ALIGN_SIZE(sizeof(USED_MEM)); - if (!mem_root->pre_alloc || mem_root->pre_alloc->size != size) + size_t size= mem_root->block_size, alloced_size; + if (!mem_root->pre_alloc || + mem_root->pre_alloc->size != mem_root->block_size) { USED_MEM *mem, **prev= &mem_root->free; /* @@ -139,26 +234,23 @@ void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size, { /* remove block from the list and free it */ *prev= mem->next; - my_free(mem); + root_free(mem_root, mem, mem->size); } else prev= &mem->next; } /* Allocate new prealloc block and add it to the end of free list */ - if ((mem= (USED_MEM *) my_malloc(mem_root->m_psi_key, size, - MYF(MALLOC_FLAG(mem_root-> - block_size))))) + if ((mem= (USED_MEM *) root_alloc(mem_root, size, &alloced_size, + MYF(MY_WME)))) { - mem->size= size; - mem->left= pre_alloc_size; + mem->size= alloced_size; + mem->left= alloced_size - ALIGN_SIZE(sizeof(USED_MEM)); mem->next= *prev; *prev= mem_root->pre_alloc= mem; TRASH_MEM(mem); } else - { mem_root->pre_alloc= 0; - } } } else @@ -171,37 +263,6 @@ void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size, void *alloc_root(MEM_ROOT *mem_root, size_t length) { -#if defined(HAVE_valgrind) && defined(EXTRA_DEBUG) - reg1 USED_MEM *next; - DBUG_ENTER("alloc_root"); - DBUG_PRINT("enter",("root: %p", mem_root)); - - DBUG_ASSERT(alloc_root_inited(mem_root)); - - DBUG_EXECUTE_IF("simulate_out_of_memory", - { - if (mem_root->error_handler) - (*mem_root->error_handler)(); - DBUG_SET("-d,simulate_out_of_memory"); - DBUG_RETURN((void*) 0); /* purecov: inspected */ - }); - - length+=ALIGN_SIZE(sizeof(USED_MEM)); - if (!(next = (USED_MEM*) my_malloc(mem_root->m_psi_key, length, - MYF(MY_WME | ME_FATAL | - MALLOC_FLAG(mem_root->block_size))))) - { - if (mem_root->error_handler) - (*mem_root->error_handler)(); - DBUG_RETURN((uchar*) 0); /* purecov: inspected */ - } - next->next= mem_root->used; - next->left= 0; - next->size= length; - mem_root->used= next; - DBUG_PRINT("exit",("ptr: %p", (((char*)next)+ALIGN_SIZE(sizeof(USED_MEM))))); - DBUG_RETURN((((uchar*) next)+ALIGN_SIZE(sizeof(USED_MEM)))); -#else size_t get_size, block_size; uchar* point; reg1 USED_MEM *next= 0; @@ -212,13 +273,36 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length) DBUG_ASSERT(alloc_root_inited(mem_root)); DBUG_EXECUTE_IF("simulate_out_of_memory", - { - /* Avoid reusing an already allocated block */ - if (mem_root->error_handler) - (*mem_root->error_handler)(); - DBUG_SET("-d,simulate_out_of_memory"); - DBUG_RETURN((void*) 0); /* purecov: inspected */ - }); + { + if (mem_root->error_handler) + (*mem_root->error_handler)(); + DBUG_SET("-d,simulate_out_of_memory"); + DBUG_RETURN((void*) 0); /* purecov: inspected */ + }); + +#if defined(HAVE_valgrind) && defined(EXTRA_DEBUG) + if (!(mem_root->flags & ROOT_FLAG_MPROTECT)) + { + length+= ALIGN_SIZE(sizeof(USED_MEM)); + if (!(next = (USED_MEM*) my_malloc(mem_root->psi_key, length, + MYF(MY_WME | ME_FATAL | + (mem_root->flags & + ROOT_FLAG_THREAD_SPECIFIC ? + MY_THREAD_SPECIFIC : 0))))) + { + if (mem_root->error_handler) + (*mem_root->error_handler)(); + DBUG_RETURN((uchar*) 0); /* purecov: inspected */ + } + next->next= mem_root->used; + next->left= 0; + next->size= length; + mem_root->used= next; + DBUG_PRINT("exit",("ptr: %p", (((char*)next)+ALIGN_SIZE(sizeof(USED_MEM))))); + DBUG_RETURN((((uchar*) next)+ALIGN_SIZE(sizeof(USED_MEM)))); + } +#endif /* defined(HAVE_valgrind) && defined(EXTRA_DEBUG) */ + length= ALIGN_SIZE(length) + REDZONE_SIZE; if ((*(prev= &mem_root->free)) != NULL) { @@ -237,14 +321,16 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length) } if (! next) { /* Time to alloc new block */ - block_size= (mem_root->block_size & ~1) * (mem_root->block_num >> 2); - get_size= length+ALIGN_SIZE(sizeof(USED_MEM)); + size_t alloced_length; + + /* Increase block size over time if there is a lot of mallocs */ + block_size= (MY_ALIGN(mem_root->block_size, ROOT_MIN_BLOCK_SIZE) * + (mem_root->block_num >> 2)- MALLOC_OVERHEAD); + get_size= length + ALIGN_SIZE(sizeof(USED_MEM)); get_size= MY_MAX(get_size, block_size); - if (!(next = (USED_MEM*) my_malloc(mem_root->m_psi_key, get_size, - MYF(MY_WME | ME_FATAL | - MALLOC_FLAG(mem_root-> - block_size))))) + if (!(next= (USED_MEM*) root_alloc(mem_root, get_size, &alloced_length, + MYF(MY_WME | ME_FATAL)))) { if (mem_root->error_handler) (*mem_root->error_handler)(); @@ -252,8 +338,8 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length) } mem_root->block_num++; next->next= *prev; - next->size= get_size; - next->left= get_size-ALIGN_SIZE(sizeof(USED_MEM)); + next->size= alloced_length; + next->left= alloced_length - ALIGN_SIZE(sizeof(USED_MEM)); *prev=next; TRASH_MEM(next); } @@ -271,7 +357,6 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length) TRASH_ALLOC(point, original_length); DBUG_PRINT("exit",("ptr: %p", point)); DBUG_RETURN((void*) point); -#endif } @@ -407,13 +492,13 @@ void free_root(MEM_ROOT *root, myf MyFlags) { old=next; next= next->next ; if (old != root->pre_alloc) - my_free(old); + root_free(root, old, old->size); } for (next=root->free ; next ;) { old=next; next= next->next; if (old != root->pre_alloc) - my_free(old); + root_free(root, old, old->size); } root->used=root->free=0; if (root->pre_alloc) @@ -428,6 +513,7 @@ void free_root(MEM_ROOT *root, myf MyFlags) DBUG_VOID_RETURN; } + /* Find block that contains an object and set the pre_alloc to it */ @@ -454,6 +540,38 @@ void set_prealloc_root(MEM_ROOT *root, char *ptr) } +/** + Change protection for all blocks in the mem root +*/ + +#if defined(HAVE_MMAP) && defined(HAVE_MPROTECT) && defined(MAP_ANONYMOUS) +void protect_root(MEM_ROOT *root, int prot) +{ + reg1 USED_MEM *next,*old; + DBUG_ENTER("protect_root"); + DBUG_PRINT("enter",("root: %p prot: %d", root, prot)); + + DBUG_ASSERT(root->flags & ROOT_FLAG_MPROTECT); + + for (next= root->used; next ;) + { + old= next; next= next->next ; + mprotect(old, old->size, prot); + } + for (next= root->free; next ;) + { + old= next; next= next->next ; + mprotect(old, old->size, prot); + } + DBUG_VOID_RETURN; +} +#else +void protect_root(MEM_ROOT *root, int prot) +{ +} +#endif /* defined(HAVE_MMAP) && ... */ + + char *strdup_root(MEM_ROOT *root, const char *str) { return strmake_root(root, str, strlen(str)); |