Forks and COW situations show that we need vm objects rather than vm_files.

This is the first commit towards implementing vm object based paging with
right COW methods.
This commit is contained in:
Bahadir Balban
2008-03-03 22:05:01 +00:00
parent e2e6c89da2
commit 58b833dd7f
11 changed files with 272 additions and 139 deletions

View File

@@ -14,27 +14,6 @@
#include <posix/sys/types.h>
#include <string.h>
/* Global list of in-memory vm files. */
struct list_head vm_file_list;
/* Allocate and initialise a vmfile, and return it */
struct vm_file *vmfile_alloc_init(void)
{
struct vm_file *file;
if (!(file = kzalloc(sizeof(*file))))
return PTR_ERR(-ENOMEM);
INIT_LIST_HEAD(&file->list);
INIT_LIST_HEAD(&file->page_cache_list);
return file;
}
void vmfile_init(void)
{
INIT_LIST_HEAD(&vm_file_list);
}
int vfs_read(unsigned long vnum, unsigned long f_offset, unsigned long npages,
void *pagebuf)

View File

@@ -62,8 +62,6 @@ void init_mm(struct initdata *initdata)
init_utcb();
printf("%s: Initialised own utcb.\n", __TASKNAME__);
vmfile_init();
/* Give the kernel some memory to use for its allocators */
l4_kmem_grant(__pfn(alloc_page(__pfn(SZ_1MB))), __pfn(SZ_1MB));
}

View File

@@ -1,15 +1,6 @@
/*
* This implements a per-process virtual private file
* server to store environment variables.
*
* Using a per-process private file for the environment
* gives the impression as if a file-backed env/arg area
* is mapped on every process. By this means the env/arg
* pages dont need special processing and are abstracted
* away as files. Same idea can be applied to other
* private regions of a process such as the stack, so
* that debuggers can use file-based process inspection
* methods.
* Anonymous files for the process (e.g. stack, data, env)
* are implemented here.
*
* Copyright (C) 2008 Bahadir Balban
*/
@@ -24,80 +15,150 @@
#include <task.h>
#include <proc.h>
struct envdata {
struct list_head list;
void *env_data;
int env_size;
int id;
};
LIST_HEAD(env_list);
static void *zpage_p;
static struct page *zpage;
/* Copies environment data into provided page. */
int task_env_pager_read_page(struct vm_file *f, unsigned long f_off_pfn,
void *dest_page)
static struct vm_object devzero;
void init_zero_page(void)
{
struct envdata *env;
void *zpage_v;
zpage_p = alloc_page(1);
zpage = phys_to_page(zpage_p);
list_for_each_entry(env, &env_list, list)
if (env->id == f->vnum)
goto copyenv;
/* Map it to self */
zpage_v = l4_map_helper(zpage_p, 1);
printf("%s: No such env id: %d, to copy environment for.\n",
__TASKNAME__, f->vnum);
return -EINVAL;
/* Zero it */
memset(zpage_v, 0, PAGE_SIZE);
copyenv:
if (f_off_pfn != 0) {
printf("%s: Environments currently have a single page.\n");
return -EINVAL;
}
/* Unmap it */
l4_unmap_helper(zpage_v, 1);
memset(dest_page, 0, PAGE_SIZE);
BUG_ON(env->env_size > PAGE_SIZE);
memcpy(dest_page, env->env_data, env->env_size);
/* Update page struct. All other fields are zero */
zpage->count++;
}
#define VM_OBJ_MASK 0xFFFF
#define VM_OBJ_DEVZERO (1 << 0) /* Devzero special file */
#define VM_OBJ_FILE (1 << 1) /* Regular VFS file */
#define VM_OBJ_SHADOW (1 << 2) /* Shadow of another object */
/* Returns the page with given offset in this vm_object */
struct page *devzero_pager_page_in(struct vm_object *vm_obj, unsigned long f_offset)
{
return zpage;
}
struct vm_pager devzero_pager {
page_in = devzero_pager_page_int,
};
void init_devzero(void)
{
init_zero_page();
INIT_LIST_HEAD(&devzero.page_cache);
INIT_LIST_HEAD(&devzero.list);
INIT_LIST_HEAD(&devzero.shadows);
/* Devzero has infinitely many pages ;-) */
devzero.npages = -1;
devzero.type = VM_OBJ_FILE;
devzero.pager = &devzero_pager;
}
struct vm_file *get_devzero(void)
{
return &devzero;
}
void *get_zero_page(void)
{
zpage->count++;
return zpage_p;
}
void put_zero_page(void)
{
zpage->count--;
BUG_ON(zpage->count < 0);
}
/* Allocates and fills in the env page. This is like a pre-faulted file. */
int task_populate_env(struct task *task)
{
void *paddr = alloc_page(1);
void *vaddr = phys_to_virt(paddr);
struct page *page = phys_to_page(paddr);
/* Map new page at a self virtual address temporarily */
l4_map(paddr, vaddr, 1, MAP_USR_RW_FLAGS, self_tid());
/* Clear the page */
memset((void *)vaddr, 0, PAGE_SIZE);
/* Fill in environment data */
memcpy((void *)vaddr, &t->utcb_address, sizeof(t->utcb_address));
/* Remove temporary mapping */
l4_unmap((void *)vaddr, 1, self_tid());
spin_lock(&page->lock);
/* Environment file owns this page */
page->owner = task->proc_files->env_file;
/* Add the page to it's owner's list of in-memory pages */
BUG_ON(!list_empty(&page->list));
insert_page_olist(page, page->owner);
/* The offset of this page in its owner file */
page->f_offset = 0;
page->count++;
page->virtual = 0;
spin_unlock(&page->lock);
return 0;
}
/* Pager for environment files */
struct vm_pager task_env_pager = {
.ops = {
.read_page = task_env_pager_read_page,
.write_page= 0,
},
};
#define TASK_DATA_VNUM 1
#define TASK_STACK_VNUM 2
#define TASK_ENV_VNUM 3
/*
* For a task that is about to execute, this dynamically
* generates its environment file, and environment data.
*/
int task_prepare_environment(struct tcb *t)
int task_setup_vm_objects(struct tcb *t)
{
struct envdata *env;
struct proc_files *pf = &t->proc_files;
/* Allocate a new vmfile for this task's environment */
if (IS_ERR(t->env_file = vmfile_alloc_init()))
if (IS_ERR(pf->stack_file = vmfile_alloc_init()))
return (int)t->stack_file;
if (IS_ERR(pf->env_file = vmfile_alloc_init()))
return (int)t->env_file;
if (IS_ERR(pf->env_file = vmfile_alloc_init()))
return (int)t->data_file;
/* Initialise and add it to global vmfile list */
/*
* NOTE: Temporarily we can use tid as the vnum because
* this is the only per-task file.
*/
t->env_file->vnum = t->tid;
t->env_file->length = PAGE_SIZE;
t->env_file->pager = &task_env_pager;
t->env_file->vnum = (t->tid << 16) | TASK_ENV_VNUM;
t->env_file->length = t->env_end - t->env_start;
t->env_file->pager = &task_anon_pager;
list_add(&t->env_file->list, &vm_file_list);
/* Allocate, initialise and add per-task env data */
BUG_ON(!(env = kzalloc(sizeof(struct envdata))));
INIT_LIST_HEAD(&env->list);
env->env_data = &t->utcb_address;
env->env_size = sizeof(t->utcb_address);
env->id = t->tid;
list_add(&env->list, &env_list);
t->stack_file->vnum = (t->tid << 16) TASK_STACK_VNUM;
t->stack_file->length = t->stack_end - t->stack_start;
t->stack_file->pager = &task_anon_pager;
list_add(&t->stack_file->list, &vm_file_list);
return 0;
t->data_file->vnum = (t->tid << 16) TASK_DATA_VNUM;
t->data_file->length = t->data_end - t->data_start;
t->data_file->pager = &task_anon_pager;
list_add(&t->data_file->list, &vm_file_list);
/* Allocate, initialise and add per-task env data */
return task_populate_env(task);
}

View File

@@ -64,11 +64,6 @@ struct tcb *create_init_tcb(struct tcb_head *tcbs)
/* Ids will be acquired from the kernel */
task->tid = TASK_ID_INVALID;
task->spid = TASK_ID_INVALID;
task->swap_file = kzalloc(sizeof(struct vm_file));
task->swap_file->pager = &swap_pager;
address_pool_init(&task->swap_file_offset_pool, 0,
__pfn(TASK_SWAPFILE_MAXSIZE));
INIT_LIST_HEAD(&task->swap_file->page_cache_list);
INIT_LIST_HEAD(&task->list);
INIT_LIST_HEAD(&task->vm_area_list);
list_add_tail(&task->list, &tcbs->list);
@@ -144,7 +139,7 @@ int start_boot_tasks(struct initdata *initdata, struct tcb_head *tcbs)
* when faulted, simply copies the task env data to the
* allocated page.
*/
if (task_prepare_environment(task) < 0) {
if (task_prepare_proc_files(task) < 0) {
printf("Could not create environment file.\n");
goto error;
}
@@ -156,8 +151,11 @@ int start_boot_tasks(struct initdata *initdata, struct tcb_head *tcbs)
task->stack_end = task->env_start;
task->stack_start = task->stack_end - PAGE_SIZE * 4;
/* Only text start is valid */
task->text_start = USER_AREA_START;
/* Currently RO text and RW data are one region */
task->data_start = USER_AREA_START;
task->data_end = USER_AREA_START + file->length;
task->text_start = task->data_start;
task->text_end = task->data_end;
/* Set up task's registers */
sp = align(task->stack_end - 1, 8);
@@ -165,15 +163,15 @@ int start_boot_tasks(struct initdata *initdata, struct tcb_head *tcbs)
/* mmap each task's physical image to task's address space. */
if ((err = do_mmap(file, 0, task, USER_AREA_START,
VM_READ | VM_WRITE | VM_EXEC,
VM_READ | VM_WRITE,
__pfn(page_align_up(file->length)))) < 0) {
printf("do_mmap: failed with %d.\n", err);
goto error;
}
/* mmap each task's environment from its env file. */
if ((err = do_mmap(task->env_file, 0, task, task->env_start,
VM_READ | VM_WRITE,
if ((err = do_mmap(task->proc_files->env_file, 0, task,
task->env_start, VM_READ | VM_WRITE,
__pfn(task->env_end - task->env_start)) < 0)) {
printf("do_mmap: Mapping environment failed with %d.\n",
err);

29
tasks/mm0/src/vm_object.c Normal file
View File

@@ -0,0 +1,29 @@
/*
* VM Objects.
*
* Copyright (C) 2008 Bahadir Balban
*/
#include <vm_area.h>
#include <l4/macros.h>
#include <l4/api/errno.h>
#include <kmalloc/kmalloc.h>
/* Global list of in-memory vm files. */
LIST_HEAD(vm_object_list);
/* Allocate and initialise a vmfile, and return it */
struct vm_object *vm_object_alloc_init(void)
{
struct vm_object *obj;
if (!(obj = kzalloc(sizeof(*obj))))
return PTR_ERR(-ENOMEM);
INIT_LIST_HEAD(&obj->list);
INIT_LIST_HEAD(&obj->page_cache);
INIT_LIST_HEAD(&obj->shadows);
return obj;
}