bonefish+axeld:
* We now have a page writer that takes some pages from the modified queue and writes it back every few seconds. It can be triggered by the page scanner to do that more often, though. That mechanism can be greatly improved once we have our I/O scheduler working. * Removed vm_page_write_modified_page() again - it was all "eaten up" by the page writer. * Reworked vm_page_write_modified_pages() a bit: it now uses vm_test_map_modification() and vm_clear_map_flags() instead of the iterating over all areas which wouldn't even work correctly. The code is much simpler now, too. * You usually put something to the tail of a queue, and remove the contents from the head, not vice versa - changed queue implementation to reflect this. * Additionally, there is now a enqueue_page_to_head() if you actually want the opposite. * vm_page_requeue() allows you to move a page in a queue to the head or tail. * Replaced vm_clear_map_activation() with vm_clear_map_flags() which allows you to clear other flags than PAGE_ACCESSED. * The page scanner dumps now some arguments with each run. * Removed the old disabled pageout_daemon() from NewOS. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22348 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
8ce98e44cc
commit
a6778735f9
|
@ -89,7 +89,6 @@ void vm_free_unused_boot_loader_range(addr_t start, addr_t end);
|
|||
addr_t vm_allocate_early(struct kernel_args *args, size_t virtualSize,
|
||||
size_t physicalSize, uint32 attributes);
|
||||
|
||||
|
||||
void slab_init(struct kernel_args *args, addr_t initialBase,
|
||||
size_t initialSize);
|
||||
void slab_init_post_sem();
|
||||
|
@ -128,8 +127,9 @@ struct vm_area *vm_area_lookup(struct vm_address_space *addressSpace,
|
|||
addr_t address);
|
||||
status_t vm_set_area_memory_type(area_id id, addr_t physicalBase, uint32 type);
|
||||
status_t vm_get_page_mapping(team_id team, addr_t vaddr, addr_t *paddr);
|
||||
bool vm_test_map_modification(struct vm_page *page);
|
||||
int32 vm_test_map_activation(struct vm_page *page, bool *_modified);
|
||||
void vm_clear_map_activation(struct vm_page *page);
|
||||
void vm_clear_map_flags(struct vm_page *page, uint32 flags);
|
||||
void vm_remove_all_page_mappings(struct vm_page *page);
|
||||
status_t vm_unmap_pages(struct vm_area *area, addr_t base, size_t length);
|
||||
status_t vm_map_page(struct vm_area *area, struct vm_page *page, addr_t address,
|
||||
|
|
|
@ -27,14 +27,14 @@ status_t vm_page_init_post_thread(struct kernel_args *args);
|
|||
status_t vm_mark_page_inuse(addr_t page);
|
||||
status_t vm_mark_page_range_inuse(addr_t startPage, addr_t length);
|
||||
status_t vm_page_set_state(struct vm_page *page, int state);
|
||||
void vm_page_requeue(struct vm_page *page, bool tail);
|
||||
|
||||
// get some data about the number of pages in the system
|
||||
size_t vm_page_num_pages(void);
|
||||
size_t vm_page_num_free_pages(void);
|
||||
|
||||
status_t vm_page_write_modified_page(struct vm_cache *cache,
|
||||
struct vm_page *page, bool fsReenter);
|
||||
status_t vm_page_write_modified_pages(struct vm_cache *cache, bool fsReenter);
|
||||
void vm_page_schedule_write_page(struct vm_page *page);
|
||||
|
||||
void vm_page_unreserve_pages(uint32 count);
|
||||
void vm_page_reserve_pages(uint32 count);
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef PAGE_CACHE_LOCKER_H
|
||||
#define PAGE_CACHE_LOCKER_H
|
||||
|
||||
|
||||
#include <null.h>
|
||||
|
||||
struct vm_page;
|
||||
|
||||
|
||||
class PageCacheLocker {
|
||||
public:
|
||||
PageCacheLocker(vm_page* page);
|
||||
~PageCacheLocker();
|
||||
|
||||
bool IsLocked() { return fPage != NULL; }
|
||||
|
||||
bool Lock(vm_page* page);
|
||||
void Unlock();
|
||||
|
||||
private:
|
||||
bool _IgnorePage(vm_page* page);
|
||||
|
||||
vm_page* fPage;
|
||||
};
|
||||
|
||||
#endif // PAGE_CACHE_LOCKER_H
|
|
@ -2354,6 +2354,32 @@ vm_get_page_mapping(team_id team, addr_t vaddr, addr_t *paddr)
|
|||
}
|
||||
|
||||
|
||||
bool
|
||||
vm_test_map_modification(vm_page *page)
|
||||
{
|
||||
MutexLocker locker(sMappingLock);
|
||||
|
||||
vm_page_mappings::Iterator iterator = page->mappings.GetIterator();
|
||||
vm_page_mapping *mapping;
|
||||
while ((mapping = iterator.Next()) != NULL) {
|
||||
vm_area *area = mapping->area;
|
||||
vm_translation_map *map = &area->address_space->translation_map;
|
||||
|
||||
addr_t physicalAddress;
|
||||
uint32 flags;
|
||||
map->ops->lock(map);
|
||||
addr_t address = area->base + (page->cache_offset << PAGE_SHIFT);
|
||||
map->ops->query_interrupt(map, address, &physicalAddress, &flags);
|
||||
map->ops->unlock(map);
|
||||
|
||||
if (flags & PAGE_MODIFIED)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
vm_test_map_activation(vm_page *page, bool *_modified)
|
||||
{
|
||||
|
@ -2389,7 +2415,7 @@ vm_test_map_activation(vm_page *page, bool *_modified)
|
|||
|
||||
|
||||
void
|
||||
vm_clear_map_activation(vm_page *page)
|
||||
vm_clear_map_flags(vm_page *page, uint32 flags)
|
||||
{
|
||||
MutexLocker locker(sMappingLock);
|
||||
|
||||
|
@ -2401,7 +2427,7 @@ vm_clear_map_activation(vm_page *page)
|
|||
|
||||
map->ops->lock(map);
|
||||
addr_t address = area->base + (page->cache_offset << PAGE_SHIFT);
|
||||
map->ops->clear_flags(map, address, PAGE_ACCESSED);
|
||||
map->ops->clear_flags(map, address, flags);
|
||||
map->ops->unlock(map);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
*/
|
||||
|
||||
|
||||
#include "PageCacheLocker.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <OS.h>
|
||||
|
@ -25,23 +27,6 @@ static sem_id sPageDaemonSem;
|
|||
static uint32 sNumPages;
|
||||
|
||||
|
||||
class PageCacheLocker {
|
||||
public:
|
||||
PageCacheLocker(vm_page* page);
|
||||
~PageCacheLocker();
|
||||
|
||||
bool IsLocked() { return fPage != NULL; }
|
||||
|
||||
bool Lock(vm_page* page);
|
||||
void Unlock();
|
||||
|
||||
private:
|
||||
bool _IgnorePage(vm_page* page);
|
||||
|
||||
vm_page* fPage;
|
||||
};
|
||||
|
||||
|
||||
PageCacheLocker::PageCacheLocker(vm_page* page)
|
||||
:
|
||||
fPage(NULL)
|
||||
|
@ -118,7 +103,7 @@ clear_page_activation(int32 index)
|
|||
return;
|
||||
|
||||
if (page->state == PAGE_STATE_ACTIVE)
|
||||
vm_clear_map_activation(page);
|
||||
vm_clear_map_flags(page, PAGE_ACCESSED);
|
||||
}
|
||||
|
||||
|
||||
|
@ -155,9 +140,9 @@ check_page_activation(int32 index)
|
|||
|
||||
if (page->usage_count < 0) {
|
||||
vm_remove_all_page_mappings(page);
|
||||
if (page->state == PAGE_STATE_MODIFIED) {
|
||||
// TODO: schedule to write back!
|
||||
} else
|
||||
if (page->state == PAGE_STATE_MODIFIED)
|
||||
vm_page_schedule_write_page(page);
|
||||
else
|
||||
vm_page_set_state(page, PAGE_STATE_INACTIVE);
|
||||
//dprintf("page %p -> move to inactive\n", page);
|
||||
}
|
||||
|
@ -193,6 +178,8 @@ page_daemon(void* /*unused*/)
|
|||
* pagesLeft / sLowPagesCount;
|
||||
uint32 leftToFree = 32 + (scanPagesCount - 32)
|
||||
* pagesLeft / sLowPagesCount;
|
||||
dprintf("wait interval %Ld, scan pages %lu, free %lu, target %lu\n",
|
||||
scanWaitInterval, scanPagesCount, pagesLeft, leftToFree);
|
||||
|
||||
for (uint32 i = 0; i < scanPagesCount && leftToFree > 0; i++) {
|
||||
if (clearPage == 0)
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include <vm_page.h>
|
||||
#include <vm_cache.h>
|
||||
|
||||
#include "PageCacheLocker.h"
|
||||
|
||||
|
||||
//#define TRACE_VM_PAGE
|
||||
#ifdef TRACE_VM_PAGE
|
||||
|
@ -59,23 +61,23 @@ static size_t sReservedPages;
|
|||
static ConditionVariable<page_queue> sFreePageCondition;
|
||||
static spinlock sPageLock;
|
||||
|
||||
static sem_id modified_pages_available;
|
||||
static sem_id sWriterWaitSem;
|
||||
|
||||
|
||||
/*! Dequeues a page from the tail of the given queue */
|
||||
/*! Dequeues a page from the head of the given queue */
|
||||
static vm_page *
|
||||
dequeue_page(page_queue *queue)
|
||||
{
|
||||
vm_page *page;
|
||||
|
||||
page = queue->tail;
|
||||
page = queue->head;
|
||||
if (page != NULL) {
|
||||
if (queue->head == page)
|
||||
queue->head = NULL;
|
||||
if (page->queue_prev != NULL)
|
||||
page->queue_prev->queue_next = NULL;
|
||||
if (queue->tail == page)
|
||||
queue->tail = NULL;
|
||||
if (page->queue_next != NULL)
|
||||
page->queue_next->queue_prev = NULL;
|
||||
|
||||
queue->tail = page->queue_prev;
|
||||
queue->head = page->queue_next;
|
||||
queue->count--;
|
||||
|
||||
#ifdef DEBUG_PAGE_QUEUE
|
||||
|
@ -92,7 +94,7 @@ dequeue_page(page_queue *queue)
|
|||
}
|
||||
|
||||
|
||||
/*! Enqueues a page to the head of the given queue */
|
||||
/*! Enqueues a page to the tail of the given queue */
|
||||
static void
|
||||
enqueue_page(page_queue *queue, vm_page *page)
|
||||
{
|
||||
|
@ -103,6 +105,32 @@ enqueue_page(page_queue *queue, vm_page *page)
|
|||
}
|
||||
#endif // DEBUG_PAGE_QUEUE
|
||||
|
||||
if (queue->tail != NULL)
|
||||
queue->tail->queue_next = page;
|
||||
page->queue_prev = queue->tail;
|
||||
queue->tail = page;
|
||||
page->queue_next = NULL;
|
||||
if (queue->head == NULL)
|
||||
queue->head = page;
|
||||
queue->count++;
|
||||
|
||||
#ifdef DEBUG_PAGE_QUEUE
|
||||
page->queue = queue;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*! Enqueues a page to the head of the given queue */
|
||||
static void
|
||||
enqueue_page_to_head(page_queue *queue, vm_page *page)
|
||||
{
|
||||
#ifdef DEBUG_PAGE_QUEUE
|
||||
if (page->queue != NULL) {
|
||||
panic("enqueue_page_to_head(queue: %p, page: %p): page thinks it is "
|
||||
"already in queue %p", queue, page, page->queue);
|
||||
}
|
||||
#endif // DEBUG_PAGE_QUEUE
|
||||
|
||||
if (queue->head != NULL)
|
||||
queue->head->queue_prev = page;
|
||||
page->queue_next = queue->head;
|
||||
|
@ -128,16 +156,16 @@ remove_page_from_queue(page_queue *queue, vm_page *page)
|
|||
}
|
||||
#endif // DEBUG_PAGE_QUEUE
|
||||
|
||||
if (page->queue_prev != NULL)
|
||||
page->queue_prev->queue_next = page->queue_next;
|
||||
else
|
||||
queue->head = page->queue_next;
|
||||
|
||||
if (page->queue_next != NULL)
|
||||
page->queue_next->queue_prev = page->queue_prev;
|
||||
else
|
||||
queue->tail = page->queue_prev;
|
||||
|
||||
if (page->queue_prev != NULL)
|
||||
page->queue_prev->queue_next = page->queue_next;
|
||||
else
|
||||
queue->head = page->queue_next;
|
||||
|
||||
queue->count--;
|
||||
|
||||
#ifdef DEBUG_PAGE_QUEUE
|
||||
|
@ -610,85 +638,6 @@ page_scrubber(void *unused)
|
|||
}
|
||||
|
||||
|
||||
#if 0
|
||||
static int pageout_daemon()
|
||||
{
|
||||
int state;
|
||||
vm_page *page;
|
||||
vm_region *region;
|
||||
IOVECS(vecs, 1);
|
||||
ssize_t err;
|
||||
|
||||
dprintf("pageout daemon starting\n");
|
||||
|
||||
for (;;) {
|
||||
acquire_sem(modified_pages_available);
|
||||
|
||||
dprintf("here\n");
|
||||
|
||||
state = disable_interrupts();
|
||||
acquire_spinlock(&sPageLock);
|
||||
page = dequeue_page(&sModifiedPageQueue);
|
||||
page->state = PAGE_STATE_BUSY;
|
||||
vm_cache_acquire_ref(page->cache_ref, true);
|
||||
release_spinlock(&sPageLock);
|
||||
restore_interrupts(state);
|
||||
|
||||
dprintf("got page %p\n", page);
|
||||
|
||||
if (page->cache_ref->cache->temporary && !trimming_cycle) {
|
||||
// unless we're in the trimming cycle, dont write out pages
|
||||
// that back anonymous stores
|
||||
state = disable_interrupts();
|
||||
acquire_spinlock(&sPageLock);
|
||||
enqueue_page(&sModifiedPageQueue, page);
|
||||
page->state = PAGE_STATE_MODIFIED;
|
||||
release_spinlock(&sPageLock);
|
||||
restore_interrupts(state);
|
||||
vm_cache_release_ref(page->cache_ref);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* clear the modified flag on this page in all it's mappings */
|
||||
mutex_lock(&page->cache_ref->lock);
|
||||
for (region = page->cache_ref->region_list; region; region = region->cache_next) {
|
||||
if (page->offset > region->cache_offset
|
||||
&& page->offset < region->cache_offset + region->size) {
|
||||
vm_translation_map *map = ®ion->aspace->translation_map;
|
||||
map->ops->lock(map);
|
||||
map->ops->clear_flags(map, page->offset - region->cache_offset + region->base, PAGE_MODIFIED);
|
||||
map->ops->unlock(map);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&page->cache_ref->lock);
|
||||
|
||||
/* write the page out to it's backing store */
|
||||
vecs->num = 1;
|
||||
vecs->total_len = PAGE_SIZE;
|
||||
vm_get_physical_page(page->physical_page_number * PAGE_SIZE, (addr_t *)&vecs->vec[0].iov_base, PHYSICAL_PAGE_CAN_WAIT);
|
||||
vecs->vec[0].iov_len = PAGE_SIZE;
|
||||
|
||||
err = page->cache_ref->cache->store->ops->write(page->cache_ref->cache->store, page->offset, vecs);
|
||||
|
||||
vm_put_physical_page((addr_t)vecs->vec[0].iov_base);
|
||||
|
||||
state = disable_interrupts();
|
||||
acquire_spinlock(&sPageLock);
|
||||
if (page->ref_count > 0) {
|
||||
page->state = PAGE_STATE_ACTIVE;
|
||||
} else {
|
||||
page->state = PAGE_STATE_INACTIVE;
|
||||
}
|
||||
enqueue_page(&sActivePageQueue, page);
|
||||
release_spinlock(&sPageLock);
|
||||
restore_interrupts(state);
|
||||
|
||||
vm_cache_release_ref(page->cache_ref);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static status_t
|
||||
write_page(vm_page *page, bool fsReenter)
|
||||
{
|
||||
|
@ -719,6 +668,97 @@ write_page(vm_page *page, bool fsReenter)
|
|||
}
|
||||
|
||||
|
||||
status_t
|
||||
page_writer(void* /*unused*/)
|
||||
{
|
||||
while (true) {
|
||||
acquire_sem_etc(sWriterWaitSem, 1, B_RELATIVE_TIMEOUT, 3000000);
|
||||
// all 3 seconds when no one triggers us
|
||||
|
||||
const uint32 kNumPages = 32;
|
||||
ConditionVariable<vm_page> busyConditions[kNumPages];
|
||||
vm_page *pages[kNumPages];
|
||||
uint32 numPages = 0;
|
||||
|
||||
// TODO: once the I/O scheduler is there, we should write back
|
||||
// a lot more pages back.
|
||||
// TODO: make this laptop friendly, too (ie. only start doing
|
||||
// something if someone else did something or there is really
|
||||
// enough to do).
|
||||
|
||||
// collect pages to be written
|
||||
|
||||
while (numPages < kNumPages) {
|
||||
InterruptsSpinLocker locker(sPageLock);
|
||||
|
||||
vm_page *page = sModifiedPageQueue.head;
|
||||
while (page != NULL && page->state == PAGE_STATE_BUSY) {
|
||||
// skip busy pages
|
||||
page = page->queue_next;
|
||||
}
|
||||
if (page == NULL)
|
||||
break;
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
PageCacheLocker cacheLocker(page);
|
||||
if (!cacheLocker.IsLocked())
|
||||
continue;
|
||||
|
||||
locker.Lock();
|
||||
remove_page_from_queue(&sModifiedPageQueue, page);
|
||||
page->state = PAGE_STATE_BUSY;
|
||||
|
||||
busyConditions[numPages].Publish(page, "page");
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
//dprintf("write page %p\n", page);
|
||||
vm_clear_map_flags(page, PAGE_MODIFIED);
|
||||
vm_cache_acquire_ref(page->cache);
|
||||
pages[numPages++] = page;
|
||||
}
|
||||
|
||||
if (numPages == 0)
|
||||
continue;
|
||||
|
||||
// write pages to disk
|
||||
|
||||
// TODO: put this as requests into the I/O scheduler
|
||||
status_t writeStatus[kNumPages];
|
||||
for (uint32 i = 0; i < numPages; i++) {
|
||||
writeStatus[i] = write_page(pages[i], false);
|
||||
}
|
||||
|
||||
// mark pages depending on if they could be written or not
|
||||
|
||||
for (uint32 i = 0; i < numPages; i++) {
|
||||
vm_cache *cache = pages[i]->cache;
|
||||
mutex_lock(&cache->lock);
|
||||
|
||||
if (writeStatus[i] == B_OK) {
|
||||
// put it into the active queue
|
||||
InterruptsSpinLocker locker(sPageLock);
|
||||
move_page_to_active_or_inactive_queue(pages[i], true);
|
||||
} else {
|
||||
// We don't have to put the PAGE_MODIFIED bit back, as it's
|
||||
// still in the modified pages list.
|
||||
InterruptsSpinLocker locker(sPageLock);
|
||||
pages[i]->state = PAGE_STATE_MODIFIED;
|
||||
enqueue_page(&sModifiedPageQueue, pages[i]);
|
||||
}
|
||||
|
||||
busyConditions[i].Unpublish();
|
||||
|
||||
mutex_unlock(&cache->lock);
|
||||
vm_cache_release_ref(cache);
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
page_thief(void* /*unused*/, int32 level)
|
||||
{
|
||||
|
@ -751,7 +791,7 @@ page_thief(void* /*unused*/, int32 level)
|
|||
// find a candidate to steal from the inactive queue
|
||||
|
||||
for (int32 i = sActivePageQueue.count; i-- > 0;) {
|
||||
// move page to the head of the queue so that we don't
|
||||
// move page to the tail of the queue so that we don't
|
||||
// scan it again directly
|
||||
page = dequeue_page(&sActivePageQueue);
|
||||
enqueue_page(&sActivePageQueue, page);
|
||||
|
@ -798,65 +838,6 @@ page_thief(void* /*unused*/, int32 level)
|
|||
// #pragma mark - private kernel API
|
||||
|
||||
|
||||
/*!
|
||||
You need to hold the vm_cache lock when calling this function.
|
||||
And \a page must obviously be in that cache.
|
||||
Note that the cache lock is released in this function.
|
||||
*/
|
||||
status_t
|
||||
vm_page_write_modified_page(vm_cache *cache, vm_page *page, bool fsReenter)
|
||||
{
|
||||
ASSERT(page->state == PAGE_STATE_MODIFIED);
|
||||
ASSERT(page->cache == cache);
|
||||
|
||||
ConditionVariable<vm_page> busyCondition;
|
||||
InterruptsSpinLocker locker(&sPageLock);
|
||||
|
||||
remove_page_from_queue(&sModifiedPageQueue, page);
|
||||
page->state = PAGE_STATE_BUSY;
|
||||
|
||||
busyCondition.Publish(page, "page");
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
off_t pageOffset = (off_t)page->cache_offset << PAGE_SHIFT;
|
||||
|
||||
for (vm_area *area = cache->areas; area; area = area->cache_next) {
|
||||
if (pageOffset >= area->cache_offset
|
||||
&& pageOffset < area->cache_offset + area->size) {
|
||||
vm_translation_map *map = &area->address_space->translation_map;
|
||||
// clear the modified flag
|
||||
map->ops->lock(map);
|
||||
map->ops->clear_flags(map, pageOffset - area->cache_offset
|
||||
+ area->base, PAGE_MODIFIED);
|
||||
map->ops->unlock(map);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&cache->lock);
|
||||
|
||||
status_t status = write_page(page, fsReenter);
|
||||
|
||||
mutex_lock(&cache->lock);
|
||||
|
||||
locker.Lock();
|
||||
|
||||
if (status == B_OK) {
|
||||
// put it into the active queue
|
||||
move_page_to_active_or_inactive_queue(page, true);
|
||||
} else {
|
||||
// We don't have to put the PAGE_MODIFIED bit back, as it's still
|
||||
// in the modified pages list.
|
||||
page->state = PAGE_STATE_MODIFIED;
|
||||
enqueue_page(&sModifiedPageQueue, page);
|
||||
}
|
||||
|
||||
busyCondition.Unpublish();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
You need to hold the vm_cache lock when calling this function.
|
||||
Note that the cache lock is released in this function.
|
||||
|
@ -864,32 +845,24 @@ vm_page_write_modified_page(vm_cache *cache, vm_page *page, bool fsReenter)
|
|||
status_t
|
||||
vm_page_write_modified_pages(vm_cache *cache, bool fsReenter)
|
||||
{
|
||||
vm_page *page = cache->page_list;
|
||||
|
||||
// ToDo: join adjacent pages into one vec list
|
||||
|
||||
for (; page; page = page->cache_next) {
|
||||
bool gotPage = false;
|
||||
bool dequeuedPage = true;
|
||||
off_t pageOffset;
|
||||
status_t status;
|
||||
vm_area *area;
|
||||
ConditionVariable<vm_page> busyCondition;
|
||||
|
||||
cpu_status state = disable_interrupts();
|
||||
acquire_spinlock(&sPageLock);
|
||||
for (vm_page *page = cache->page_list; page; page = page->cache_next) {
|
||||
bool dequeuedPage = false;
|
||||
|
||||
if (page->state == PAGE_STATE_MODIFIED) {
|
||||
InterruptsSpinLocker locker(&sPageLock);
|
||||
remove_page_from_queue(&sModifiedPageQueue, page);
|
||||
dequeuedPage = true;
|
||||
} else if (!vm_test_map_modification(page))
|
||||
continue;
|
||||
|
||||
page->state = PAGE_STATE_BUSY;
|
||||
|
||||
ConditionVariable<vm_page> busyCondition;
|
||||
busyCondition.Publish(page, "page");
|
||||
gotPage = true;
|
||||
}
|
||||
|
||||
release_spinlock(&sPageLock);
|
||||
restore_interrupts(state);
|
||||
|
||||
// We may have a modified page - however, while we're writing it back,
|
||||
// We have a modified page - however, while we're writing it back,
|
||||
// the page is still mapped. In order not to lose any changes to the
|
||||
// page, we mark it clean before actually writing it back; if writing
|
||||
// the page fails for some reason, we just keep it in the modified page
|
||||
|
@ -899,79 +872,53 @@ vm_page_write_modified_pages(vm_cache *cache, bool fsReenter)
|
|||
// had the chance to write it back, then we'll write it again later -
|
||||
// that will probably not happen that often, though.
|
||||
|
||||
pageOffset = (off_t)page->cache_offset << PAGE_SHIFT;
|
||||
|
||||
for (area = cache->areas; area; area = area->cache_next) {
|
||||
if (pageOffset >= area->cache_offset
|
||||
&& pageOffset < area->cache_offset + area->size) {
|
||||
vm_translation_map *map = &area->address_space->translation_map;
|
||||
map->ops->lock(map);
|
||||
|
||||
if (!gotPage) {
|
||||
// Check if the PAGE_MODIFIED bit hasn't been propagated yet
|
||||
addr_t physicalAddress;
|
||||
uint32 flags;
|
||||
map->ops->query(map, pageOffset - area->cache_offset + area->base,
|
||||
&physicalAddress, &flags);
|
||||
if (flags & PAGE_MODIFIED) {
|
||||
page->state = PAGE_STATE_BUSY;
|
||||
busyCondition.Publish(page, "page");
|
||||
gotPage = true;
|
||||
dequeuedPage = false;
|
||||
}
|
||||
}
|
||||
if (gotPage) {
|
||||
// clear the modified flag
|
||||
map->ops->clear_flags(map, pageOffset - area->cache_offset
|
||||
+ area->base, PAGE_MODIFIED);
|
||||
}
|
||||
map->ops->unlock(map);
|
||||
}
|
||||
}
|
||||
|
||||
if (!gotPage)
|
||||
continue;
|
||||
vm_clear_map_flags(page, PAGE_MODIFIED);
|
||||
|
||||
mutex_unlock(&cache->lock);
|
||||
|
||||
status = write_page(page, fsReenter);
|
||||
status_t status = write_page(page, fsReenter);
|
||||
|
||||
mutex_lock(&cache->lock);
|
||||
|
||||
InterruptsSpinLocker locker(&sPageLock);
|
||||
|
||||
if (status == B_OK) {
|
||||
// put it into the active/inactive queue
|
||||
|
||||
state = disable_interrupts();
|
||||
acquire_spinlock(&sPageLock);
|
||||
|
||||
move_page_to_active_or_inactive_queue(page, dequeuedPage);
|
||||
busyCondition.Unpublish();
|
||||
|
||||
release_spinlock(&sPageLock);
|
||||
restore_interrupts(state);
|
||||
} else {
|
||||
// We don't have to put the PAGE_MODIFIED bit back, as it's still
|
||||
// in the modified pages list.
|
||||
state = disable_interrupts();
|
||||
acquire_spinlock(&sPageLock);
|
||||
|
||||
if (dequeuedPage) {
|
||||
page->state = PAGE_STATE_MODIFIED;
|
||||
enqueue_page(&sModifiedPageQueue, page);
|
||||
} else
|
||||
set_page_state_nolock(page, PAGE_STATE_MODIFIED);
|
||||
}
|
||||
|
||||
busyCondition.Unpublish();
|
||||
|
||||
release_spinlock(&sPageLock);
|
||||
restore_interrupts(state);
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*! Schedules the page writer to write back the specified \a page.
|
||||
Note, however, that it doesn't do this immediately, and it might
|
||||
take several seconds until the page is actually written out.
|
||||
*/
|
||||
void
|
||||
vm_page_schedule_write_page(vm_page *page)
|
||||
{
|
||||
ASSERT(page->state == PAGE_STATE_MODIFIED);
|
||||
|
||||
vm_page_requeue(page, false);
|
||||
|
||||
release_sem_etc(sWriterWaitSem, 1,
|
||||
B_RELEASE_IF_WAITING_ONLY | B_DO_NOT_RESCHEDULE);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
vm_page_init_num_pages(kernel_args *args)
|
||||
{
|
||||
|
@ -1086,17 +1033,17 @@ vm_page_init_post_thread(kernel_args *args)
|
|||
thread = spawn_kernel_thread(&page_scrubber, "page scrubber", B_LOWEST_ACTIVE_PRIORITY, NULL);
|
||||
send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE);
|
||||
|
||||
modified_pages_available = create_sem(0, "modified_pages_avail_sem");
|
||||
#if 0
|
||||
// create a kernel thread to schedule modified pages to write
|
||||
tid = thread_create_kernel_thread("pageout daemon", &pageout_daemon, B_FIRST_REAL_TIME_PRIORITY + 1);
|
||||
thread_resume_thread(tid);
|
||||
#endif
|
||||
|
||||
new (&sFreePageCondition) ConditionVariable<page_queue>;
|
||||
sFreePageCondition.Publish(&sFreePageQueue, "free page");
|
||||
|
||||
register_low_memory_handler(page_thief, NULL, 0);
|
||||
|
||||
sWriterWaitSem = create_sem(0, "page writer");
|
||||
|
||||
thread = spawn_kernel_thread(&page_writer, "page writer",
|
||||
B_LOW_PRIORITY, NULL);
|
||||
send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
@ -1374,15 +1321,47 @@ vm_lookup_page(addr_t pageNumber)
|
|||
status_t
|
||||
vm_page_set_state(vm_page *page, int pageState)
|
||||
{
|
||||
cpu_status state = disable_interrupts();
|
||||
acquire_spinlock(&sPageLock);
|
||||
InterruptsSpinLocker _(sPageLock);
|
||||
|
||||
status_t status = set_page_state_nolock(page, pageState);
|
||||
return set_page_state_nolock(page, pageState);
|
||||
}
|
||||
|
||||
release_spinlock(&sPageLock);
|
||||
restore_interrupts(state);
|
||||
|
||||
return status;
|
||||
void
|
||||
vm_page_requeue(struct vm_page *page, bool tail)
|
||||
{
|
||||
InterruptsSpinLocker _(sPageLock);
|
||||
page_queue *queue = NULL;
|
||||
|
||||
switch (page->state) {
|
||||
case PAGE_STATE_BUSY:
|
||||
case PAGE_STATE_ACTIVE:
|
||||
case PAGE_STATE_INACTIVE:
|
||||
case PAGE_STATE_WIRED:
|
||||
case PAGE_STATE_UNUSED:
|
||||
queue = &sActivePageQueue;
|
||||
break;
|
||||
case PAGE_STATE_MODIFIED:
|
||||
queue = &sModifiedPageQueue;
|
||||
break;
|
||||
case PAGE_STATE_FREE:
|
||||
queue = &sFreePageQueue;
|
||||
break;
|
||||
case PAGE_STATE_CLEAR:
|
||||
queue = &sClearPageQueue;
|
||||
break;
|
||||
default:
|
||||
panic("vm_page_touch: vm_page %p in invalid state %d\n",
|
||||
page, page->state);
|
||||
break;
|
||||
}
|
||||
|
||||
remove_page_from_queue(queue, page);
|
||||
|
||||
if (tail)
|
||||
enqueue_page(queue, page);
|
||||
else
|
||||
enqueue_page_to_head(queue, page);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue