* Use condition variables when waiting for busy pages or busy caches.

* Removed a few instances where the page state was set busy directly after 
  allocating it. This is a no-op, since a page is always busy after 
  allocation.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21875 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2007-08-09 20:08:25 +00:00
parent 227402f2bf
commit 279c6b76dc
9 changed files with 142 additions and 60 deletions

View File

@ -152,6 +152,8 @@ struct thread {
int32 flags;
} sem;
struct PrivateConditionVariable *condition_variable;
struct {
sem_id write_sem;
sem_id read_sem;

View File

@ -133,6 +133,16 @@ enum {
CACHE_TYPE_NULL
};
#ifdef __cplusplus
#include <condition_variable.h>
struct vm_dummy_page : vm_page {
ConditionVariable<vm_page> busy_condition;
};
#endif // __cplusplus
// vm_cache
typedef struct vm_cache {
mutex lock;

View File

@ -9,13 +9,14 @@
#include <KernelExport.h>
#include <fs_cache.h>
#include <util/kernel_cpp.h>
#include <condition_variable.h>
#include <file_cache.h>
#include <generic_syscall.h>
#include <util/kernel_cpp.h>
#include <vfs.h>
#include <vm.h>
#include <vm_page.h>
#include <vm_cache.h>
#include <generic_syscall.h>
#include <unistd.h>
#include <stdlib.h>
@ -30,7 +31,7 @@
#endif
// maximum number of iovecs per request
#define MAX_IO_VECS 64 // 256 kB
#define MAX_IO_VECS 32 // 128 kB
#define MAX_FILE_IO_VECS 32
#define MAX_TEMP_IO_VECS 8
@ -522,10 +523,13 @@ read_chunk_into_cache(file_cache_ref *ref, off_t offset, size_t numBytes,
vm_cache *cache = ref->cache;
// TODO: We're using way too much stack! Rather allocate a sufficiently
// large chunk on the heap.
iovec vecs[MAX_IO_VECS];
int32 vecCount = 0;
vm_page *pages[MAX_IO_VECS];
ConditionVariable<vm_page> busyConditions[MAX_IO_VECS];
int32 pageIndex = 0;
// allocate pages for the cache and mark them busy
@ -534,7 +538,7 @@ read_chunk_into_cache(file_cache_ref *ref, off_t offset, size_t numBytes,
if (page == NULL)
panic("no more pages!");
page->state = PAGE_STATE_BUSY;
busyConditions[pageIndex - 1].Publish(page, "page");
vm_cache_insert_page(cache, page, offset + pos);
@ -569,6 +573,7 @@ read_chunk_into_cache(file_cache_ref *ref, off_t offset, size_t numBytes,
mutex_lock(&cache->lock);
for (int32 i = 0; i < pageIndex; i++) {
busyConditions[i].Unpublish();
vm_cache_remove_page(cache, pages[i]);
vm_page_set_state(pages[i], PAGE_STATE_FREE);
}
@ -599,8 +604,10 @@ read_chunk_into_cache(file_cache_ref *ref, off_t offset, size_t numBytes,
mutex_lock(&cache->lock);
// make the pages accessible in the cache
for (int32 i = pageIndex; i-- > 0;)
for (int32 i = pageIndex; i-- > 0;) {
pages[i]->state = PAGE_STATE_ACTIVE;
busyConditions[i].Unpublish();
}
return B_OK;
}
@ -664,11 +671,14 @@ static inline status_t
write_chunk_to_cache(file_cache_ref *ref, off_t offset, size_t numBytes,
int32 pageOffset, addr_t buffer, size_t bufferSize)
{
// TODO: We're using way too much stack! Rather allocate a sufficiently
// large chunk on the heap.
iovec vecs[MAX_IO_VECS];
int32 vecCount = 0;
vm_page *pages[MAX_IO_VECS];
int32 pageIndex = 0;
status_t status = B_OK;
ConditionVariable<vm_page> busyConditions[MAX_IO_VECS];
// ToDo: this should be settable somewhere
bool writeThrough = false;
@ -679,7 +689,7 @@ write_chunk_to_cache(file_cache_ref *ref, off_t offset, size_t numBytes,
// big - shouldn't we better steal the pages directly in that case?
// (a working set like approach for the file cache)
vm_page *page = pages[pageIndex++] = vm_page_allocate_page(PAGE_STATE_FREE);
page->state = PAGE_STATE_BUSY;
busyConditions[pageIndex - 1].Publish(page, "page");
vm_cache_insert_page(ref->cache, page, offset + pos);
@ -769,6 +779,8 @@ write_chunk_to_cache(file_cache_ref *ref, off_t offset, size_t numBytes,
// make the pages accessible in the cache
for (int32 i = pageIndex; i-- > 0;) {
busyConditions[i].Unpublish();
if (writeThrough)
pages[i]->state = PAGE_STATE_ACTIVE;
else
@ -914,9 +926,10 @@ cache_io(void *_cacheRef, off_t offset, addr_t buffer, size_t *_size, bool doWri
return B_NO_MEMORY;
}
} else {
ConditionVariableEntry<vm_page> entry;
entry.Add(page);
mutex_unlock(&cache->lock);
// ToDo: don't wait forever!
snooze(20000);
entry.Wait();
mutex_lock(&cache->lock);
goto restart;
}
@ -974,9 +987,10 @@ cache_io(void *_cacheRef, off_t offset, addr_t buffer, size_t *_size, bool doWri
// we let the other party add our page
currentPage->queue_next = page;
} else {
ConditionVariableEntry<vm_page> entry;
entry.Add(page);
mutex_unlock(&cache->lock);
// ToDo: don't wait forever!
snooze(20000);
entry.Wait();
mutex_lock(&cache->lock);
goto restart_dummy_lookup;
}
@ -1113,9 +1127,11 @@ cache_prefetch_vnode(void *vnode, off_t offset, size_t size)
vm_page *page = vm_cache_lookup_page(cache, offset);
if (page != NULL) {
if (page->state == PAGE_STATE_BUSY) {
// if busy retry again a little later
// if busy retry again later
ConditionVariableEntry<vm_page> entry;
entry.Add(page);
mutex_unlock(&cache->lock);
snooze(20000);
entry.Wait();
mutex_lock(&cache->lock);
goto restart;

View File

@ -14,6 +14,7 @@
#include <arch/platform.h>
#include <boot_item.h>
#include <cbuf.h>
#include <condition_variable.h>
#include <cpu.h>
#include <debug.h>
#include <elf.h>
@ -131,6 +132,7 @@ _start(kernel_args *bootKernelArgs, int currentCPU)
TRACE("init semaphores\n");
sem_init(&sKernelArgs);
condition_variable_init();
// now we can create and use semaphores
TRACE("init VM semaphores\n");

View File

@ -210,6 +210,7 @@ create_thread_struct(struct thread *inthread, const char *name,
thread->team = NULL;
thread->cpu = cpu;
thread->sem.blocking = -1;
thread->condition_variable = NULL;
thread->fault_handler = 0;
thread->page_faults_allowed = 1;
thread->kernel_stack_area = -1;
@ -723,6 +724,7 @@ _dump_thread_info(struct thread *thread)
kprintf(" sem.count: 0x%lx\n", thread->sem.count);
kprintf(" sem.acquire_status: 0x%lx\n", thread->sem.acquire_status);
kprintf(" sem.flags: 0x%lx\n", thread->sem.flags);
kprintf("condition variable: %p\n", thread->condition_variable);
kprintf("fault_handler: %p\n", (void *)thread->fault_handler);
kprintf("args: %p %p\n", thread->args1, thread->args2);
kprintf("entry: %p\n", (void *)thread->entry);
@ -826,7 +828,8 @@ dump_thread_list(int argc, char **argv)
kprintf("ignoring invalid team argument.\n");
}
kprintf("thread id state sem cpu pri stack team name\n");
kprintf("thread id state sem/cv cpu pri stack team "
"name\n");
hash_open(sThreadHash, &i);
while ((thread = hash_next(sThreadHash, &i)) != NULL) {
@ -840,11 +843,14 @@ dump_thread_list(int argc, char **argv)
kprintf("%p %6lx %-9s", thread, thread->id, state_to_text(thread,
thread->state));
// does it block on a semaphore?
if (thread->state == B_THREAD_WAITING)
kprintf("%6lx ", thread->sem.blocking);
else
kprintf(" - ");
// does it block on a semaphore or a condition variable?
if (thread->state == B_THREAD_WAITING) {
if (thread->condition_variable)
kprintf("%p ", thread->condition_variable);
else
kprintf("%10lx ", thread->sem.blocking);
} else
kprintf(" - ");
// on which CPU does it run?
if (thread->cpu)

View File

@ -6,7 +6,7 @@ KernelMergeObject kernel_vm.o :
vm_cache.cpp
vm_daemons.c
vm_low_memory.cpp
vm_page.c
vm_page.cpp
vm_store_anonymous_noswap.c
vm_store_device.c
vm_store_null.c

View File

@ -22,6 +22,7 @@
#include <vm_low_memory.h>
#include <file_cache.h>
#include <memheap.h>
#include <condition_variable.h>
#include <debug.h>
#include <console.h>
#include <int.h>
@ -3496,11 +3497,13 @@ retry:
away by grabbing a reference to it.
*/
static inline void
fault_insert_dummy_page(vm_cache *cache, vm_page &dummyPage, off_t cacheOffset)
fault_insert_dummy_page(vm_cache *cache, vm_dummy_page &dummyPage,
off_t cacheOffset)
{
dummyPage.state = PAGE_STATE_BUSY;
vm_cache_acquire_ref(cache);
vm_cache_insert_page(cache, &dummyPage, cacheOffset);
dummyPage.busy_condition.Publish(&dummyPage, "page");
}
@ -3509,7 +3512,7 @@ fault_insert_dummy_page(vm_cache *cache, vm_page &dummyPage, off_t cacheOffset)
the cache.
*/
static inline void
fault_remove_dummy_page(vm_page &dummyPage, bool isLocked)
fault_remove_dummy_page(vm_dummy_page &dummyPage, bool isLocked)
{
vm_cache *cache = dummyPage.cache;
if (!isLocked)
@ -3518,6 +3521,7 @@ fault_remove_dummy_page(vm_page &dummyPage, bool isLocked)
if (dummyPage.state == PAGE_STATE_BUSY) {
vm_cache_remove_page(cache, &dummyPage);
dummyPage.state = PAGE_STATE_INACTIVE;
dummyPage.busy_condition.Unpublish();
}
if (!isLocked)
@ -3537,8 +3541,8 @@ fault_remove_dummy_page(vm_page &dummyPage, bool isLocked)
*/
static inline status_t
fault_find_page(vm_translation_map *map, vm_cache *topCache,
off_t cacheOffset, bool isWrite, vm_page &dummyPage, vm_cache **_pageCache,
vm_page** _page, bool* _restart)
off_t cacheOffset, bool isWrite, vm_dummy_page &dummyPage,
vm_cache **_pageCache, vm_page** _page, bool* _restart)
{
*_restart = false;
vm_cache *cache = topCache;
@ -3560,24 +3564,36 @@ fault_find_page(vm_translation_map *map, vm_cache *topCache,
for (;;) {
page = vm_cache_lookup_page(cache, cacheOffset);
if (page != NULL && page->state != PAGE_STATE_BUSY) {
// Note: We set the page state to busy, but we don't need a
// condition variable here, since we keep the cache locked
// till we mark the page unbusy again (in fault_get_page()
// the source page, or in vm_soft_fault() when mapping the
// page), so no one will ever know the page was busy in the
// first place.
vm_page_set_state(page, PAGE_STATE_BUSY);
break;
}
if (page == NULL || page == &dummyPage)
break;
// page must be busy
// ToDo: don't wait forever!
mutex_unlock(&cache->lock);
thread_yield();
mutex_lock(&cache->lock);
// page must be busy -- wait for it to become unbusy
{
ConditionVariableEntry<vm_page> entry;
entry.Add(page);
mutex_unlock(&cache->lock);
entry.Wait();
mutex_lock(&cache->lock);
}
if (cache->busy) {
// The cache became busy, which means, it is about to be
// removed by vm_cache_remove_consumer(). We start again with
// the top cache.
ConditionVariableEntry<vm_cache> entry;
entry.Add(cache);
mutex_unlock(&cache->lock);
vm_cache_release_ref(cache);
entry.Wait();
*_restart = true;
return B_OK;
}
@ -3595,9 +3611,11 @@ fault_find_page(vm_translation_map *map, vm_cache *topCache,
// insert a fresh page and mark it busy -- we're going to read it in
page = vm_page_allocate_page(PAGE_STATE_FREE);
page->state = PAGE_STATE_BUSY;
vm_cache_insert_page(cache, page, cacheOffset);
ConditionVariable<vm_page> busyCondition;
busyCondition.Publish(page, "page");
mutex_unlock(&cache->lock);
// get a virtual address for the page
@ -3620,6 +3638,7 @@ fault_find_page(vm_translation_map *map, vm_cache *topCache,
dprintf("reading page from store %p (cache %p) returned: %s!\n",
store, cache, strerror(status));
busyCondition.Unpublish();
vm_cache_remove_page(cache, page);
vm_page_set_state(page, PAGE_STATE_FREE);
@ -3628,6 +3647,7 @@ fault_find_page(vm_translation_map *map, vm_cache *topCache,
// mark the page unbusy again
page->state = PAGE_STATE_ACTIVE;
busyCondition.Unpublish();
break;
}
@ -3651,8 +3671,11 @@ fault_find_page(vm_translation_map *map, vm_cache *topCache,
// The cache became busy, which means, it is about to be
// removed by vm_cache_remove_consumer(). We start again with
// the top cache.
ConditionVariableEntry<vm_cache> entry;
entry.Add(cache);
mutex_unlock(&cache->lock);
vm_cache_release_ref(cache);
entry.Wait();
*_restart = true;
return B_OK;
}
@ -3682,8 +3705,11 @@ fault_find_page(vm_translation_map *map, vm_cache *topCache,
// The cache became busy, which means, it is about to be
// removed by vm_cache_remove_consumer(). We start again with
// the top cache.
ConditionVariableEntry<vm_cache> entry;
entry.Add(cache);
mutex_unlock(&cache->lock);
vm_cache_release_ref(cache);
entry.Wait();
*_restart = true;
} else {
vm_page* newPage = vm_cache_lookup_page(cache, cacheOffset);
@ -3718,8 +3744,8 @@ fault_find_page(vm_translation_map *map, vm_cache *topCache,
to it, and has also locked it on exit.
*/
static inline status_t
fault_get_page(vm_translation_map *map, vm_cache *topCache,
off_t cacheOffset, bool isWrite, vm_page &dummyPage, vm_cache **_sourceCache,
fault_get_page(vm_translation_map *map, vm_cache *topCache, off_t cacheOffset,
bool isWrite, vm_dummy_page &dummyPage, vm_cache **_sourceCache,
vm_cache **_copiedSource, vm_page** _page)
{
vm_cache *cache;
@ -3848,8 +3874,10 @@ if (cacheOffset == 0x12000)
break;
// The page is busy, wait till it becomes unbusy.
ConditionVariableEntry<vm_page> entry;
entry.Add(newPage);
mutex_unlock(&topCache->lock);
snooze(10000);
entry.Wait();
mutex_lock(&topCache->lock);
}
@ -3968,7 +3996,7 @@ vm_soft_fault(addr_t originalAddress, bool isWrite, bool isUser)
// already have the page we're searching for (we're going from top to bottom)
vm_translation_map *map = &addressSpace->translation_map;
vm_page dummyPage;
vm_dummy_page dummyPage;
dummyPage.cache = NULL;
dummyPage.state = PAGE_STATE_INACTIVE;
dummyPage.type = PAGE_TYPE_DUMMY;

View File

@ -6,23 +6,24 @@
* Distributed under the terms of the NewOS License.
*/
#include <kernel.h>
#include <vm.h>
#include <vm_priv.h>
#include <vm_cache.h>
#include <vm_page.h>
#include <int.h>
#include <util/khash.h>
#include <lock.h>
#include <debug.h>
#include <lock.h>
#include <smp.h>
#include <arch/cpu.h>
#include <stddef.h>
#include <stdlib.h>
#include <arch/cpu.h>
#include <condition_variable.h>
#include <debug.h>
#include <int.h>
#include <kernel.h>
#include <lock.h>
#include <smp.h>
#include <util/khash.h>
#include <vm.h>
#include <vm_page.h>
#include <vm_priv.h>
//#define TRACE_VM_CACHE
#ifdef TRACE_VM_CACHE
# define TRACE(x) dprintf x
@ -479,9 +480,12 @@ vm_cache_remove_consumer(vm_cache *cache, vm_cache *consumer)
}
}
ConditionVariable<vm_cache> busyCondition;
if (merge) {
// But since we need to keep the locking order upper->lower cache, we
// need to unlock our cache now
busyCondition.Publish(cache, "cache");
cache->busy = true;
mutex_unlock(&cache->lock);
@ -496,6 +500,7 @@ vm_cache_remove_consumer(vm_cache *cache, vm_cache *consumer)
"not merging it\n", cache);
merge = false;
cache->busy = false;
busyCondition.Unpublish();
mutex_unlock(&consumer->lock);
vm_cache_release_ref(consumer);
}
@ -535,6 +540,7 @@ if (consumer->virtual_base == 0x11000)
//dprintf("%ld: merged busy page %p, cache %p, offset %ld\n", find_thread(NULL), page, cacheRef->cache, page->cache_offset);
vm_cache_remove_page(consumer, consumerPage);
consumerPage->state = PAGE_STATE_INACTIVE;
((vm_dummy_page*)consumerPage)->busy_condition.Unpublish();
vm_cache_remove_page(cache, page);
vm_cache_insert_page(consumer, page,
@ -582,6 +588,9 @@ panic("cacheRef %p ref count too low!\n", cache);
mutex_unlock(&consumer->lock);
vm_cache_release_ref(consumer);
}
if (cache->busy)
busyCondition.Unpublish();
}
mutex_unlock(&cache->lock);

View File

@ -10,16 +10,17 @@
#include <KernelExport.h>
#include <OS.h>
#include <kernel.h>
#include <arch/cpu.h>
#include <arch/vm_translation_map.h>
#include <boot/kernel_args.h>
#include <condition_variable.h>
#include <kernel.h>
#include <thread.h>
#include <vm.h>
#include <vm_address_space.h>
#include <vm_priv.h>
#include <vm_page.h>
#include <vm_cache.h>
#include <arch/vm_translation_map.h>
#include <boot/kernel_args.h>
#include <signal.h>
#include <string.h>
@ -215,7 +216,6 @@ find_page(int argc, char **argv)
static int
dump_page(int argc, char **argv)
{
struct vm_page_mapping *mapping;
struct vm_page *page;
addr_t address;
bool physical = false;
@ -277,8 +277,9 @@ dump_page(int argc, char **argv)
#endif // DEBUG_PAGE_CACHE_TRANSITIONS
kprintf("area mappings:\n");
mapping = page->mappings;
while (mapping != NULL) {
vm_page_mappings::Iterator iterator = page->mappings.GetIterator();
vm_page_mapping *mapping;
while ((mapping = iterator.Next()) != NULL) {
kprintf(" %p (%#lx)\n", mapping->area, mapping->area->id);
mapping = mapping->page_link.next;
}
@ -676,6 +677,7 @@ vm_page_write_modified(vm_cache *cache, bool fsReenter)
off_t pageOffset;
status_t status;
vm_area *area;
ConditionVariable<vm_page> busyCondition;
cpu_status state = disable_interrupts();
acquire_spinlock(&sPageLock);
@ -683,20 +685,22 @@ vm_page_write_modified(vm_cache *cache, bool fsReenter)
if (page->state == PAGE_STATE_MODIFIED) {
remove_page_from_queue(&page_modified_queue, page);
page->state = PAGE_STATE_BUSY;
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, 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 list, but that should happen only rarely.
// We may 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
// list, but that should happen only rarely.
// If the page is changed after we cleared the dirty flag, but before we had
// the chance to write it back, then we'll write it again later - that will
// probably not happen that often, though.
// If the page is changed after we cleared the dirty flag, but before we
// 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;
@ -713,6 +717,7 @@ vm_page_write_modified(vm_cache *cache, bool fsReenter)
map->ops->query(map, pageOffset - area->cache_offset + area->base,
&physicalAddress, &flags);
if (flags & PAGE_MODIFIED) {
// TODO: Mark busy?
gotPage = true;
dequeuedPage = false;
}
@ -742,11 +747,13 @@ vm_page_write_modified(vm_cache *cache, bool fsReenter)
state = disable_interrupts();
acquire_spinlock(&sPageLock);
if (page->mappings != NULL || page->wired_count)
if (!page->mappings.IsEmpty() || page->wired_count)
page->state = PAGE_STATE_ACTIVE;
else
page->state = PAGE_STATE_INACTIVE;
busyCondition.Unpublish();
enqueue_page(&page_active_queue, page);
release_spinlock(&sPageLock);
@ -762,6 +769,8 @@ vm_page_write_modified(vm_cache *cache, bool fsReenter)
page->state = PAGE_STATE_MODIFIED;
enqueue_page(&page_modified_queue, page);
busyCondition.Unpublish();
release_spinlock(&sPageLock);
restore_interrupts(state);
}
@ -828,7 +837,7 @@ vm_page_init(kernel_args *args)
sPages[i].physical_page_number = sPhysicalPageOffset + i;
sPages[i].type = PAGE_TYPE_PHYSICAL;
sPages[i].state = PAGE_STATE_FREE;
sPages[i].mappings = NULL;
new(&sPages[i].mappings) vm_page_mappings();
sPages[i].wired_count = 0;
sPages[i].usage_count = 0;
sPages[i].cache = NULL;