/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
#include "lint.h"
#include "thr_uberdata.h"
extern long __systemcall6(sysret_t *, int, ...);
/*
* This is a small and simple power of two memory allocator that is
* used internally by libc. Allocations are fast and memory is never
* returned to the system, except for allocations of 64 Kbytes and larger,
* which are simply mmap()ed and munmap()ed as needed. Smaller allocations
* (minimum size is 64 bytes) are obtained from mmap() of 64K chunks
* broken up into unit allocations and maintained on free lists.
* The interface requires the caller to keep track of the size of an
* allocated block and to pass that size back when freeing a block.
*
* This allocator is called during initialization, from code called
* from the dynamic linker, so it must not call anything that might
* re-invoke the dynamic linker to resolve a symbol. That is,
* it must only call functions that are wholly private to libc.
*
* Also, this allocator must be unique across all link maps
* because pointers returned by lmalloc() are stored in the
* thread structure, which is constant across all link maps.
*
* Memory blocks returned by lmalloc() are initialized to zero.
*/
/*
* bucketnum allocation size
* 0 64
* 1 128
* 2 256
* 3 512
* 4 1024
* 5 2048
* 6 4096
* 7 8192
* 8 16384
* 9 32768
*/
/*
* See "thr_uberdata.h" for the definition of bucket_t.
* The 10 (NBUCKETS) buckets are allocated in uberdata.
*/
/*
* Performance hack:
*
* On the very first lmalloc(), before any memory has been allocated,
* mmap() a 24K block of memory and carve out six 2K chunks, each
* of which is subdivided for the initial allocations from buckets
* 0, 1, 2, 3, 4 and 5, giving them initial numbers of elements
* 32, 16, 8, 4, 2 and 1, respectively. The remaining 12K is cut
* into one 4K buffer for bucket 6 and one 8K buffer for bucket 7.
*
* This results in almost all simple single-threaded processes,
* such as those employed in the kenbus test suite, having to
* allocate only this one 24K block during their lifetimes.
*/
static void
{
void *ptr;
size_t n;
int bucketnum;
void *base;
/*
* We do this seemingly obtuse call to __systemcall6(SYS_mmap)
* instead of simply calling mmap() directly because, if the
* mmap() system call fails, we must make sure that __cerror()
* is not called, because that would call ___errno()
* which would dereference curthread and, because we are very
* early in libc initialization, curthread is NULL and we would
* draw a hard-to-debug SIGSEGV core dump, or worse.
* We opt to give a thread panic message instead.
*/
thr_panic("initial allocation failed; swap space exhausted?");
n = SUBCHUNKSIZE / size;
while (--n != 0) {
}
}
bp++;
}
/*
* This highbit code is the same as the code in fls_impl().
* We inline it here for speed.
*/
static int
{
return (0);
#ifdef _LP64
if (size & 0xffffffff00000000ul)
#endif
if (size & 0xffff0000)
if (size & 0xff00)
if (size & 0xf0)
if (size & 0xc)
if (size & 0x2)
highbit += 1;
}
void *
{
void *ptr;
/*
* ulwp_t structures must be allocated from a rwx mapping since it
* is a normal data object _and_ it contains instructions that are
* executed for user-land DTrace tracing with the fasttrap provider.
*/
/* round size up to the proper power of 2 */
/* mmap() allocates memory already set to zero */
if (ptr == MAP_FAILED)
return (ptr);
}
udp = &__uberdata;
else
if (udp->bucket_init == 0) {
}
size_t n;
/*
* Double the number of chunks mmap()ed each time,
* in case of large numbers of allocations.
*/
else
for (;;) {
if (ptr != MAP_FAILED)
break;
/* try a smaller chunk allocation */
return (NULL);
}
}
while (--n != 0) {
}
}
/*
* We maintain the free list already zeroed except for the pointer
* stored at the head of the block (mmap() allocates memory already
* set to zero), so all we have to do is zero out the pointer.
*/
return (ptr);
}
void
{
/* round size up to the proper power of 2 */
/* see comment below */
goto bad;
return;
}
/*
* If the low order bits are not all zero as expected, then panic.
* This can be caused by an application calling, for example,
* pthread_attr_destroy() without having first called
* pthread_attr_init() (thereby passing uninitialized data
* to pthread_attr_destroy() who then calls lfree() with
* the uninitialized data).
*/
goto bad;
/*
* Zeroing the memory here saves time later when reallocating it.
*/
else {
}
return;
bad:
thr_panic("lfree() called with a misaligned pointer");
}
/*
* The following functions can be used internally to libc
* to make memory allocations in the style of malloc()/free()
* (where the size of the allocation is not remembered by the caller)
* but which are safe to use within critical sections, that is,
* sections of code bounded by enter_critical()/exit_critical(),
* lmutex_lock()/lmutex_unlock() or lrw_rdlock()/lrw_wrlock()/lrw_unlock().
*
* These functions must never be used to allocate memory that is
* passed out of libc, for example by strdup(), because it is a
* fatal error to free() an object allocated by libc_malloc().
* Such objects can only be freed by calling libc_free().
*/
#ifdef _LP64
#else
#endif
typedef union {
void *
{
return (NULL);
return (ptr + 1);
}
void *
{
void *new;
return (NULL);
}
return (new);
}
void
libc_free(void *p)
{
if (p) {
}
}
char *
{
if (s2)
return (s2);
}