393fceb5a0
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
367 lines
8.6 KiB
C++
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;
|
|
}
|