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 }