haiku/src/system/kernel/vm/vm_address_space.cpp
Axel Dörfler 393fceb5a0 * Cleaned up vm_types.h a bit, and made vm_page, vm_cache, and vm_area
opaque types for C.
* As a result, I've renamed some more source files to .cpp, and fixed
  all warnings caused by that.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22326 a95241bf-73f2-0310-859d-f6bbb57e9c96
2007-09-27 10:29:05 +00:00

367 lines
8.6 KiB
C++

/*
* Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include <KernelExport.h>
#include <vm.h>
#include <vm_address_space.h>
#include <vm_priv.h>
#include <thread.h>
#include <util/khash.h>
#include <stdlib.h>
//#define TRACE_VM
#ifdef TRACE_VM
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
static vm_address_space *sKernelAddressSpace;
#define ASPACE_HASH_TABLE_SIZE 1024
static struct hash_table *sAddressSpaceTable;
static sem_id sAddressSpaceHashSem;
static void
_dump_aspace(vm_address_space *aspace)
{
vm_area *area;
dprintf("dump of address space at %p:\n", aspace);
dprintf("id: 0x%lx\n", aspace->id);
dprintf("ref_count: %ld\n", aspace->ref_count);
dprintf("fault_count: %ld\n", aspace->fault_count);
dprintf("working_set_size: 0x%lx\n", aspace->working_set_size);
dprintf("translation_map: %p\n", &aspace->translation_map);
dprintf("base: 0x%lx\n", aspace->base);
dprintf("size: 0x%lx\n", aspace->size);
dprintf("change_count: 0x%lx\n", aspace->change_count);
dprintf("sem: 0x%lx\n", aspace->sem);
dprintf("area_hint: %p\n", aspace->area_hint);
dprintf("area_list:\n");
for (area = aspace->areas; area != NULL; area = area->address_space_next) {
dprintf(" area 0x%lx: ", area->id);
dprintf("base_addr = 0x%lx ", area->base);
dprintf("size = 0x%lx ", area->size);
dprintf("name = '%s' ", area->name);
dprintf("protection = 0x%lx\n", area->protection);
}
}
static int
dump_aspace(int argc, char **argv)
{
vm_address_space *aspace;
if (argc < 2) {
dprintf("aspace: not enough arguments\n");
return 0;
}
// if the argument looks like a number, treat it as such
{
team_id id = strtoul(argv[1], NULL, 0);
aspace = (vm_address_space *)hash_lookup(sAddressSpaceTable, &id);
if (aspace == NULL) {
dprintf("invalid aspace id\n");
} else {
_dump_aspace(aspace);
}
return 0;
}
return 0;
}
static int
dump_aspace_list(int argc, char **argv)
{
vm_address_space *space;
struct hash_iterator iter;
dprintf("addr\tid\tbase\t\tsize\n");
hash_open(sAddressSpaceTable, &iter);
while ((space = (vm_address_space *)hash_next(sAddressSpaceTable,
&iter)) != NULL) {
dprintf("%p\t0x%lx\t0x%lx\t\t0x%lx\n",
space, space->id, space->base, space->size);
}
hash_close(sAddressSpaceTable, &iter, false);
return 0;
}
static int
aspace_compare(void *_a, const void *key)
{
vm_address_space *aspace = (vm_address_space *)_a;
const team_id *id = (const team_id *)key;
if (aspace->id == *id)
return 0;
return -1;
}
static uint32
aspace_hash(void *_a, const void *key, uint32 range)
{
vm_address_space *aspace = (vm_address_space *)_a;
const team_id *id = (const team_id *)key;
if (aspace != NULL)
return aspace->id % range;
return *id % range;
}
/*! When this function is called, all references to this address space
have been released, so it's safe to remove it.
*/
static void
delete_address_space(vm_address_space *addressSpace)
{
TRACE(("delete_address_space: called on aspace 0x%lx\n", addressSpace->id));
if (addressSpace == sKernelAddressSpace)
panic("tried to delete the kernel aspace!\n");
acquire_sem_etc(addressSpace->sem, WRITE_COUNT, 0, 0);
(*addressSpace->translation_map.ops->destroy)(&addressSpace->translation_map);
delete_sem(addressSpace->sem);
free(addressSpace);
}
// #pragma mark -
vm_address_space *
vm_get_address_space_by_id(team_id id)
{
vm_address_space *addressSpace;
acquire_sem_etc(sAddressSpaceHashSem, READ_COUNT, 0, 0);
addressSpace = (vm_address_space *)hash_lookup(sAddressSpaceTable, &id);
if (addressSpace)
atomic_add(&addressSpace->ref_count, 1);
release_sem_etc(sAddressSpaceHashSem, READ_COUNT, 0);
return addressSpace;
}
vm_address_space *
vm_get_kernel_address_space(void)
{
/* we can treat this one a little differently since it can't be deleted */
atomic_add(&sKernelAddressSpace->ref_count, 1);
return sKernelAddressSpace;
}
vm_address_space *
vm_kernel_address_space(void)
{
return sKernelAddressSpace;
}
team_id
vm_kernel_address_space_id(void)
{
return sKernelAddressSpace->id;
}
vm_address_space *
vm_get_current_user_address_space(void)
{
struct thread *thread = thread_get_current_thread();
if (thread != NULL) {
vm_address_space *addressSpace = thread->team->address_space;
if (addressSpace != NULL) {
atomic_add(&addressSpace->ref_count, 1);
return addressSpace;
}
}
return NULL;
}
team_id
vm_current_user_address_space_id(void)
{
struct thread *thread = thread_get_current_thread();
if (thread != NULL && thread->team->address_space != NULL)
return thread->team->id;
return B_ERROR;
}
void
vm_put_address_space(vm_address_space *addressSpace)
{
bool remove = false;
acquire_sem_etc(sAddressSpaceHashSem, WRITE_COUNT, 0, 0);
if (atomic_add(&addressSpace->ref_count, -1) == 1) {
hash_remove(sAddressSpaceTable, addressSpace);
remove = true;
}
release_sem_etc(sAddressSpaceHashSem, WRITE_COUNT, 0);
if (remove)
delete_address_space(addressSpace);
}
/*! Deletes all areas in the specified address space, and the address
space by decreasing all reference counters. It also marks the
address space of being in deletion state, so that no more areas
can be created in it.
After this, the address space is not operational anymore, but might
still be in memory until the last reference has been released.
*/
void
vm_delete_address_space(vm_address_space *addressSpace)
{
acquire_sem_etc(addressSpace->sem, WRITE_COUNT, 0, 0);
addressSpace->state = VM_ASPACE_STATE_DELETION;
release_sem_etc(addressSpace->sem, WRITE_COUNT, 0);
vm_delete_areas(addressSpace);
vm_put_address_space(addressSpace);
}
status_t
vm_create_address_space(team_id id, addr_t base, addr_t size,
bool kernel, vm_address_space **_addressSpace)
{
vm_address_space *addressSpace;
status_t status;
addressSpace = (vm_address_space *)malloc(sizeof(vm_address_space));
if (addressSpace == NULL)
return B_NO_MEMORY;
TRACE(("vm_create_aspace: %s: %lx bytes starting at 0x%lx => %p\n",
name, size, base, addressSpace));
addressSpace->base = base;
addressSpace->size = size;
addressSpace->areas = NULL;
addressSpace->area_hint = NULL;
addressSpace->change_count = 0;
if (!kernel) {
// the kernel address space will create its semaphore later
addressSpace->sem = create_sem(WRITE_COUNT, "address space");
if (addressSpace->sem < B_OK) {
status_t status = addressSpace->sem;
free(addressSpace);
return status;
}
}
addressSpace->id = id;
addressSpace->ref_count = 1;
addressSpace->state = VM_ASPACE_STATE_NORMAL;
addressSpace->fault_count = 0;
addressSpace->scan_va = base;
addressSpace->working_set_size = kernel
? DEFAULT_KERNEL_WORKING_SET : DEFAULT_WORKING_SET;
addressSpace->max_working_set = DEFAULT_MAX_WORKING_SET;
addressSpace->min_working_set = DEFAULT_MIN_WORKING_SET;
addressSpace->last_working_set_adjust = system_time();
// initialize the corresponding translation map
status = arch_vm_translation_map_init_map(&addressSpace->translation_map,
kernel);
if (status < B_OK) {
free(addressSpace);
return status;
}
// add the aspace to the global hash table
acquire_sem_etc(sAddressSpaceHashSem, WRITE_COUNT, 0, 0);
hash_insert(sAddressSpaceTable, addressSpace);
release_sem_etc(sAddressSpaceHashSem, WRITE_COUNT, 0);
*_addressSpace = addressSpace;
return B_OK;
}
status_t
vm_address_space_init(void)
{
sAddressSpaceHashSem = -1;
// create the area and address space hash tables
{
vm_address_space *aspace;
sAddressSpaceTable = hash_init(ASPACE_HASH_TABLE_SIZE,
(addr_t)&aspace->hash_next - (addr_t)aspace, &aspace_compare,
&aspace_hash);
if (sAddressSpaceTable == NULL)
panic("vm_init: error creating aspace hash table\n");
}
sKernelAddressSpace = NULL;
// create the initial kernel address space
if (vm_create_address_space(1, KERNEL_BASE, KERNEL_SIZE,
true, &sKernelAddressSpace) != B_OK)
panic("vm_init: error creating kernel address space!\n");
add_debugger_command("aspaces", &dump_aspace_list,
"Dump a list of all address spaces");
add_debugger_command("aspace", &dump_aspace,
"Dump info about a particular address space");
return B_OK;
}
status_t
vm_address_space_init_post_sem(void)
{
status_t status = arch_vm_translation_map_init_kernel_map_post_sem(
&sKernelAddressSpace->translation_map);
if (status < B_OK)
return status;
status = sKernelAddressSpace->sem = create_sem(WRITE_COUNT,
"kernel_aspacelock");
if (status < B_OK)
return status;
status = sAddressSpaceHashSem = create_sem(WRITE_COUNT, "aspace_hash_sem");
if (status < B_OK)
return status;
return B_OK;
}