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