mirror of
https://github.com/drasko/codezero.git
synced 2026-01-12 02:43:15 +01:00
Bugfixes, enhancements
- Fixed an important bug with shadow object handling. When a shadow is dropped, if there are references left to it, both the object in front and dropped object becomes a shadow of the original object underneath. We had thought of this case but had not increase the shadow count. - Added a test mechanism that tests the number of objects, vmfiles, shadows etc. by first counting them and trying to reach the same number by other means, i.e. per-object-shadow counts. It discovered a plethora of bugs. - Added new set of functions to register objects, files and tasks globally with the pager, these functions introduce a refcount as well as adding structures to linked lists. - fork/exit now seems to work stably i.e. no negative shadow counts etc.
This commit is contained in:
13
tasks/mm0/include/globals.h
Normal file
13
tasks/mm0/include/globals.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef __GLOBALS_H__
|
||||
#define __GLOBALS_H__
|
||||
|
||||
struct global_list {
|
||||
int total;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
extern struct global_list global_vm_files;
|
||||
extern struct global_list global_vm_objects;
|
||||
extern struct global_list global_tasks;
|
||||
|
||||
#endif /* __GLOBALS_H__ */
|
||||
@@ -111,8 +111,14 @@ struct task_data_head {
|
||||
struct task_data tdata[];
|
||||
};
|
||||
|
||||
struct tcb_head {
|
||||
struct list_head list;
|
||||
int total; /* Total threads */
|
||||
};
|
||||
|
||||
struct tcb *find_task(int tid);
|
||||
void task_add_global(struct tcb *t);
|
||||
void global_add_task(struct tcb *task);
|
||||
void global_remove_task(struct tcb *task);
|
||||
int send_task_data(struct tcb *requester);
|
||||
void task_map_prefault_utcb(struct tcb *mapper, struct tcb *owner);
|
||||
int task_mmap_regions(struct tcb *task, struct vm_file *file);
|
||||
@@ -122,8 +128,8 @@ int task_setup_registers(struct tcb *task, unsigned int pc,
|
||||
unsigned int sp, l4id_t pager);
|
||||
struct tcb *tcb_alloc_init(unsigned int flags);
|
||||
int tcb_destroy(struct tcb *task);
|
||||
int task_exec(struct vm_file *f, unsigned long task_region_start,
|
||||
unsigned long task_region_end, struct task_ids *ids);
|
||||
struct tcb *task_exec(struct vm_file *f, unsigned long task_region_start,
|
||||
unsigned long task_region_end, struct task_ids *ids);
|
||||
int task_start(struct tcb *task, struct task_ids *ids);
|
||||
int copy_tcb(struct tcb *to, struct tcb *from, unsigned int flags);
|
||||
int task_release_vmas(struct task_vma_head *vma_head);
|
||||
|
||||
7
tasks/mm0/include/test.h
Normal file
7
tasks/mm0/include/test.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef __TEST_H__
|
||||
#define __TEST_H__
|
||||
|
||||
|
||||
int mm0_test_global_vm_integrity(void);
|
||||
|
||||
#endif /* __TEST_H__ */
|
||||
@@ -223,7 +223,8 @@ struct vm_file *vm_file_create(void);
|
||||
int vm_file_delete(struct vm_file *f);
|
||||
int vm_object_delete(struct vm_object *vmo);
|
||||
void vm_object_print(struct vm_object *vmo);
|
||||
void vm_object_print1(struct vm_object *vmo);
|
||||
void vm_print_objects(struct list_head *vmo_list);
|
||||
void vm_print_files(struct list_head *file_list);
|
||||
|
||||
/* Used for pre-faulting a page from mm0 */
|
||||
int prefault_page(struct tcb *task, unsigned long address,
|
||||
@@ -249,4 +250,9 @@ int page_fault_handler(struct tcb *faulty_task, fault_kdata_t *fkdata);
|
||||
int vma_drop_merge_delete(struct vm_area *vma, struct vm_obj_link *link);
|
||||
int vma_drop_merge_delete_all(struct vm_area *vma);
|
||||
|
||||
void global_add_vm_object(struct vm_object *obj);
|
||||
void global_remove_vm_object(struct vm_object *obj);
|
||||
void global_add_vm_file(struct vm_file *f);
|
||||
void global_remove_vm_file(struct vm_file *f);
|
||||
|
||||
#endif /* __VM_AREA_H__ */
|
||||
|
||||
@@ -100,7 +100,7 @@ int sys_fork(struct tcb *parent)
|
||||
vfs_notify_fork(child, parent);
|
||||
|
||||
/* Add child to global task list */
|
||||
task_add_global(child);
|
||||
global_add_task(child);
|
||||
|
||||
/* Start forked child. */
|
||||
l4_thread_control(THREAD_RUN, &ids);
|
||||
@@ -145,7 +145,7 @@ int sys_clone(struct tcb *parent, void *child_stack, unsigned int flags)
|
||||
vfs_notify_fork(child, parent);
|
||||
|
||||
/* Add child to global task list */
|
||||
task_add_global(child);
|
||||
global_add_task(child);
|
||||
|
||||
/* Start forked child. */
|
||||
printf("%s/%s: Starting forked child.\n", __TASKNAME__, __FUNCTION__);
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include <memory.h>
|
||||
#include <shm.h>
|
||||
#include <file.h>
|
||||
#include <test.h>
|
||||
|
||||
|
||||
/* Given a page and the vma it is in, returns that page's virtual address */
|
||||
unsigned long vma_page_to_virtual(struct vm_area *vma, struct page *p)
|
||||
@@ -117,6 +119,20 @@ static inline int vm_object_is_droppable(struct vm_object *shadow,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* vma_merge_object()
|
||||
*
|
||||
* FIXME: Currently this is an optimisation that needs to go
|
||||
* away when swapping is available. We have this solely because
|
||||
* currently a shadow needs to identically mirror the whole
|
||||
* object underneath, in order to drop it. A file that is 1MB
|
||||
* long would spend 2MB until dropped. When swapping is available,
|
||||
* we will go back to identical mirroring instead of merging the
|
||||
* last shadow, since most unused pages would be swapped out.
|
||||
*/
|
||||
|
||||
/*
|
||||
* When one shadow object is redundant, merges it into the shadow in front of it.
|
||||
* Note it must be determined that it is redundant before calling this function.
|
||||
@@ -164,13 +180,13 @@ int vma_merge_object(struct vm_object *redundant)
|
||||
BUG_ON(!list_empty(&redundant->shdw_list));
|
||||
|
||||
/* Redundant won't be a shadow of its next object */
|
||||
list_del(&redundant->shref);
|
||||
list_del_init(&redundant->shref);
|
||||
|
||||
/* Front is now a shadow of redundant's next object */
|
||||
list_add(&front->shref, &redundant->orig_obj->shdw_list);
|
||||
front->orig_obj = redundant->orig_obj;
|
||||
|
||||
/* Find, unlink and delete the last link for the object */
|
||||
/* Find last link for the object */
|
||||
last_link = list_entry(redundant->link_list.next,
|
||||
struct vm_obj_link, linkref);
|
||||
|
||||
@@ -264,13 +280,14 @@ int vm_object_is_deletable(struct vm_object *obj)
|
||||
{
|
||||
struct vm_file *f;
|
||||
|
||||
printf("%s: Checking: ", __FUNCTION__);
|
||||
vm_object_print(obj);
|
||||
//printf("%s: Checking: ", __FUNCTION__);
|
||||
//vm_object_print(obj);
|
||||
|
||||
if (obj->nlinks != 0)
|
||||
return 0;
|
||||
|
||||
BUG_ON(obj->shadows != 0);
|
||||
BUG_ON(!list_empty(&obj->shref));
|
||||
|
||||
if (obj->flags & VM_OBJ_SHADOW)
|
||||
return 1;
|
||||
@@ -296,10 +313,15 @@ int vm_object_is_deletable(struct vm_object *obj)
|
||||
}
|
||||
|
||||
/*
|
||||
* Drops a link to an object if possible, and if it has dropped it,
|
||||
* decides and takes action on the dropped object, depending on
|
||||
* how many links and shadows it has left, and the type of the object.
|
||||
* This covers both copy_on_write() shadow drops and exit() cases.
|
||||
* exit has: !prev, next || !next
|
||||
* shadow drop has: prev, next
|
||||
*/
|
||||
|
||||
/*
|
||||
* Shadow drops: Dropping a link to shadow does not mean the shadow's
|
||||
* next object has lost a shadow. There may be other links to both. But
|
||||
* when the shadow has dropped its last link, and is going to be deleted,
|
||||
* it is then true that the shadow is lost by the next object.
|
||||
*/
|
||||
int vma_drop_merge_delete(struct vm_area *vma, struct vm_obj_link *link)
|
||||
{
|
||||
@@ -316,66 +338,76 @@ int vma_drop_merge_delete(struct vm_area *vma, struct vm_obj_link *link)
|
||||
/* Drop the link */
|
||||
obj = vma_drop_link(link);
|
||||
|
||||
/*
|
||||
* If there was an object in front, that implies it was
|
||||
* a shadow. Current object has lost it, so deduce it.
|
||||
*/
|
||||
/* If there is an object in front, this is a shadow drop */
|
||||
if (prev) {
|
||||
BUG_ON(!(prev->obj->flags & VM_OBJ_SHADOW));
|
||||
BUG_ON(!(prev->obj->flags & VM_WRITE));
|
||||
BUG_ON(--obj->shadows < 0);
|
||||
list_del_init(&prev->obj->shref);
|
||||
}
|
||||
vm_object_print(obj);
|
||||
|
||||
/*
|
||||
* If there was an object after, that implies current object
|
||||
* is a shadow, deduce it from the object after.
|
||||
*/
|
||||
if (next && (obj->flags & VM_OBJ_SHADOW)) {
|
||||
BUG_ON(obj->orig_obj != next->obj);
|
||||
BUG_ON(--next->obj->shadows < 0);
|
||||
list_del_init(&obj->shref);
|
||||
/* Remove prev from current object's shadow list */
|
||||
BUG_ON(list_empty(&prev->obj->shref));
|
||||
list_del_init(&prev->obj->shref);
|
||||
|
||||
/*
|
||||
* Furthermore, if there was an object in front,
|
||||
* that means front will become a shadow of after.
|
||||
* We don't allow dropping non-shadow objects yet,
|
||||
* (see ...is_droppable) so there must be a next.
|
||||
*/
|
||||
if (prev) {
|
||||
list_add(&prev->obj->shref,
|
||||
&next->obj->shdw_list);
|
||||
prev->obj->orig_obj = next->obj;
|
||||
BUG_ON(!next);
|
||||
|
||||
/* prev is now shadow of next */
|
||||
list_add(&prev->obj->shref,
|
||||
&next->obj->shdw_list);
|
||||
prev->obj->orig_obj = next->obj;
|
||||
|
||||
/*
|
||||
* No referrers left, meaning this object is not
|
||||
* shadowing its original object anymore.
|
||||
*/
|
||||
if (obj->nlinks == 0) {
|
||||
BUG_ON(obj->orig_obj != next->obj);
|
||||
list_del_init(&obj->shref);
|
||||
} else {
|
||||
/*
|
||||
* Dropped object still has referrers, which
|
||||
* means next has gained a new shadow.
|
||||
* Here's why:
|
||||
*
|
||||
* T1 and T2: T2: drop-
|
||||
* prev->drop->next \
|
||||
* became: T1: prev--- next
|
||||
*
|
||||
* Now we have both prev and current object
|
||||
* in next's shadow list.
|
||||
*/
|
||||
next->obj->shadows++;
|
||||
}
|
||||
/* It's an exit, we check if there's a shadow loss */
|
||||
} else {
|
||||
if (obj->nlinks == 0) {
|
||||
/* Is it a shadow delete? Sort out next */
|
||||
if (next && obj->flags & VM_OBJ_SHADOW) {
|
||||
BUG_ON(obj->orig_obj != next->obj);
|
||||
BUG_ON(--next->obj->shadows < 0);
|
||||
vm_object_print(next->obj);
|
||||
list_del_init(&obj->shref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now deal with the object itself:
|
||||
*/
|
||||
if(vm_object_is_deletable(obj)) {
|
||||
/* Now deal with the object itself */
|
||||
if (vm_object_is_deletable(obj)) {
|
||||
dprintf("Deleting object:\n");
|
||||
// vm_object_print(obj);
|
||||
vm_object_delete(obj);
|
||||
}
|
||||
|
||||
/*
|
||||
* Only one link and one shadow left.
|
||||
* Merge it with its only shadow.
|
||||
*
|
||||
* FIXME: Currently this is an optimisation that needs to go
|
||||
* away when swapping is available. We have this solely because
|
||||
* currently a shadow needs to identically mirror the whole
|
||||
* object underneath, in order to drop it. A file that is 1MB
|
||||
* long would spend 2MB until dropped. When swapping is available,
|
||||
* we will go back to identical mirroring instead of merging the
|
||||
* last shadow, since most unused pages would be swapped out.
|
||||
*/
|
||||
if ((obj->flags & VM_OBJ_SHADOW) &&
|
||||
obj->nlinks == 1 &&
|
||||
obj->shadows == 1) {
|
||||
} else if ((obj->flags & VM_OBJ_SHADOW) &&
|
||||
obj->nlinks == 1 && obj->shadows == 1) {
|
||||
dprintf("Merging object:\n");
|
||||
// vm_object_print(obj);
|
||||
vma_merge_object(obj);
|
||||
}
|
||||
|
||||
mm0_test_global_vm_integrity();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -395,8 +427,8 @@ int vma_drop_merge_delete(struct vm_area *vma, struct vm_obj_link *link)
|
||||
*
|
||||
* Sobj Sobj Sobj Fobj
|
||||
*
|
||||
* Sobj Sobj
|
||||
* l l l l l l T
|
||||
* Sobj Sobj Sobj
|
||||
* l l l l l l l T
|
||||
*
|
||||
* l l l l l l l T
|
||||
* Sobj
|
||||
@@ -439,7 +471,6 @@ int vma_drop_merge_delete_all_old(struct vm_area *vma)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* TODO:
|
||||
* - Why not allocate a swap descriptor in vma_create_shadow() rather than
|
||||
* a bare vm_object? It will be needed.
|
||||
@@ -486,7 +517,8 @@ struct page *copy_on_write(struct fault_data *fault)
|
||||
shadow->flags = VM_OBJ_SHADOW | VM_WRITE;
|
||||
shadow->pager = &swap_pager;
|
||||
vmo_link->obj->shadows++;
|
||||
|
||||
printf("%s: ", __FUNCTION__);
|
||||
vm_object_print(vmo_link->obj);
|
||||
dprintf("%s: Created a shadow:\n", __TASKNAME__);
|
||||
// vm_object_print(shadow);
|
||||
dprintf("%s: Original object:\n", __TASKNAME__);
|
||||
@@ -506,7 +538,7 @@ struct page *copy_on_write(struct fault_data *fault)
|
||||
list_add(&shadow->shref, &shadow->orig_obj->shdw_list);
|
||||
|
||||
/* Add to global object list */
|
||||
list_add(&shadow->list, &vm_object_list);
|
||||
global_add_vm_object(shadow);
|
||||
|
||||
} else {
|
||||
dprintf("No new shadows. Going to add to "
|
||||
@@ -555,6 +587,8 @@ struct page *copy_on_write(struct fault_data *fault)
|
||||
insert_page_olist(new_page, new_page->owner);
|
||||
new_page->owner->npages++;
|
||||
|
||||
mm0_test_global_vm_integrity();
|
||||
|
||||
/* Shared faults don't have shadows so we don't look for collapses */
|
||||
if (!(vma->flags & VMA_SHARED)) {
|
||||
|
||||
|
||||
@@ -13,12 +13,9 @@
|
||||
#include <l4/api/kip.h>
|
||||
#include <posix/sys/types.h>
|
||||
#include <string.h>
|
||||
#include <globals.h>
|
||||
#include <file.h>
|
||||
|
||||
/* List of all generic files */
|
||||
LIST_HEAD(vm_file_list);
|
||||
|
||||
|
||||
/* Copy from one page's buffer into another page */
|
||||
int page_copy(struct page *dst, struct page *src,
|
||||
unsigned long dst_offset, unsigned long src_offset,
|
||||
@@ -134,7 +131,7 @@ int do_open(struct tcb *task, int fd, unsigned long vnum, unsigned long length)
|
||||
task->files->fd[fd].cursor = 0;
|
||||
|
||||
/* Check if that vm_file is already in the list */
|
||||
list_for_each_entry(vmfile, &vm_file_list, list) {
|
||||
list_for_each_entry(vmfile, &global_vm_files.list, list) {
|
||||
|
||||
/* Check whether it is a vfs file and if so vnums match. */
|
||||
if ((vmfile->type & VM_FILE_VFS) &&
|
||||
@@ -159,10 +156,7 @@ int do_open(struct tcb *task, int fd, unsigned long vnum, unsigned long length)
|
||||
vmfile->openers++;
|
||||
|
||||
/* Add to file list */
|
||||
list_add(&vmfile->list, &vm_file_list);
|
||||
|
||||
/* Add to object list */
|
||||
list_add(&vmfile->vm_obj.list, &vm_object_list);
|
||||
global_add_vm_file(vmfile);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <file.h>
|
||||
#include <init.h>
|
||||
#include <utcb.h>
|
||||
#include <test.h>
|
||||
|
||||
/* A separate list than the generic file list that keeps just the boot files */
|
||||
LIST_HEAD(boot_file_list);
|
||||
@@ -50,13 +51,10 @@ int mm0_task_init(struct vm_file *f, unsigned long task_start,
|
||||
return err;
|
||||
|
||||
/* Add the task to the global task list */
|
||||
task_add_global(task);
|
||||
global_add_task(task);
|
||||
|
||||
/* Add the file to global vm lists */
|
||||
list_del_init(&f->list);
|
||||
list_del_init(&f->vm_obj.list);
|
||||
list_add(&f->list, &vm_file_list);
|
||||
list_add(&f->vm_obj.list, &vm_object_list);
|
||||
global_add_vm_file(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -78,13 +76,14 @@ struct vm_file *initdata_next_bootfile(struct initdata *initdata)
|
||||
*/
|
||||
int start_boot_tasks(struct initdata *initdata)
|
||||
{
|
||||
struct vm_file *file = 0, *fs0 = 0, *mm0 = 0, *n;
|
||||
struct vm_file *file = 0, *fs0_file = 0, *mm0_file = 0, *n;
|
||||
struct tcb *fs0_task;
|
||||
struct svc_image *img;
|
||||
struct task_ids ids;
|
||||
struct list_head files;
|
||||
struct list_head other_files;
|
||||
int total = 0;
|
||||
|
||||
INIT_LIST_HEAD(&files);
|
||||
INIT_LIST_HEAD(&other_files);
|
||||
|
||||
/* Separate out special server tasks and regular files */
|
||||
do {
|
||||
@@ -94,11 +93,11 @@ int start_boot_tasks(struct initdata *initdata)
|
||||
BUG_ON(file->type != VM_FILE_BOOTFILE);
|
||||
img = file->priv_data;
|
||||
if (!strcmp(img->name, __PAGERNAME__))
|
||||
mm0 = file;
|
||||
mm0_file = file;
|
||||
else if (!strcmp(img->name, __VFSNAME__))
|
||||
fs0 = file;
|
||||
fs0_file = file;
|
||||
else
|
||||
list_add(&file->list, &files);
|
||||
list_add(&file->list, &other_files);
|
||||
} else
|
||||
break;
|
||||
} while (1);
|
||||
@@ -109,7 +108,7 @@ int start_boot_tasks(struct initdata *initdata)
|
||||
ids.spid = PAGER_TID;
|
||||
ids.tgid = PAGER_TID;
|
||||
|
||||
if (mm0_task_init(mm0, INITTASK_AREA_START, INITTASK_AREA_END, &ids) < 0)
|
||||
if (mm0_task_init(mm0_file, INITTASK_AREA_START, INITTASK_AREA_END, &ids) < 0)
|
||||
BUG();
|
||||
total++;
|
||||
|
||||
@@ -119,18 +118,17 @@ int start_boot_tasks(struct initdata *initdata)
|
||||
ids.tgid = VFS_TID;
|
||||
|
||||
printf("%s: Initialising fs0\n",__TASKNAME__);
|
||||
if (task_exec(fs0, USER_AREA_START, USER_AREA_END, &ids) < 0)
|
||||
BUG();
|
||||
BUG_ON((IS_ERR(fs0_task = task_exec(fs0_file, USER_AREA_START, USER_AREA_END, &ids))));
|
||||
total++;
|
||||
|
||||
/* Initialise other tasks */
|
||||
list_for_each_entry_safe(file, n, &files, list) {
|
||||
list_for_each_entry_safe(file, n, &other_files, list) {
|
||||
printf("%s: Initialising new boot task.\n", __TASKNAME__);
|
||||
ids.tid = TASK_ID_INVALID;
|
||||
ids.spid = TASK_ID_INVALID;
|
||||
ids.tgid = TASK_ID_INVALID;
|
||||
if (task_exec(file, USER_AREA_START, USER_AREA_END, &ids) < 0)
|
||||
BUG();
|
||||
list_del_init(&file->list);
|
||||
BUG_ON(IS_ERR(task_exec(file, USER_AREA_START, USER_AREA_END, &ids)));
|
||||
total++;
|
||||
}
|
||||
|
||||
@@ -189,6 +187,7 @@ void initialise(void)
|
||||
|
||||
start_boot_tasks(&initdata);
|
||||
|
||||
mm0_test_global_vm_integrity();
|
||||
printf("%s: Initialised the memory/process manager.\n", __TASKNAME__);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <mm/alloc_page.h>
|
||||
#include <vm_area.h>
|
||||
#include <string.h>
|
||||
#include <globals.h>
|
||||
#include <file.h>
|
||||
#include <init.h>
|
||||
#include INC_ARCH(bootdesc.h)
|
||||
@@ -295,9 +296,6 @@ int init_boot_files(struct initdata *initdata)
|
||||
boot_file->vm_obj.flags = VM_OBJ_FILE;
|
||||
boot_file->vm_obj.pager = &bootfile_pager;
|
||||
|
||||
/* Add the object to global vm_object list */
|
||||
list_add(&boot_file->vm_obj.list, &vm_object_list);
|
||||
|
||||
/* Add the file to initdata's bootfile list */
|
||||
list_add_tail(&boot_file->list, &initdata->boot_file_list);
|
||||
}
|
||||
@@ -332,7 +330,7 @@ struct vm_file *get_devzero(void)
|
||||
{
|
||||
struct vm_file *f;
|
||||
|
||||
list_for_each_entry(f, &vm_file_list, list)
|
||||
list_for_each_entry(f, &global_vm_files.list, list)
|
||||
if (f->type == VM_FILE_DEVZERO)
|
||||
return f;
|
||||
return 0;
|
||||
@@ -364,8 +362,7 @@ int init_devzero(void)
|
||||
zpage->refcnt++;
|
||||
zpage->owner = &devzero->vm_obj;
|
||||
|
||||
list_add(&devzero->vm_obj.list, &vm_object_list);
|
||||
list_add(&devzero->list, &vm_file_list);
|
||||
global_add_vm_file(devzero);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <mmap.h>
|
||||
#include <utcb.h>
|
||||
#include <vm_area.h>
|
||||
#include <globals.h>
|
||||
#include <kmalloc/kmalloc.h>
|
||||
#include <l4lib/arch/syscalls.h>
|
||||
#include <l4lib/arch/syslib.h>
|
||||
@@ -28,9 +29,6 @@
|
||||
#define shm_file_to_desc(shm_file) \
|
||||
((struct shm_descriptor *)shm_file->priv_data)
|
||||
|
||||
/* The list of shared memory areas that are already set up and working */
|
||||
static LIST_HEAD(shm_file_list);
|
||||
|
||||
/* Unique shared memory ids */
|
||||
static struct id_pool *shm_ids;
|
||||
|
||||
@@ -133,8 +131,9 @@ void *sys_shmat(struct tcb *task, l4id_t shmid, void *shmaddr, int shmflg)
|
||||
{
|
||||
struct vm_file *shm_file, *n;
|
||||
|
||||
list_for_each_entry_safe(shm_file, n, &shm_file_list, list) {
|
||||
if (shm_file_to_desc(shm_file)->shmid == shmid)
|
||||
list_for_each_entry_safe(shm_file, n, &global_vm_files.list, list) {
|
||||
if (shm_file->type == VM_FILE_SHM &&
|
||||
shm_file_to_desc(shm_file)->shmid == shmid)
|
||||
return do_shmat(shm_file, shmaddr,
|
||||
shmflg, task);
|
||||
}
|
||||
@@ -159,8 +158,9 @@ int sys_shmdt(struct tcb *task, const void *shmaddr)
|
||||
struct vm_file *shm_file, *n;
|
||||
int err;
|
||||
|
||||
list_for_each_entry_safe(shm_file, n, &shm_file_list, list) {
|
||||
if (shm_file_to_desc(shm_file)->shm_addr == shmaddr) {
|
||||
list_for_each_entry_safe(shm_file, n, &global_vm_files.list, list) {
|
||||
if (shm_file->type == VM_FILE_SHM &&
|
||||
shm_file_to_desc(shm_file)->shm_addr == shmaddr) {
|
||||
if ((err = do_shmdt(task, shm_file) < 0))
|
||||
return err;
|
||||
else
|
||||
@@ -235,8 +235,7 @@ struct vm_file *shm_new(key_t key, unsigned long npages)
|
||||
shm_file->vm_obj.flags = VM_OBJ_FILE | VM_WRITE;
|
||||
|
||||
/* Add to shm file and global object list */
|
||||
list_add(&shm_file->list, &shm_file_list);
|
||||
list_add(&shm_file->vm_obj.list, &vm_object_list);
|
||||
global_add_vm_file(shm_file);
|
||||
|
||||
return shm_file;
|
||||
}
|
||||
@@ -250,12 +249,14 @@ void *shmat_shmget_internal(struct tcb *task, key_t key, void *shmaddr)
|
||||
struct vm_file *shm_file;
|
||||
struct shm_descriptor *shm_desc;
|
||||
|
||||
list_for_each_entry(shm_file, &shm_file_list, list) {
|
||||
shm_desc = shm_file_to_desc(shm_file);
|
||||
/* Found the key, shmat that area */
|
||||
if (shm_desc->key == key)
|
||||
return do_shmat(shm_file, shmaddr,
|
||||
0, task);
|
||||
list_for_each_entry(shm_file, &global_vm_files.list, list) {
|
||||
if(shm_file->type == VM_FILE_SHM) {
|
||||
shm_desc = shm_file_to_desc(shm_file);
|
||||
/* Found the key, shmat that area */
|
||||
if (shm_desc->key == key)
|
||||
return do_shmat(shm_file, shmaddr,
|
||||
0, task);
|
||||
}
|
||||
}
|
||||
|
||||
return PTR_ERR(-EEXIST);
|
||||
@@ -268,6 +269,7 @@ void *shmat_shmget_internal(struct tcb *task, key_t key, void *shmaddr)
|
||||
int sys_shmget(key_t key, int size, int shmflg)
|
||||
{
|
||||
unsigned long npages = __pfn(page_align_up(size));
|
||||
struct shm_descriptor *shm_desc;
|
||||
struct vm_file *shm;
|
||||
|
||||
/* First check argument validity */
|
||||
@@ -286,8 +288,11 @@ int sys_shmget(key_t key, int size, int shmflg)
|
||||
return shm_file_to_desc(shm)->shmid;
|
||||
}
|
||||
|
||||
list_for_each_entry(shm, &shm_file_list, list) {
|
||||
struct shm_descriptor *shm_desc = shm_file_to_desc(shm);
|
||||
list_for_each_entry(shm, &global_vm_files.list, list) {
|
||||
if (shm->type != VM_FILE_SHM)
|
||||
continue;
|
||||
|
||||
shm_desc = shm_file_to_desc(shm);
|
||||
|
||||
if (shm_desc->key == key) {
|
||||
/*
|
||||
|
||||
@@ -27,34 +27,40 @@
|
||||
#include <task.h>
|
||||
#include <shm.h>
|
||||
#include <mmap.h>
|
||||
#include <globals.h>
|
||||
|
||||
struct tcb_head {
|
||||
struct list_head list;
|
||||
int total; /* Total threads */
|
||||
} tcb_head = {
|
||||
.list = { &tcb_head.list, &tcb_head.list },
|
||||
struct global_list global_tasks = {
|
||||
.list = { &global_tasks.list, &global_tasks.list },
|
||||
.total = 0,
|
||||
};
|
||||
|
||||
void print_tasks(void)
|
||||
{
|
||||
struct tcb *task;
|
||||
printf("Tasks:\n========\n");
|
||||
list_for_each_entry(task, &tcb_head.list, list) {
|
||||
list_for_each_entry(task, &global_tasks.list, list) {
|
||||
printf("Task tid: %d, spid: %d\n", task->tid, task->spid);
|
||||
}
|
||||
}
|
||||
|
||||
void task_add_global(struct tcb *task)
|
||||
void global_add_task(struct tcb *task)
|
||||
{
|
||||
list_add_tail(&task->list, &tcb_head.list);
|
||||
tcb_head.total++;
|
||||
BUG_ON(!list_empty(&task->list));
|
||||
list_add_tail(&task->list, &global_tasks.list);
|
||||
global_tasks.total++;
|
||||
}
|
||||
void global_remove_task(struct tcb *task)
|
||||
{
|
||||
BUG_ON(list_empty(&task->list));
|
||||
list_del_init(&task->list);
|
||||
BUG_ON(--global_tasks.total < 0);
|
||||
}
|
||||
|
||||
struct tcb *find_task(int tid)
|
||||
{
|
||||
struct tcb *t;
|
||||
|
||||
list_for_each_entry(t, &tcb_head.list, list) {
|
||||
list_for_each_entry(t, &global_tasks.list, list) {
|
||||
/* A temporary precaution */
|
||||
BUG_ON(t->tid != t->spid);
|
||||
if (t->tid == tid) {
|
||||
@@ -107,8 +113,7 @@ struct tcb *tcb_alloc_init(unsigned int flags)
|
||||
/* NOTE: We may need to delete shared tcb parts here as well. */
|
||||
int tcb_destroy(struct tcb *task)
|
||||
{
|
||||
list_del(&task->list);
|
||||
tcb_head.total--;
|
||||
global_remove_task(task);
|
||||
|
||||
kfree(task);
|
||||
|
||||
@@ -402,43 +407,41 @@ int task_prefault_regions(struct tcb *task, struct vm_file *f)
|
||||
* Main entry point for the creation, initialisation and
|
||||
* execution of a new task.
|
||||
*/
|
||||
int task_exec(struct vm_file *f, unsigned long task_region_start,
|
||||
unsigned long task_region_end, struct task_ids *ids)
|
||||
struct tcb *task_exec(struct vm_file *f, unsigned long task_region_start,
|
||||
unsigned long task_region_end, struct task_ids *ids)
|
||||
{
|
||||
struct tcb *task;
|
||||
int err;
|
||||
|
||||
if (IS_ERR(task = task_create(0, ids, THREAD_NEW_SPACE,
|
||||
TCB_NO_SHARING)))
|
||||
return (int)task;
|
||||
return task;
|
||||
|
||||
if ((err = task_setup_regions(f, task, task_region_start,
|
||||
task_region_end)) < 0)
|
||||
return err;
|
||||
return PTR_ERR(err);
|
||||
|
||||
if ((err = task_mmap_regions(task, f)) < 0)
|
||||
return err;
|
||||
|
||||
if (ids->tid == VFS_TID)
|
||||
if ((err = task_prefault_regions(task, f)) < 0)
|
||||
return err;
|
||||
return PTR_ERR(err);
|
||||
|
||||
if ((err = task_setup_registers(task, 0, 0, 0)) < 0)
|
||||
return err;
|
||||
return PTR_ERR(err);
|
||||
|
||||
/* Add the task to the global task list */
|
||||
task_add_global(task);
|
||||
global_add_task(task);
|
||||
|
||||
/* Add the file to global vm lists */
|
||||
list_del_init(&f->list);
|
||||
list_del_init(&f->vm_obj.list);
|
||||
list_add(&f->list, &vm_file_list);
|
||||
list_add(&f->vm_obj.list, &vm_object_list);
|
||||
global_add_vm_file(f);
|
||||
|
||||
/* Prefault all its regions */
|
||||
if (ids->tid == VFS_TID)
|
||||
task_prefault_regions(task, f);
|
||||
|
||||
/* Start the task */
|
||||
if ((err = task_start(task, ids)) < 0)
|
||||
return err;
|
||||
return PTR_ERR(err);
|
||||
|
||||
return 0;
|
||||
return task;
|
||||
}
|
||||
|
||||
/* Maps and prefaults the utcb of a task into another task */
|
||||
@@ -495,10 +498,10 @@ int send_task_data(struct tcb *vfs)
|
||||
tdata_head = (struct task_data_head *)vfs->utcb;
|
||||
|
||||
/* First word is total number of tcbs */
|
||||
tdata_head->total = tcb_head.total;
|
||||
tdata_head->total = global_tasks.total;
|
||||
|
||||
/* Write per-task data for all tasks */
|
||||
list_for_each_entry(t, &tcb_head.list, list) {
|
||||
list_for_each_entry(t, &global_tasks.list, list) {
|
||||
tdata_head->tdata[li].tid = t->tid;
|
||||
tdata_head->tdata[li].utcb_address = (unsigned long)t->utcb;
|
||||
li++;
|
||||
|
||||
113
tasks/mm0/src/test.c
Normal file
113
tasks/mm0/src/test.c
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* These functions here do run-time checks on all fields
|
||||
* of tasks, vmas, and vm objects to see that they
|
||||
* have expected values.
|
||||
*
|
||||
* Copyright (C) 2008 Bahadir Balban
|
||||
*/
|
||||
|
||||
#include <vm_area.h>
|
||||
#include <mmap.h>
|
||||
#include <shm.h>
|
||||
#include <globals.h>
|
||||
|
||||
struct vm_statistics {
|
||||
int tasks;
|
||||
int vm_objects;
|
||||
int shadow_objects; /* Shadows counted by hand (well almost!) */
|
||||
int shadows_referred; /* Shadows that objects say they have */
|
||||
int file_objects;
|
||||
int vm_files;
|
||||
int tasks_total;
|
||||
};
|
||||
|
||||
/* Count links in objects link list, and compare with nlinks */
|
||||
int vm_object_test_link_count(struct vm_object *vmo)
|
||||
{
|
||||
int links = 0;
|
||||
struct vm_obj_link *l;
|
||||
|
||||
list_for_each_entry(l, &vmo->link_list, linkref)
|
||||
links++;
|
||||
|
||||
BUG_ON(links != vmo->nlinks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vm_object_test_shadow_count(struct vm_object *vmo)
|
||||
{
|
||||
struct vm_object *sh;
|
||||
int shadows = 0;
|
||||
|
||||
list_for_each_entry(sh, &vmo->shdw_list, shref)
|
||||
shadows++;
|
||||
|
||||
BUG_ON(shadows != vmo->shadows);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mm0_test_global_vm_integrity(void)
|
||||
{
|
||||
struct tcb *task;
|
||||
struct vm_object *vmo;
|
||||
struct vm_statistics vmstat;
|
||||
struct vm_file *f;
|
||||
|
||||
|
||||
memset(&vmstat, 0, sizeof(vmstat));
|
||||
|
||||
/* Count all shadow and file objects */
|
||||
list_for_each_entry(vmo, &global_vm_objects.list, list) {
|
||||
vmstat.shadows_referred += vmo->shadows;
|
||||
if (vmo->flags & VM_OBJ_SHADOW)
|
||||
vmstat.shadow_objects++;
|
||||
if (vmo->flags & VM_OBJ_FILE)
|
||||
vmstat.file_objects++;
|
||||
vmstat.vm_objects++;
|
||||
vm_object_test_shadow_count(vmo);
|
||||
vm_object_test_link_count(vmo);
|
||||
}
|
||||
|
||||
/* Count all registered vmfiles */
|
||||
list_for_each_entry(f, &global_vm_files.list, list)
|
||||
vmstat.vm_files++;
|
||||
|
||||
if (vmstat.vm_files != global_vm_files.total) {
|
||||
printf("Total counted files don't match "
|
||||
"global_vm_files total\n");
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (vmstat.vm_objects != global_vm_objects.total) {
|
||||
printf("Total counted vm_objects don't "
|
||||
"match global_vm_objects total\n");
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Total file objects must be equal to total vm files */
|
||||
if (vmstat.vm_files != vmstat.file_objects) {
|
||||
printf("\nTotal files don't match total file objects.\n");
|
||||
printf("vm files:\n");
|
||||
vm_print_files(&global_vm_files.list);
|
||||
printf("\nvm objects:\n");
|
||||
vm_print_objects(&global_vm_objects.list);
|
||||
printf("\n");
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* Counted and referred shadows must match */
|
||||
BUG_ON(vmstat.shadow_objects != vmstat.shadows_referred);
|
||||
|
||||
/* Count all tasks */
|
||||
list_for_each_entry(task, &global_tasks.list, list)
|
||||
vmstat.tasks++;
|
||||
|
||||
if (vmstat.tasks != global_tasks.total) {
|
||||
printf("Total counted tasks don't match global_tasks total\n");
|
||||
BUG();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,52 @@
|
||||
#include <l4/macros.h>
|
||||
#include <l4/api/errno.h>
|
||||
#include <kmalloc/kmalloc.h>
|
||||
#include <globals.h>
|
||||
|
||||
/* Global list of all in-memory files on the system */
|
||||
struct global_list global_vm_files = {
|
||||
.list = { &global_vm_files.list, &global_vm_files.list },
|
||||
.total = 0,
|
||||
};
|
||||
|
||||
/* Global list of in-memory vm objects in the system */
|
||||
struct global_list global_vm_objects = {
|
||||
.list = { &global_vm_objects.list, &global_vm_objects.list },
|
||||
.total = 0,
|
||||
};
|
||||
|
||||
|
||||
void global_add_vm_object(struct vm_object *obj)
|
||||
{
|
||||
BUG_ON(!list_empty(&obj->list));
|
||||
list_add(&obj->list, &global_vm_objects.list);
|
||||
global_vm_objects.total++;
|
||||
}
|
||||
|
||||
void global_remove_vm_object(struct vm_object *obj)
|
||||
{
|
||||
BUG_ON(list_empty(&obj->list));
|
||||
list_del_init(&obj->list);
|
||||
BUG_ON(--global_vm_objects.total < 0);
|
||||
}
|
||||
|
||||
void global_add_vm_file(struct vm_file *f)
|
||||
{
|
||||
BUG_ON(!list_empty(&f->list));
|
||||
list_add(&f->list, &global_vm_files.list);
|
||||
global_vm_files.total++;
|
||||
|
||||
global_add_vm_object(&f->vm_obj);
|
||||
}
|
||||
|
||||
void global_remove_vm_file(struct vm_file *f)
|
||||
{
|
||||
BUG_ON(list_empty(&f->list));
|
||||
list_del_init(&f->list);
|
||||
BUG_ON(--global_vm_files.total < 0);
|
||||
|
||||
global_remove_vm_object(&f->vm_obj);
|
||||
}
|
||||
|
||||
void print_cache_pages(struct vm_object *vmo)
|
||||
{
|
||||
@@ -51,8 +97,21 @@ void vm_object_print(struct vm_object *vmo)
|
||||
// printf("\n");
|
||||
}
|
||||
|
||||
/* Global list of in-memory vm objects. */
|
||||
LIST_HEAD(vm_object_list);
|
||||
void vm_print_files(struct list_head *files)
|
||||
{
|
||||
struct vm_file *f;
|
||||
|
||||
list_for_each_entry(f, files, list)
|
||||
vm_object_print(&f->vm_obj);
|
||||
}
|
||||
|
||||
void vm_print_objects(struct list_head *objects)
|
||||
{
|
||||
struct vm_object *vmo;
|
||||
|
||||
list_for_each_entry(vmo, objects, list)
|
||||
vm_object_print(vmo);
|
||||
}
|
||||
|
||||
struct vm_object *vm_object_init(struct vm_object *obj)
|
||||
{
|
||||
@@ -118,7 +177,11 @@ int vm_object_delete(struct vm_object *vmo)
|
||||
vmo->pager->ops.release_pages(vmo);
|
||||
|
||||
/* Remove from global list */
|
||||
list_del(&vmo->list);
|
||||
if (vmo->flags & VM_OBJ_FILE)
|
||||
global_remove_vm_file(vm_object_to_file(vmo));
|
||||
else if (vmo->flags & VM_OBJ_SHADOW)
|
||||
global_remove_vm_object(vmo);
|
||||
else BUG();
|
||||
|
||||
/* Check any references */
|
||||
BUG_ON(vmo->nlinks);
|
||||
@@ -126,6 +189,7 @@ int vm_object_delete(struct vm_object *vmo)
|
||||
BUG_ON(!list_empty(&vmo->shdw_list));
|
||||
BUG_ON(!list_empty(&vmo->link_list));
|
||||
BUG_ON(!list_empty(&vmo->page_cache));
|
||||
BUG_ON(!list_empty(&vmo->shref));
|
||||
|
||||
/* Obtain and free via the base object */
|
||||
if (vmo->flags & VM_OBJ_FILE) {
|
||||
@@ -147,9 +211,6 @@ int vm_object_delete(struct vm_object *vmo)
|
||||
|
||||
int vm_file_delete(struct vm_file *f)
|
||||
{
|
||||
/* Delete it from global file list */
|
||||
list_del_init(&f->list);
|
||||
|
||||
/* Delete file via base object */
|
||||
return vm_object_delete(&f->vm_obj);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user