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 
ZPageCache()37 ZPageCache::ZPageCache() :
38     _available(0),
39     _small(),
40     _medium(),
41     _large() {}
42 
alloc_small_page()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 
alloc_medium_page()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 
alloc_large_page(size_t size)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 
alloc_page(uint8_t type,size_t size)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 
free_page(ZPage * page)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 
flush_list(ZList<ZPage> * from,size_t requested,ZList<ZPage> * to,size_t * flushed)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 
flush_per_numa_lists(ZPerNUMA<ZList<ZPage>> * from,size_t requested,ZList<ZPage> * to,size_t * flushed)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 
flush(ZList<ZPage> * to,size_t requested)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 }
198