Skip to content

Commit 70f600f

Browse files
committed
Add vMemory::merge_leaf_pages_into_hugepages()
1 parent 9db9ba8 commit 70f600f

File tree

4 files changed

+69
-5
lines changed

4 files changed

+69
-5
lines changed

lib/tinykvm/amd64/paging.cpp

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,17 +236,21 @@ uint64_t setup_amd64_paging(vMemory& memory,
236236

237237
if (split_all_hugepages_during_loading)
238238
{
239+
// Stop at 1MB address, to prevent trampling user space
240+
uint64_t max = 512 * PD_PAGES;
241+
if (max > 512U * 1U)
242+
max = 512U * 1U;
243+
239244
// Split all hugepages into 4k pages for the entire memory area
240-
for (uint64_t i = base_2mb_page; i < 512*PD_PAGES; i++)
245+
for (uint64_t i = base_2mb_page+2; i < max; i++)
241246
{
242247
if (pd[i] & PDE64_PS) {
243248
// Set default attributes + free PTE page
244-
pd[i] = PDE64_PRESENT | PDE64_USER | PDE64_RW | free_page;
245-
// Fill new page with default attributes
249+
pd[i] = PDE64_PRESENT | heap_flags | free_page;
250+
// Fill new page with default heap attributes
246251
auto* pagetable = (uint64_t*) memory.at(free_page);
247252
for (uint64_t j = 0; j < 512; j++) {
248-
// Set writable 4k attributes
249-
uint64_t addr4k = (base_giga_page << 30) | (i << 21) | (j << 12);
253+
const uint64_t addr4k = (base_giga_page << 30) | (i << 21) | (j << 12);
250254
pagetable[j] =
251255
PDE64_PRESENT | heap_flags | addr4k;
252256
}
@@ -937,4 +941,54 @@ void WritablePage::set_protections(int prot)
937941
}
938942
}
939943

944+
size_t paging_merge_leaf_pages_into_hugepages(vMemory& memory)
945+
{
946+
unsigned merged_pages = 0;
947+
// Try to merge contiguous 4k pages with the same permissions,
948+
// ignoring accessed/dirty bits, into 2MB pages. We will not
949+
// try to optimize the page tables, rather just turn 2MB entry
950+
// pages directly into leaf 2MB pages.
951+
auto* pml4 = memory.page_at(memory.page_tables);
952+
for (size_t i = 0; i < 4; i++) { // 512GB entries
953+
if (pml4[i] & PDE64_PRESENT) {
954+
const auto [pdpt_base, pdpt_mem, pdpt_size] = pdpt_from_index(i, pml4);
955+
auto* pdpt = memory.page_at(pdpt_mem);
956+
for (uint64_t j = 0; j < 512; j++) { // 1GB entries
957+
if (pdpt[j] & PDE64_PRESENT) {
958+
const auto [pd_base, pd_mem, pd_size] = pd_from_index(j, pdpt_base, pdpt);
959+
auto* pd = memory.page_at(pd_mem);
960+
for (uint64_t k = 0; k < 512; k++) { // 2MB entries
961+
if (pd[k] & PDE64_PRESENT) {
962+
// Only consider page tables
963+
if (pd[k] & PDE64_PS)
964+
continue;
965+
const auto [pt_base, pt_mem, pt_size] = pt_from_index(k, pd_base, pd);
966+
auto* pt = memory.page_at(pt_mem);
967+
// Check if we can merge 512 entries
968+
bool can_merge = true;
969+
static constexpr uint64_t MERGE_MASK =
970+
PDE64_PRESENT | PDE64_RW | PDE64_USER | PDE64_NX | PDE64_CLONEABLE | PDE64_G;
971+
uint64_t first_entry = pt[0] & MERGE_MASK;
972+
for (size_t e = 1; e < 512; e++) {
973+
if ((pt[e] & MERGE_MASK) != first_entry) {
974+
can_merge = false;
975+
break;
976+
}
977+
}
978+
if (can_merge) {
979+
// Merge into 2MB page with same flags
980+
pd[k] = (pt[0] & PDE64_ADDR_MASK) | first_entry
981+
| PDE64_PS | PDE64_PRESENT;
982+
CLPRINT("Merged 4k pages into 2MB page at PD index %lu\n", k);
983+
merged_pages += 512;
984+
}
985+
} // pd present
986+
} // pd[k]
987+
} // pdpt present
988+
} // pdpt[j]
989+
} // pml4 present
990+
} // pml4[i]
991+
return merged_pages;
992+
} // paging_merge_leaf_pages_into_hugepages()
993+
940994
} // tinykvm

lib/tinykvm/amd64/paging.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ struct WritablePageOptions {
3232
};
3333
extern WritablePage writable_page_at(vMemory&, uint64_t addr, uint64_t flags, WritablePageOptions = {});
3434
extern char * readable_page_at(const vMemory&, uint64_t addr, uint64_t flags);
35+
// Merges leaf pages back into hugepages where possible. Returns number of merged pages.
36+
extern size_t paging_merge_leaf_pages_into_hugepages(vMemory&);
3537

3638
static inline bool page_is_zeroed(const uint64_t* page) {
3739
for (size_t i = 0; i < 512; i += 8) {

lib/tinykvm/memory.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,11 @@ MemoryBank::Page vMemory::new_hugepage()
479479
return banks.get_available_bank(512u).get_next_page(512u);
480480
}
481481

482+
size_t vMemory::merge_leaf_pages_into_hugepages()
483+
{
484+
return paging_merge_leaf_pages_into_hugepages(*this);
485+
}
486+
482487
char* vMemory::get_writable_page(uint64_t addr, uint64_t flags, bool zeroes, bool dirty)
483488
{
484489
// printf("*** Need a writable page at 0x%lX (%s)\n", addr, (zeroes) ? "zeroed" : "copy");

lib/tinykvm/memory.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ struct vMemory {
8282
size_t unlocked_memory_pages() const noexcept {
8383
return unlocked_pages;
8484
}
85+
/* Merge leaf pages back into hugepages where possible, ignoring
86+
access bits. This is done to reduce page walking overhead. */
87+
size_t merge_leaf_pages_into_hugepages();
8588

8689
VirtualMem vmem() const;
8790

0 commit comments

Comments
 (0)