Skip to content

Commit b33f4aa

Browse files
committed
Merge into 2MB pages only if dirty rules are met
Also, stop allocating 4k leaf pages at 509 leaving some for the kernel
1 parent 70f600f commit b33f4aa

File tree

2 files changed

+38
-9
lines changed

2 files changed

+38
-9
lines changed

lib/tinykvm/amd64/paging.cpp

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -236,13 +236,14 @@ uint64_t setup_amd64_paging(vMemory& memory,
236236

237237
if (split_all_hugepages_during_loading)
238238
{
239+
static constexpr uint64_t MAX_FREE_PAGE = 509ULL * 0x1000;
239240
// Stop at 1MB address, to prevent trampling user space
240241
uint64_t max = 512 * PD_PAGES;
241242
if (max > 512U * 1U)
242243
max = 512U * 1U;
243244

244245
// Split all hugepages into 4k pages for the entire memory area
245-
for (uint64_t i = base_2mb_page+2; i < max; i++)
246+
for (uint64_t i = base_2mb_page+2; i < max && free_page < MAX_FREE_PAGE; i++)
246247
{
247248
if (pd[i] & PDE64_PS) {
248249
// Set default attributes + free PTE page
@@ -941,7 +942,7 @@ void WritablePage::set_protections(int prot)
941942
}
942943
}
943944

944-
size_t paging_merge_leaf_pages_into_hugepages(vMemory& memory)
945+
size_t paging_merge_leaf_pages_into_hugepages(vMemory& memory, bool merge_if_dirty)
945946
{
946947
unsigned merged_pages = 0;
947948
// Try to merge contiguous 4k pages with the same permissions,
@@ -960,26 +961,54 @@ size_t paging_merge_leaf_pages_into_hugepages(vMemory& memory)
960961
for (uint64_t k = 0; k < 512; k++) { // 2MB entries
961962
if (pd[k] & PDE64_PRESENT) {
962963
// Only consider page tables
963-
if (pd[k] & PDE64_PS)
964+
if ((pd[k] & PDE64_PS) != 0)
964965
continue;
965966
const auto [pt_base, pt_mem, pt_size] = pt_from_index(k, pd_base, pd);
966967
auto* pt = memory.page_at(pt_mem);
967968
// Check if we can merge 512 entries
968969
bool can_merge = true;
970+
bool any_dirty = (pt[0] & PDE64_DIRTY) != 0;
971+
bool all_dirty = (pt[0] & PDE64_DIRTY) != 0;
969972
static constexpr uint64_t MERGE_MASK =
970973
PDE64_PRESENT | PDE64_RW | PDE64_USER | PDE64_NX | PDE64_CLONEABLE | PDE64_G;
971-
uint64_t first_entry = pt[0] & MERGE_MASK;
974+
const uint64_t first_entry = pt[0] & MERGE_MASK;
975+
const uint64_t first_addr = pt[0] & PDE64_ADDR_MASK;
976+
// 2MB leaf page must be 2MB-aligned
977+
if ((first_addr & 0x1FFFFF) != 0) {
978+
continue;
979+
}
980+
// All entries must be present and have the same flags
981+
// and be contiguous in physical memory
982+
uint64_t expected_addr = first_addr;
972983
for (size_t e = 1; e < 512; e++) {
973984
if ((pt[e] & MERGE_MASK) != first_entry) {
974985
can_merge = false;
975986
break;
976987
}
988+
expected_addr += 0x1000;
989+
if ((pt[e] & PDE64_ADDR_MASK) != expected_addr) {
990+
can_merge = false;
991+
break;
992+
}
993+
if (pt[e] & PDE64_DIRTY) {
994+
any_dirty = true;
995+
}
996+
else {
997+
all_dirty = false;
998+
}
977999
}
978-
if (can_merge) {
1000+
// Perform merge if possible:
1001+
// - either all pages are clean
1002+
// - or merge_if_dirty is set (and zero or more pages are dirty)
1003+
// - or all pages are dirty
1004+
if (can_merge && (merge_if_dirty || !any_dirty || all_dirty)) {
9791005
// 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);
1006+
pd[k] = first_addr | first_entry | PDE64_PS | PDE64_PRESENT;
1007+
if (any_dirty) {
1008+
pd[k] |= PDE64_DIRTY;
1009+
}
1010+
//printf("Entry: PDPT[%lu] PD[%lu] merged 512 pages into 2MB page 0x%lX with flags 0x%lX\n",
1011+
// j, k, pd[k] & PDE64_ADDR_MASK, pd[k] & ~PDE64_ADDR_MASK);
9831012
merged_pages += 512;
9841013
}
9851014
} // pd present

lib/tinykvm/amd64/paging.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ struct WritablePageOptions {
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);
3535
// Merges leaf pages back into hugepages where possible. Returns number of merged pages.
36-
extern size_t paging_merge_leaf_pages_into_hugepages(vMemory&);
36+
extern size_t paging_merge_leaf_pages_into_hugepages(vMemory&, bool merge_if_dirty = false);
3737

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

0 commit comments

Comments
 (0)