1 /* -----------------------------------------------------------------------------
2 * $Id: MBlock.c,v 1.47 2003/06/26 20:49:32 panne 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"
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
33 #ifndef mingw32_TARGET_OS
34 # ifdef HAVE_SYS_MMAN_H
35 # include <sys/mman.h>
45 #include <mach/vm_map.h>
50 lnat mblocks_allocated = 0;
52 /* -----------------------------------------------------------------------------
53 The MBlock Map: provides our implementation of HEAP_ALLOCED()
54 -------------------------------------------------------------------------- */
56 #ifdef MBLOCK_MAP_SIZE
57 StgWord8 mblock_map[MBLOCK_MAP_SIZE]; // initially all zeros
60 /* -----------------------------------------------------------------------------
61 Allocate new mblock(s)
62 -------------------------------------------------------------------------- */
70 /* -----------------------------------------------------------------------------
73 On Unix-like systems, we use mmap() to allocate our memory. We
74 want memory in chunks of MBLOCK_SIZE, and aligned on an MBLOCK_SIZE
75 boundary. The mmap() interface doesn't give us this level of
76 control, so we have to use some heuristics.
78 In the general case, if we want a block of n megablocks, then we
79 allocate n+1 and trim off the slop from either side (using
80 munmap()) to get an aligned chunk of size n. However, the next
81 time we'll try to allocate directly after the previously allocated
82 chunk, on the grounds that this is aligned and likely to be free.
83 If it turns out that we were wrong, we have to munmap() and try
84 again using the general method.
85 -------------------------------------------------------------------------- */
87 #if !defined(mingw32_TARGET_OS) && !defined(cygwin32_TARGET_OS)
89 // A wrapper around mmap(), to abstract away from OS differences in
90 // the mmap() interface.
93 my_mmap (void *addr, lnat size)
97 #ifdef solaris2_TARGET_OS
99 int fd = open("/dev/zero",O_RDONLY);
100 ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
104 ret = mmap(addr, size, PROT_READ | PROT_WRITE,
105 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
106 #elif darwin_TARGET_OS
107 // Without MAP_FIXED, Apple's mmap ignores addr.
108 // With MAP_FIXED, it overwrites already mapped regions, whic
109 // mmap(0, ... MAP_FIXED ...) is worst of all: It unmaps the program text
110 // and replaces it with zeroes, causing instant death.
111 // This behaviour seems to be conformant with IEEE Std 1003.1-2001.
112 // Let's just use the underlying Mach Microkernel calls directly,
113 // they're much nicer.
117 if(addr) // try to allocate at adress
118 err = vm_allocate(mach_task_self(),(vm_address_t*) &ret, size, FALSE);
119 if(!addr || err) // try to allocate anywhere
120 err = vm_allocate(mach_task_self(),(vm_address_t*) &ret, size, TRUE);
122 if(err) // don't know what the error codes mean exactly
123 barf("memory allocation failed (requested %d bytes)", size);
125 vm_protect(mach_task_self(),ret,size,FALSE,VM_PROT_READ|VM_PROT_WRITE);
127 ret = mmap(addr, size, PROT_READ | PROT_WRITE | PROT_EXEC,
128 MAP_ANON | MAP_PRIVATE, -1, 0);
131 if (ret == (void *)-1) {
132 if (errno == ENOMEM ||
133 (errno == EINVAL && sizeof(void*)==4 && size >= 0xc0000000)) {
134 // If we request more than 3Gig, then we get EINVAL
135 // instead of ENOMEM (at least on Linux).
136 prog_belch("out of memory (requested %d bytes)", size);
137 stg_exit(EXIT_FAILURE);
139 barf("getMBlock: mmap: %s", strerror(errno));
146 // Implements the general case: allocate a chunk of memory of 'size'
150 gen_map_mblocks (lnat size)
155 // Try to map a larger block, and take the aligned portion from
156 // it (unmap the rest).
158 ret = my_mmap(0, size);
160 // unmap the slop bits around the chunk we allocated
161 slop = (W_)ret & MBLOCK_MASK;
163 if (munmap(ret, MBLOCK_SIZE - slop) == -1) {
164 barf("gen_map_mblocks: munmap failed");
166 if (slop > 0 && munmap(ret+size-slop, slop) == -1) {
167 barf("gen_map_mblocks: munmap failed");
170 // ToDo: if we happened to get an aligned block, then don't
171 // unmap the excess, just use it. For this to work, you
172 // need to keep in mind the following:
173 // * Calling my_mmap() with an 'addr' arg pointing to
174 // already my_mmap()ed space is OK and won't fail.
175 // * If my_mmap() can't satisfy the request at the
176 // given 'next_request' address in getMBlocks(), that
177 // you unmap the extra mblock mmap()ed here (or simply
178 // satisfy yourself that the slop introduced isn't worth
182 // next time, try after the block we just got.
183 ret += MBLOCK_SIZE - slop;
188 // The external interface: allocate 'n' mblocks, and return the
194 static caddr_t next_request = (caddr_t)HEAP_BASE;
196 lnat size = MBLOCK_SIZE * n;
199 if (next_request == 0) {
200 // use gen_map_mblocks the first time.
201 ret = gen_map_mblocks(size);
203 ret = my_mmap(next_request, size);
205 if (((W_)ret & MBLOCK_MASK) != 0) {
207 #if 0 // defined(DEBUG)
208 belch("warning: getMBlock: misaligned block %p returned when allocating %d megablock(s) at %p", ret, n, next_request);
211 // unmap this block...
212 if (munmap(ret, size) == -1) {
213 barf("getMBlock: munmap failed");
215 // and do it the hard way
216 ret = gen_map_mblocks(size);
220 // Next time, we'll try to allocate right after the block we just got.
221 // ToDo: check that we haven't already grabbed the memory at next_request
222 next_request = ret + size;
224 IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at %p\n",n,ret));
227 for (i = 0; i < n; i++) {
228 MARK_HEAP_ALLOCED( ret + i * MBLOCK_SIZE );
231 mblocks_allocated += n;
236 #else /* defined(mingw32_TARGET_OS) || defined(cygwin32_TARGET_OS) */
239 On Win32 platforms we make use of the two-phased virtual memory API
240 to allocate mega blocks. We proceed as follows:
242 Reserve a large chunk of VM (256M at the time, or what the user asked
243 for via the -M option), but don't supply a base address that's aligned on
244 a MB boundary. Instead we round up to the nearest mblock from the chunk of
245 VM we're handed back from the OS (at the moment we just leave the 'slop' at
246 the beginning of the reserved chunk unused - ToDo: reuse it .)
248 Reserving memory doesn't allocate physical storage (not even in the
249 page file), this is done later on by committing pages (or mega-blocks in
253 char* base_non_committed = (char*)0;
254 char* end_non_committed = (char*)0;
256 /* Default is to reserve 256M of VM to minimise the slop cost. */
257 #define SIZE_RESERVED_POOL ( 256 * 1024 * 1024 )
259 /* Number of bytes reserved */
260 static unsigned long size_reserved_pool = SIZE_RESERVED_POOL;
265 static char* base_mblocks = (char*)0;
266 static char* next_request = (char*)0;
267 void* ret = (void*)0;
270 lnat size = MBLOCK_SIZE * n;
272 if ( (base_non_committed == 0) || (next_request + size > end_non_committed) ) {
273 if (base_non_committed) {
274 barf("RTS exhausted max heap size (%d bytes)\n", size_reserved_pool);
276 if (RtsFlags.GcFlags.maxHeapSize != 0) {
277 size_reserved_pool = BLOCK_SIZE * RtsFlags.GcFlags.maxHeapSize;
278 if (size_reserved_pool < MBLOCK_SIZE) {
279 size_reserved_pool = 2*MBLOCK_SIZE;
282 base_non_committed = VirtualAlloc ( NULL
287 if ( base_non_committed == 0 ) {
288 fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %ld\n", GetLastError());
291 end_non_committed = (char*)base_non_committed + (unsigned long)size_reserved_pool;
292 /* The returned pointer is not aligned on a mega-block boundary. Make it. */
293 base_mblocks = (char*)((unsigned long)base_non_committed & (unsigned long)~MBLOCK_MASK) + MBLOCK_SIZE;
295 fprintf(stderr, "getMBlocks: Dropping %d bytes off of 256M chunk\n",
296 (unsigned)base_mblocks - (unsigned)base_non_committed);
299 if ( ((char*)base_mblocks + size) > end_non_committed ) {
300 fprintf(stderr, "getMBlocks: oops, committed too small a region to start with.");
303 next_request = base_mblocks;
307 /* Commit the mega block(s) to phys mem */
308 if ( ret != (void*)-1 ) {
309 ret = VirtualAlloc(next_request, size, MEM_COMMIT, PAGE_READWRITE);
311 fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %ld\n", GetLastError());
316 if (((W_)ret & MBLOCK_MASK) != 0) {
317 barf("getMBlocks: misaligned block returned");
320 if (ret == (void*)-1) {
321 barf("getMBlocks: unknown memory allocation failure on Win32.");
324 IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at 0x%x\n",n,(nat)ret));
325 next_request = (char*)next_request + size;
327 mblocks_allocated += n;
330 for (i = 0; i < n; i++) {
331 MARK_HEAP_ALLOCED ( ret + i * MBLOCK_SIZE );
337 /* Hand back the physical memory that is allocated to a mega-block.
338 ToDo: chain the released mega block onto some list so that
339 getMBlocks() can get at it.
345 freeMBlock(void* p, nat n)
349 rc = VirtualFree(p, n * MBLOCK_SIZE , MEM_DECOMMIT );
353 fprintf(stderr, "freeMBlocks: VirtualFree failed with: %d\n", GetLastError());