ddf8fd1dbb0c12f5862183ef31ab99d2b23958c9
[ghc-hetmet.git] / ghc / rts / MBlock.c
1 /* -----------------------------------------------------------------------------
2  * $Id: MBlock.c,v 1.6 1999/01/18 10:54:15 simonm Exp $
3  *
4  * MegaBlock Allocator Interface.  This file contains all the dirty
5  * architecture-dependent hackery required to get a chunk of aligned
6  * memory from the operating system.
7  *
8  * ---------------------------------------------------------------------------*/
9
10 #define NON_POSIX_SOURCE
11
12 #include "Rts.h"
13 #include "RtsUtils.h"
14 #include "RtsFlags.h"
15 #include "MBlock.h"
16 #include "BlockAlloc.h"
17
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21
22 #ifdef HAVE_SYS_TYPES_H
23 #include <sys/types.h>
24 #endif
25
26 #ifdef HAVE_SYS_MMAN_H
27 #include <sys/mman.h>
28 #endif
29
30 #ifdef HAVE_FCNTL_H
31 #include <fcntl.h>
32 #endif
33
34 #if cygwin32_TARGET_OS
35 #include <windows.h>
36 #endif
37
38 #if freebsd2_TARGET_OS || freebsd3_TARGET_OS
39 /* Executable is loaded from      0x0
40  * Shared libraries are loaded at 0x2000000
41  * Stack is at the top of the address space.  The kernel probably owns
42  * 0x8000000 onwards, so we'll pick 0x5000000.
43  */
44 #define ASK_FOR_MEM_AT 0x50000000
45
46 #elif linux_TARGET_OS
47 /* Any ideas?
48  */
49 #define ASK_FOR_MEM_AT 0x50000000
50
51 #elif solaris2_TARGET_OS
52 /* guess */
53 #define ASK_FOR_MEM_AT 0x50000000
54
55 #elif _WIN32
56 /* doesn't matter, we use a reserve/commit algorithm */
57
58 #else
59 #error Dont know where to get memory from on this architecture
60 /* ToDo: memory locations on other architectures */
61 #endif
62
63 lnat mblocks_allocated = 0;
64
65 void *
66 getMBlock(void)
67 {
68   return getMBlocks(1);
69 }
70
71 #ifndef _WIN32
72
73 void *
74 getMBlocks(nat n)
75 {
76   static caddr_t next_request = (caddr_t)ASK_FOR_MEM_AT;
77   caddr_t ret;
78   lnat size = MBLOCK_SIZE * n;
79  
80 #ifdef solaris2_TARGET_OS
81   { 
82       int fd = open("/dev/zero",O_RDONLY);
83       ret = mmap(next_request, size, PROT_READ | PROT_WRITE, 
84                  MAP_FIXED | MAP_PRIVATE, fd, 0);
85       close(fd);
86   }
87 #else
88   ret = mmap(next_request, size, PROT_READ | PROT_WRITE, 
89              MAP_ANON | MAP_PRIVATE, -1, 0);
90 #endif
91   
92   if (ret == (void *)-1) {
93     if (errno == ENOMEM) {
94       barf("getMBlock: out of memory");
95     } else {
96       barf("GetMBlock: mmap failed");
97     }
98   }
99
100   if (((W_)ret & MBLOCK_MASK) != 0) {
101     barf("GetMBlock: misaligned block returned");
102   }
103
104   IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at %x\n",n,(nat)ret));
105
106   next_request += size;
107
108   mblocks_allocated += n;
109   
110   return ret;
111 }
112
113 #else /* _WIN32 */
114
115 /*
116  On Win32 platforms we make use of the two-phased virtual memory API
117  to allocate mega blocks. We proceed as follows:
118
119  Reserve a large chunk of VM (128M at the time), but don't supply a 
120  base address that's aligned on a MB boundary. Instead we round up to the
121  nearest from the chunk of VM we're given back from the OS (at the
122  moment we just leave the 'slop' at the beginning of the reserved
123  chunk unused - ToDo: reuse it .)
124
125  Reserving memory doesn't allocate physical storage (not even in the
126  page file), this is done by committing pages (or mega-blocks in
127  our case).
128
129 */
130
131 void *
132 getMBlocks(nat n)
133 {
134   static char* base_non_committed = (char*)0;
135   static char* base_mblocks       = (char*)0;
136   static char* next_request       = (char*)0;
137   void* ret                       = (void*)0;
138
139   lnat size = MBLOCK_SIZE * n;
140
141   /* Reserve VM 128M at the time to try to minimise the slop cost. */
142 #define SIZE_RESERVED_POOL  ( 128 * 1024 * 1024 )
143
144   if ( (base_non_committed == 0) || 
145        (next_request + size > base_non_committed + SIZE_RESERVED_POOL) ) {
146     base_non_committed = VirtualAlloc ( NULL
147                                       , SIZE_RESERVED_POOL
148                                       , MEM_RESERVE
149                                       , PAGE_READWRITE
150                                       );
151     if ( base_non_committed == 0 ) {
152 # ifdef DEBUG
153          fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %d\n", GetLastError());
154 # endif
155          ret=(void*)-1;
156     } else {
157     /* The returned pointer is not aligned on a mega-block boundary. Make it. */
158        base_mblocks = (char*)((unsigned)base_non_committed & (unsigned)0xfff00000) + 0x100000;
159 # if 0
160        fprintf(stderr, "Dropping %d bytes off of 128M chunk\n", 
161                        (unsigned)base_mblocks - (unsigned)base_non_committed);
162 # endif
163
164        if ( ((char*)base_mblocks + size) > ((char*)base_non_committed + SIZE_RESERVED_POOL) ) {
165 # ifdef DEBUG
166           fprintf(stderr, "oops, committed too small a region to start with.");
167 # endif
168           ret=(void*)-1;
169        } else {
170           next_request = base_mblocks;
171        }
172     }
173   }
174   /* Commit the mega block(s) to phys mem */
175   if ( ret != (void*)-1 ) {
176      ret = VirtualAlloc(next_request, size, MEM_COMMIT, PAGE_READWRITE);
177      if (ret == NULL) {
178 # ifdef DEBUG
179         fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %d\n", GetLastError());
180 # endif
181         ret=(void*)-1;
182      }
183   }
184
185   if (((W_)ret & MBLOCK_MASK) != 0) {
186     barf("GetMBlock: misaligned block returned");
187   }
188
189   IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at %x\n",n,(nat)ret));
190
191   next_request = (char*)next_request + size;
192
193   mblocks_allocated += n;
194   
195   return ret;
196 }
197
198 /* Hand back the physical memory that is allocated to a mega-block. 
199    ToDo: chain the released mega block onto some list so that
200          getMBlocks() can get at it.
201
202    Currently unused.
203 */
204 #if 0
205 void
206 freeMBlock(void* p, nat n)
207 {
208   BOOL rc;
209
210   rc = VirtualFree(p, n * MBLOCK_SIZE , MEM_DECOMMIT );
211   
212   if (rc == FALSE) {
213 # ifdef DEBUG
214      fprintf(stderr, "freeMBlocks: VirtualFree failed with: %d\n", GetLastError());
215 # endif
216   }
217
218 }
219 #endif
220
221 #endif