diff --git a/core/mmu.cc b/core/mmu.cc index c4294a948ad7722cf4eee76f1eb643ee78b143d7..749c26851357b801989e569b34c8d22291923159 100644 --- a/core/mmu.cc +++ b/core/mmu.cc @@ -123,6 +123,10 @@ public: hw_ptep(const hw_ptep& a) : p(a.p) {} pt_element read() const { return *p; } void write(pt_element pte) { *const_cast<volatile u64*>(&p->x) = pte.x; } + bool compare_exchange(pt_element oldval, pt_element newval) { + std::atomic<u64> *x = reinterpret_cast<std::atomic<u64>*>(&p->x); + return x->compare_exchange_strong(oldval.x, newval.x, std::memory_order_relaxed); + } hw_ptep at(unsigned idx) { return hw_ptep(p + idx); } static hw_ptep force(pt_element* ptep) { return hw_ptep(ptep); } // no longer using this as a page table @@ -460,20 +464,29 @@ public: populate(fill_page *fill, unsigned int perm) : fill(fill), perm(perm) { } protected: virtual void small_page(hw_ptep ptep, uintptr_t offset){ + if (!ptep.read().empty()) { + return; + } phys page = virt_to_phys(memory::alloc_page()); fill->fill(phys_to_virt(page), offset, page_size); - assert(ptep.read().empty()); // don't populate an already populated page! - ptep.write(make_normal_pte(page, perm)); + if (!ptep.compare_exchange(make_empty_pte(), make_normal_pte(page, perm))) { + memory::free_page(phys_to_virt(page)); + } } virtual void huge_page(hw_ptep ptep, uintptr_t offset){ - phys page = virt_to_phys(memory::alloc_huge_page(huge_page_size)); - fill->fill(phys_to_virt(page), offset, huge_page_size); - if (!ptep.read().empty()) { - assert(!ptep.read().large()); // don't populate an already populated page! + auto pte = ptep.read(); + if (!pte.empty()) { + if (pte.large()) { + return; + } // held smallpages (already evacuated), now will be used for huge page free_intermediate_level(ptep); } - ptep.write(make_large_pte(page, perm)); + phys page = virt_to_phys(memory::alloc_huge_page(huge_page_size)); + fill->fill(phys_to_virt(page), offset, huge_page_size); + if (!ptep.compare_exchange(make_empty_pte(), make_large_pte(page, perm))) { + memory::free_huge_page(phys_to_virt(page), huge_page_size); + } } virtual bool should_allocate_intermediate(){ return true;