1 /*
   2  * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2020 SAP SE. All rights reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  *
  24  */
  25 
  26 #ifndef SHARE_MEMORY_METASPACE_MSROOTCHUNKAREA_HPP
  27 #define SHARE_MEMORY_METASPACE_MSROOTCHUNKAREA_HPP
  28 
  29 #include "memory/allocation.hpp"
  30 #include "memory/metaspace/msChunklevel.hpp"
  31 #include "utilities/debug.hpp"
  32 #include "utilities/globalDefinitions.hpp"
  33 
  34 class outputStream;
  35 
  36 namespace metaspace {
  37 
  38 class Metachunk;
  39 class MetachunkClosure;
  40 class FreeChunkListVector;
  41 class VirtualSpaceNode;
  42 
  43 // RootChunkArea manages a memory area covering a single root chunk.
  44 //
  45 // Such an area may contain a single root chunk, or a number of chunks the
  46 //  root chunk was split into.
  47 //
  48 // RootChunkArea contains the functionality to merge and split chunks in
  49 //  buddy allocator fashion.
  50 //
  51 
  52 class RootChunkArea {
  53 
  54   // The base address of this area.
  55   // Todo: this may be somewhat superfluous since RootChunkArea only exist in the
  56   //  context of a series of chunks, so the address is somewhat implicit. Remove?
  57   const MetaWord* const _base;
  58 
  59   // The first chunk in this area; if this area is maximally
  60   // folded, this is the root chunk covering the whole area size.
  61   Metachunk* _first_chunk;
  62 
  63 public:
  64 
  65   RootChunkArea(const MetaWord* base);
  66   ~RootChunkArea();
  67 
  68   // Initialize: allocate a root node and a root chunk header; return the
  69   // root chunk header. It will be partly initialized.
  70   // Note: this just allocates a memory-less header; memory itself is allocated inside VirtualSpaceNode.
  71   Metachunk* alloc_root_chunk_header(VirtualSpaceNode* node);
  72 
  73   // Given a chunk c, split it recursively until you get a chunk of the given target_level.
  74   //
  75   // The resulting target chunk resides at the same address as the original chunk.
  76   // The resulting splinters are added to freelists.
  77   //
  78   // Returns pointer to the result chunk; the splitted-off chunks are added as
  79   //  free chunks to the freelists.
  80   void split(chunklevel_t target_level, Metachunk* c, FreeChunkListVector* freelists);
  81 
  82   // Given a chunk, attempt to merge it recursively with its neighboring chunks.
  83   //
  84   // If successful (merged at least once), returns address of
  85   // the merged chunk; NULL otherwise.
  86   //
  87   // The merged chunks are removed from the freelists.
  88   //
  89   // !!! Please note that if this method returns a non-NULL value, the
  90   // original chunk will be invalid and should not be accessed anymore! !!!
  91   Metachunk* merge(Metachunk* c, FreeChunkListVector* freelists);
  92 
  93   // Given a chunk c, which must be "in use" and must not be a root chunk, attempt to
  94   // enlarge it in place by claiming its trailing buddy.
  95   //
  96   // This will only work if c is the leader of the buddy pair and the trailing buddy is free.
  97   //
  98   // If successful, the follower chunk will be removed from the freelists, the leader chunk c will
  99   // double in size (level decreased by one).
 100   //
 101   // On success, true is returned, false otherwise.
 102   bool attempt_enlarge_chunk(Metachunk* c, FreeChunkListVector* freelists);
 103 
 104   /// range ///
 105 
 106   const MetaWord* base() const  { return _base; }
 107   size_t word_size() const      { return chunklevel::MAX_CHUNK_WORD_SIZE; }
 108   const MetaWord* end() const   { return _base + word_size(); }
 109 
 110   // Direct access to the first chunk (use with care)
 111   Metachunk* first_chunk()              { return _first_chunk; }
 112   const Metachunk* first_chunk() const  { return _first_chunk; }
 113 
 114   // Returns true if this root chunk area is completely free:
 115   //  In that case, it should only contain one chunk (maximally merged, so a root chunk)
 116   //  and it should be free.
 117   bool is_free() const;
 118 
 119   //// Debug stuff ////
 120 
 121 #ifdef ASSERT
 122   void check_pointer(const MetaWord* p) const {
 123     assert(p >= _base && p < _base + word_size(),
 124            "pointer " PTR_FORMAT " oob for this root area [" PTR_FORMAT ".." PTR_FORMAT ")",
 125            p2i(p), p2i(_base), p2i(_base + word_size()));
 126   }
 127   void verify() const;
 128 
 129   // This is a separate operation from verify(). We should be able to call verify()
 130   // from almost anywhere, regardless of state, but verify_area_is_ideally_merged()
 131   // can only be called outside split and merge ops.
 132   void verify_area_is_ideally_merged() const;
 133 #endif // ASSERT
 134 
 135   void print_on(outputStream* st) const;
 136 
 137 };
 138 
 139 // RootChunkAreaLUT (lookup table) manages a series of contiguous root chunk areas
 140 //  in memory (in the context of a VirtualSpaceNode). It allows finding the containing
 141 //  root chunk for any given memory address. It allows for easy iteration over all
 142 //  root chunks.
 143 // Beyond that it is unexciting.
 144 class RootChunkAreaLUT {
 145 
 146   // Base address of the whole area.
 147   const MetaWord* const _base;
 148 
 149   // Number of root chunk areas.
 150   const int _num;
 151 
 152   // Array of RootChunkArea objects.
 153   RootChunkArea* _arr;
 154 
 155 #ifdef ASSERT
 156   void check_pointer(const MetaWord* p) const {
 157     assert(p >= base() && p < base() + word_size(), "Invalid pointer");
 158   }
 159 #endif
 160 
 161   // Given an address into this range, return the index into the area array for the
 162   // area this address falls into.
 163   int index_by_address(const MetaWord* p) const {
 164     DEBUG_ONLY(check_pointer(p);)
 165     int idx = (int)((p - base()) / chunklevel::MAX_CHUNK_WORD_SIZE);
 166     assert(idx >= 0 && idx < _num, "Sanity");
 167     return idx;
 168   }
 169 
 170 public:
 171 
 172   RootChunkAreaLUT(const MetaWord* base, size_t word_size);
 173   ~RootChunkAreaLUT();
 174 
 175   // Given a memory address into the range this array covers, return the
 176   // corresponding area object. If none existed at this position, create it
 177   // on demand.
 178   RootChunkArea* get_area_by_address(const MetaWord* p) const {
 179     const int idx = index_by_address(p);
 180     RootChunkArea* ra = _arr + idx;
 181     DEBUG_ONLY(ra->check_pointer(p);)
 182     return _arr + idx;
 183   }
 184 
 185   // Access area by its index
 186   int number_of_areas() const                               { return _num; }
 187   RootChunkArea* get_area_by_index(int index)               { assert(index >= 0 && index < _num, "oob"); return _arr + index; }
 188   const RootChunkArea* get_area_by_index(int index) const   { assert(index >= 0 && index < _num, "oob"); return _arr + index; }
 189 
 190   /// range ///
 191 
 192   const MetaWord* base() const  { return _base; }
 193   size_t word_size() const      { return _num * chunklevel::MAX_CHUNK_WORD_SIZE; }
 194   const MetaWord* end() const   { return _base + word_size(); }
 195 
 196   // Returns true if all areas in this area table are free (only contain free chunks).
 197   bool is_free() const;
 198 
 199   DEBUG_ONLY(void verify() const;)
 200 
 201   void print_on(outputStream* st) const;
 202 
 203 };
 204 
 205 } // namespace metaspace
 206 
 207 #endif // SHARE_MEMORY_METASPACE_MSROOTCHUNKAREA_HPP