Pulled the algorithm for dynamically mapping physical pages into an

"IO space" out of the x86 specific source into arch/generic. We'll use
it for PPC as well.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15853 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2006-01-07 03:13:42 +00:00
parent 1e1b86ef54
commit a71974c1f8
4 changed files with 294 additions and 138 deletions
@@ -0,0 +1,237 @@
/*
* Copyright 2002-2005, Axel Dörfler, [email protected]. 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 "generic_vm_physical_page_mapper.h"
#include <vm_address_space.h>
#include <vm_page.h>
#include <vm_priv.h>
#include <smp.h>
#include <util/queue.h>
#include <memheap.h>
#include <string.h>
#include <stdlib.h>
//#define TRACE_VM_PHYSICAL_PAGE_MAPPER
#ifdef TRACE_VM_PHYSICAL_PAGE_MAPPER
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
// data and structures used to represent physical pages mapped into iospace
typedef struct paddr_chunk_descriptor {
struct paddr_chunk_descriptor *next_q;
// must remain first in structure, queue code uses it
int ref_count;
addr_t va;
} paddr_chunk_desc;
static paddr_chunk_desc *paddr_desc; // will be one per physical chunk
static paddr_chunk_desc **virtual_pmappings; // will be one ptr per virtual chunk in iospace
static int first_free_vmapping;
static int num_virtual_chunks;
static queue mapped_paddr_lru;
static mutex iospace_mutex;
static sem_id iospace_full_sem;
static generic_map_iospace_chunk_func sMapIOSpaceChunk;
static addr_t sIOSpaceBase;
static size_t sIOSpaceSize;
static size_t sIOSpaceChunkSize;
status_t
generic_get_physical_page(addr_t pa, addr_t *va, uint32 flags)
{
int index;
paddr_chunk_desc *replaced_pchunk;
restart:
mutex_lock(&iospace_mutex);
// see if the page is already mapped
index = pa / sIOSpaceChunkSize;
if (paddr_desc[index].va != 0) {
if (paddr_desc[index].ref_count++ == 0) {
// pull this descriptor out of the lru list
queue_remove_item(&mapped_paddr_lru, &paddr_desc[index]);
}
*va = paddr_desc[index].va + pa % sIOSpaceChunkSize;
mutex_unlock(&iospace_mutex);
return B_OK;
}
// map it
if (first_free_vmapping < num_virtual_chunks) {
// there's a free hole
paddr_desc[index].va = first_free_vmapping * sIOSpaceChunkSize + sIOSpaceBase;
*va = paddr_desc[index].va + pa % sIOSpaceChunkSize;
virtual_pmappings[first_free_vmapping] = &paddr_desc[index];
paddr_desc[index].ref_count++;
// push up the first_free_vmapping pointer
for (; first_free_vmapping < num_virtual_chunks; first_free_vmapping++) {
if(virtual_pmappings[first_free_vmapping] == NULL)
break;
}
sMapIOSpaceChunk(paddr_desc[index].va,
index * sIOSpaceChunkSize);
mutex_unlock(&iospace_mutex);
return B_OK;
}
// replace an earlier mapping
if (queue_peek(&mapped_paddr_lru) == NULL) {
// no free slots available
if (flags == PHYSICAL_PAGE_NO_WAIT) {
// punt back to the caller and let them handle this
mutex_unlock(&iospace_mutex);
return B_NO_MEMORY;
} else {
mutex_unlock(&iospace_mutex);
acquire_sem(iospace_full_sem);
goto restart;
}
}
replaced_pchunk = (paddr_chunk_desc*)queue_dequeue(&mapped_paddr_lru);
paddr_desc[index].va = replaced_pchunk->va;
replaced_pchunk->va = 0;
*va = paddr_desc[index].va + pa % sIOSpaceChunkSize;
paddr_desc[index].ref_count++;
sMapIOSpaceChunk(paddr_desc[index].va, index * sIOSpaceChunkSize);
mutex_unlock(&iospace_mutex);
return B_OK;
}
status_t
generic_put_physical_page(addr_t va)
{
paddr_chunk_desc *desc;
if (va < sIOSpaceBase || va >= sIOSpaceBase + sIOSpaceSize)
panic("someone called put_physical_page on an invalid va 0x%lx\n", va);
va -= sIOSpaceBase;
mutex_lock(&iospace_mutex);
desc = virtual_pmappings[va / sIOSpaceChunkSize];
if (desc == NULL) {
mutex_unlock(&iospace_mutex);
panic("put_physical_page called on page at va 0x%lx which is not checked out\n", va);
return B_ERROR;
}
if (--desc->ref_count == 0) {
// put it on the mapped lru list
queue_enqueue(&mapped_paddr_lru, desc);
// no sense rescheduling on this one, there's likely a race in the waiting
// thread to grab the iospace_mutex, which would block and eventually get back to
// this thread. waste of time.
release_sem_etc(iospace_full_sem, 1, B_DO_NOT_RESCHEDULE);
}
mutex_unlock(&iospace_mutex);
return B_OK;
}
// #pragma mark -
// VM API
status_t
generic_vm_physical_page_mapper_init(kernel_args *args,
generic_map_iospace_chunk_func mapIOSpaceChunk, addr_t *ioSpaceBase,
size_t ioSpaceSize, size_t ioSpaceChunkSize)
{
TRACE(("generic_vm_physical_page_mapper_init: entry\n"));
sMapIOSpaceChunk = mapIOSpaceChunk;
sIOSpaceBase = *ioSpaceBase;
sIOSpaceSize = ioSpaceSize;
sIOSpaceChunkSize = ioSpaceChunkSize;
// allocate some space to hold physical page mapping info
paddr_desc = (paddr_chunk_desc *)vm_alloc_from_kernel_args(args,
sizeof(paddr_chunk_desc) * 1024, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
num_virtual_chunks = sIOSpaceSize / sIOSpaceChunkSize;
virtual_pmappings = (paddr_chunk_desc **)vm_alloc_from_kernel_args(args,
sizeof(paddr_chunk_desc *) * num_virtual_chunks, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
TRACE(("paddr_desc %p, virtual_pmappings %p"/*", iospace_pgtables %p"*/"\n",
paddr_desc, virtual_pmappings/*, iospace_pgtables*/));
// initialize our data structures
memset(paddr_desc, 0, sizeof(paddr_chunk_desc) * 1024);
memset(virtual_pmappings, 0, sizeof(paddr_chunk_desc *) * num_virtual_chunks);
first_free_vmapping = 0;
queue_init(&mapped_paddr_lru);
iospace_mutex.sem = -1;
iospace_mutex.holder = -1;
iospace_full_sem = -1;
TRACE(("generic_vm_physical_page_mapper_init: done\n"));
return B_OK;
}
status_t
generic_vm_physical_page_mapper_init_post_area(kernel_args *args)
{
void *temp;
TRACE(("generic_vm_physical_page_mapper_init_post_area: entry\n"));
temp = (void *)paddr_desc;
create_area("physical_page_mapping_descriptors", &temp, B_EXACT_ADDRESS,
ROUNDUP(sizeof(paddr_chunk_desc) * 1024, B_PAGE_SIZE),
B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
temp = (void *)virtual_pmappings;
create_area("iospace_virtual_chunk_descriptors", &temp, B_EXACT_ADDRESS,
ROUNDUP(sizeof(paddr_chunk_desc *) * num_virtual_chunks, B_PAGE_SIZE),
B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
TRACE(("generic_vm_physical_page_mapper_init_post_area: creating iospace\n"));
temp = (void *)sIOSpaceBase;
area_id ioSpaceArea = vm_create_null_area(vm_kernel_address_space_id(),
"iospace", &temp, B_EXACT_ADDRESS, sIOSpaceSize);
// TODO: We don't reserve the virtual address space for the IO space in
// generic_vm_physical_page_mapper_init() yet. So theoretically it could
// happen that a part of that space has been reserved by someone else in
// the meantime.
if (ioSpaceArea < 0) {
panic("generic_vm_physical_page_mapper_init_post_area(): Failed to "
"create null area for IO space!\n");
return ioSpaceArea;
}
TRACE(("generic_vm_physical_page_mapper_init_post_area: done\n"));
return B_OK;
}
status_t
generic_vm_physical_page_mapper_init_post_sem(kernel_args *args)
{
mutex_init(&iospace_mutex, "iospace_mutex");
iospace_full_sem = create_sem(1, "iospace_full_sem");
return iospace_full_sem >= B_OK ? B_OK : iospace_full_sem;
}
@@ -0,0 +1,29 @@
/*
* Copyright 2006, Ingo Weinhold <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_GENERIC_VM_PHYSICAL_PAGE_MAPPER_H
#define _KERNEL_GENERIC_VM_PHYSICAL_PAGE_MAPPER_H
#include <boot/kernel_args.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef status_t (*generic_map_iospace_chunk_func)(addr_t, addr_t);
status_t generic_get_physical_page(addr_t pa, addr_t *va, uint32 flags);
status_t generic_put_physical_page(addr_t va);
status_t generic_vm_physical_page_mapper_init(kernel_args *args,
generic_map_iospace_chunk_func mapIOSpaceChunk, addr_t *ioSpaceBase,
size_t ioSpaceSize, size_t ioSpaceChunkSize);
status_t generic_vm_physical_page_mapper_init_post_area(kernel_args *args);
status_t generic_vm_physical_page_mapper_init_post_sem(kernel_args *args);
#ifdef __cplusplus
}
#endif
#endif // _KERNEL_GENERIC_VM_PHYSICAL_PAGE_MAPPER_H
+5
View File
@@ -3,6 +3,9 @@ SubDir HAIKU_TOP src system kernel arch x86 ;
SubDirHdrs [ FDirName $(TARGET_COMMON_DEBUG_OBJECT_DIR) system kernel ] ;
# for syscall_numbers.h
SubDirHdrs $(HAIKU_TOP) src add-ons kernel drivers input ps2_hid ;
SubDirHdrs $(SUBDIR) $(DOTDOT) generic ;
SEARCH_SOURCE += [ FDirName $(SUBDIR) $(DOTDOT) generic ] ;
KernelStaticLibrary libx86 :
arch_cpu.c
@@ -24,6 +27,8 @@ KernelStaticLibrary libx86 :
arch_user_debugger.cpp
bios.cpp
cpuid.S
generic_vm_physical_page_mapper.cpp
:
$(TARGET_KERNEL_PIC_CCFLAGS) -Wno-unused
;
@@ -19,6 +19,8 @@
#include <string.h>
#include <stdlib.h>
#include "generic_vm_physical_page_mapper.h"
//#define TRACE_VM_TMAP
#ifdef TRACE_VM_TMAP
# define TRACE(x) dprintf x
@@ -61,21 +63,7 @@ typedef struct page_directory_entry {
uint32 addr:20;
} page_directory_entry;
// data and structures used to represent physical pages mapped into iospace
typedef struct paddr_chunk_descriptor {
struct paddr_chunk_descriptor *next_q; // must remain first in structure, queue code uses it
int ref_count;
addr_t va;
} paddr_chunk_desc;
static paddr_chunk_desc *paddr_desc; // will be one per physical chunk
static paddr_chunk_desc **virtual_pmappings; // will be one ptr per virtual chunk in iospace
static int first_free_vmapping;
static int num_virtual_chunks;
static queue mapped_paddr_lru;
static page_table_entry *iospace_pgtables = NULL;
static mutex iospace_mutex;
static sem_id iospace_full_sem;
#define PAGE_INVALIDATE_CACHE_SIZE 64
@@ -96,6 +84,8 @@ static page_directory_entry *sKernelVirtualPageDirectory = NULL;
static vm_translation_map *tmap_list;
static spinlock tmap_list_lock;
static addr_t sIOSpaceBase;
#define CHATTY_TMAP 0
#define ADDR_SHIFT(x) ((x)>>12)
@@ -648,11 +638,11 @@ map_iospace_chunk(addr_t va, addr_t pa)
pa &= ~(B_PAGE_SIZE - 1); // make sure it's page aligned
va &= ~(B_PAGE_SIZE - 1); // make sure it's page aligned
if (va < IOSPACE_BASE || va >= (IOSPACE_BASE + IOSPACE_SIZE))
if (va < sIOSpaceBase || va >= (sIOSpaceBase + IOSPACE_SIZE))
panic("map_iospace_chunk: passed invalid va 0x%lx\n", va);
ppn = ADDR_SHIFT(pa);
pt = &iospace_pgtables[(va - IOSPACE_BASE) / B_PAGE_SIZE];
pt = &iospace_pgtables[(va - sIOSpaceBase) / B_PAGE_SIZE];
for (i = 0; i < 1024; i++) {
init_page_table_entry(&pt[i]);
pt[i].addr = ppn + i;
@@ -676,101 +666,14 @@ map_iospace_chunk(addr_t va, addr_t pa)
static status_t
get_physical_page_tmap(addr_t pa, addr_t *va, uint32 flags)
{
int index;
paddr_chunk_desc *replaced_pchunk;
restart:
mutex_lock(&iospace_mutex);
// see if the page is already mapped
index = pa / IOSPACE_CHUNK_SIZE;
if (paddr_desc[index].va != 0) {
if (paddr_desc[index].ref_count++ == 0) {
// pull this descriptor out of the lru list
queue_remove_item(&mapped_paddr_lru, &paddr_desc[index]);
}
*va = paddr_desc[index].va + pa % IOSPACE_CHUNK_SIZE;
mutex_unlock(&iospace_mutex);
return B_OK;
}
// map it
if (first_free_vmapping < num_virtual_chunks) {
// there's a free hole
paddr_desc[index].va = first_free_vmapping * IOSPACE_CHUNK_SIZE + IOSPACE_BASE;
*va = paddr_desc[index].va + pa % IOSPACE_CHUNK_SIZE;
virtual_pmappings[first_free_vmapping] = &paddr_desc[index];
paddr_desc[index].ref_count++;
// push up the first_free_vmapping pointer
for (; first_free_vmapping < num_virtual_chunks; first_free_vmapping++) {
if(virtual_pmappings[first_free_vmapping] == NULL)
break;
}
map_iospace_chunk(paddr_desc[index].va, index * IOSPACE_CHUNK_SIZE);
mutex_unlock(&iospace_mutex);
return B_OK;
}
// replace an earlier mapping
if (queue_peek(&mapped_paddr_lru) == NULL) {
// no free slots available
if (flags == PHYSICAL_PAGE_NO_WAIT) {
// punt back to the caller and let them handle this
mutex_unlock(&iospace_mutex);
return B_NO_MEMORY;
} else {
mutex_unlock(&iospace_mutex);
acquire_sem(iospace_full_sem);
goto restart;
}
}
replaced_pchunk = queue_dequeue(&mapped_paddr_lru);
paddr_desc[index].va = replaced_pchunk->va;
replaced_pchunk->va = 0;
*va = paddr_desc[index].va + pa % IOSPACE_CHUNK_SIZE;
paddr_desc[index].ref_count++;
map_iospace_chunk(paddr_desc[index].va, index * IOSPACE_CHUNK_SIZE);
mutex_unlock(&iospace_mutex);
return B_OK;
return generic_get_physical_page(pa, va, flags);
}
static status_t
put_physical_page_tmap(addr_t va)
{
paddr_chunk_desc *desc;
if (va < IOSPACE_BASE || va >= IOSPACE_BASE + IOSPACE_SIZE)
panic("someone called put_physical_page on an invalid va 0x%lx\n", va);
va -= IOSPACE_BASE;
mutex_lock(&iospace_mutex);
desc = virtual_pmappings[va / IOSPACE_CHUNK_SIZE];
if (desc == NULL) {
mutex_unlock(&iospace_mutex);
panic("put_physical_page called on page at va 0x%lx which is not checked out\n", va);
return B_ERROR;
}
if (--desc->ref_count == 0) {
// put it on the mapped lru list
queue_enqueue(&mapped_paddr_lru, desc);
// no sense rescheduling on this one, there's likely a race in the waiting
// thread to grab the iospace_mutex, which would block and eventually get back to
// this thread. waste of time.
release_sem_etc(iospace_full_sem, 1, B_DO_NOT_RESCHEDULE);
}
mutex_unlock(&iospace_mutex);
return B_OK;
return generic_put_physical_page(va);
}
@@ -881,6 +784,7 @@ status_t
arch_vm_translation_map_init(kernel_args *args)
{
cpuid_info info;
status_t error;
TRACE(("vm_translation_map_init: entry\n"));
@@ -898,26 +802,20 @@ arch_vm_translation_map_init(kernel_args *args)
tmap_list = NULL;
// allocate some space to hold physical page mapping info
paddr_desc = (paddr_chunk_desc *)vm_alloc_from_kernel_args(args,
sizeof(paddr_chunk_desc) * 1024, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
num_virtual_chunks = IOSPACE_SIZE / IOSPACE_CHUNK_SIZE;
virtual_pmappings = (paddr_chunk_desc **)vm_alloc_from_kernel_args(args,
sizeof(paddr_chunk_desc *) * num_virtual_chunks, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
iospace_pgtables = (page_table_entry *)vm_alloc_from_kernel_args(args,
B_PAGE_SIZE * (IOSPACE_SIZE / (B_PAGE_SIZE * 1024)), B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
TRACE(("paddr_desc %p, virtual_pmappings %p, iospace_pgtables %p\n",
paddr_desc, virtual_pmappings, iospace_pgtables));
TRACE(("iospace_pgtables %p\n", iospace_pgtables));
// init physical page mapper
sIOSpaceBase = IOSPACE_BASE;
error = generic_vm_physical_page_mapper_init(args, map_iospace_chunk,
&sIOSpaceBase, IOSPACE_SIZE, IOSPACE_CHUNK_SIZE);
if (error != B_OK)
return error;
// initialize our data structures
memset(paddr_desc, 0, sizeof(paddr_chunk_desc) * 1024);
memset(virtual_pmappings, 0, sizeof(paddr_chunk_desc *) * num_virtual_chunks);
first_free_vmapping = 0;
queue_init(&mapped_paddr_lru);
memset(iospace_pgtables, 0, B_PAGE_SIZE * (IOSPACE_SIZE / (B_PAGE_SIZE * 1024)));
iospace_mutex.sem = -1;
iospace_mutex.holder = -1;
iospace_full_sem = -1;
TRACE(("mapping iospace_pgtables\n"));
@@ -932,7 +830,7 @@ arch_vm_translation_map_init(kernel_args *args)
virt_pgtable = (addr_t)iospace_pgtables;
for (i = 0; i < (IOSPACE_SIZE / (B_PAGE_SIZE * 1024)); i++, virt_pgtable += B_PAGE_SIZE) {
early_query(virt_pgtable, &phys_pgtable);
e = &page_hole_pgdir[(IOSPACE_BASE / (B_PAGE_SIZE * 1024)) + i];
e = &page_hole_pgdir[(sIOSpaceBase / (B_PAGE_SIZE * 1024)) + i];
put_pgtable_in_pgdir(e, phys_pgtable, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
}
}
@@ -953,10 +851,7 @@ arch_vm_translation_map_init(kernel_args *args)
status_t
arch_vm_translation_map_init_post_sem(kernel_args *args)
{
mutex_init(&iospace_mutex, "iospace_mutex");
iospace_full_sem = create_sem(1, "iospace_full_sem");
return iospace_full_sem >= B_OK ? B_OK : iospace_full_sem;
return generic_vm_physical_page_mapper_init_post_sem(args);
}
@@ -966,6 +861,7 @@ arch_vm_translation_map_init_post_area(kernel_args *args)
// now that the vm is initialized, create a region that represents
// the page hole
void *temp;
status_t error;
TRACE(("vm_translation_map_init_post_area: entry\n"));
@@ -978,25 +874,14 @@ arch_vm_translation_map_init_post_area(kernel_args *args)
create_area("kernel_pgdir", &temp, B_EXACT_ADDRESS, B_PAGE_SIZE,
B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
temp = (void *)paddr_desc;
create_area("physical_page_mapping_descriptors", &temp, B_EXACT_ADDRESS,
ROUNDUP(sizeof(paddr_chunk_desc) * 1024, B_PAGE_SIZE),
B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
temp = (void *)virtual_pmappings;
create_area("iospace_virtual_chunk_descriptors", &temp, B_EXACT_ADDRESS,
ROUNDUP(sizeof(paddr_chunk_desc *) * num_virtual_chunks, B_PAGE_SIZE),
B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
temp = (void *)iospace_pgtables;
create_area("iospace_pgtables", &temp, B_EXACT_ADDRESS,
B_PAGE_SIZE * (IOSPACE_SIZE / (B_PAGE_SIZE * 1024)),
B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
TRACE(("vm_translation_map_init_post_area: creating iospace\n"));
temp = (void *)IOSPACE_BASE;
vm_create_null_area(vm_kernel_address_space_id(), "iospace", &temp,
B_EXACT_ADDRESS, IOSPACE_SIZE);
error = generic_vm_physical_page_mapper_init_post_area(args);
if (error != B_OK)
return error;
TRACE(("vm_translation_map_init_post_area: done\n"));