1 /* -----------------------------------------------------------------------------
2 * $Id: MBlock.c,v 1.46 2003/05/21 13:22:46 stolz Exp $
4 * (c) The GHC Team 1998-1999
6 * MegaBlock Allocator Interface. This file contains all the dirty
7 * architecture-dependent hackery required to get a chunk of aligned
8 * memory from the operating system.
10 * ---------------------------------------------------------------------------*/
12 /* This is non-posix compliant. */
13 /* #include "PosixSource.h" */
19 #include "BlockAlloc.h"
27 #ifdef HAVE_SYS_TYPES_H
28 #include <sys/types.h>
30 #ifndef mingw32_TARGET_OS
31 # ifdef HAVE_SYS_MMAN_H
32 # include <sys/mman.h>
42 #include <mach/vm_map.h>
47 lnat mblocks_allocated = 0;
49 /* -----------------------------------------------------------------------------
50 The MBlock Map: provides our implementation of HEAP_ALLOCED()
51 -------------------------------------------------------------------------- */
53 #ifdef MBLOCK_MAP_SIZE
54 StgWord8 mblock_map[MBLOCK_MAP_SIZE]; // initially all zeros
57 /* -----------------------------------------------------------------------------
58 Allocate new mblock(s)
59 -------------------------------------------------------------------------- */
67 /* -----------------------------------------------------------------------------
70 On Unix-like systems, we use mmap() to allocate our memory. We
71 want memory in chunks of MBLOCK_SIZE, and aligned on an MBLOCK_SIZE
72 boundary. The mmap() interface doesn't give us this level of
73 control, so we have to use some heuristics.
75 In the general case, if we want a block of n megablocks, then we
76 allocate n+1 and trim off the slop from either side (using
77 munmap()) to get an aligned chunk of size n. However, the next
78 time we'll try to allocate directly after the previously allocated
79 chunk, on the grounds that this is aligned and likely to be free.
80 If it turns out that we were wrong, we have to munmap() and try
81 again using the general method.
82 -------------------------------------------------------------------------- */
84 #if !defined(mingw32_TARGET_OS) && !defined(cygwin32_TARGET_OS)
86 // A wrapper around mmap(), to abstract away from OS differences in
87 // the mmap() interface.
90 my_mmap (void *addr, lnat size)
94 #ifdef solaris2_TARGET_OS
96 int fd = open("/dev/zero",O_RDONLY);
97 ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
101 ret = mmap(addr, size, PROT_READ | PROT_WRITE,
102 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
103 #elif darwin_TARGET_OS
104 // Without MAP_FIXED, Apple's mmap ignores addr.
105 // With MAP_FIXED, it overwrites already mapped regions, whic
106 // mmap(0, ... MAP_FIXED ...) is worst of all: It unmaps the program text
107 // and replaces it with zeroes, causing instant death.
108 // This behaviour seems to be conformant with IEEE Std 1003.1-2001.
109 // Let's just use the underlying Mach Microkernel calls directly,
110 // they're much nicer.
114 if(addr) // try to allocate at adress
115 err = vm_allocate(mach_task_self(),(vm_address_t*) &ret, size, FALSE);
116 if(!addr || err) // try to allocate anywhere
117 err = vm_allocate(mach_task_self(),(vm_address_t*) &ret, size, TRUE);
119 if(err) // don't know what the error codes mean exactly
120 barf("memory allocation failed (requested %d bytes)", size);
122 vm_protect(mach_task_self(),ret,size,FALSE,VM_PROT_READ|VM_PROT_WRITE);
124 ret = mmap(addr, size, PROT_READ | PROT_WRITE | PROT_EXEC,
125 MAP_ANON | MAP_PRIVATE, -1, 0);
128 if (ret == (void *)-1) {
129 if (errno == ENOMEM ||
130 (errno == EINVAL && sizeof(void*)==4 && size >= 0xc0000000)) {
131 // If we request more than 3Gig, then we get EINVAL
132 // instead of ENOMEM (at least on Linux).
133 prog_belch("out of memory (requested %d bytes)", size);
134 stg_exit(EXIT_FAILURE);
136 barf("getMBlock: mmap: %s", strerror(errno));
143 // Implements the general case: allocate a chunk of memory of 'size'
147 gen_map_mblocks (lnat size)
152 // Try to map a larger block, and take the aligned portion from
153 // it (unmap the rest).
155 ret = my_mmap(0, size);
157 // unmap the slop bits around the chunk we allocated
158 slop = (W_)ret & MBLOCK_MASK;
160 if (munmap(ret, MBLOCK_SIZE - slop) == -1) {
161 barf("gen_map_mblocks: munmap failed");
163 if (slop > 0 && munmap(ret+size-slop, slop) == -1) {
164 barf("gen_map_mblocks: munmap failed");
167 // ToDo: if we happened to get an aligned block, then don't
168 // unmap the excess, just use it. For this to work, you
169 // need to keep in mind the following:
170 // * Calling my_mmap() with an 'addr' arg pointing to
171 // already my_mmap()ed space is OK and won't fail.
172 // * If my_mmap() can't satisfy the request at the
173 // given 'next_request' address in getMBlocks(), that
174 // you unmap the extra mblock mmap()ed here (or simply
175 // satisfy yourself that the slop introduced isn't worth
179 // next time, try after the block we just got.
180 ret += MBLOCK_SIZE - slop;
185 // The external interface: allocate 'n' mblocks, and return the
191 static caddr_t next_request = (caddr_t)HEAP_BASE;
193 lnat size = MBLOCK_SIZE * n;
196 if (next_request == 0) {
197 // use gen_map_mblocks the first time.
198 ret = gen_map_mblocks(size);
200 ret = my_mmap(next_request, size);
202 if (((W_)ret & MBLOCK_MASK) != 0) {
204 #if 0 // defined(DEBUG)
205 belch("warning: getMBlock: misaligned block %p returned when allocating %d megablock(s) at %p", ret, n, next_request);
208 // unmap this block...
209 if (munmap(ret, size) == -1) {
210 barf("getMBlock: munmap failed");
212 // and do it the hard way
213 ret = gen_map_mblocks(size);
217 // Next time, we'll try to allocate right after the block we just got.
218 // ToDo: check that we haven't already grabbed the memory at next_request
219 next_request = ret + size;
221 IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at %p\n",n,ret));
224 for (i = 0; i < n; i++) {
225 MARK_HEAP_ALLOCED( ret + i * MBLOCK_SIZE );
228 mblocks_allocated += n;
233 #else /* defined(mingw32_TARGET_OS) || defined(cygwin32_TARGET_OS) */
236 On Win32 platforms we make use of the two-phased virtual memory API
237 to allocate mega blocks. We proceed as follows:
239 Reserve a large chunk of VM (256M at the time, or what the user asked
240 for via the -M option), but don't supply a base address that's aligned on
241 a MB boundary. Instead we round up to the nearest mblock from the chunk of
242 VM we're handed back from the OS (at the moment we just leave the 'slop' at
243 the beginning of the reserved chunk unused - ToDo: reuse it .)
245 Reserving memory doesn't allocate physical storage (not even in the
246 page file), this is done later on by committing pages (or mega-blocks in
250 char* base_non_committed = (char*)0;
251 char* end_non_committed = (char*)0;
253 /* Default is to reserve 256M of VM to minimise the slop cost. */
254 #define SIZE_RESERVED_POOL ( 256 * 1024 * 1024 )
256 /* Number of bytes reserved */
257 static unsigned long size_reserved_pool = SIZE_RESERVED_POOL;
262 static char* base_mblocks = (char*)0;
263 static char* next_request = (char*)0;
264 void* ret = (void*)0;
267 lnat size = MBLOCK_SIZE * n;
269 if ( (base_non_committed == 0) || (next_request + size > end_non_committed) ) {
270 if (base_non_committed) {
271 barf("RTS exhausted max heap size (%d bytes)\n", size_reserved_pool);
273 if (RtsFlags.GcFlags.maxHeapSize != 0) {
274 size_reserved_pool = BLOCK_SIZE * RtsFlags.GcFlags.maxHeapSize;
275 if (size_reserved_pool < MBLOCK_SIZE) {
276 size_reserved_pool = 2*MBLOCK_SIZE;
279 base_non_committed = VirtualAlloc ( NULL
284 if ( base_non_committed == 0 ) {
285 fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %ld\n", GetLastError());
288 end_non_committed = (char*)base_non_committed + (unsigned long)size_reserved_pool;
289 /* The returned pointer is not aligned on a mega-block boundary. Make it. */
290 base_mblocks = (char*)((unsigned long)base_non_committed & (unsigned long)~MBLOCK_MASK) + MBLOCK_SIZE;
292 fprintf(stderr, "getMBlocks: Dropping %d bytes off of 256M chunk\n",
293 (unsigned)base_mblocks - (unsigned)base_non_committed);
296 if ( ((char*)base_mblocks + size) > end_non_committed ) {
297 fprintf(stderr, "getMBlocks: oops, committed too small a region to start with.");
300 next_request = base_mblocks;
304 /* Commit the mega block(s) to phys mem */
305 if ( ret != (void*)-1 ) {
306 ret = VirtualAlloc(next_request, size, MEM_COMMIT, PAGE_READWRITE);
308 fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %ld\n", GetLastError());
313 if (((W_)ret & MBLOCK_MASK) != 0) {
314 barf("getMBlocks: misaligned block returned");
317 if (ret == (void*)-1) {
318 barf("getMBlocks: unknown memory allocation failure on Win32.");
321 IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at 0x%x\n",n,(nat)ret));
322 next_request = (char*)next_request + size;
324 mblocks_allocated += n;
327 for (i = 0; i < n; i++) {
328 MARK_HEAP_ALLOCED ( ret + i * MBLOCK_SIZE );
334 /* Hand back the physical memory that is allocated to a mega-block.
335 ToDo: chain the released mega block onto some list so that
336 getMBlocks() can get at it.
342 freeMBlock(void* p, nat n)
346 rc = VirtualFree(p, n * MBLOCK_SIZE , MEM_DECOMMIT );
350 fprintf(stderr, "freeMBlocks: VirtualFree failed with: %d\n", GetLastError());