10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
53866Sraf * Common Development and Distribution License (the "License").
63866Sraf * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
213866Sraf
223866Sraf /*
23*6812Sraf * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
243866Sraf * Use is subject to license terms.
253866Sraf */
263866Sraf
270Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
280Sstevel@tonic-gate /* All Rights Reserved */
290Sstevel@tonic-gate
30*6812Sraf #pragma ident "%Z%%M% %I% %E% SMI"
310Sstevel@tonic-gate
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate
340Sstevel@tonic-gate
350Sstevel@tonic-gate /*
360Sstevel@tonic-gate * Simplified version of malloc(), free() and realloc(), to be linked with
370Sstevel@tonic-gate * utilities that use [s]brk() and do not define their own version of the
380Sstevel@tonic-gate * routines.
390Sstevel@tonic-gate *
400Sstevel@tonic-gate * The algorithm used to get extra memory space by mmap'ing /dev/zero. This
410Sstevel@tonic-gate * breaks if the application closes the open descriptor, so now it uses
420Sstevel@tonic-gate * mmap's MAP_ANON feature.
430Sstevel@tonic-gate *
440Sstevel@tonic-gate * Each call to mmap() creates a page. The pages are linked in a list.
450Sstevel@tonic-gate * Each page is divided in blocks. There is at least one block in a page.
460Sstevel@tonic-gate * New memory chunks are allocated on a first-fit basis.
470Sstevel@tonic-gate * Freed blocks are joined in larger blocks. Free pages are unmapped.
480Sstevel@tonic-gate */
490Sstevel@tonic-gate #include <stdlib.h>
500Sstevel@tonic-gate #include <sys/types.h>
510Sstevel@tonic-gate #include <sys/mman.h>
520Sstevel@tonic-gate #include <fcntl.h>
530Sstevel@tonic-gate #include <errno.h>
540Sstevel@tonic-gate #include <unistd.h>
550Sstevel@tonic-gate #include <thread.h>
563866Sraf #include <pthread.h>
570Sstevel@tonic-gate #include <synch.h>
580Sstevel@tonic-gate #include <string.h>
590Sstevel@tonic-gate
600Sstevel@tonic-gate static mutex_t lock = DEFAULTMUTEX;
610Sstevel@tonic-gate
620Sstevel@tonic-gate struct block {
630Sstevel@tonic-gate size_t size; /* Space available for user */
640Sstevel@tonic-gate struct page *page; /* Backwards reference to page */
650Sstevel@tonic-gate int status;
660Sstevel@tonic-gate struct block *next;
670Sstevel@tonic-gate void *memstart[1];
680Sstevel@tonic-gate };
690Sstevel@tonic-gate
700Sstevel@tonic-gate struct page {
710Sstevel@tonic-gate size_t size; /* Total page size (incl. header) */
720Sstevel@tonic-gate struct page *next;
730Sstevel@tonic-gate struct block block[1];
740Sstevel@tonic-gate };
750Sstevel@tonic-gate
760Sstevel@tonic-gate #define FREE 0
770Sstevel@tonic-gate #define BUSY 1
780Sstevel@tonic-gate
790Sstevel@tonic-gate #define HDR_BLOCK (sizeof (struct block) - sizeof (void *))
800Sstevel@tonic-gate #define HDR_PAGE (sizeof (struct page) - sizeof (void *))
810Sstevel@tonic-gate #define MINSZ sizeof (double)
820Sstevel@tonic-gate
830Sstevel@tonic-gate /* for convenience */
840Sstevel@tonic-gate #ifndef NULL
850Sstevel@tonic-gate #define NULL (0)
860Sstevel@tonic-gate #endif
870Sstevel@tonic-gate
880Sstevel@tonic-gate struct page *memstart;
890Sstevel@tonic-gate static int pagesize;
900Sstevel@tonic-gate static void defrag(struct page *);
910Sstevel@tonic-gate static void split(struct block *, size_t);
920Sstevel@tonic-gate static void *malloc_unlocked(size_t);
930Sstevel@tonic-gate static size_t align(size_t, int);
940Sstevel@tonic-gate
950Sstevel@tonic-gate void *
malloc(size_t size)960Sstevel@tonic-gate malloc(size_t size)
970Sstevel@tonic-gate {
980Sstevel@tonic-gate void *retval;
990Sstevel@tonic-gate (void) mutex_lock(&lock);
1000Sstevel@tonic-gate retval = malloc_unlocked(size);
1010Sstevel@tonic-gate (void) mutex_unlock(&lock);
1020Sstevel@tonic-gate return (retval);
1030Sstevel@tonic-gate }
1040Sstevel@tonic-gate
1050Sstevel@tonic-gate
1060Sstevel@tonic-gate static void *
malloc_unlocked(size_t size)1070Sstevel@tonic-gate malloc_unlocked(size_t size)
1080Sstevel@tonic-gate {
1090Sstevel@tonic-gate struct block *block;
1100Sstevel@tonic-gate struct page *page;
1110Sstevel@tonic-gate
1120Sstevel@tonic-gate if (pagesize == 0)
1130Sstevel@tonic-gate pagesize = (int)sysconf(_SC_PAGESIZE);
1140Sstevel@tonic-gate
1150Sstevel@tonic-gate size = align(size, MINSZ);
1160Sstevel@tonic-gate
1170Sstevel@tonic-gate /*
1180Sstevel@tonic-gate * Try to locate necessary space
1190Sstevel@tonic-gate */
1200Sstevel@tonic-gate for (page = memstart; page; page = page->next) {
1210Sstevel@tonic-gate for (block = page->block; block; block = block->next) {
1220Sstevel@tonic-gate if (block->status == FREE && block->size >= size)
1230Sstevel@tonic-gate goto found;
1240Sstevel@tonic-gate }
1250Sstevel@tonic-gate }
1260Sstevel@tonic-gate found:
1270Sstevel@tonic-gate
1280Sstevel@tonic-gate /*
1290Sstevel@tonic-gate * Need to allocate a new page
1300Sstevel@tonic-gate */
1310Sstevel@tonic-gate if (!page) {
1320Sstevel@tonic-gate size_t totsize = size + HDR_PAGE;
1330Sstevel@tonic-gate size_t totpage = align(totsize, pagesize);
1340Sstevel@tonic-gate
1350Sstevel@tonic-gate if ((page = (struct page *)mmap(0, totpage,
1360Sstevel@tonic-gate PROT_READ|PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0))
137*6812Sraf == MAP_FAILED)
1380Sstevel@tonic-gate return (0);
1390Sstevel@tonic-gate
1400Sstevel@tonic-gate page->next = memstart;
1410Sstevel@tonic-gate memstart = page;
1420Sstevel@tonic-gate page->size = totpage;
1430Sstevel@tonic-gate block = page->block;
1440Sstevel@tonic-gate block->next = 0;
1450Sstevel@tonic-gate block->status = FREE;
1460Sstevel@tonic-gate block->size = totpage - HDR_PAGE;
1470Sstevel@tonic-gate block->page = page;
1480Sstevel@tonic-gate }
1490Sstevel@tonic-gate
1500Sstevel@tonic-gate split(block, size);
1510Sstevel@tonic-gate
1520Sstevel@tonic-gate block->status = BUSY;
1530Sstevel@tonic-gate return (&block->memstart);
1540Sstevel@tonic-gate }
1550Sstevel@tonic-gate
1560Sstevel@tonic-gate void *
realloc(void * ptr,size_t size)1570Sstevel@tonic-gate realloc(void *ptr, size_t size)
1580Sstevel@tonic-gate {
1590Sstevel@tonic-gate struct block *block;
1600Sstevel@tonic-gate size_t osize;
1610Sstevel@tonic-gate void *newptr;
1620Sstevel@tonic-gate
1630Sstevel@tonic-gate (void) mutex_lock(&lock);
1640Sstevel@tonic-gate if (ptr == NULL) {
1650Sstevel@tonic-gate newptr = malloc_unlocked(size);
1660Sstevel@tonic-gate (void) mutex_unlock(&lock);
1670Sstevel@tonic-gate return (newptr);
1680Sstevel@tonic-gate }
1690Sstevel@tonic-gate block = (struct block *)((char *)ptr - HDR_BLOCK);
1700Sstevel@tonic-gate size = align(size, MINSZ);
1710Sstevel@tonic-gate osize = block->size;
1720Sstevel@tonic-gate
1730Sstevel@tonic-gate /*
1740Sstevel@tonic-gate * Join block with next one if it is free
1750Sstevel@tonic-gate */
1760Sstevel@tonic-gate if (block->next && block->next->status == FREE) {
1770Sstevel@tonic-gate block->size += block->next->size + HDR_BLOCK;
1780Sstevel@tonic-gate block->next = block->next->next;
1790Sstevel@tonic-gate }
1800Sstevel@tonic-gate
1810Sstevel@tonic-gate if (size <= block->size) {
1820Sstevel@tonic-gate split(block, size);
1830Sstevel@tonic-gate (void) mutex_unlock(&lock);
1840Sstevel@tonic-gate return (ptr);
1850Sstevel@tonic-gate }
1860Sstevel@tonic-gate
1870Sstevel@tonic-gate newptr = malloc_unlocked(size);
1880Sstevel@tonic-gate (void) memcpy(newptr, ptr, osize);
1890Sstevel@tonic-gate block->status = FREE;
1900Sstevel@tonic-gate defrag(block->page);
1910Sstevel@tonic-gate (void) mutex_unlock(&lock);
1920Sstevel@tonic-gate return (newptr);
1930Sstevel@tonic-gate }
1940Sstevel@tonic-gate
1950Sstevel@tonic-gate void
free(void * ptr)1960Sstevel@tonic-gate free(void *ptr)
1970Sstevel@tonic-gate {
1980Sstevel@tonic-gate struct block *block;
1990Sstevel@tonic-gate
2000Sstevel@tonic-gate (void) mutex_lock(&lock);
2010Sstevel@tonic-gate if (ptr == NULL) {
2020Sstevel@tonic-gate (void) mutex_unlock(&lock);
2030Sstevel@tonic-gate return;
2040Sstevel@tonic-gate }
2050Sstevel@tonic-gate block = (struct block *)((char *)ptr - HDR_BLOCK);
2060Sstevel@tonic-gate block->status = FREE;
2070Sstevel@tonic-gate
2080Sstevel@tonic-gate defrag(block->page);
2090Sstevel@tonic-gate (void) mutex_unlock(&lock);
2100Sstevel@tonic-gate }
2110Sstevel@tonic-gate
2120Sstevel@tonic-gate /*
2130Sstevel@tonic-gate * Align size on an appropriate boundary
2140Sstevel@tonic-gate */
2150Sstevel@tonic-gate static size_t
align(size_t size,int bound)2160Sstevel@tonic-gate align(size_t size, int bound)
2170Sstevel@tonic-gate {
2180Sstevel@tonic-gate if (size < bound)
2190Sstevel@tonic-gate return ((size_t)bound);
2200Sstevel@tonic-gate else
2210Sstevel@tonic-gate return (size + bound - 1 - (size + bound - 1) % bound);
2220Sstevel@tonic-gate }
2230Sstevel@tonic-gate
2240Sstevel@tonic-gate static void
split(struct block * block,size_t size)2250Sstevel@tonic-gate split(struct block *block, size_t size)
2260Sstevel@tonic-gate {
2270Sstevel@tonic-gate if (block->size > size + sizeof (struct block)) {
2280Sstevel@tonic-gate struct block *newblock;
2290Sstevel@tonic-gate newblock = (struct block *)((char *)block + HDR_BLOCK + size);
2300Sstevel@tonic-gate newblock->next = block->next;
2310Sstevel@tonic-gate block->next = newblock;
2320Sstevel@tonic-gate newblock->status = FREE;
2330Sstevel@tonic-gate newblock->page = block->page;
2340Sstevel@tonic-gate newblock->size = block->size - size - HDR_BLOCK;
2350Sstevel@tonic-gate block->size = size;
2360Sstevel@tonic-gate }
2370Sstevel@tonic-gate }
2380Sstevel@tonic-gate
2390Sstevel@tonic-gate /*
2400Sstevel@tonic-gate * Defragmentation
2410Sstevel@tonic-gate */
2420Sstevel@tonic-gate static void
defrag(struct page * page)2430Sstevel@tonic-gate defrag(struct page *page)
2440Sstevel@tonic-gate {
2450Sstevel@tonic-gate struct block *block;
2460Sstevel@tonic-gate
2470Sstevel@tonic-gate for (block = page->block; block; block = block->next) {
2480Sstevel@tonic-gate struct block *block2;
2490Sstevel@tonic-gate
2500Sstevel@tonic-gate if (block->status == BUSY)
2510Sstevel@tonic-gate continue;
2520Sstevel@tonic-gate for (block2 = block->next; block2 && block2->status == FREE;
253*6812Sraf block2 = block2->next) {
2540Sstevel@tonic-gate block->next = block2->next;
2550Sstevel@tonic-gate block->size += block2->size + HDR_BLOCK;
2560Sstevel@tonic-gate }
2570Sstevel@tonic-gate }
2580Sstevel@tonic-gate
2590Sstevel@tonic-gate /*
2600Sstevel@tonic-gate * Free page
2610Sstevel@tonic-gate */
2620Sstevel@tonic-gate if (page->block->size == page->size - HDR_PAGE) {
2630Sstevel@tonic-gate if (page == memstart)
2640Sstevel@tonic-gate memstart = page->next;
2650Sstevel@tonic-gate else {
2660Sstevel@tonic-gate struct page *page2;
2670Sstevel@tonic-gate for (page2 = memstart; page2->next;
268*6812Sraf page2 = page2->next) {
2690Sstevel@tonic-gate if (page2->next == page) {
2700Sstevel@tonic-gate page2->next = page->next;
2710Sstevel@tonic-gate break;
2720Sstevel@tonic-gate }
2730Sstevel@tonic-gate }
2740Sstevel@tonic-gate }
2750Sstevel@tonic-gate (void) munmap((caddr_t)page, page->size);
2760Sstevel@tonic-gate }
2770Sstevel@tonic-gate }
2783866Sraf
2793866Sraf static void
malloc_prepare()2803866Sraf malloc_prepare()
2813866Sraf {
2823866Sraf (void) mutex_lock(&lock);
2833866Sraf }
2843866Sraf
2853866Sraf static void
malloc_release()2863866Sraf malloc_release()
2873866Sraf {
2883866Sraf (void) mutex_unlock(&lock);
2893866Sraf }
2903866Sraf
2913866Sraf #pragma init(malloc_init)
2923866Sraf static void
malloc_init(void)2933866Sraf malloc_init(void)
2943866Sraf {
2953866Sraf (void) pthread_atfork(malloc_prepare, malloc_release, malloc_release);
2963866Sraf }
297