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