Optimized implementations for UnmapPages() and UnmapArea().
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37193 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -372,8 +372,121 @@ void
|
|||||||
X86VMTranslationMapPAE::UnmapPages(VMArea* area, addr_t base, size_t size,
|
X86VMTranslationMapPAE::UnmapPages(VMArea* area, addr_t base, size_t size,
|
||||||
bool updatePageQueue)
|
bool updatePageQueue)
|
||||||
{
|
{
|
||||||
// TODO: Implement for real!
|
if (size == 0)
|
||||||
X86VMTranslationMap::UnmapPages(area, base, size, updatePageQueue);
|
return;
|
||||||
|
|
||||||
|
addr_t start = base;
|
||||||
|
addr_t end = base + size - 1;
|
||||||
|
|
||||||
|
TRACE("X86VMTranslationMapPAE::UnmapPages(%p, %#" B_PRIxADDR ", %#"
|
||||||
|
B_PRIxADDR ")\n", area, start, end);
|
||||||
|
|
||||||
|
VMAreaMappings queue;
|
||||||
|
|
||||||
|
RecursiveLocker locker(fLock);
|
||||||
|
|
||||||
|
do {
|
||||||
|
pae_page_directory_entry* pageDirEntry
|
||||||
|
= X86PagingMethodPAE::PageDirEntryForAddress(
|
||||||
|
fPagingStructures->VirtualPageDirs(), start);
|
||||||
|
if ((*pageDirEntry & X86_PAE_PDE_PRESENT) == 0) {
|
||||||
|
// no page table here, move the start up to access the next page
|
||||||
|
// table
|
||||||
|
start = ROUNDUP(start + 1, kPAEPageTableRange);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct thread* thread = thread_get_current_thread();
|
||||||
|
ThreadCPUPinner pinner(thread);
|
||||||
|
|
||||||
|
pae_page_table_entry* pageTable
|
||||||
|
= (pae_page_table_entry*)fPageMapper->GetPageTableAt(
|
||||||
|
*pageDirEntry & X86_PAE_PDE_ADDRESS_MASK);
|
||||||
|
|
||||||
|
uint32 index = start / B_PAGE_SIZE % kPAEPageTableEntryCount;
|
||||||
|
for (; index < kPAEPageTableEntryCount && start < end;
|
||||||
|
index++, start += B_PAGE_SIZE) {
|
||||||
|
pae_page_table_entry oldEntry
|
||||||
|
= X86PagingMethodPAE::ClearPageTableEntry(&pageTable[index]);
|
||||||
|
if ((oldEntry & X86_PAE_PTE_PRESENT) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fMapCount--;
|
||||||
|
|
||||||
|
if ((oldEntry & X86_PAE_PTE_ACCESSED) != 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(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (area->cache_type != CACHE_TYPE_DEVICE) {
|
||||||
|
// get the page
|
||||||
|
vm_page* page = vm_lookup_page(
|
||||||
|
(oldEntry & X86_PAE_PTE_ADDRESS_MASK) / B_PAGE_SIZE);
|
||||||
|
ASSERT(page != NULL);
|
||||||
|
|
||||||
|
DEBUG_PAGE_ACCESS_START(page);
|
||||||
|
|
||||||
|
// transfer the accessed/dirty flags to the page
|
||||||
|
if ((oldEntry & X86_PAE_PTE_ACCESSED) != 0)
|
||||||
|
page->accessed = true;
|
||||||
|
if ((oldEntry & X86_PAE_PTE_DIRTY) != 0)
|
||||||
|
page->modified = true;
|
||||||
|
|
||||||
|
// remove the mapping object/decrement the wired_count of the
|
||||||
|
// page
|
||||||
|
if (area->wiring == B_NO_LOCK) {
|
||||||
|
vm_page_mapping* mapping = NULL;
|
||||||
|
vm_page_mappings::Iterator iterator
|
||||||
|
= page->mappings.GetIterator();
|
||||||
|
while ((mapping = iterator.Next()) != NULL) {
|
||||||
|
if (mapping->area == area)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(mapping != NULL);
|
||||||
|
|
||||||
|
area->mappings.Remove(mapping);
|
||||||
|
page->mappings.Remove(mapping);
|
||||||
|
queue.Add(mapping);
|
||||||
|
} else
|
||||||
|
page->wired_count--;
|
||||||
|
|
||||||
|
if (page->wired_count == 0 && page->mappings.IsEmpty()) {
|
||||||
|
atomic_add(&gMappedPagesCount, -1);
|
||||||
|
|
||||||
|
if (updatePageQueue) {
|
||||||
|
if (page->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Flush();
|
||||||
|
// flush explicitly, since we directly use the lock
|
||||||
|
} while (start != 0 && start < end);
|
||||||
|
|
||||||
|
// TODO: As in UnmapPage() we can lose page dirty flags here. ATM it's not
|
||||||
|
// really critical here, as in all cases this method is used, the unmapped
|
||||||
|
// area range is unmapped for good (resized/cut) and the pages will likely
|
||||||
|
// be freed.
|
||||||
|
|
||||||
|
locker.Unlock();
|
||||||
|
|
||||||
|
// free removed mappings
|
||||||
|
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 = queue.RemoveHead())
|
||||||
|
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -381,9 +494,102 @@ void
|
|||||||
X86VMTranslationMapPAE::UnmapArea(VMArea* area, bool deletingAddressSpace,
|
X86VMTranslationMapPAE::UnmapArea(VMArea* area, bool deletingAddressSpace,
|
||||||
bool ignoreTopCachePageFlags)
|
bool ignoreTopCachePageFlags)
|
||||||
{
|
{
|
||||||
// TODO: Implement for real!
|
if (area->cache_type == CACHE_TYPE_DEVICE || area->wiring != B_NO_LOCK) {
|
||||||
X86VMTranslationMap::UnmapArea(area, deletingAddressSpace,
|
X86VMTranslationMapPAE::UnmapPages(area, area->Base(), area->Size(),
|
||||||
ignoreTopCachePageFlags);
|
true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unmapPages = !deletingAddressSpace || !ignoreTopCachePageFlags;
|
||||||
|
|
||||||
|
RecursiveLocker locker(fLock);
|
||||||
|
|
||||||
|
VMAreaMappings mappings;
|
||||||
|
mappings.MoveFrom(&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->wired_count == 0 && page->mappings.IsEmpty()) {
|
||||||
|
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::ClearPageTableEntry(
|
||||||
|
&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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user