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 * 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 * 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 * 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 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 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 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 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 2803866Sraf malloc_prepare() 2813866Sraf { 2823866Sraf (void) mutex_lock(&lock); 2833866Sraf } 2843866Sraf 2853866Sraf static void 2863866Sraf malloc_release() 2873866Sraf { 2883866Sraf (void) mutex_unlock(&lock); 2893866Sraf } 2903866Sraf 2913866Sraf #pragma init(malloc_init) 2923866Sraf static void 2933866Sraf malloc_init(void) 2943866Sraf { 2953866Sraf (void) pthread_atfork(malloc_prepare, malloc_release, malloc_release); 2963866Sraf } 297