blob: 45a66f5622787d602a0dd7467e15e18eae404b95 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <fbl/ref_counted_upgradeable.h>
#include <vm/page_queues.h>
#include <vm/vm_object_paged.h>
PageQueues::PageQueues() {
for (size_t i = 0; i < kNumPagerBacked; i++) {
list_initialize(&pager_backed_[i]);
}
list_initialize(&unswappable_);
list_initialize(&wired_);
}
PageQueues::~PageQueues() {
for (size_t i = 0; i < kNumPagerBacked; i++) {
DEBUG_ASSERT(list_is_empty(&pager_backed_[i]));
}
DEBUG_ASSERT(list_is_empty(&unswappable_));
DEBUG_ASSERT(list_is_empty(&wired_));
}
void PageQueues::RotatePagerBackedQueues() {
Guard<SpinLock, IrqSave> guard{&lock_};
for (size_t i = kNumPagerBacked - 1; i > 0; i--) {
list_splice_after(&pager_backed_[i - 1], &pager_backed_[i]);
}
}
void PageQueues::SetWired(vm_page_t* page) {
DEBUG_ASSERT(page->state() == VM_PAGE_STATE_OBJECT);
DEBUG_ASSERT(!page->is_free());
Guard<SpinLock, IrqSave> guard{&lock_};
DEBUG_ASSERT(!list_in_list(&page->queue_node));
page->object.set_object(nullptr);
page->object.set_page_offset(0);
list_add_head(&wired_, &page->queue_node);
}
void PageQueues::MoveToWired(vm_page_t* page) {
DEBUG_ASSERT(page->state() == VM_PAGE_STATE_OBJECT);
DEBUG_ASSERT(!page->is_free());
Guard<SpinLock, IrqSave> guard{&lock_};
DEBUG_ASSERT(list_in_list(&page->queue_node));
page->object.set_object(nullptr);
page->object.set_page_offset(0);
list_delete(&page->queue_node);
list_add_head(&wired_, &page->queue_node);
}
void PageQueues::SetUnswappable(vm_page_t* page) {
DEBUG_ASSERT(page->state() == VM_PAGE_STATE_OBJECT);
DEBUG_ASSERT(!page->is_free());
DEBUG_ASSERT(page->object.pin_count == 0);
Guard<SpinLock, IrqSave> guard{&lock_};
DEBUG_ASSERT(!list_in_list(&page->queue_node));
page->object.set_object(nullptr);
page->object.set_page_offset(0);
list_add_head(&unswappable_, &page->queue_node);
}
void PageQueues::MoveToUnswappable(vm_page_t* page) {
DEBUG_ASSERT(page->state() == VM_PAGE_STATE_OBJECT);
DEBUG_ASSERT(!page->is_free());
DEBUG_ASSERT(page->object.pin_count == 0);
Guard<SpinLock, IrqSave> guard{&lock_};
DEBUG_ASSERT(list_in_list(&page->queue_node));
page->object.set_object(nullptr);
page->object.set_page_offset(0);
list_delete(&page->queue_node);
list_add_head(&unswappable_, &page->queue_node);
}
void PageQueues::SetPagerBacked(vm_page_t* page, VmObjectPaged* object, uint64_t page_offset) {
DEBUG_ASSERT(page->state() == VM_PAGE_STATE_OBJECT);
DEBUG_ASSERT(!page->is_free());
DEBUG_ASSERT(page->object.pin_count == 0);
DEBUG_ASSERT(object);
Guard<SpinLock, IrqSave> guard{&lock_};
DEBUG_ASSERT(!list_in_list(&page->queue_node));
page->object.set_object(reinterpret_cast<void*>(object));
page->object.set_page_offset(page_offset);
list_add_head(&pager_backed_[0], &page->queue_node);
}
void PageQueues::MoveToPagerBacked(vm_page_t* page, VmObjectPaged* object, uint64_t page_offset) {
DEBUG_ASSERT(page->state() == VM_PAGE_STATE_OBJECT);
DEBUG_ASSERT(!page->is_free());
DEBUG_ASSERT(page->object.pin_count == 0);
DEBUG_ASSERT(object);
Guard<SpinLock, IrqSave> guard{&lock_};
DEBUG_ASSERT(list_in_list(&page->queue_node));
page->object.set_object(reinterpret_cast<void*>(object));
page->object.set_page_offset(page_offset);
list_delete(&page->queue_node);
list_add_head(&pager_backed_[0], &page->queue_node);
}
void PageQueues::RemoveLocked(vm_page_t* page) {
DEBUG_ASSERT(list_in_list(&page->queue_node));
page->object.set_object(nullptr);
page->object.set_page_offset(0);
list_delete(&page->queue_node);
}
void PageQueues::Remove(vm_page_t* page) {
Guard<SpinLock, IrqSave> guard{&lock_};
RemoveLocked(page);
}
void PageQueues::RemoveArrayIntoList(vm_page_t** pages, size_t count, list_node_t* out_list) {
DEBUG_ASSERT(pages);
Guard<SpinLock, IrqSave> guard{&lock_};
for (size_t i = 0; i < count; i++) {
DEBUG_ASSERT(pages[i]);
RemoveLocked(pages[i]);
list_add_tail(out_list, &pages[i]->queue_node);
}
}
PageQueues::Counts PageQueues::DebugQueueCounts() const {
Counts counts;
Guard<SpinLock, IrqSave> guard{&lock_};
for (size_t i = 0; i < kNumPagerBacked; i++) {
counts.pager_backed[i] = list_length(&pager_backed_[i]);
}
counts.unswappable = list_length(&unswappable_);
counts.wired = list_length(&wired_);
return counts;
}
bool PageQueues::DebugPageInListLocked(const list_node_t* list, const vm_page_t* page) const {
const vm_page_t* p;
list_for_every_entry (list, p, vm_page_t, queue_node) {
if (p == page) {
return true;
}
}
return false;
}
bool PageQueues::DebugPageInList(const list_node_t* list, const vm_page_t* page) const {
Guard<SpinLock, IrqSave> guard{&lock_};
return DebugPageInListLocked(list, page);
}
bool PageQueues::DebugPageIsPagerBacked(const vm_page_t* page, size_t* queue) const {
Guard<SpinLock, IrqSave> guard{&lock_};
for (size_t i = 0; i < kNumPagerBacked; i++) {
if (DebugPageInListLocked(&pager_backed_[i], page)) {
if (queue) {
*queue = i;
}
return true;
}
}
return false;
}
bool PageQueues::DebugPageIsUnswappable(const vm_page_t* page) const {
return DebugPageInList(&unswappable_, page);
}
bool PageQueues::DebugPageIsWired(const vm_page_t* page) const {
return DebugPageInList(&wired_, page);
}