From 651901d8b9a4e5ade4b300be888c1baef2828c8e Mon Sep 17 00:00:00 2001 From: Bahadir Balban Date: Fri, 24 Oct 2008 13:45:39 +0300 Subject: [PATCH] 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. --- tasks/mm0/include/globals.h | 13 ++++ tasks/mm0/include/task.h | 12 ++- tasks/mm0/include/test.h | 7 ++ tasks/mm0/include/vm_area.h | 8 +- tasks/mm0/src/clone.c | 4 +- tasks/mm0/src/fault.c | 142 ++++++++++++++++++++++-------------- tasks/mm0/src/file.c | 12 +-- tasks/mm0/src/init.c | 33 ++++----- tasks/mm0/src/pagers.c | 9 +-- tasks/mm0/src/shm.c | 39 +++++----- tasks/mm0/src/task.c | 65 +++++++++-------- tasks/mm0/src/test.c | 113 ++++++++++++++++++++++++++++ tasks/mm0/src/vm_object.c | 73 ++++++++++++++++-- 13 files changed, 384 insertions(+), 146 deletions(-) create mode 100644 tasks/mm0/include/globals.h create mode 100644 tasks/mm0/include/test.h create mode 100644 tasks/mm0/src/test.c diff --git a/tasks/mm0/include/globals.h b/tasks/mm0/include/globals.h new file mode 100644 index 0000000..d21bc0d --- /dev/null +++ b/tasks/mm0/include/globals.h @@ -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__ */ diff --git a/tasks/mm0/include/task.h b/tasks/mm0/include/task.h index f5cbd80..275aa80 100644 --- a/tasks/mm0/include/task.h +++ b/tasks/mm0/include/task.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); diff --git a/tasks/mm0/include/test.h b/tasks/mm0/include/test.h new file mode 100644 index 0000000..ecfa1f3 --- /dev/null +++ b/tasks/mm0/include/test.h @@ -0,0 +1,7 @@ +#ifndef __TEST_H__ +#define __TEST_H__ + + +int mm0_test_global_vm_integrity(void); + +#endif /* __TEST_H__ */ diff --git a/tasks/mm0/include/vm_area.h b/tasks/mm0/include/vm_area.h index 57ed1aa..dbb098a 100644 --- a/tasks/mm0/include/vm_area.h +++ b/tasks/mm0/include/vm_area.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__ */ diff --git a/tasks/mm0/src/clone.c b/tasks/mm0/src/clone.c index ef1041a..f8e79e9 100755 --- a/tasks/mm0/src/clone.c +++ b/tasks/mm0/src/clone.c @@ -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__); diff --git a/tasks/mm0/src/fault.c b/tasks/mm0/src/fault.c index 391c9f3..efc17d9 100644 --- a/tasks/mm0/src/fault.c +++ b/tasks/mm0/src/fault.c @@ -18,6 +18,8 @@ #include #include #include +#include + /* 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)) { diff --git a/tasks/mm0/src/file.c b/tasks/mm0/src/file.c index 79ffd35..dbff94e 100644 --- a/tasks/mm0/src/file.c +++ b/tasks/mm0/src/file.c @@ -13,12 +13,9 @@ #include #include #include +#include #include -/* 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; } diff --git a/tasks/mm0/src/init.c b/tasks/mm0/src/init.c index d2ca0cc..435cd72 100644 --- a/tasks/mm0/src/init.c +++ b/tasks/mm0/src/init.c @@ -16,6 +16,7 @@ #include #include #include +#include /* 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__); } diff --git a/tasks/mm0/src/pagers.c b/tasks/mm0/src/pagers.c index 0fe7f3a..184b4d9 100644 --- a/tasks/mm0/src/pagers.c +++ b/tasks/mm0/src/pagers.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #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; } diff --git a/tasks/mm0/src/shm.c b/tasks/mm0/src/shm.c index 10e8863..1dcf150 100644 --- a/tasks/mm0/src/shm.c +++ b/tasks/mm0/src/shm.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -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) { /* diff --git a/tasks/mm0/src/task.c b/tasks/mm0/src/task.c index 3d4150f..8df579d 100644 --- a/tasks/mm0/src/task.c +++ b/tasks/mm0/src/task.c @@ -27,34 +27,40 @@ #include #include #include +#include -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++; diff --git a/tasks/mm0/src/test.c b/tasks/mm0/src/test.c new file mode 100644 index 0000000..942dc6c --- /dev/null +++ b/tasks/mm0/src/test.c @@ -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 +#include +#include +#include + +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; +} + + + diff --git a/tasks/mm0/src/vm_object.c b/tasks/mm0/src/vm_object.c index d681f50..547cc51 100644 --- a/tasks/mm0/src/vm_object.c +++ b/tasks/mm0/src/vm_object.c @@ -8,6 +8,52 @@ #include #include #include +#include + +/* 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); }