diff --git a/headers/private/kernel/vm_page.h b/headers/private/kernel/vm_page.h index e2049c4293..99b5497447 100644 --- a/headers/private/kernel/vm_page.h +++ b/headers/private/kernel/vm_page.h @@ -54,6 +54,7 @@ status_t vm_page_allocate_pages(int pageState, struct vm_page **pages, uint32 numPages); struct vm_page *vm_page_allocate_page_run(int state, addr_t base, addr_t length); +struct vm_page *vm_page_allocate_page_run_no_base(int state, addr_t count); struct vm_page *vm_page_at_index(int32 index); struct vm_page *vm_lookup_page(addr_t pageNumber); diff --git a/src/system/kernel/vm/vm_page.cpp b/src/system/kernel/vm/vm_page.cpp index 653dec8e2e..92efeb9dca 100644 --- a/src/system/kernel/vm/vm_page.cpp +++ b/src/system/kernel/vm/vm_page.cpp @@ -2263,6 +2263,85 @@ vm_page_allocate_page_run(int pageState, addr_t base, addr_t length) } +vm_page * +vm_page_allocate_page_run_no_base(int pageState, addr_t count) +{ + InterruptsSpinLocker locker(sPageLock); + + if (free_page_queue_count() - sReservedPages < count) { + // TODO: add more tries, ie. free some inactive, ... + // no free space + return NULL; + } + + page_queue *queue; + page_queue *otherQueue; + switch (pageState) { + case PAGE_STATE_FREE: + queue = &sFreePageQueue; + otherQueue = &sClearPageQueue; + break; + case PAGE_STATE_CLEAR: + queue = &sClearPageQueue; + otherQueue = &sFreePageQueue; + break; + default: + return NULL; // invalid + } + + vm_page *firstPage = NULL; + for (uint32 twice = 0; twice < 2; twice++) { + vm_page *page = queue->head; + for (; page != NULL; page = page->queue_next) { + vm_page *current = page; + if (current >= &sPages[sNumPages - count]) + continue; + + bool foundRun = true; + for (uint32 i = 0; i < count; i++, current++) { + if (current->state != PAGE_STATE_FREE + && current->state != PAGE_STATE_CLEAR) { + foundRun = false; + break; + } + } + + if (foundRun) { + // pull the pages out of the appropriate queues + current = page; + for (uint32 i = 0; i < count; i++, current++) { + current->is_cleared = current->state == PAGE_STATE_CLEAR; + set_page_state_nolock(current, PAGE_STATE_BUSY); + current->usage_count = 2; + } + + firstPage = page; + break; + } + } + + if (firstPage != NULL) + break; + + queue = otherQueue; + } + + T(AllocatePageRun(count)); + + locker.Unlock(); + + if (firstPage != NULL && pageState == PAGE_STATE_CLEAR) { + vm_page *current = firstPage; + for (uint32 i = 0; i < count; i++, current++) { + if (!current->is_cleared) + clear_page(current); + } + } + + return firstPage; +} + + vm_page * vm_page_at_index(int32 index) {