--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/z/zPageCache.cpp Tue Jun 12 17:40:28 2018 +0200
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "precompiled.hpp"
+#include "gc/z/zList.inline.hpp"
+#include "gc/z/zNUMA.hpp"
+#include "gc/z/zPage.inline.hpp"
+#include "gc/z/zPageCache.hpp"
+#include "gc/z/zStat.hpp"
+#include "logging/log.hpp"
+
+static const ZStatCounter ZCounterPageCacheHitL1("Memory", "Page Cache Hit L1", ZStatUnitOpsPerSecond);
+static const ZStatCounter ZCounterPageCacheHitL2("Memory", "Page Cache Hit L2", ZStatUnitOpsPerSecond);
+static const ZStatCounter ZCounterPageCacheMiss("Memory", "Page Cache Miss", ZStatUnitOpsPerSecond);
+static const ZStatCounter ZCounterPageCacheFlush("Memory", "Page Cache Flush", ZStatUnitBytesPerSecond);
+
+ZPageCache::ZPageCache() :
+ _available(0),
+ _small(),
+ _medium(),
+ _large() {}
+
+ZPage* ZPageCache::alloc_small_page() {
+ const uint32_t numa_id = ZNUMA::id();
+ const uint32_t numa_count = ZNUMA::count();
+
+ // Try NUMA local page cache
+ ZPage* const l1_page = _small.get(numa_id).remove_first();
+ if (l1_page != NULL) {
+ ZStatInc(ZCounterPageCacheHitL1);
+ return l1_page;
+ }
+
+ // Try NUMA remote page cache(s)
+ uint32_t remote_numa_id = numa_id + 1;
+ const uint32_t remote_numa_count = numa_count - 1;
+ for (uint32_t i = 0; i < remote_numa_count; i++) {
+ if (remote_numa_id == numa_count) {
+ remote_numa_id = 0;
+ }
+
+ ZPage* const l2_page = _small.get(remote_numa_id).remove_first();
+ if (l2_page != NULL) {
+ ZStatInc(ZCounterPageCacheHitL2);
+ return l2_page;
+ }
+
+ remote_numa_id++;
+ }
+
+ ZStatInc(ZCounterPageCacheMiss);
+ return NULL;
+}
+
+ZPage* ZPageCache::alloc_medium_page() {
+ ZPage* const l1_page = _medium.remove_first();
+ if (l1_page != NULL) {
+ ZStatInc(ZCounterPageCacheHitL1);
+ return l1_page;
+ }
+
+ ZStatInc(ZCounterPageCacheMiss);
+ return NULL;
+}
+
+ZPage* ZPageCache::alloc_large_page(size_t size) {
+ // Find a page with the right size
+ ZListIterator<ZPage> iter(&_large);
+ for (ZPage* l1_page; iter.next(&l1_page);) {
+ if (l1_page->size() == size) {
+ // Page found
+ _large.remove(l1_page);
+ ZStatInc(ZCounterPageCacheHitL1);
+ return l1_page;
+ }
+ }
+
+ ZStatInc(ZCounterPageCacheMiss);
+ return NULL;
+}
+
+ZPage* ZPageCache::alloc_page(uint8_t type, size_t size) {
+ ZPage* page;
+
+ if (type == ZPageTypeSmall) {
+ page = alloc_small_page();
+ } else if (type == ZPageTypeMedium) {
+ page = alloc_medium_page();
+ } else {
+ page = alloc_large_page(size);
+ }
+
+ if (page != NULL) {
+ _available -= page->size();
+ }
+
+ return page;
+}
+
+void ZPageCache::free_page(ZPage* page) {
+ assert(!page->is_active(), "Invalid page state");
+ assert(!page->is_pinned(), "Invalid page state");
+ assert(!page->is_detached(), "Invalid page state");
+
+ const uint8_t type = page->type();
+ if (type == ZPageTypeSmall) {
+ _small.get(page->numa_id()).insert_first(page);
+ } else if (type == ZPageTypeMedium) {
+ _medium.insert_first(page);
+ } else {
+ _large.insert_first(page);
+ }
+
+ _available += page->size();
+}
+
+void ZPageCache::flush_list(ZList<ZPage>* from, size_t requested, ZList<ZPage>* to, size_t* flushed) {
+ while (*flushed < requested) {
+ // Flush least recently used
+ ZPage* const page = from->remove_last();
+ if (page == NULL) {
+ break;
+ }
+
+ *flushed += page->size();
+ to->insert_last(page);
+ }
+}
+
+void ZPageCache::flush_per_numa_lists(ZPerNUMA<ZList<ZPage> >* from, size_t requested, ZList<ZPage>* to, size_t* flushed) {
+ const uint32_t numa_count = ZNUMA::count();
+ uint32_t numa_empty = 0;
+ uint32_t numa_next = 0;
+
+ // Flush lists round-robin
+ while (*flushed < requested) {
+ ZPage* const page = from->get(numa_next).remove_last();
+
+ if (++numa_next == numa_count) {
+ numa_next = 0;
+ }
+
+ if (page == NULL) {
+ // List is empty
+ if (++numa_empty == numa_count) {
+ // All lists are empty
+ break;
+ }
+
+ // Try next list
+ continue;
+ }
+
+ // Flush page
+ numa_empty = 0;
+ *flushed += page->size();
+ to->insert_last(page);
+ }
+}
+
+void ZPageCache::flush(ZList<ZPage>* to, size_t requested) {
+ size_t flushed = 0;
+
+ // Prefer flushing large, then medium and last small pages
+ flush_list(&_large, requested, to, &flushed);
+ flush_list(&_medium, requested, to, &flushed);
+ flush_per_numa_lists(&_small, requested, to, &flushed);
+
+ ZStatInc(ZCounterPageCacheFlush, flushed);
+
+ log_info(gc, heap)("Page Cache Flushed: "
+ SIZE_FORMAT "M requested, "
+ SIZE_FORMAT "M(" SIZE_FORMAT "M->" SIZE_FORMAT "M) flushed",
+ requested / M, flushed / M , _available / M, (_available - flushed) / M);
+
+ _available -= flushed;
+}