001 /*
002 * This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 * This file is licensed to You under the Eclipse Public License (EPL);
005 * You may not use this file except in compliance with the License. You
006 * may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 * See the COPYRIGHT.txt file distributed with this work for information
011 * regarding copyright ownership.
012 */
013 package org.mmtk.utility.heap;
014
015 import org.mmtk.policy.Space;
016 import org.mmtk.utility.Conversions;
017 import org.mmtk.utility.alloc.EmbeddedMetaData;
018 import org.mmtk.utility.options.Options;
019
020 import org.mmtk.vm.VM;
021
022 import org.vmmagic.pragma.Inline;
023 import org.vmmagic.pragma.Uninterruptible;
024 import org.vmmagic.unboxed.Address;
025 import org.vmmagic.unboxed.Extent;
026 import org.vmmagic.unboxed.Offset;
027 import org.vmmagic.unboxed.Word;
028
029 /**
030 * This class manages the allocation of pages for a space. When a
031 * page is requested by the space both a page budget and the use of
032 * virtual address space are checked. If the request for space can't
033 * be satisfied (for either reason) a GC may be triggered.<p>
034 */
035 @Uninterruptible
036 public final class MonotonePageResource extends PageResource {
037
038 /****************************************************************************
039 *
040 * Instance variables
041 */
042
043 /**
044 *
045 */
046 private Address cursor;
047 private Address sentinel;
048 private final int metaDataPagesPerRegion;
049 private Address currentChunk = Address.zero();
050 private volatile Address zeroingCursor;
051 private Address zeroingSentinel;
052
053 /**
054 * Constructor
055 *
056 * Contiguous monotone resource. The address range is pre-defined at
057 * initialization time and is immutable.
058 *
059 * @param space The space to which this resource is attached
060 * @param start The start of the address range allocated to this resource
061 * @param bytes The size of the address rage allocated to this resource
062 * @param metaDataPagesPerRegion The number of pages of meta data
063 * that are embedded in each region.
064 */
065 public MonotonePageResource(Space space, Address start, Extent bytes, int metaDataPagesPerRegion) {
066 super(space, start);
067 this.cursor = start;
068 this.sentinel = start.plus(bytes);
069 this.zeroingCursor = this.sentinel;
070 this.zeroingSentinel = start;
071 this.metaDataPagesPerRegion = metaDataPagesPerRegion;
072 }
073
074 /**
075 * Constructor
076 *
077 * Discontiguous monotone resource. The address range is <i>not</i>
078 * pre-defined at initialization time and is dynamically defined to
079 * be some set of pages, according to demand and availability.
080 *
081 * @param space The space to which this resource is attached
082 * @param metaDataPagesPerRegion The number of pages of meta data
083 * that are embedded in each region.
084 */
085 public MonotonePageResource(Space space, int metaDataPagesPerRegion) {
086 super(space);
087 this.cursor = Address.zero();
088 this.sentinel = Address.zero();
089 this.metaDataPagesPerRegion = metaDataPagesPerRegion;
090 }
091
092
093 @Override
094 public int getAvailablePhysicalPages() {
095 int rtn = Conversions.bytesToPages(sentinel.diff(cursor));
096 if (!contiguous)
097 rtn += Map.getAvailableDiscontiguousChunks()*Space.PAGES_IN_CHUNK;
098 return rtn;
099 }
100
101 /**
102 * Allocate <code>pages</code> pages from this resource. Simply
103 * bump the cursor, and fail if we hit the sentinel.<p>
104 *
105 * If the request can be satisfied, then ensure the pages are
106 * mmpapped and zeroed before returning the address of the start of
107 * the region. If the request cannot be satisfied, return zero.
108 *
109 * @param reservedPages The number of pages reserved due to the initial request.
110 * @param requiredPages The number of pages required to be allocated.
111 * @return The start of the first page if successful, zero on
112 * failure.
113 */
114 @Override
115 @Inline
116 protected Address allocPages(int reservedPages, int requiredPages, boolean zeroed) {
117 boolean newChunk = false;
118 lock();
119 Address rtn = cursor;
120 if (Space.chunkAlign(rtn, true).NE(currentChunk)) {
121 newChunk = true;
122 currentChunk = Space.chunkAlign(rtn, true);
123 }
124
125 if (metaDataPagesPerRegion != 0) {
126 /* adjust allocation for metadata */
127 Address regionStart = getRegionStart(cursor.plus(Conversions.pagesToBytes(requiredPages)));
128 Offset regionDelta = regionStart.diff(cursor);
129 if (regionDelta.sGE(Offset.zero())) {
130 /* start new region, so adjust pages and return address accordingly */
131 requiredPages += Conversions.bytesToPages(regionDelta) + metaDataPagesPerRegion;
132 rtn = regionStart.plus(Conversions.pagesToBytes(metaDataPagesPerRegion));
133 }
134 }
135 Extent bytes = Conversions.pagesToBytes(requiredPages);
136 Address tmp = cursor.plus(bytes);
137
138 if (!contiguous && tmp.GT(sentinel)) {
139 /* we're out of virtual memory within our discontiguous region, so ask for more */
140 int requiredChunks = Space.requiredChunks(requiredPages);
141 Address chunk = space.growDiscontiguousSpace(requiredChunks); // Returns zero on failure
142 cursor = chunk;
143 sentinel = cursor.plus(chunk.isZero() ? 0 : requiredChunks<<Space.LOG_BYTES_IN_CHUNK);
144 rtn = cursor;
145 tmp = cursor.plus(bytes);
146 newChunk = true;
147 }
148 if (VM.VERIFY_ASSERTIONS)
149 VM.assertions._assert(rtn.GE(cursor) && rtn.LT(cursor.plus(bytes)));
150 if (tmp.GT(sentinel)) {
151 unlock();
152 return Address.zero();
153 } else {
154 Address old = cursor;
155 cursor = tmp;
156 commitPages(reservedPages, requiredPages);
157 space.growSpace(old, bytes, newChunk);
158 unlock();
159 Mmapper.ensureMapped(old, requiredPages);
160 if (zeroed) {
161 if (!zeroConcurrent) {
162 VM.memory.zero(zeroNT, old, bytes);
163 } else {
164 while (cursor.GT(zeroingCursor));
165 }
166 }
167 VM.events.tracePageAcquired(space, rtn, requiredPages);
168 return rtn;
169 }
170 }
171
172 /**
173 * {@inheritDoc}<p>
174 *
175 * In this case we simply report the expected page cost. We can't use
176 * worst case here because we would exhaust our budget every time.
177 */
178 @Override
179 public int adjustForMetaData(int pages) {
180 return pages + ((pages + EmbeddedMetaData.PAGES_IN_REGION - 1) >> EmbeddedMetaData.LOG_PAGES_IN_REGION) * metaDataPagesPerRegion;
181 }
182
183 /**
184 * Adjust a page request to include metadata requirements, if any.<p>
185 *
186 * Note that there could be a race here, with multiple threads each
187 * adjusting their request on account of the same single metadata
188 * region. This should not be harmful, as the failing requests will
189 * just retry, and if multiple requests succeed, only one of them
190 * will actually have the metadata accounted against it, the others
191 * will simply have more space than they originally requested.
192 *
193 * @param pages The size of the pending allocation in pages
194 * @param begin The start address of the region assigned to this pending
195 * request
196 * @return The number of required pages, inclusive of any metadata
197 */
198 public int adjustForMetaData(int pages, Address begin) {
199 if (getRegionStart(begin).plus(metaDataPagesPerRegion<<LOG_BYTES_IN_PAGE).EQ(begin)) {
200 pages += metaDataPagesPerRegion;
201 }
202 return pages;
203 }
204
205 private static Address getRegionStart(Address addr) {
206 return addr.toWord().and(Word.fromIntSignExtend(EmbeddedMetaData.BYTES_IN_REGION - 1).not()).toAddress();
207 }
208
209 /**
210 * Reset this page resource, freeing all pages and resetting
211 * reserved and committed pages appropriately.
212 */
213 @Inline
214 public void reset() {
215 lock();
216 reserved = 0;
217 committed = 0;
218 releasePages();
219 unlock();
220 }
221
222 /**
223 * Notify that several pages are no longer in use.
224 *
225 * @param pages The number of pages
226 */
227 public void unusePages(int pages) {
228 lock();
229 reserved -= pages;
230 committed -= pages;
231 unlock();
232 }
233
234 /**
235 * Notify that previously unused pages are in use again.
236 *
237 * @param pages The number of pages
238 */
239 public void reusePages(int pages) {
240 lock();
241 reserved += pages;
242 committed += pages;
243 unlock();
244 }
245
246 /**
247 * Release all pages associated with this page resource, optionally
248 * zeroing on release and optionally memory protecting on release.
249 */
250 @Inline
251 private void releasePages() {
252 if (contiguous) {
253 // TODO: We will perform unnecessary zeroing if the nursery size has decreased.
254 if (zeroConcurrent) {
255 // Wait for current zeroing to finish.
256 while(zeroingCursor.LT(zeroingSentinel)) {}
257 }
258 // Reset zeroing region.
259 if (cursor.GT(zeroingSentinel)) {
260 zeroingSentinel = cursor;
261 }
262 zeroingCursor = start;
263 cursor = start;
264 } else {/* Not contiguous */
265 if (!cursor.isZero()) {
266 do {
267 Extent bytes = cursor.diff(currentChunk).toWord().toExtent();
268 releasePages(currentChunk, bytes);
269 } while (moveToNextChunk());
270
271 currentChunk = Address.zero();
272 sentinel = Address.zero();
273 cursor = Address.zero();
274 space.releaseAllChunks();
275 }
276 }
277 }
278
279 /**
280 * Adjust the currentChunk and cursor fields to point to the next chunk
281 * in the linked list of chunks tied down by this page resource.
282 *
283 * @return {@code true} if we moved to the next chunk; {@code false} if we hit the
284 * end of the linked list.
285 */
286 private boolean moveToNextChunk() {
287 currentChunk = Map.getNextContiguousRegion(currentChunk);
288 if (currentChunk.isZero())
289 return false;
290 else {
291 cursor = currentChunk.plus(Map.getContiguousRegionSize(currentChunk));
292 return true;
293 }
294 }
295
296 /**
297 * Release a range of pages associated with this page resource, optionally
298 * zeroing on release and optionally memory protecting on release.
299 */
300 @Inline
301 private void releasePages(Address first, Extent bytes) {
302 int pages = Conversions.bytesToPages(bytes);
303 if (VM.VERIFY_ASSERTIONS)
304 VM.assertions._assert(bytes.EQ(Conversions.pagesToBytes(pages)));
305 if (ZERO_ON_RELEASE)
306 VM.memory.zero(false, first, bytes);
307 if (Options.protectOnRelease.getValue())
308 Mmapper.protect(first, pages);
309 VM.events.tracePageReleased(space, first, pages);
310 }
311
312 private static int CONCURRENT_ZEROING_BLOCKSIZE = 1<<16;
313
314 @Override
315 public void concurrentZeroing() {
316 if (VM.VERIFY_ASSERTIONS) {
317 VM.assertions._assert(zeroConcurrent);
318 }
319 Address first = start;
320 while (first.LT(zeroingSentinel)) {
321 Address last = first.plus(CONCURRENT_ZEROING_BLOCKSIZE);
322 if (last.GT(zeroingSentinel)) last = zeroingSentinel;
323 VM.memory.zero(zeroNT, first, Extent.fromIntSignExtend(last.diff(first).toInt()));
324 zeroingCursor = last;
325 first = first.plus(CONCURRENT_ZEROING_BLOCKSIZE);
326 }
327 zeroingCursor = sentinel;
328 }
329 }