Make page_manager::unmap_pages() handle multiple blocks

This commit is contained in:
Justin C. Miller
2018-04-22 13:37:44 -07:00
parent 95d52b87f4
commit 8cb0803605
2 changed files with 72 additions and 25 deletions

View File

@@ -28,7 +28,7 @@ page_block::list_append(page_block *list)
}
page_block *
page_block::list_insert(page_block *block)
page_block::list_insert_physical(page_block *block)
{
page_block *cur = this;
page_block **prev = nullptr;
@@ -45,6 +45,24 @@ page_block::list_insert(page_block *block)
return block;
}
page_block *
page_block::list_insert_virtual(page_block *block)
{
page_block *cur = this;
page_block **prev = nullptr;
while (cur->virtual_address < block->virtual_address) {
prev = &cur->next;
cur = cur->next;
}
block->next = cur;
if (prev) {
*prev = block;
return this;
}
return block;
}
page_block *
page_block::list_consolidate()
{
@@ -219,30 +237,53 @@ page_manager::unmap_pages(uint64_t address, unsigned count)
kassert(cur, "Couldn't find existing mapped pages to unmap");
uint64_t end = address + page_size * count;
while (cur && cur->contains(address)) {
uint64_t leading = address - cur->virtual_address;
uint64_t trailing = cur->virtual_end() - (address + page_size*count);
uint64_t trailing =
end > cur->virtual_end() ?
0 : (cur->virtual_end() - end);
if (leading) {
uint64_t pages = leading / page_size;
page_block *lead_block = get_block();
lead_block->copy(cur);
lead_block->next = cur;
lead_block->count = leading / page_size;
lead_block->count = pages;
cur->count -= pages;
cur->physical_address += leading;
cur->virtual_address += leading;
*prev = lead_block;
prev = &lead_block->next;
}
if (trailing) {
uint64_t pages = trailing / page_size;
page_block *trail_block = get_block();
trail_block->copy(cur);
trail_block->next = cur->next;
trail_block->count = trailing / page_size;
trail_block->count = pages;
cur->count -= pages;
cur->next = trail_block;
}
address += cur->count * page_size;
page_block *next = cur->next;
*prev = cur->next;
cur->next = nullptr;
cur->flags = cur->flags & ~(page_block_flags::used | page_block_flags::mapped);
m_free->list_insert(cur);
m_free->list_insert_physical(cur);
cur = next;
}
}
void

View File

@@ -93,6 +93,7 @@ struct page_block
inline bool has_flag(page_block_flags f) const { return bitfield_contains(flags, f); }
inline uint64_t physical_end() const { return physical_address + (count * page_manager::page_size); }
inline uint64_t virtual_end() const { return virtual_address + (count * page_manager::page_size); }
inline bool contains(uint64_t vaddr) const { return vaddr >= virtual_address && vaddr < virtual_end(); }
/// Helper to zero out a block and optionally set the next pointer.
@@ -115,10 +116,15 @@ struct page_block
/// \arg list The list to append to the current list
void list_append(page_block *list);
/// Sorted-insert of a block into the list.
/// Sorted-insert of a block into the list by physical address.
/// \arg block The single block to insert
/// \returns The new list head
page_block * list_insert(page_block *block);
page_block * list_insert_physical(page_block *block);
/// Sorted-insert of a block into the list by virtual address.
/// \arg block The single block to insert
/// \returns The new list head
page_block * list_insert_virtual(page_block *block);
/// Traverse the list, joining adjacent blocks where possible.
/// \returns A linked list of freed page_block structures.