kernel/vm: Consolidate implementations of VMTranslationMap::UnmapArea.

This adds some new parameters to UnmapPage and UnmapPages. The important
one is a "_flags" pointer to UnmapPage, which if specified will be
filled with the page flags instead of PageUnmapped() being called
(and Flush() won't be invoked, either.)

This removes the remaining PAGE_STATE_* changes from VM architecture code.

Change-Id: Iacbc424dd8a75a79986edcd7f04d15a10f773c87
Reviewed-on: https://review.haiku-os.org/c/haiku/+/8728
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Augustin Cavalier
2024-12-30 18:50:10 +00:00
committed by waddlesplash
parent 783347df2b
commit 7651b97c0a
20 changed files with 321 additions and 1127 deletions
+5 -2
View File
@@ -44,9 +44,12 @@ public:
// map not locked
virtual status_t UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue) = 0;
bool updatePageQueue,
bool deletingAddressSpace = false,
uint32* _flags = NULL) = 0;
virtual void UnmapPages(VMArea* area, addr_t base,
size_t size, bool updatePageQueue);
size_t size, bool updatePageQueue,
bool deletingAddressSpace = false);
virtual void UnmapArea(VMArea* area,
bool deletingAddressSpace,
bool ignoreTopCachePageFlags);
@@ -335,21 +335,19 @@ ARMVMTranslationMap32Bit::DebugMarkRangePresent(addr_t start, addr_t end,
}
/*! Caller must have locked the cache of the page to be unmapped.
This object shouldn't be locked.
*/
status_t
ARMVMTranslationMap32Bit::UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace, uint32* _flags)
{
ASSERT(address % B_PAGE_SIZE == 0);
page_directory_entry* pd = fPagingStructures->pgdir_virt;
ASSERT(_flags == NULL || !updatePageQueue);
TRACE("ARMVMTranslationMap32Bit::UnmapPage(%#" B_PRIxADDR ")\n", address);
RecursiveLocker locker(fLock);
page_directory_entry* pd = fPagingStructures->pgdir_virt;
int index = VADDR_TO_PDENT(address);
if ((pd[index] & ARM_PDE_TYPE_MASK) == 0)
return B_ENTRY_NOT_FOUND;
@@ -372,13 +370,15 @@ ARMVMTranslationMap32Bit::UnmapPage(VMArea* area, addr_t address,
fMapCount--;
if ((oldEntry & ARM_MMU_L2_FLAG_AP0) != 0) {
// Note, that we only need to invalidate the address, if the
// accessed flags was set, since only then the entry could have been
// in any TLB.
InvalidatePage(address);
Flush();
if (!deletingAddressSpace)
InvalidatePage(address);
if (_flags == NULL)
Flush();
// NOTE: Between clearing the page table entry and Flush() other
// processors (actually even this processor with another thread of the
@@ -392,12 +392,21 @@ ARMVMTranslationMap32Bit::UnmapPage(VMArea* area, addr_t address,
// (cf. pmap_remove_all()), unless I've missed something.
}
locker.Detach();
// PageUnmapped() will unlock for us
if (_flags == NULL) {
locker.Detach();
// PageUnmapped() will unlock for us
PageUnmapped(area, (oldEntry & ARM_PTE_ADDRESS_MASK) / B_PAGE_SIZE,
(oldEntry & ARM_MMU_L2_FLAG_AP0) != 0, false /*(oldEntry & ARM_PTE_DIRTY) != 0*/,
updatePageQueue);
PageUnmapped(area, (oldEntry & ARM_PTE_ADDRESS_MASK) / B_PAGE_SIZE,
(oldEntry & ARM_MMU_L2_FLAG_AP0) != 0, (oldEntry & ARM_MMU_L2_FLAG_AP2) != 0,
updatePageQueue);
} else {
uint32 flags = PAGE_PRESENT;
if ((oldEntry & ARM_MMU_L2_FLAG_AP0) != 0)
flags |= PAGE_ACCESSED;
if ((oldEntry & ARM_MMU_L2_FLAG_AP2) != 0)
flags |= PAGE_MODIFIED;
*_flags = flags;
}
return B_OK;
}
@@ -405,7 +414,7 @@ ARMVMTranslationMap32Bit::UnmapPage(VMArea* area, addr_t address,
void
ARMVMTranslationMap32Bit::UnmapPages(VMArea* area, addr_t base, size_t size,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace)
{
if (size == 0)
return;
@@ -450,7 +459,8 @@ ARMVMTranslationMap32Bit::UnmapPages(VMArea* area, addr_t base, size_t size,
// Note, that we only need to invalidate the address, if the
// accessed flags was set, since only then the entry could have
// been in any TLB.
InvalidatePage(start);
if (!deletingAddressSpace)
InvalidatePage(start);
}
if (area->cache_type != CACHE_TYPE_DEVICE) {
@@ -482,108 +492,6 @@ ARMVMTranslationMap32Bit::UnmapPages(VMArea* area, addr_t base, size_t size,
}
void
ARMVMTranslationMap32Bit::UnmapArea(VMArea* area, bool deletingAddressSpace,
bool ignoreTopCachePageFlags)
{
if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) {
ARMVMTranslationMap32Bit::UnmapPages(area, area->Base(), area->Size(),
true);
return;
}
bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
page_directory_entry* pd = fPagingStructures->pgdir_virt;
RecursiveLocker locker(fLock);
VMAreaMappings mappings;
mappings.TakeFrom(&area->mappings);
for (VMAreaMappings::Iterator it = mappings.GetIterator();
vm_page_mapping* mapping = it.Next();) {
vm_page* page = mapping->page;
page->mappings.Remove(mapping);
VMCache* cache = page->Cache();
bool pageFullyUnmapped = false;
if (!page->IsMapped()) {
atomic_add(&gMappedPagesCount, -1);
pageFullyUnmapped = true;
}
if (unmapPages || cache != area->cache) {
addr_t address = area->Base()
+ ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset);
int index = VADDR_TO_PDENT(address);
if ((pd[index] & ARM_PDE_TYPE_MASK) == 0) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no page dir entry", page, area, address);
continue;
}
ThreadCPUPinner pinner(thread_get_current_thread());
page_table_entry* pt
= (page_table_entry*)fPageMapper->GetPageTableAt(
pd[index] & ARM_PDE_ADDRESS_MASK);
page_table_entry oldEntry
= ARMPagingMethod32Bit::ClearPageTableEntry(
&pt[VADDR_TO_PTENT(address)]);
pinner.Unlock();
if ((oldEntry & ARM_PTE_TYPE_MASK) == 0) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no page table entry", page, area, address);
continue;
}
// transfer the accessed/dirty flags to the page and invalidate
// the mapping, if necessary
if ((oldEntry & ARM_MMU_L2_FLAG_AP0) != 0) {
page->accessed = true;
if (!deletingAddressSpace)
InvalidatePage(address);
}
if (false /*(oldEntry & ARM_PTE_DIRTY) != 0*/)
page->modified = true;
if (pageFullyUnmapped) {
DEBUG_PAGE_ACCESS_START(page);
if (cache->temporary)
vm_page_set_state(page, PAGE_STATE_INACTIVE);
else if (page->modified)
vm_page_set_state(page, PAGE_STATE_MODIFIED);
else
vm_page_set_state(page, PAGE_STATE_CACHED);
DEBUG_PAGE_ACCESS_END(page);
}
}
fMapCount--;
}
Flush();
// flush explicitely, since we directly use the lock
locker.Unlock();
bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
while (vm_page_mapping* mapping = mappings.RemoveHead())
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
}
status_t
ARMVMTranslationMap32Bit::Query(addr_t va, phys_addr_t *_physical,
uint32 *_flags)
@@ -31,12 +31,11 @@ struct ARMVMTranslationMap32Bit : ARMVMTranslationMap {
bool markPresent);
virtual status_t UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue);
bool updatePageQueue,
bool deletingAddressSpace, uint32* _flags);
virtual void UnmapPages(VMArea* area, addr_t base,
size_t size, bool updatePageQueue);
virtual void UnmapArea(VMArea* area,
bool deletingAddressSpace,
bool ignoreTopCachePageFlags);
size_t size, bool updatePageQueue,
bool deletingAddressSpace);
virtual status_t Query(addr_t virtualAddress,
phys_addr_t* _physicalAddress,
@@ -400,7 +400,8 @@ flush_va_if_accessed(uint64_t pte, addr_t va, int asid)
}
bool
VMSAv8TranslationMap::FlushVAIfAccessed(uint64_t pte, addr_t va) {
VMSAv8TranslationMap::FlushVAIfAccessed(uint64_t pte, addr_t va)
{
InterruptsSpinLocker locker(sAsidLock);
return flush_va_if_accessed(pte, va, fASID);
}
@@ -633,8 +634,12 @@ VMSAv8TranslationMap::Unmap(addr_t start, addr_t end)
status_t
VMSAv8TranslationMap::UnmapPage(VMArea* area, addr_t address, bool updatePageQueue)
VMSAv8TranslationMap::UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue, bool deletingAddressSpace, uint32* _flags)
{
ASSERT(address % B_PAGE_SIZE == 0);
ASSERT(_flags == NULL || !updatePageQueue);
TRACE("VMSAv8TranslationMap::UnmapPage(0x%" B_PRIxADDR "(%s), 0x%"
B_PRIxADDR ", %d)\n", (addr_t)area, area->name, address,
updatePageQueue);
@@ -647,23 +652,35 @@ VMSAv8TranslationMap::UnmapPage(VMArea* area, addr_t address, bool updatePageQue
ProcessRange(fPageTable, fInitialLevel, address, B_PAGE_SIZE, nullptr,
[=, &oldPte](uint64_t* ptePtr, uint64_t effectiveVa) {
oldPte = atomic_get_and_set64((int64_t*)ptePtr, 0);
FlushVAIfAccessed(oldPte, effectiveVa);
if (!deletingAddressSpace)
FlushVAIfAccessed(oldPte, effectiveVa);
});
if ((oldPte & kPteValidMask) == 0)
return B_ENTRY_NOT_FOUND;
pinner.Unlock();
locker.Detach();
PageUnmapped(area, (oldPte & kPteAddrMask) >> fPageBits, is_pte_accessed(oldPte),
is_pte_dirty(oldPte), updatePageQueue);
if (_flags != NULL) {
locker.Detach();
PageUnmapped(area, (oldPte & kPteAddrMask) >> fPageBits, is_pte_accessed(oldPte),
is_pte_dirty(oldPte), updatePageQueue);
} else {
uint32 flags = PAGE_PRESENT;
if (is_pte_accessed(oldPte))
flags |= PAGE_ACCESSED;
if (is_pte_dirty(oldPte))
flags |= PAGE_MODIFIED;
*_flags = flags;
}
return B_OK;
}
void
VMSAv8TranslationMap::UnmapPages(VMArea* area, addr_t address, size_t size, bool updatePageQueue)
VMSAv8TranslationMap::UnmapPages(VMArea* area, addr_t address, size_t size,
bool updatePageQueue, bool deletingAddressSpace)
{
TRACE("VMSAv8TranslationMap::UnmapPages(0x%" B_PRIxADDR "(%s), 0x%"
B_PRIxADDR ", 0x%" B_PRIxSIZE ", %d)\n", (addr_t)area,
@@ -707,99 +724,6 @@ VMSAv8TranslationMap::UnmapPages(VMArea* area, addr_t address, size_t size, bool
}
void
VMSAv8TranslationMap::UnmapArea(VMArea* area, bool deletingAddressSpace,
bool ignoreTopCachePageFlags)
{
TRACE("VMSAv8TranslationMap::UnmapArea(0x%" B_PRIxADDR "(%s), 0x%"
B_PRIxADDR ", 0x%" B_PRIxSIZE ", %d, %d)\n", (addr_t)area,
area->name, area->Base(), area->Size(), deletingAddressSpace,
ignoreTopCachePageFlags);
if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) {
UnmapPages(area, area->Base(), area->Size(), true);
return;
}
bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
RecursiveLocker locker(fLock);
ThreadCPUPinner pinner(thread_get_current_thread());
VMAreaMappings mappings;
mappings.TakeFrom(&area->mappings);
for (VMAreaMappings::Iterator it = mappings.GetIterator();
vm_page_mapping* mapping = it.Next();) {
vm_page* page = mapping->page;
page->mappings.Remove(mapping);
VMCache* cache = page->Cache();
bool pageFullyUnmapped = false;
if (!page->IsMapped()) {
atomic_add(&gMappedPagesCount, -1);
pageFullyUnmapped = true;
}
if (unmapPages || cache != area->cache) {
addr_t address = area->Base()
+ ((page->cache_offset * B_PAGE_SIZE)
- area->cache_offset);
uint64_t oldPte = 0;
ProcessRange(fPageTable, fInitialLevel, address, B_PAGE_SIZE, nullptr,
[=, &oldPte](uint64_t* ptePtr, uint64_t effectiveVa) {
oldPte = atomic_get_and_set64((int64_t*)ptePtr, 0);
if (!deletingAddressSpace)
FlushVAIfAccessed(oldPte, effectiveVa);
});
if ((oldPte & kPteValidMask) == 0) {
panic("page %p has mapping for area %p "
"(%#" B_PRIxADDR "), but has no "
"page table", page, area, address);
continue;
}
// transfer the accessed/dirty flags to the page and
// invalidate the mapping, if necessary
if (is_pte_dirty(oldPte))
page->modified = true;
if (is_pte_accessed(oldPte))
page->accessed = true;
if (pageFullyUnmapped) {
DEBUG_PAGE_ACCESS_START(page);
if (cache->temporary) {
vm_page_set_state(page,
PAGE_STATE_INACTIVE);
} else if (page->modified) {
vm_page_set_state(page,
PAGE_STATE_MODIFIED);
} else {
vm_page_set_state(page,
PAGE_STATE_CACHED);
}
DEBUG_PAGE_ACCESS_END(page);
}
}
}
locker.Unlock();
bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
while (vm_page_mapping* mapping = mappings.RemoveHead())
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
}
bool
VMSAv8TranslationMap::ValidateVa(addr_t va)
{
@@ -57,12 +57,11 @@ public:
virtual status_t Unmap(addr_t start, addr_t end);
virtual status_t UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue);
bool updatePageQueue,
bool deletingAddressSpace, uint32* _flags);
virtual void UnmapPages(VMArea* area, addr_t base,
size_t size, bool updatePageQueue);
virtual void UnmapArea(VMArea* area,
bool deletingAddressSpace,
bool ignoreTopCachePageFlags);
size_t size, bool updatePageQueue,
bool deletingAddressSpace);
virtual status_t Query(addr_t virtualAddress,
phys_addr_t* _physicalAddress,
@@ -418,14 +418,12 @@ M68KVMTranslationMap040::Unmap(addr_t start, addr_t end)
}
/*! Caller must have locked the cache of the page to be unmapped.
This object shouldn't be locked.
*/
status_t
M68KVMTranslationMap040::UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace, uint32* _flags)
{
ASSERT(address % B_PAGE_SIZE == 0);
ASSERT(_flags == NULL || !updatePageQueue);
page_root_entry* pr = fPagingStructures->pgroot_virt;
@@ -475,8 +473,11 @@ M68KVMTranslationMap040::UnmapPage(VMArea* area, addr_t address,
// Note, that we only need to invalidate the address, if the
// accessed flags was set, since only then the entry could have been
// in any TLB.
InvalidatePage(address);
Flush();
if (!deletingAddressSpace)
InvalidatePage(address);
if (_flags == NULL)
Flush();
// NOTE: Between clearing the page table entry and Flush() other
// processors (actually even this processor with another thread of the
@@ -490,12 +491,21 @@ M68KVMTranslationMap040::UnmapPage(VMArea* area, addr_t address,
// (cf. pmap_remove_all()), unless I've missed something.
}
locker.Detach();
// PageUnmapped() will unlock for us
if (_flags == NULL) {
locker.Detach();
// PageUnmapped() will unlock for us
PageUnmapped(area, (oldEntry & M68K_PTE_ADDRESS_MASK) / B_PAGE_SIZE,
(oldEntry & M68K_PTE_ACCESSED) != 0, (oldEntry & M68K_PTE_DIRTY) != 0,
updatePageQueue);
PageUnmapped(area, (oldEntry & M68K_PTE_ADDRESS_MASK) / B_PAGE_SIZE,
(oldEntry & M68K_PTE_ACCESSED) != 0, (oldEntry & M68K_PTE_DIRTY) != 0,
updatePageQueue);
} else {
uint32 flags = PAGE_PRESENT;
if ((oldEntry & M68K_PTE_ACCESSED) != 0)
flags |= PAGE_ACCESSED;
if ((oldEntry & M68K_PTE_DIRTY) != 0)
flags |= PAGE_MODIFIED;
*_flags = flags;
}
return B_OK;
}
@@ -503,7 +513,7 @@ M68KVMTranslationMap040::UnmapPage(VMArea* area, addr_t address,
void
M68KVMTranslationMap040::UnmapPages(VMArea* area, addr_t base, size_t size,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace)
{
int index;
@@ -570,7 +580,8 @@ M68KVMTranslationMap040::UnmapPages(VMArea* area, addr_t base, size_t size,
// Note, that we only need to invalidate the address, if the
// accessed flags was set, since only then the entry could have
// been in any TLB.
InvalidatePage(start);
if (!deletingAddressSpace)
InvalidatePage(start);
}
if (area->cache_type != CACHE_TYPE_DEVICE) {
@@ -602,123 +613,6 @@ M68KVMTranslationMap040::UnmapPages(VMArea* area, addr_t base, size_t size,
}
void
M68KVMTranslationMap040::UnmapArea(VMArea* area, bool deletingAddressSpace,
bool ignoreTopCachePageFlags)
{
if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) {
M68KVMTranslationMap040::UnmapPages(area, area->Base(), area->Size(),
true);
return;
}
bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
page_root_entry* pr = fPagingStructures->pgroot_virt;
RecursiveLocker locker(fLock);
VMAreaMappings mappings;
mappings.TakeFrom(&area->mappings);
for (VMAreaMappings::Iterator it = mappings.GetIterator();
vm_page_mapping* mapping = it.Next();) {
vm_page* page = mapping->page;
page->mappings.Remove(mapping);
VMCache* cache = page->Cache();
bool pageFullyUnmapped = false;
if (!page->IsMapped()) {
atomic_add(&gMappedPagesCount, -1);
pageFullyUnmapped = true;
}
if (unmapPages || cache != area->cache) {
addr_t address = area->Base()
+ ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset);
int index;
index = VADDR_TO_PRENT(address);
if (PRE_TYPE(pr[index]) != DT_ROOT) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no page root entry", page, area, address);
continue;
}
ThreadCPUPinner pinner(thread_get_current_thread());
page_directory_entry* pd
= (page_directory_entry*)MapperGetPageTableAt(
pr[index] & M68K_PRE_ADDRESS_MASK);
index = VADDR_TO_PDENT(address);
if (PDE_TYPE(pr[index]) != DT_DIR) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no page dir entry", page, area, address);
continue;
}
page_table_entry* pt
= (page_table_entry*)MapperGetPageTableAt(
pd[index] & M68K_PDE_ADDRESS_MASK);
//XXX:M68K: DT_INDIRECT here?
page_table_entry oldEntry
= M68KPagingMethod040::ClearPageTableEntry(
&pt[VADDR_TO_PTENT(address)]);
pinner.Unlock();
if (PTE_TYPE(oldEntry) != DT_PAGE) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no page table entry", page, area, address);
continue;
}
// transfer the accessed/dirty flags to the page and invalidate
// the mapping, if necessary
if ((oldEntry & M68K_PTE_ACCESSED) != 0) {
page->accessed = true;
if (!deletingAddressSpace)
InvalidatePage(address);
}
if ((oldEntry & M68K_PTE_DIRTY) != 0)
page->modified = true;
if (pageFullyUnmapped) {
DEBUG_PAGE_ACCESS_START(page);
if (cache->temporary)
vm_page_set_state(page, PAGE_STATE_INACTIVE);
else if (page->modified)
vm_page_set_state(page, PAGE_STATE_MODIFIED);
else
vm_page_set_state(page, PAGE_STATE_CACHED);
DEBUG_PAGE_ACCESS_END(page);
}
}
fMapCount--;
}
Flush();
// flush explicitely, since we directly use the lock
locker.Unlock();
bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
while (vm_page_mapping* mapping = mappings.RemoveHead())
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
}
status_t
M68KVMTranslationMap040::Query(addr_t va, phys_addr_t *_physical,
uint32 *_flags)
@@ -27,12 +27,11 @@ struct M68KVMTranslationMap040 : M68KVMTranslationMap {
virtual status_t Unmap(addr_t start, addr_t end);
virtual status_t UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue);
bool updatePageQueue,
bool deletingAddressSpace, uint32* _flags);
virtual void UnmapPages(VMArea* area, addr_t base,
size_t size, bool updatePageQueue);
virtual void UnmapArea(VMArea* area,
bool deletingAddressSpace,
bool ignoreTopCachePageFlags);
size_t size, bool updatePageQueue,
bool deletingAddressSpace);
virtual status_t Query(addr_t virtualAddress,
phys_addr_t* _physicalAddress,
@@ -644,14 +644,12 @@ PPCVMTranslationMap460::DebugMarkRangePresent(addr_t start, addr_t end,
}
/*! Caller must have locked the cache of the page to be unmapped.
This object shouldn't be locked.
*/
status_t
PPCVMTranslationMap460::UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace, uint32* _flags)
{
ASSERT(address % B_PAGE_SIZE == 0);
ASSERT(_flags == NULL || !updatePageQueue);
RecursiveLocker locker(fLock);
@@ -675,10 +673,19 @@ PPCVMTranslationMap460::UnmapPage(VMArea* area, addr_t address,
fMapCount--;
locker.Detach();
// PageUnmapped() will unlock for us
if (_flags == NULL) {
locker.Detach();
// PageUnmapped() will unlock for us
PageUnmapped(area, pageNumber, accessed, modified, updatePageQueue);
PageUnmapped(area, pageNumber, accessed, modified, updatePageQueue);
} else {
uint32 flags = PAGE_PRESENT;
if (accessed)
flags |= PAGE_ACCESSED;
if (modified)
flags |= PAGE_MODIFIED;
*_flags = flags;
}
return B_OK;
@@ -747,7 +754,7 @@ PPCVMTranslationMap460::UnmapPage(VMArea* area, addr_t address,
void
PPCVMTranslationMap460::UnmapPages(VMArea* area, addr_t base, size_t size,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace)
{
panic("%s: UNIMPLEMENTED", __FUNCTION__);
#if 0//X86
@@ -794,7 +801,8 @@ PPCVMTranslationMap460::UnmapPages(VMArea* area, addr_t base, size_t size,
// Note, that we only need to invalidate the address, if the
// accessed flags was set, since only then the entry could have
// been in any TLB.
InvalidatePage(start);
if (!deletingAddressSpace)
InvalidatePage(start);
}
if (area->cache_type != CACHE_TYPE_DEVICE) {
@@ -827,111 +835,6 @@ PPCVMTranslationMap460::UnmapPages(VMArea* area, addr_t base, size_t size,
}
void
PPCVMTranslationMap460::UnmapArea(VMArea* area, bool deletingAddressSpace,
bool ignoreTopCachePageFlags)
{
panic("%s: UNIMPLEMENTED", __FUNCTION__);
#if 0//X86
if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) {
PPCVMTranslationMap460::UnmapPages(area, area->Base(), area->Size(),
true);
return;
}
bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
page_directory_entry* pd = fPagingStructures->pgdir_virt;
RecursiveLocker locker(fLock);
VMAreaMappings mappings;
mappings.TakeFrom(&area->mappings);
for (VMAreaMappings::Iterator it = mappings.GetIterator();
vm_page_mapping* mapping = it.Next();) {
vm_page* page = mapping->page;
page->mappings.Remove(mapping);
VMCache* cache = page->Cache();
bool pageFullyUnmapped = false;
if (!page->IsMapped()) {
atomic_add(&gMappedPagesCount, -1);
pageFullyUnmapped = true;
}
if (unmapPages || cache != area->cache) {
addr_t address = area->Base()
+ ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset);
int index = VADDR_TO_PDENT(address);
if ((pd[index] & PPC_PDE_PRESENT) == 0) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no page dir entry", page, area, address);
continue;
}
ThreadCPUPinner pinner(thread_get_current_thread());
page_table_entry* pt
= (page_table_entry*)fPageMapper->GetPageTableAt(
pd[index] & PPC_PDE_ADDRESS_MASK);
page_table_entry oldEntry
= PPCPagingMethod460::ClearPageTableEntry(
&pt[VADDR_TO_PTENT(address)]);
pinner.Unlock();
if ((oldEntry & PPC_PTE_PRESENT) == 0) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no page table entry", page, area, address);
continue;
}
// transfer the accessed/dirty flags to the page and invalidate
// the mapping, if necessary
if ((oldEntry & PPC_PTE_ACCESSED) != 0) {
page->accessed = true;
if (!deletingAddressSpace)
InvalidatePage(address);
}
if ((oldEntry & PPC_PTE_DIRTY) != 0)
page->modified = true;
if (pageFullyUnmapped) {
DEBUG_PAGE_ACCESS_START(page);
if (cache->temporary)
vm_page_set_state(page, PAGE_STATE_INACTIVE);
else if (page->modified)
vm_page_set_state(page, PAGE_STATE_MODIFIED);
else
vm_page_set_state(page, PAGE_STATE_CACHED);
DEBUG_PAGE_ACCESS_END(page);
}
}
fMapCount--;
}
Flush();
// flush explicitely, since we directly use the lock
locker.Unlock();
bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
while (vm_page_mapping* mapping = mappings.RemoveHead())
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
#endif
}
status_t
PPCVMTranslationMap460::Query(addr_t va, phys_addr_t *_outPhysical,
uint32 *_outFlags)
@@ -42,12 +42,11 @@ struct PPCVMTranslationMap460 : PPCVMTranslationMap {
bool markPresent);
virtual status_t UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue);
bool updatePageQueue,
bool deletingAddressSpace, uint32* _flags);
virtual void UnmapPages(VMArea* area, addr_t base,
size_t size, bool updatePageQueue);
virtual void UnmapArea(VMArea* area,
bool deletingAddressSpace,
bool ignoreTopCachePageFlags);
size_t size, bool updatePageQueue,
bool deletingAddressSpace);
virtual status_t Query(addr_t virtualAddress,
phys_addr_t* _physicalAddress,
@@ -644,14 +644,12 @@ PPCVMTranslationMapClassic::DebugMarkRangePresent(addr_t start, addr_t end,
}
/*! Caller must have locked the cache of the page to be unmapped.
This object shouldn't be locked.
*/
status_t
PPCVMTranslationMapClassic::UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace, uint32* _flags)
{
ASSERT(address % B_PAGE_SIZE == 0);
ASSERT(_flags == NULL || !updatePageQueue);
RecursiveLocker locker(fLock);
@@ -675,10 +673,19 @@ PPCVMTranslationMapClassic::UnmapPage(VMArea* area, addr_t address,
fMapCount--;
locker.Detach();
// PageUnmapped() will unlock for us
if (_flags == NULL) {
locker.Detach();
// PageUnmapped() will unlock for us
PageUnmapped(area, pageNumber, accessed, modified, updatePageQueue);
PageUnmapped(area, pageNumber, accessed, modified, updatePageQueue);
} else {
uint32 flags = PAGE_PRESENT;
if (accessed)
flags |= PAGE_ACCESSED;
if (modified)
flags |= PAGE_MODIFIED;
*_flags = flags;
}
return B_OK;
@@ -747,7 +754,7 @@ PPCVMTranslationMapClassic::UnmapPage(VMArea* area, addr_t address,
void
PPCVMTranslationMapClassic::UnmapPages(VMArea* area, addr_t base, size_t size,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace)
{
panic("%s: UNIMPLEMENTED", __FUNCTION__);
#if 0//X86
@@ -794,7 +801,8 @@ PPCVMTranslationMapClassic::UnmapPages(VMArea* area, addr_t base, size_t size,
// Note, that we only need to invalidate the address, if the
// accessed flags was set, since only then the entry could have
// been in any TLB.
InvalidatePage(start);
if (!deletingAddressSpace)
InvalidatePage(start);
}
if (area->cache_type != CACHE_TYPE_DEVICE) {
@@ -827,111 +835,6 @@ PPCVMTranslationMapClassic::UnmapPages(VMArea* area, addr_t base, size_t size,
}
void
PPCVMTranslationMapClassic::UnmapArea(VMArea* area, bool deletingAddressSpace,
bool ignoreTopCachePageFlags)
{
panic("%s: UNIMPLEMENTED", __FUNCTION__);
#if 0//X86
if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) {
PPCVMTranslationMapClassic::UnmapPages(area, area->Base(), area->Size(),
true);
return;
}
bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
page_directory_entry* pd = fPagingStructures->pgdir_virt;
RecursiveLocker locker(fLock);
VMAreaMappings mappings;
mappings.TakeFrom(&area->mappings);
for (VMAreaMappings::Iterator it = mappings.GetIterator();
vm_page_mapping* mapping = it.Next();) {
vm_page* page = mapping->page;
page->mappings.Remove(mapping);
VMCache* cache = page->Cache();
bool pageFullyUnmapped = false;
if (!page->IsMapped()) {
atomic_add(&gMappedPagesCount, -1);
pageFullyUnmapped = true;
}
if (unmapPages || cache != area->cache) {
addr_t address = area->Base()
+ ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset);
int index = VADDR_TO_PDENT(address);
if ((pd[index] & PPC_PDE_PRESENT) == 0) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no page dir entry", page, area, address);
continue;
}
ThreadCPUPinner pinner(thread_get_current_thread());
page_table_entry* pt
= (page_table_entry*)fPageMapper->GetPageTableAt(
pd[index] & PPC_PDE_ADDRESS_MASK);
page_table_entry oldEntry
= PPCPagingMethodClassic::ClearPageTableEntry(
&pt[VADDR_TO_PTENT(address)]);
pinner.Unlock();
if ((oldEntry & PPC_PTE_PRESENT) == 0) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no page table entry", page, area, address);
continue;
}
// transfer the accessed/dirty flags to the page and invalidate
// the mapping, if necessary
if ((oldEntry & PPC_PTE_ACCESSED) != 0) {
page->accessed = true;
if (!deletingAddressSpace)
InvalidatePage(address);
}
if ((oldEntry & PPC_PTE_DIRTY) != 0)
page->modified = true;
if (pageFullyUnmapped) {
DEBUG_PAGE_ACCESS_START(page);
if (cache->temporary)
vm_page_set_state(page, PAGE_STATE_INACTIVE);
else if (page->modified)
vm_page_set_state(page, PAGE_STATE_MODIFIED);
else
vm_page_set_state(page, PAGE_STATE_CACHED);
DEBUG_PAGE_ACCESS_END(page);
}
}
fMapCount--;
}
Flush();
// flush explicitely, since we directly use the lock
locker.Unlock();
bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
while (vm_page_mapping* mapping = mappings.RemoveHead())
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
#endif
}
status_t
PPCVMTranslationMapClassic::Query(addr_t va, phys_addr_t *_outPhysical,
uint32 *_outFlags)
@@ -42,12 +42,11 @@ struct PPCVMTranslationMapClassic : PPCVMTranslationMap {
bool markPresent);
virtual status_t UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue);
bool updatePageQueue,
bool deletingAddressSpace, uint32* _flags);
virtual void UnmapPages(VMArea* area, addr_t base,
size_t size, bool updatePageQueue);
virtual void UnmapArea(VMArea* area,
bool deletingAddressSpace,
bool ignoreTopCachePageFlags);
size_t size, bool updatePageQueue,
bool deletingAddressSpace);
virtual status_t Query(addr_t virtualAddress,
phys_addr_t* _physicalAddress,
@@ -320,23 +320,13 @@ RISCV64VMTranslationMap::DebugMarkRangePresent(addr_t start, addr_t end,
}
/*
Things need to be done when unmapping VMArea pages
update vm_page::accessed, modified
MMIO pages:
just unmap
wired pages:
decrement wired count
non-wired pages:
remove from VMArea and vm_page `mappings` list
wired and non-wird pages
vm_page_set_state
*/
status_t
RISCV64VMTranslationMap::UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace, uint32* _flags)
{
ASSERT(address % B_PAGE_SIZE == 0);
ASSERT(_flags == NULL || !updatePageQueue);
TRACE("RISCV64VMTranslationMap::UnmapPage(0x%" B_PRIxADDR "(%s), 0x%"
B_PRIxADDR ", %d)\n", (addr_t)area, area->name, address,
updatePageQueue);
@@ -353,20 +343,35 @@ RISCV64VMTranslationMap::UnmapPage(VMArea* area, addr_t address,
fMapCount--;
pinner.Unlock();
if (oldPte.isAccessed)
InvalidatePage(address);
if (oldPte.isAccessed) {
if (!deletingAddressSpace)
InvalidatePage(address);
Flush();
if (_flags == NULL)
Flush();
}
if (_flags == NULL) {
locker.Detach();
// PageUnmapped() will unlock for us
PageUnmapped(area, oldPte.ppn, oldPte.isAccessed, oldPte.isDirty, updatePageQueue);
} else {
uint32 flags = PAGE_PRESENT;
if (oldPte.isAccessed)
flags |= PAGE_ACCESSED;
if (oldPte.isDirty)
flags |= PAGE_MODIFIED;
*_flags = flags;
}
locker.Detach(); // PageUnmapped takes ownership
PageUnmapped(area, oldPte.ppn, oldPte.isAccessed, oldPte.isDirty, updatePageQueue);
return B_OK;
}
void
RISCV64VMTranslationMap::UnmapPages(VMArea* area, addr_t base, size_t size,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace)
{
TRACE("RISCV64VMTranslationMap::UnmapPages(0x%" B_PRIxADDR "(%s), 0x%"
B_PRIxADDR ", 0x%" B_PRIxSIZE ", %d)\n", (addr_t)area,
@@ -392,7 +397,7 @@ RISCV64VMTranslationMap::UnmapPages(VMArea* area, addr_t base, size_t size,
fMapCount--;
if (oldPte.isAccessed)
if (oldPte.isAccessed && !deletingAddressSpace)
InvalidatePage(start);
if (area->cache_type != CACHE_TYPE_DEVICE) {
@@ -421,104 +426,6 @@ RISCV64VMTranslationMap::UnmapPages(VMArea* area, addr_t base, size_t size,
}
void
RISCV64VMTranslationMap::UnmapArea(VMArea* area, bool deletingAddressSpace,
bool ignoreTopCachePageFlags)
{
TRACE("RISCV64VMTranslationMap::UnmapArea(0x%" B_PRIxADDR "(%s), 0x%"
B_PRIxADDR ", 0x%" B_PRIxSIZE ", %d, %d)\n", (addr_t)area,
area->name, area->Base(), area->Size(), deletingAddressSpace,
ignoreTopCachePageFlags);
if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) {
UnmapPages(area, area->Base(), area->Size(), true);
return;
}
bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
RecursiveLocker locker(fLock);
ThreadCPUPinner pinner(thread_get_current_thread());
VMAreaMappings mappings;
mappings.TakeFrom(&area->mappings);
for (VMAreaMappings::Iterator it = mappings.GetIterator();
vm_page_mapping* mapping = it.Next();) {
vm_page* page = mapping->page;
page->mappings.Remove(mapping);
VMCache* cache = page->Cache();
bool pageFullyUnmapped = false;
if (!page->IsMapped()) {
atomic_add(&gMappedPagesCount, -1);
pageFullyUnmapped = true;
}
if (unmapPages || cache != area->cache) {
addr_t address = area->Base()
+ ((page->cache_offset * B_PAGE_SIZE)
- area->cache_offset);
std::atomic<Pte>* pte = LookupPte(address, false, NULL);
if (pte == NULL || !pte->load().isValid) {
panic("page %p has mapping for area %p "
"(%#" B_PRIxADDR "), but has no "
"page table", page, area, address);
continue;
}
Pte oldPte = pte->exchange({});
// transfer the accessed/dirty flags to the page and
// invalidate the mapping, if necessary
if (oldPte.isAccessed) {
page->accessed = true;
if (!deletingAddressSpace)
InvalidatePage(address);
}
if (oldPte.isDirty)
page->modified = true;
if (pageFullyUnmapped) {
DEBUG_PAGE_ACCESS_START(page);
if (cache->temporary) {
vm_page_set_state(page,
PAGE_STATE_INACTIVE);
} else if (page->modified) {
vm_page_set_state(page,
PAGE_STATE_MODIFIED);
} else {
vm_page_set_state(page,
PAGE_STATE_CACHED);
}
DEBUG_PAGE_ACCESS_END(page);
}
}
fMapCount--;
}
Flush();
// flush explicitely, since we directly use the lock
locker.Unlock();
bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
while (vm_page_mapping* mapping = mappings.RemoveHead())
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
}
status_t
RISCV64VMTranslationMap::Query(addr_t virtualAddress,
phys_addr_t* _physicalAddress, uint32* _flags)
@@ -43,12 +43,11 @@ struct RISCV64VMTranslationMap: public VMTranslationMap {
bool markPresent);
virtual status_t UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue);
bool updatePageQueue,
bool deletingAddressSpace, uint32* _flags);
virtual void UnmapPages(VMArea* area, addr_t base,
size_t size, bool updatePageQueue);
virtual void UnmapArea(VMArea* area,
bool deletingAddressSpace,
bool ignoreTopCachePageFlags);
size_t size, bool updatePageQueue,
bool deletingAddressSpace);
virtual status_t Query(addr_t virtualAddress,
phys_addr_t* _physicalAddress,
@@ -319,14 +319,12 @@ X86VMTranslationMap32Bit::DebugMarkRangePresent(addr_t start, addr_t end,
}
/*! Caller must have locked the cache of the page to be unmapped.
This object shouldn't be locked.
*/
status_t
X86VMTranslationMap32Bit::UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace, uint32* _flags)
{
ASSERT(address % B_PAGE_SIZE == 0);
ASSERT(_flags == NULL || !updatePageQueue);
page_directory_entry* pd = fPagingStructures->pgdir_virt;
@@ -360,8 +358,13 @@ X86VMTranslationMap32Bit::UnmapPage(VMArea* area, addr_t address,
// Note, that we only need to invalidate the address, if the
// accessed flags was set, since only then the entry could have been
// in any TLB.
InvalidatePage(address);
Flush();
if (!deletingAddressSpace)
InvalidatePage(address);
if (_flags == NULL) {
Flush();
// flush explicitly, since we directly use the lock
}
// NOTE: Between clearing the page table entry and Flush() other
// processors (actually even this processor with another thread of the
@@ -375,12 +378,21 @@ X86VMTranslationMap32Bit::UnmapPage(VMArea* area, addr_t address,
// (cf. pmap_remove_all()), unless I've missed something.
}
locker.Detach();
// PageUnmapped() will unlock for us
if (_flags == NULL) {
locker.Detach();
// PageUnmapped() will unlock for us
PageUnmapped(area, (oldEntry & X86_PTE_ADDRESS_MASK) / B_PAGE_SIZE,
(oldEntry & X86_PTE_ACCESSED) != 0, (oldEntry & X86_PTE_DIRTY) != 0,
updatePageQueue);
PageUnmapped(area, (oldEntry & X86_PTE_ADDRESS_MASK) / B_PAGE_SIZE,
(oldEntry & X86_PTE_ACCESSED) != 0,
(oldEntry & X86_PTE_DIRTY) != 0, updatePageQueue);
} else {
uint32 flags = PAGE_PRESENT;
if ((oldEntry & X86_PTE_ACCESSED) != 0)
flags |= PAGE_ACCESSED;
if ((oldEntry & X86_PTE_DIRTY) != 0)
flags |= PAGE_MODIFIED;
*_flags = flags;
}
return B_OK;
}
@@ -388,7 +400,7 @@ X86VMTranslationMap32Bit::UnmapPage(VMArea* area, addr_t address,
void
X86VMTranslationMap32Bit::UnmapPages(VMArea* area, addr_t base, size_t size,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace)
{
if (size == 0)
return;
@@ -433,7 +445,8 @@ X86VMTranslationMap32Bit::UnmapPages(VMArea* area, addr_t base, size_t size,
// Note, that we only need to invalidate the address, if the
// accessed flags was set, since only then the entry could have
// been in any TLB.
InvalidatePage(start);
if (!deletingAddressSpace)
InvalidatePage(start);
}
if (area->cache_type != CACHE_TYPE_DEVICE) {
@@ -465,108 +478,6 @@ X86VMTranslationMap32Bit::UnmapPages(VMArea* area, addr_t base, size_t size,
}
void
X86VMTranslationMap32Bit::UnmapArea(VMArea* area, bool deletingAddressSpace,
bool ignoreTopCachePageFlags)
{
if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) {
X86VMTranslationMap32Bit::UnmapPages(area, area->Base(), area->Size(),
true);
return;
}
bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
page_directory_entry* pd = fPagingStructures->pgdir_virt;
RecursiveLocker locker(fLock);
VMAreaMappings mappings;
mappings.TakeFrom(&area->mappings);
for (VMAreaMappings::Iterator it = mappings.GetIterator();
vm_page_mapping* mapping = it.Next();) {
vm_page* page = mapping->page;
page->mappings.Remove(mapping);
VMCache* cache = page->Cache();
bool pageFullyUnmapped = false;
if (!page->IsMapped()) {
atomic_add(&gMappedPagesCount, -1);
pageFullyUnmapped = true;
}
if (unmapPages || cache != area->cache) {
addr_t address = area->Base()
+ ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset);
int index = VADDR_TO_PDENT(address);
if ((pd[index] & X86_PDE_PRESENT) == 0) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no page dir entry", page, area, address);
continue;
}
ThreadCPUPinner pinner(thread_get_current_thread());
page_table_entry* pt
= (page_table_entry*)fPageMapper->GetPageTableAt(
pd[index] & X86_PDE_ADDRESS_MASK);
page_table_entry oldEntry
= X86PagingMethod32Bit::ClearPageTableEntry(
&pt[VADDR_TO_PTENT(address)]);
pinner.Unlock();
if ((oldEntry & X86_PTE_PRESENT) == 0) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no page table entry", page, area, address);
continue;
}
// transfer the accessed/dirty flags to the page and invalidate
// the mapping, if necessary
if ((oldEntry & X86_PTE_ACCESSED) != 0) {
page->accessed = true;
if (!deletingAddressSpace)
InvalidatePage(address);
}
if ((oldEntry & X86_PTE_DIRTY) != 0)
page->modified = true;
if (pageFullyUnmapped) {
DEBUG_PAGE_ACCESS_START(page);
if (cache->temporary)
vm_page_set_state(page, PAGE_STATE_INACTIVE);
else if (page->modified)
vm_page_set_state(page, PAGE_STATE_MODIFIED);
else
vm_page_set_state(page, PAGE_STATE_CACHED);
DEBUG_PAGE_ACCESS_END(page);
}
}
fMapCount--;
}
Flush();
// flush explicitely, since we directly use the lock
locker.Unlock();
bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
while (vm_page_mapping* mapping = mappings.RemoveHead())
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
}
status_t
X86VMTranslationMap32Bit::Query(addr_t va, phys_addr_t *_physical,
uint32 *_flags)
@@ -31,12 +31,11 @@ struct X86VMTranslationMap32Bit final : X86VMTranslationMap {
bool markPresent);
virtual status_t UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue);
bool updatePageQueue,
bool deletingAddressSpace, uint32* _flags);
virtual void UnmapPages(VMArea* area, addr_t base,
size_t size, bool updatePageQueue);
virtual void UnmapArea(VMArea* area,
bool deletingAddressSpace,
bool ignoreTopCachePageFlags);
size_t size, bool updatePageQueue,
bool deletingAddressSpace);
virtual status_t Query(addr_t virtualAddress,
phys_addr_t* _physicalAddress,
@@ -337,9 +337,10 @@ X86VMTranslationMap64Bit::DebugMarkRangePresent(addr_t start, addr_t end,
status_t
X86VMTranslationMap64Bit::UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace, uint32* _flags)
{
ASSERT(address % B_PAGE_SIZE == 0);
ASSERT(_flags == NULL || !updatePageQueue);
TRACE("X86VMTranslationMap64Bit::UnmapPage(%#" B_PRIxADDR ")\n", address);
@@ -367,9 +368,13 @@ X86VMTranslationMap64Bit::UnmapPage(VMArea* area, addr_t address,
// Note, that we only need to invalidate the address, if the
// accessed flags was set, since only then the entry could have been
// in any TLB.
InvalidatePage(address);
if (!deletingAddressSpace)
InvalidatePage(address);
Flush();
if (_flags == NULL) {
Flush();
// flush explicitly, since we directly use the lock
}
// NOTE: Between clearing the page table entry and Flush() other
// processors (actually even this processor with another thread of the
@@ -383,12 +388,21 @@ X86VMTranslationMap64Bit::UnmapPage(VMArea* area, addr_t address,
// (cf. pmap_remove_all()), unless I've missed something.
}
locker.Detach();
// PageUnmapped() will unlock for us
if (_flags == NULL) {
locker.Detach();
// PageUnmapped() will unlock for us
PageUnmapped(area, (oldEntry & X86_64_PTE_ADDRESS_MASK) / B_PAGE_SIZE,
(oldEntry & X86_64_PTE_ACCESSED) != 0,
(oldEntry & X86_64_PTE_DIRTY) != 0, updatePageQueue);
PageUnmapped(area, (oldEntry & X86_64_PTE_ADDRESS_MASK) / B_PAGE_SIZE,
(oldEntry & X86_64_PTE_ACCESSED) != 0,
(oldEntry & X86_64_PTE_DIRTY) != 0, updatePageQueue);
} else {
uint32 flags = PAGE_PRESENT;
if ((oldEntry & X86_64_PTE_ACCESSED) != 0)
flags |= PAGE_ACCESSED;
if ((oldEntry & X86_64_PTE_DIRTY) != 0)
flags |= PAGE_MODIFIED;
*_flags = flags;
}
return B_OK;
}
@@ -396,7 +410,7 @@ X86VMTranslationMap64Bit::UnmapPage(VMArea* area, addr_t address,
void
X86VMTranslationMap64Bit::UnmapPages(VMArea* area, addr_t base, size_t size,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace)
{
if (size == 0)
return;
@@ -436,7 +450,8 @@ X86VMTranslationMap64Bit::UnmapPages(VMArea* area, addr_t base, size_t size,
// Note, that we only need to invalidate the address, if the
// accessed flags was set, since only then the entry could have
// been in any TLB.
InvalidatePage(start);
if (!deletingAddressSpace)
InvalidatePage(start);
}
if (area->cache_type != CACHE_TYPE_DEVICE) {
@@ -468,102 +483,6 @@ X86VMTranslationMap64Bit::UnmapPages(VMArea* area, addr_t base, size_t size,
}
void
X86VMTranslationMap64Bit::UnmapArea(VMArea* area, bool deletingAddressSpace,
bool ignoreTopCachePageFlags)
{
TRACE("X86VMTranslationMap64Bit::UnmapArea(%p)\n", area);
if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) {
X86VMTranslationMap64Bit::UnmapPages(area, area->Base(), area->Size(),
true);
return;
}
bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
RecursiveLocker locker(fLock);
ThreadCPUPinner pinner(thread_get_current_thread());
VMAreaMappings mappings;
mappings.TakeFrom(&area->mappings);
for (VMAreaMappings::Iterator it = mappings.GetIterator();
vm_page_mapping* mapping = it.Next();) {
vm_page* page = mapping->page;
page->mappings.Remove(mapping);
VMCache* cache = page->Cache();
bool pageFullyUnmapped = false;
if (!page->IsMapped()) {
atomic_add(&gMappedPagesCount, -1);
pageFullyUnmapped = true;
}
if (unmapPages || cache != area->cache) {
addr_t address = area->Base()
+ ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset);
uint64* entry = X86PagingMethod64Bit::PageTableEntryForAddress(
fPagingStructures->VirtualPMLTop(), address, fIsKernelMap,
false, NULL, fPageMapper, fMapCount);
if (entry == NULL) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no page table", page, area, address);
continue;
}
uint64 oldEntry = X86PagingMethod64Bit::ClearTableEntry(entry);
if ((oldEntry & X86_64_PTE_PRESENT) == 0) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no page table entry", page, area, address);
continue;
}
// transfer the accessed/dirty flags to the page and invalidate
// the mapping, if necessary
if ((oldEntry & X86_64_PTE_ACCESSED) != 0) {
page->accessed = true;
if (!deletingAddressSpace)
InvalidatePage(address);
}
if ((oldEntry & X86_64_PTE_DIRTY) != 0)
page->modified = true;
if (pageFullyUnmapped) {
DEBUG_PAGE_ACCESS_START(page);
if (cache->temporary)
vm_page_set_state(page, PAGE_STATE_INACTIVE);
else if (page->modified)
vm_page_set_state(page, PAGE_STATE_MODIFIED);
else
vm_page_set_state(page, PAGE_STATE_CACHED);
DEBUG_PAGE_ACCESS_END(page);
}
}
fMapCount--;
}
Flush();
// flush explicitely, since we directly use the lock
locker.Unlock();
bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
while (vm_page_mapping* mapping = mappings.RemoveHead())
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
}
status_t
X86VMTranslationMap64Bit::Query(addr_t virtualAddress,
phys_addr_t* _physicalAddress, uint32* _flags)
@@ -32,12 +32,11 @@ struct X86VMTranslationMap64Bit final : X86VMTranslationMap {
bool markPresent);
virtual status_t UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue);
bool updatePageQueue,
bool deletingAddressSpace, uint32* _flags);
virtual void UnmapPages(VMArea* area, addr_t base,
size_t size, bool updatePageQueue);
virtual void UnmapArea(VMArea* area,
bool deletingAddressSpace,
bool ignoreTopCachePageFlags);
size_t size, bool updatePageQueue,
bool deletingAddressSpace);
virtual status_t Query(addr_t virtualAddress,
phys_addr_t* _physicalAddress,
@@ -558,27 +558,25 @@ X86VMTranslationMapPAE::DebugMarkRangePresent(addr_t start, addr_t end,
}
/*! Caller must have locked the cache of the page to be unmapped.
This object shouldn't be locked.
*/
status_t
X86VMTranslationMapPAE::UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace, uint32* _flags)
{
ASSERT(address % B_PAGE_SIZE == 0);
ASSERT(_flags == NULL || !updatePageQueue);
TRACE("X86VMTranslationMapPAE::UnmapPage(%#" B_PRIxADDR ")\n", address);
ThreadCPUPinner pinner(thread_get_current_thread());
pae_page_directory_entry* pageDirEntry
= X86PagingMethodPAE::PageDirEntryForAddress(
fPagingStructures->VirtualPageDirs(), address);
TRACE("X86VMTranslationMapPAE::UnmapPage(%#" B_PRIxADDR ")\n", address);
RecursiveLocker locker(fLock);
if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0)
return B_ENTRY_NOT_FOUND;
ThreadCPUPinner pinner(thread_get_current_thread());
RecursiveLocker locker(fLock);
pae_page_table_entry* pageTable
= (pae_page_table_entry*)fPageMapper->GetPageTableAt(
@@ -602,9 +600,13 @@ X86VMTranslationMapPAE::UnmapPage(VMArea* area, addr_t address,
// Note, that we only need to invalidate the address, if the
// accessed flags was set, since only then the entry could have been
// in any TLB.
InvalidatePage(address);
if (!deletingAddressSpace)
InvalidatePage(address);
Flush();
if (_flags == NULL) {
Flush();
// flush explicitly, since we directly use the lock
}
// NOTE: Between clearing the page table entry and Flush() other
// processors (actually even this processor with another thread of the
@@ -631,7 +633,7 @@ X86VMTranslationMapPAE::UnmapPage(VMArea* area, addr_t address,
void
X86VMTranslationMapPAE::UnmapPages(VMArea* area, addr_t base, size_t size,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace)
{
if (size == 0)
return;
@@ -680,7 +682,8 @@ X86VMTranslationMapPAE::UnmapPages(VMArea* area, addr_t base, size_t size,
// Note, that we only need to invalidate the address, if the
// accessed flags was set, since only then the entry could have
// been in any TLB.
InvalidatePage(start);
if (!deletingAddressSpace)
InvalidatePage(start);
}
if (area->cache_type != CACHE_TYPE_DEVICE) {
@@ -712,134 +715,6 @@ X86VMTranslationMapPAE::UnmapPages(VMArea* area, addr_t base, size_t size,
}
void
X86VMTranslationMapPAE::UnmapArea(VMArea* area, bool deletingAddressSpace,
bool ignoreTopCachePageFlags)
{
if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) {
X86VMTranslationMapPAE::UnmapPages(area, area->Base(), area->Size(),
true);
return;
}
bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
RecursiveLocker locker(fLock);
VMAreaMappings mappings;
mappings.TakeFrom(&area->mappings);
for (VMAreaMappings::Iterator it = mappings.GetIterator();
vm_page_mapping* mapping = it.Next();) {
vm_page* page = mapping->page;
page->mappings.Remove(mapping);
VMCache* cache = page->Cache();
bool pageFullyUnmapped = false;
if (!page->IsMapped()) {
atomic_add(&gMappedPagesCount, -1);
pageFullyUnmapped = true;
}
if (unmapPages || cache != area->cache) {
addr_t address = area->Base()
+ ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset);
pae_page_directory_entry* pageDirEntry
= X86PagingMethodPAE::PageDirEntryForAddress(
fPagingStructures->VirtualPageDirs(), address);
if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no page dir entry", page, area, address);
continue;
}
ThreadCPUPinner pinner(thread_get_current_thread());
pae_page_table_entry* pageTable
= (pae_page_table_entry*)fPageMapper->GetPageTableAt(
*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
pae_page_table_entry oldEntry
= X86PagingMethodPAE::ClearTableEntry(
&pageTable[address / B_PAGE_SIZE
% kPAEPageTableEntryCount]);
pinner.Unlock();
if ((oldEntry & X86_PAE_PTE_PRESENT) == 0) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no page table entry", page, area, address);
continue;
}
T(Unmap(this, address, oldEntry));
// transfer the accessed/dirty flags to the page and invalidate
// the mapping, if necessary
if ((oldEntry & X86_PAE_PTE_ACCESSED) != 0) {
page->accessed = true;
if (!deletingAddressSpace)
InvalidatePage(address);
}
if ((oldEntry & X86_PAE_PTE_DIRTY) != 0)
page->modified = true;
if (pageFullyUnmapped) {
DEBUG_PAGE_ACCESS_START(page);
if (cache->temporary)
vm_page_set_state(page, PAGE_STATE_INACTIVE);
else if (page->modified)
vm_page_set_state(page, PAGE_STATE_MODIFIED);
else
vm_page_set_state(page, PAGE_STATE_CACHED);
DEBUG_PAGE_ACCESS_END(page);
}
} else {
#if TRANSLATION_MAP_TRACING
addr_t address = area->Base()
+ ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset);
ThreadCPUPinner pinner(thread_get_current_thread());
pae_page_directory_entry* pageDirEntry
= X86PagingMethodPAE::PageDirEntryForAddress(
fPagingStructures->VirtualPageDirs(), address);
if ((*pageDirEntry & X86_PAE_PDE_PRESENT) != 0) {
pae_page_table_entry* pageTable
= (pae_page_table_entry*)fPageMapper->GetPageTableAt(
*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
pae_page_table_entry oldEntry = pageTable[
address / B_PAGE_SIZE % kPAEPageTableEntryCount];
pinner.Unlock();
if ((oldEntry & X86_PAE_PTE_PRESENT) != 0)
T(Unmap(this, address, oldEntry));
}
#endif
}
fMapCount--;
}
Flush();
// flush explicitely, since we directly use the lock
locker.Unlock();
bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
while (vm_page_mapping* mapping = mappings.RemoveHead())
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
}
status_t
X86VMTranslationMapPAE::Query(addr_t virtualAddress,
phys_addr_t* _physicalAddress, uint32* _flags)
@@ -34,12 +34,11 @@ struct X86VMTranslationMapPAE final : X86VMTranslationMap {
bool markPresent);
virtual status_t UnmapPage(VMArea* area, addr_t address,
bool updatePageQueue);
bool updatePageQueue,
bool deletingAddressSpace, uint32* _flags);
virtual void UnmapPages(VMArea* area, addr_t base,
size_t size, bool updatePageQueue);
virtual void UnmapArea(VMArea* area,
bool deletingAddressSpace,
bool ignoreTopCachePageFlags);
size_t size, bool updatePageQueue,
bool deletingAddressSpace);
virtual status_t Query(addr_t virtualAddress,
phys_addr_t* _physicalAddress,
+79 -23
View File
@@ -46,7 +46,7 @@ VMTranslationMap::DebugMarkRangePresent(addr_t start, addr_t end,
*/
void
VMTranslationMap::UnmapPages(VMArea* area, addr_t base, size_t size,
bool updatePageQueue)
bool updatePageQueue, bool deletingAddressSpace)
{
ASSERT(base % B_PAGE_SIZE == 0);
ASSERT(size % B_PAGE_SIZE == 0);
@@ -62,57 +62,113 @@ VMTranslationMap::UnmapPages(VMArea* area, addr_t base, size_t size,
vm_page* page = vm_lookup_page(physicalAddress / B_PAGE_SIZE);
if (page != NULL) {
DEBUG_PAGE_ACCESS_START(page);
UnmapPage(area, address, updatePageQueue);
UnmapPage(area, address, updatePageQueue, deletingAddressSpace);
DEBUG_PAGE_ACCESS_END(page);
} else
UnmapPage(area, address, updatePageQueue);
UnmapPage(area, address, updatePageQueue, deletingAddressSpace);
}
}
#else
for (; address != end; address += B_PAGE_SIZE)
UnmapPage(area, address, updatePageQueue);
UnmapPage(area, address, updatePageQueue, deletingAddressSpace);
#endif
}
/*! Unmaps all of an area's pages.
If \a deletingAddressSpace is \c true, the address space the area belongs to
is in the process of being destroyed and isn't used by anyone anymore. For
some architectures this can be used for optimizations (e.g. not unmapping
pages or at least not needing to invalidate TLB entries).
If \a ignoreTopCachePageFlags is \c true, the area is in the process of
being destroyed and its top cache is otherwise unreferenced. I.e. all mapped
pages that live in the top cache area going to be freed and the page
accessed and modified flags don't need to be propagated.
The default implementation just iterates over all virtual pages of the
area and calls UnmapPage(). This is obviously not particularly efficient.
The default implementation iterates over all mapping objects, and calls
UnmapPage() (with \c _flags specified to avoid calls to PageUnmapped).
It skips unmapping pages owned by the top cache if \a deletingAddressSpace
is \c true, or if \a ignoreTopCachePageFlags is set. This behavior should
be sufficient for most (if not all) architectures.
*/
void
VMTranslationMap::UnmapArea(VMArea* area, bool deletingAddressSpace,
bool ignoreTopCachePageFlags)
{
addr_t address = area->Base();
addr_t end = address + area->Size();
#if DEBUG_PAGE_ACCESS
for (; address != end; address += B_PAGE_SIZE) {
phys_addr_t physicalAddress;
uint32 flags;
if (Query(address, &physicalAddress, &flags) == B_OK
&& (flags & PAGE_PRESENT) != 0) {
vm_page* page = vm_lookup_page(physicalAddress / B_PAGE_SIZE);
if (page != NULL) {
if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) {
UnmapPages(area, area->Base(), area->Size(), true, deletingAddressSpace);
return;
}
const bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
Lock();
VMAreaMappings mappings;
mappings.TakeFrom(&area->mappings);
for (VMAreaMappings::Iterator it = mappings.GetIterator();
vm_page_mapping* mapping = it.Next();) {
vm_page* page = mapping->page;
page->mappings.Remove(mapping);
VMCache* cache = page->Cache();
bool pageFullyUnmapped = false;
if (!page->IsMapped()) {
atomic_add(&gMappedPagesCount, -1);
pageFullyUnmapped = true;
}
if (unmapPages || cache != area->cache) {
const addr_t address = area->Base()
+ ((page->cache_offset * B_PAGE_SIZE) - area->cache_offset);
// UnmapPage should skip flushing and calling PageUnmapped when we pass &flags.
uint32 flags = 0;
status_t status = UnmapPage(area, address, false, deletingAddressSpace, &flags);
if (status == B_ENTRY_NOT_FOUND) {
panic("page %p has mapping for area %p (%#" B_PRIxADDR "), but "
"has no translation map entry", page, area, address);
continue;
}
if (status != B_OK) {
panic("unmapping page %p for area %p (%#" B_PRIxADDR ") failed: %x",
page, area, address, status);
continue;
}
// Transfer the accessed/dirty flags to the page.
if ((flags & PAGE_ACCESSED) != 0)
page->accessed = true;
if ((flags & PAGE_MODIFIED) != 0)
page->modified = true;
if (pageFullyUnmapped) {
DEBUG_PAGE_ACCESS_START(page);
UnmapPage(area, address, true);
if (cache->temporary)
vm_page_set_state(page, PAGE_STATE_INACTIVE);
else if (page->modified)
vm_page_set_state(page, PAGE_STATE_MODIFIED);
else
vm_page_set_state(page, PAGE_STATE_CACHED);
DEBUG_PAGE_ACCESS_END(page);
} else
UnmapPage(area, address, true);
}
}
}
#else
for (; address != end; address += B_PAGE_SIZE)
UnmapPage(area, address, true);
#endif
// This should Flush(), if necessary.
Unlock();
bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
while (vm_page_mapping* mapping = mappings.RemoveHead())
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
}