[project @ 1999-01-25 11:55:06 by sof]
[ghc-hetmet.git] / ghc / rts / MBlock.c
1 /* -----------------------------------------------------------------------------
2  * $Id: MBlock.c,v 1.7 1999/01/25 11:55:06 sof 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 osf3_TARGET_OS
56 /* guess */
57 #define ASK_FOR_MEM_AT 0x50000000
58
59 #elif _WIN32
60 /* doesn't matter, we use a reserve/commit algorithm */
61
62 #else
63 #error Dont know where to get memory from on this architecture
64 /* ToDo: memory locations on other architectures */
65 #endif
66
67 lnat mblocks_allocated = 0;
68
69 void *
70 getMBlock(void)
71 {
72   return getMBlocks(1);
73 }
74
75 #ifndef _WIN32
76
77 void *
78 getMBlocks(nat n)
79 {
80   static caddr_t next_request = (caddr_t)ASK_FOR_MEM_AT;
81   caddr_t ret;
82   lnat size = MBLOCK_SIZE * n;
83  
84 #ifdef solaris2_TARGET_OS
85   { 
86       int fd = open("/dev/zero",O_RDONLY);
87       ret = mmap(next_request, size, PROT_READ | PROT_WRITE, 
88                  MAP_FIXED | MAP_PRIVATE, fd, 0);
89       close(fd);
90   }
91 #else
92   ret = mmap(next_request, size, PROT_READ | PROT_WRITE, 
93              MAP_ANON | MAP_PRIVATE, -1, 0);
94 #endif
95   
96   if (ret == (void *)-1) {
97     if (errno == ENOMEM) {
98       barf("getMBlock: out of memory");
99     } else {
100       barf("GetMBlock: mmap failed");
101     }
102   }
103
104   if (((W_)ret & MBLOCK_MASK) != 0) {
105     barf("GetMBlock: misaligned block returned");
106   }
107
108   IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at %x\n",n,(nat)ret));
109
110   next_request += size;
111
112   mblocks_allocated += n;
113   
114   return ret;
115 }
116
117 #else /* _WIN32 */
118
119 /*
120  On Win32 platforms we make use of the two-phased virtual memory API
121  to allocate mega blocks. We proceed as follows:
122
123  Reserve a large chunk of VM (128M at the time), but don't supply a 
124  base address that's aligned on a MB boundary. Instead we round up to the
125  nearest from the chunk of VM we're given back from the OS (at the
126  moment we just leave the 'slop' at the beginning of the reserved
127  chunk unused - ToDo: reuse it .)
128
129  Reserving memory doesn't allocate physical storage (not even in the
130  page file), this is done by committing pages (or mega-blocks in
131  our case).
132
133 */
134
135 void *
136 getMBlocks(nat n)
137 {
138   static char* base_non_committed = (char*)0;
139   static char* base_mblocks       = (char*)0;
140   static char* next_request       = (char*)0;
141   void* ret                       = (void*)0;
142
143   lnat size = MBLOCK_SIZE * n;
144
145   /* Reserve VM 128M at the time to try to minimise the slop cost. */
146 #define SIZE_RESERVED_POOL  ( 128 * 1024 * 1024 )
147
148   if ( (base_non_committed == 0) || 
149        (next_request + size > base_non_committed + SIZE_RESERVED_POOL) ) {
150     base_non_committed = VirtualAlloc ( NULL
151                                       , SIZE_RESERVED_POOL
152                                       , MEM_RESERVE
153                                       , PAGE_READWRITE
154                                       );
155     if ( base_non_committed == 0 ) {
156 # ifdef DEBUG
157          fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %d\n", GetLastError());
158 # endif
159          ret=(void*)-1;
160     } else {
161     /* The returned pointer is not aligned on a mega-block boundary. Make it. */
162        base_mblocks = (char*)((unsigned)base_non_committed & (unsigned)0xfff00000) + 0x100000;
163 # if 0
164        fprintf(stderr, "Dropping %d bytes off of 128M chunk\n", 
165                        (unsigned)base_mblocks - (unsigned)base_non_committed);
166 # endif
167
168        if ( ((char*)base_mblocks + size) > ((char*)base_non_committed + SIZE_RESERVED_POOL) ) {
169 # ifdef DEBUG
170           fprintf(stderr, "oops, committed too small a region to start with.");
171 # endif
172           ret=(void*)-1;
173        } else {
174           next_request = base_mblocks;
175        }
176     }
177   }
178   /* Commit the mega block(s) to phys mem */
179   if ( ret != (void*)-1 ) {
180      ret = VirtualAlloc(next_request, size, MEM_COMMIT, PAGE_READWRITE);
181      if (ret == NULL) {
182 # ifdef DEBUG
183         fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %d\n", GetLastError());
184 # endif
185         ret=(void*)-1;
186      }
187   }
188
189   if (((W_)ret & MBLOCK_MASK) != 0) {
190     barf("GetMBlock: misaligned block returned");
191   }
192
193   IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at %x\n",n,(nat)ret));
194
195   next_request = (char*)next_request + size;
196
197   mblocks_allocated += n;
198   
199   return ret;
200 }
201
202 /* Hand back the physical memory that is allocated to a mega-block. 
203    ToDo: chain the released mega block onto some list so that
204          getMBlocks() can get at it.
205
206    Currently unused.
207 */
208 #if 0
209 void
210 freeMBlock(void* p, nat n)
211 {
212   BOOL rc;
213
214   rc = VirtualFree(p, n * MBLOCK_SIZE , MEM_DECOMMIT );
215   
216   if (rc == FALSE) {
217 # ifdef DEBUG
218      fprintf(stderr, "freeMBlocks: VirtualFree failed with: %d\n", GetLastError());
219 # endif
220   }
221
222 }
223 #endif
224
225 #endif