1 /*
   2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 #include "precompiled.hpp"
  25 #include "gc/z/zList.inline.hpp"
  26 #include "gc/z/zNUMA.hpp"
  27 #include "gc/z/zPage.inline.hpp"
  28 #include "gc/z/zPageCache.hpp"
  29 #include "gc/z/zStat.hpp"
  30 #include "logging/log.hpp"
  31 
  32 static const ZStatCounter ZCounterPageCacheHitL1("Memory", "Page Cache Hit L1", ZStatUnitOpsPerSecond);
  33 static const ZStatCounter ZCounterPageCacheHitL2("Memory", "Page Cache Hit L2", ZStatUnitOpsPerSecond);
  34 static const ZStatCounter ZCounterPageCacheMiss("Memory", "Page Cache Miss", ZStatUnitOpsPerSecond);
  35 static const ZStatCounter ZCounterPageCacheFlush("Memory", "Page Cache Flush", ZStatUnitBytesPerSecond);
  36 
  37 ZPageCache::ZPageCache() :
  38     _available(0),
  39     _small(),
  40     _medium(),
  41     _large() {}
  42 
  43 ZPage* ZPageCache::alloc_small_page() {
  44   const uint32_t numa_id = ZNUMA::id();
  45   const uint32_t numa_count = ZNUMA::count();
  46 
  47   // Try NUMA local page cache
  48   ZPage* const l1_page = _small.get(numa_id).remove_first();
  49   if (l1_page != NULL) {
  50     ZStatInc(ZCounterPageCacheHitL1);
  51     return l1_page;
  52   }
  53 
  54   // Try NUMA remote page cache(s)
  55   uint32_t remote_numa_id = numa_id + 1;
  56   const uint32_t remote_numa_count = numa_count - 1;
  57   for (uint32_t i = 0; i < remote_numa_count; i++) {
  58     if (remote_numa_id == numa_count) {
  59       remote_numa_id = 0;
  60     }
  61 
  62     ZPage* const l2_page = _small.get(remote_numa_id).remove_first();
  63     if (l2_page != NULL) {
  64       ZStatInc(ZCounterPageCacheHitL2);
  65       return l2_page;
  66     }
  67 
  68     remote_numa_id++;
  69   }
  70 
  71   ZStatInc(ZCounterPageCacheMiss);
  72   return NULL;
  73 }
  74 
  75 ZPage* ZPageCache::alloc_medium_page() {
  76   ZPage* const l1_page = _medium.remove_first();
  77   if (l1_page != NULL) {
  78     ZStatInc(ZCounterPageCacheHitL1);
  79     return l1_page;
  80   }
  81 
  82   ZStatInc(ZCounterPageCacheMiss);
  83   return NULL;
  84 }
  85 
  86 ZPage* ZPageCache::alloc_large_page(size_t size) {
  87   // Find a page with the right size
  88   ZListIterator<ZPage> iter(&_large);
  89   for (ZPage* l1_page; iter.next(&l1_page);) {
  90     if (l1_page->size() == size) {
  91       // Page found
  92       _large.remove(l1_page);
  93       ZStatInc(ZCounterPageCacheHitL1);
  94       return l1_page;
  95     }
  96   }
  97 
  98   ZStatInc(ZCounterPageCacheMiss);
  99   return NULL;
 100 }
 101 
 102 ZPage* ZPageCache::alloc_page(uint8_t type, size_t size) {
 103   ZPage* page;
 104 
 105   if (type == ZPageTypeSmall) {
 106     page = alloc_small_page();
 107   } else if (type == ZPageTypeMedium) {
 108     page = alloc_medium_page();
 109   } else {
 110     page = alloc_large_page(size);
 111   }
 112 
 113   if (page != NULL) {
 114     _available -= page->size();
 115   }
 116 
 117   return page;
 118 }
 119 
 120 void ZPageCache::free_page(ZPage* page) {
 121   assert(!page->is_active(), "Invalid page state");
 122   assert(!page->is_pinned(), "Invalid page state");
 123   assert(!page->is_detached(), "Invalid page state");
 124 
 125   const uint8_t type = page->type();
 126   if (type == ZPageTypeSmall) {
 127     _small.get(page->numa_id()).insert_first(page);
 128   } else if (type == ZPageTypeMedium) {
 129     _medium.insert_first(page);
 130   } else {
 131     _large.insert_first(page);
 132   }
 133 
 134   _available += page->size();
 135 }
 136 
 137 void ZPageCache::flush_list(ZList<ZPage>* from, size_t requested, ZList<ZPage>* to, size_t* flushed) {
 138   while (*flushed < requested) {
 139     // Flush least recently used
 140     ZPage* const page = from->remove_last();
 141     if (page == NULL) {
 142       break;
 143     }
 144 
 145     *flushed += page->size();
 146     to->insert_last(page);
 147   }
 148 }
 149 
 150 void ZPageCache::flush_per_numa_lists(ZPerNUMA<ZList<ZPage> >* from, size_t requested, ZList<ZPage>* to, size_t* flushed) {
 151   const uint32_t numa_count = ZNUMA::count();
 152   uint32_t numa_empty = 0;
 153   uint32_t numa_next = 0;
 154 
 155   // Flush lists round-robin
 156   while (*flushed < requested) {
 157     ZPage* const page = from->get(numa_next).remove_last();
 158 
 159     if (++numa_next == numa_count) {
 160       numa_next = 0;
 161     }
 162 
 163     if (page == NULL) {
 164       // List is empty
 165       if (++numa_empty == numa_count) {
 166         // All lists are empty
 167         break;
 168       }
 169 
 170       // Try next list
 171       continue;
 172     }
 173 
 174     // Flush page
 175     numa_empty = 0;
 176     *flushed += page->size();
 177     to->insert_last(page);
 178   }
 179 }
 180 
 181 void ZPageCache::flush(ZList<ZPage>* to, size_t requested) {
 182   size_t flushed = 0;
 183 
 184   // Prefer flushing large, then medium and last small pages
 185   flush_list(&_large, requested, to, &flushed);
 186   flush_list(&_medium, requested, to, &flushed);
 187   flush_per_numa_lists(&_small, requested, to, &flushed);
 188 
 189   ZStatInc(ZCounterPageCacheFlush, flushed);
 190 
 191   log_info(gc, heap)("Page Cache Flushed: "
 192                      SIZE_FORMAT "M requested, "
 193                      SIZE_FORMAT "M(" SIZE_FORMAT "M->" SIZE_FORMAT "M) flushed",
 194                      requested / M, flushed / M , _available / M, (_available - flushed) / M);
 195 
 196   _available -= flushed;
 197 }