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 5*3866Sraf * Common Development and Distribution License (the "License"). 6*3866Sraf * 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 */ 21*3866Sraf 22*3866Sraf /* 23*3866Sraf * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24*3866Sraf * Use is subject to license terms. 25*3866Sraf */ 26*3866Sraf 270Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 280Sstevel@tonic-gate /* All Rights Reserved */ 290Sstevel@tonic-gate 300Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" /* SVR4/MNLS 1.1.2.1 */ 310Sstevel@tonic-gate 320Sstevel@tonic-gate /*LINTLIBRARY*/ 330Sstevel@tonic-gate 340Sstevel@tonic-gate #include <sys/types.h> 350Sstevel@tonic-gate 360Sstevel@tonic-gate 370Sstevel@tonic-gate /* 380Sstevel@tonic-gate * Simplified version of malloc(), free() and realloc(), to be linked with 390Sstevel@tonic-gate * utilities that use [s]brk() and do not define their own version of the 400Sstevel@tonic-gate * routines. 410Sstevel@tonic-gate * 420Sstevel@tonic-gate * The algorithm used to get extra memory space by mmap'ing /dev/zero. This 430Sstevel@tonic-gate * breaks if the application closes the open descriptor, so now it uses 440Sstevel@tonic-gate * mmap's MAP_ANON feature. 450Sstevel@tonic-gate * 460Sstevel@tonic-gate * Each call to mmap() creates a page. The pages are linked in a list. 470Sstevel@tonic-gate * Each page is divided in blocks. There is at least one block in a page. 480Sstevel@tonic-gate * New memory chunks are allocated on a first-fit basis. 490Sstevel@tonic-gate * Freed blocks are joined in larger blocks. Free pages are unmapped. 500Sstevel@tonic-gate */ 51*3866Sraf #include <c_synonyms.h> 520Sstevel@tonic-gate #include <stdlib.h> 530Sstevel@tonic-gate #include <sys/types.h> 540Sstevel@tonic-gate #include <sys/mman.h> 550Sstevel@tonic-gate #include <fcntl.h> 560Sstevel@tonic-gate #include <errno.h> 570Sstevel@tonic-gate #include <unistd.h> 580Sstevel@tonic-gate #include <thread.h> 59*3866Sraf #include <pthread.h> 600Sstevel@tonic-gate #include <synch.h> 610Sstevel@tonic-gate #include <string.h> 620Sstevel@tonic-gate 630Sstevel@tonic-gate static mutex_t lock = DEFAULTMUTEX; 640Sstevel@tonic-gate 650Sstevel@tonic-gate struct block { 660Sstevel@tonic-gate size_t size; /* Space available for user */ 670Sstevel@tonic-gate struct page *page; /* Backwards reference to page */ 680Sstevel@tonic-gate int status; 690Sstevel@tonic-gate struct block *next; 700Sstevel@tonic-gate void *memstart[1]; 710Sstevel@tonic-gate }; 720Sstevel@tonic-gate 730Sstevel@tonic-gate struct page { 740Sstevel@tonic-gate size_t size; /* Total page size (incl. header) */ 750Sstevel@tonic-gate struct page *next; 760Sstevel@tonic-gate struct block block[1]; 770Sstevel@tonic-gate }; 780Sstevel@tonic-gate 790Sstevel@tonic-gate #define FREE 0 800Sstevel@tonic-gate #define BUSY 1 810Sstevel@tonic-gate 820Sstevel@tonic-gate #define HDR_BLOCK (sizeof (struct block) - sizeof (void *)) 830Sstevel@tonic-gate #define HDR_PAGE (sizeof (struct page) - sizeof (void *)) 840Sstevel@tonic-gate #define MINSZ sizeof (double) 850Sstevel@tonic-gate 860Sstevel@tonic-gate /* for convenience */ 870Sstevel@tonic-gate #ifndef NULL 880Sstevel@tonic-gate #define NULL (0) 890Sstevel@tonic-gate #endif 900Sstevel@tonic-gate 910Sstevel@tonic-gate struct page *memstart; 920Sstevel@tonic-gate static int pagesize; 930Sstevel@tonic-gate static void defrag(struct page *); 940Sstevel@tonic-gate static void split(struct block *, size_t); 950Sstevel@tonic-gate static void *malloc_unlocked(size_t); 960Sstevel@tonic-gate static size_t align(size_t, int); 970Sstevel@tonic-gate 980Sstevel@tonic-gate void * 990Sstevel@tonic-gate malloc(size_t size) 1000Sstevel@tonic-gate { 1010Sstevel@tonic-gate void *retval; 1020Sstevel@tonic-gate (void) mutex_lock(&lock); 1030Sstevel@tonic-gate retval = malloc_unlocked(size); 1040Sstevel@tonic-gate (void) mutex_unlock(&lock); 1050Sstevel@tonic-gate return (retval); 1060Sstevel@tonic-gate } 1070Sstevel@tonic-gate 1080Sstevel@tonic-gate 1090Sstevel@tonic-gate static void * 1100Sstevel@tonic-gate malloc_unlocked(size_t size) 1110Sstevel@tonic-gate { 1120Sstevel@tonic-gate struct block *block; 1130Sstevel@tonic-gate struct page *page; 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate if (pagesize == 0) 1160Sstevel@tonic-gate pagesize = (int)sysconf(_SC_PAGESIZE); 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate size = align(size, MINSZ); 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate /* 1210Sstevel@tonic-gate * Try to locate necessary space 1220Sstevel@tonic-gate */ 1230Sstevel@tonic-gate for (page = memstart; page; page = page->next) { 1240Sstevel@tonic-gate for (block = page->block; block; block = block->next) { 1250Sstevel@tonic-gate if (block->status == FREE && block->size >= size) 1260Sstevel@tonic-gate goto found; 1270Sstevel@tonic-gate } 1280Sstevel@tonic-gate } 1290Sstevel@tonic-gate found: 1300Sstevel@tonic-gate 1310Sstevel@tonic-gate /* 1320Sstevel@tonic-gate * Need to allocate a new page 1330Sstevel@tonic-gate */ 1340Sstevel@tonic-gate if (!page) { 1350Sstevel@tonic-gate size_t totsize = size + HDR_PAGE; 1360Sstevel@tonic-gate size_t totpage = align(totsize, pagesize); 1370Sstevel@tonic-gate 1380Sstevel@tonic-gate if ((page = (struct page *)mmap(0, totpage, 1390Sstevel@tonic-gate PROT_READ|PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0)) 1400Sstevel@tonic-gate == MAP_FAILED) 1410Sstevel@tonic-gate return (0); 1420Sstevel@tonic-gate 1430Sstevel@tonic-gate page->next = memstart; 1440Sstevel@tonic-gate memstart = page; 1450Sstevel@tonic-gate page->size = totpage; 1460Sstevel@tonic-gate block = page->block; 1470Sstevel@tonic-gate block->next = 0; 1480Sstevel@tonic-gate block->status = FREE; 1490Sstevel@tonic-gate block->size = totpage - HDR_PAGE; 1500Sstevel@tonic-gate block->page = page; 1510Sstevel@tonic-gate } 1520Sstevel@tonic-gate 1530Sstevel@tonic-gate split(block, size); 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate block->status = BUSY; 1560Sstevel@tonic-gate return (&block->memstart); 1570Sstevel@tonic-gate } 1580Sstevel@tonic-gate 1590Sstevel@tonic-gate void * 1600Sstevel@tonic-gate realloc(void *ptr, size_t size) 1610Sstevel@tonic-gate { 1620Sstevel@tonic-gate struct block *block; 1630Sstevel@tonic-gate size_t osize; 1640Sstevel@tonic-gate void *newptr; 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate (void) mutex_lock(&lock); 1670Sstevel@tonic-gate if (ptr == NULL) { 1680Sstevel@tonic-gate newptr = malloc_unlocked(size); 1690Sstevel@tonic-gate (void) mutex_unlock(&lock); 1700Sstevel@tonic-gate return (newptr); 1710Sstevel@tonic-gate } 1720Sstevel@tonic-gate block = (struct block *)((char *)ptr - HDR_BLOCK); 1730Sstevel@tonic-gate size = align(size, MINSZ); 1740Sstevel@tonic-gate osize = block->size; 1750Sstevel@tonic-gate 1760Sstevel@tonic-gate /* 1770Sstevel@tonic-gate * Join block with next one if it is free 1780Sstevel@tonic-gate */ 1790Sstevel@tonic-gate if (block->next && block->next->status == FREE) { 1800Sstevel@tonic-gate block->size += block->next->size + HDR_BLOCK; 1810Sstevel@tonic-gate block->next = block->next->next; 1820Sstevel@tonic-gate } 1830Sstevel@tonic-gate 1840Sstevel@tonic-gate if (size <= block->size) { 1850Sstevel@tonic-gate split(block, size); 1860Sstevel@tonic-gate (void) mutex_unlock(&lock); 1870Sstevel@tonic-gate return (ptr); 1880Sstevel@tonic-gate } 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate newptr = malloc_unlocked(size); 1910Sstevel@tonic-gate (void) memcpy(newptr, ptr, osize); 1920Sstevel@tonic-gate block->status = FREE; 1930Sstevel@tonic-gate defrag(block->page); 1940Sstevel@tonic-gate (void) mutex_unlock(&lock); 1950Sstevel@tonic-gate return (newptr); 1960Sstevel@tonic-gate } 1970Sstevel@tonic-gate 1980Sstevel@tonic-gate void 1990Sstevel@tonic-gate free(void *ptr) 2000Sstevel@tonic-gate { 2010Sstevel@tonic-gate struct block *block; 2020Sstevel@tonic-gate 2030Sstevel@tonic-gate (void) mutex_lock(&lock); 2040Sstevel@tonic-gate if (ptr == NULL) { 2050Sstevel@tonic-gate (void) mutex_unlock(&lock); 2060Sstevel@tonic-gate return; 2070Sstevel@tonic-gate } 2080Sstevel@tonic-gate block = (struct block *)((char *)ptr - HDR_BLOCK); 2090Sstevel@tonic-gate block->status = FREE; 2100Sstevel@tonic-gate 2110Sstevel@tonic-gate defrag(block->page); 2120Sstevel@tonic-gate (void) mutex_unlock(&lock); 2130Sstevel@tonic-gate } 2140Sstevel@tonic-gate 2150Sstevel@tonic-gate /* 2160Sstevel@tonic-gate * Align size on an appropriate boundary 2170Sstevel@tonic-gate */ 2180Sstevel@tonic-gate static size_t 2190Sstevel@tonic-gate align(size_t size, int bound) 2200Sstevel@tonic-gate { 2210Sstevel@tonic-gate if (size < bound) 2220Sstevel@tonic-gate return ((size_t)bound); 2230Sstevel@tonic-gate else 2240Sstevel@tonic-gate return (size + bound - 1 - (size + bound - 1) % bound); 2250Sstevel@tonic-gate } 2260Sstevel@tonic-gate 2270Sstevel@tonic-gate static void 2280Sstevel@tonic-gate split(struct block *block, size_t size) 2290Sstevel@tonic-gate { 2300Sstevel@tonic-gate if (block->size > size + sizeof (struct block)) { 2310Sstevel@tonic-gate struct block *newblock; 2320Sstevel@tonic-gate newblock = (struct block *)((char *)block + HDR_BLOCK + size); 2330Sstevel@tonic-gate newblock->next = block->next; 2340Sstevel@tonic-gate block->next = newblock; 2350Sstevel@tonic-gate newblock->status = FREE; 2360Sstevel@tonic-gate newblock->page = block->page; 2370Sstevel@tonic-gate newblock->size = block->size - size - HDR_BLOCK; 2380Sstevel@tonic-gate block->size = size; 2390Sstevel@tonic-gate } 2400Sstevel@tonic-gate } 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate /* 2430Sstevel@tonic-gate * Defragmentation 2440Sstevel@tonic-gate */ 2450Sstevel@tonic-gate static void 2460Sstevel@tonic-gate defrag(struct page *page) 2470Sstevel@tonic-gate { 2480Sstevel@tonic-gate struct block *block; 2490Sstevel@tonic-gate 2500Sstevel@tonic-gate for (block = page->block; block; block = block->next) { 2510Sstevel@tonic-gate struct block *block2; 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate if (block->status == BUSY) 2540Sstevel@tonic-gate continue; 2550Sstevel@tonic-gate for (block2 = block->next; block2 && block2->status == FREE; 2560Sstevel@tonic-gate block2 = block2->next) { 2570Sstevel@tonic-gate block->next = block2->next; 2580Sstevel@tonic-gate block->size += block2->size + HDR_BLOCK; 2590Sstevel@tonic-gate } 2600Sstevel@tonic-gate } 2610Sstevel@tonic-gate 2620Sstevel@tonic-gate /* 2630Sstevel@tonic-gate * Free page 2640Sstevel@tonic-gate */ 2650Sstevel@tonic-gate if (page->block->size == page->size - HDR_PAGE) { 2660Sstevel@tonic-gate if (page == memstart) 2670Sstevel@tonic-gate memstart = page->next; 2680Sstevel@tonic-gate else { 2690Sstevel@tonic-gate struct page *page2; 2700Sstevel@tonic-gate for (page2 = memstart; page2->next; 2710Sstevel@tonic-gate page2 = page2->next) { 2720Sstevel@tonic-gate if (page2->next == page) { 2730Sstevel@tonic-gate page2->next = page->next; 2740Sstevel@tonic-gate break; 2750Sstevel@tonic-gate } 2760Sstevel@tonic-gate } 2770Sstevel@tonic-gate } 2780Sstevel@tonic-gate (void) munmap((caddr_t)page, page->size); 2790Sstevel@tonic-gate } 2800Sstevel@tonic-gate } 281*3866Sraf 282*3866Sraf static void 283*3866Sraf malloc_prepare() 284*3866Sraf { 285*3866Sraf (void) mutex_lock(&lock); 286*3866Sraf } 287*3866Sraf 288*3866Sraf static void 289*3866Sraf malloc_release() 290*3866Sraf { 291*3866Sraf (void) mutex_unlock(&lock); 292*3866Sraf } 293*3866Sraf 294*3866Sraf #pragma init(malloc_init) 295*3866Sraf static void 296*3866Sraf malloc_init(void) 297*3866Sraf { 298*3866Sraf (void) pthread_atfork(malloc_prepare, malloc_release, malloc_release); 299*3866Sraf } 300