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    }