diff --git a/tasks/mm0/include/vm_area.h b/tasks/mm0/include/vm_area.h index 72af745..3fc95eb 100644 --- a/tasks/mm0/include/vm_area.h +++ b/tasks/mm0/include/vm_area.h @@ -121,9 +121,11 @@ struct vm_pager { */ struct vm_object { int npages; /* Number of pages in memory */ - int refcnt; /* Number of shadows (or vmas) that refer */ + int nlinks; /* Number of mapper links that refer */ + int shadows; /* Number of shadows that refer */ struct list_head shref; /* Shadow reference from original object */ - struct list_head shadowers; /* List of vm objects that shadows this one */ + struct list_head shdw_list; /* List of vm objects that shadows this one */ + struct list_head link_list; /* List of links that refer to this object */ struct vm_object *orig_obj; /* Original object that this one shadows */ unsigned int flags; /* Defines the type and flags of the object */ struct list_head list; /* List of all vm objects in memory */ @@ -133,6 +135,7 @@ struct vm_object { /* In memory representation of either a vfs file, a device. */ struct vm_file { + int openers; unsigned long length; unsigned int type; struct list_head list; @@ -143,9 +146,28 @@ struct vm_file { /* To create per-vma vm_object lists */ struct vm_obj_link { struct list_head list; + struct list_head linkref; struct vm_object *obj; }; +static inline void vm_link_object(struct vm_obj_link *link, struct vm_object *obj) +{ + link->obj = obj; + list_add(&link->linkref, &obj->link_list); + obj->nlinks++; +} + +static inline struct vm_object *vm_unlink_object(struct vm_obj_link *link) +{ + /* Delete link from object's link list */ + list_del(&link->linkref); + + /* Reduce object's mapper link count */ + link->obj->nlinks--; + + return link->obj; +} + #define vm_object_to_file(obj) container_of(obj, struct vm_file, vm_obj) /* diff --git a/tasks/mm0/src/clone.c b/tasks/mm0/src/clone.c index 3a978d7..952a135 100755 --- a/tasks/mm0/src/clone.c +++ b/tasks/mm0/src/clone.c @@ -38,8 +38,8 @@ int copy_vmas(struct tcb *to, struct tcb *from) /* Create a new link */ new_link = vm_objlink_create(); - /* Copy object field from original link. */ - new_link->obj = vmo_link->obj; + /* Link object with new link */ + vm_link_object(new_link, vmo_link->obj); /* Add the new link to vma in object order */ list_add_tail(&new_link->list, &new_vma->vm_obj_list); diff --git a/tasks/mm0/src/fault.c b/tasks/mm0/src/fault.c index f20c139..33f8f3c 100644 --- a/tasks/mm0/src/fault.c +++ b/tasks/mm0/src/fault.c @@ -60,29 +60,22 @@ struct vm_obj_link *vma_next_link(struct list_head *link, } /* Unlinks orig_link from its vma and deletes it but keeps the object. */ -int vma_drop_link(struct vm_obj_link *shadower_link, - struct vm_obj_link *orig_link) +struct vm_object *vma_drop_link(struct vm_obj_link *shadower_link, + struct vm_obj_link *orig_link) { + struct vm_object *dropped = orig_link->obj; + /* Remove object link from vma's list */ list_del(&orig_link->list); - /* Reduce object's ref count */ - orig_link->obj->refcnt--; + /* Unlink the link from object */ + vm_unlink_object(orig_link); /* - * Refcount could go as low as 1 but not zero because shortly - * after it goes down to one, it is removed from the link - * chain so it can never exist with a refcount less than 1 - * in the chain. + * Reduce object's shadow count since its not shadowed + * by this shadower anymore. */ - if (orig_link->obj->refcnt < 1) { - printf("%s: Shadower:\n", __FUNCTION__); - vm_object_print(shadower_link->obj); - - printf("%s: Original:\n", __FUNCTION__); - vm_object_print(orig_link->obj); - BUG(); - } + dropped->shadows--; /* * Remove the shadower from original's shadower list. @@ -94,7 +87,7 @@ int vma_drop_link(struct vm_obj_link *shadower_link, /* Delete the original link */ kfree(orig_link); - return 0; + return dropped; } /* @@ -135,18 +128,19 @@ int vm_object_is_subset(struct vm_object *copier, * Front Redundant Next * Shadow Shadow Object (E.g. shadow or file) */ -int vma_merge_link(struct vm_obj_link *redundant_shlink) +int vma_merge_object(struct vm_object *redundant) { /* The redundant shadow object */ - struct vm_object *redundant = redundant_shlink->obj; struct vm_object *front; /* Shadow in front of redundant */ + struct vm_obj_link *last_link; struct page *p1, *p2; - /* Check refcount is really 1 */ - BUG_ON(redundant->refcnt != 1); + /* Check link and shadow count is really 1 */ + BUG_ON(redundant->nlinks != 1); + BUG_ON(redundant->shadows != 1); /* Get the last shadower object in front */ - front = list_entry(redundant->shadowers.next, + front = list_entry(redundant->shdw_list.next, struct vm_object, shref); /* Move all non-intersecting pages to front shadow. */ @@ -168,21 +162,27 @@ int vma_merge_link(struct vm_obj_link *redundant_shlink) list_del_init(&front->shref); /* Check that there really was one shadower of redundant left */ - BUG_ON(!list_empty(&redundant->shadowers)); + BUG_ON(!list_empty(&redundant->shdw_list)); /* Redundant won't be a shadow of its next object */ list_del(&redundant->shref); /* Front is now a shadow of redundant's next object */ - list_add(&front->shref, &redundant->orig_obj->shadowers); + 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 */ + last_link = list_entry(redundant->link_list.next, + struct vm_obj_link, linkref); + vm_unlink_object(last_link); + kfree(last_link); + + /* Redundant shadow has no shadows anymore */ + redundant->shadows--; + /* Delete the redundant shadow along with all its pages. */ vm_object_delete(redundant); - /* Delete the last link for the object */ - kfree(redundant_shlink); - return 0; } @@ -193,6 +193,7 @@ struct vm_obj_link *vm_objlink_create(void) if (!(vmo_link = kzalloc(sizeof(*vmo_link)))) return PTR_ERR(-ENOMEM); INIT_LIST_HEAD(&vmo_link->list); + INIT_LIST_HEAD(&vmo_link->linkref); return vmo_link; } @@ -214,7 +215,8 @@ struct vm_obj_link *vma_create_shadow(void) return 0; } vmo->flags = VM_OBJ_SHADOW; - vmo_link->obj = vmo; + + vm_link_object(vmo_link, vmo); return vmo_link; } @@ -244,6 +246,61 @@ struct page *copy_to_new_page(struct page *orig) return new; } +/* + * 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. + */ +int vma_drop_merge_delete(struct vm_obj_link *shadow_link, + struct vm_obj_link *orig_link) +{ + /* Can we can drop one link? */ + if (vm_object_is_subset(shadow_link->obj, orig_link->obj)) { + struct vm_object *dropped; + + printf("VM OBJECT is a subset of its shadow.\nShadow:\n"); + vm_object_print(shadow_link->obj); + printf("Original:\n"); + vm_object_print(orig_link->obj); + + /* We can drop the link to original object */ + dropped = vma_drop_link(shadow_link, orig_link); + printf("Dropped link to object:\n"); + vm_object_print(dropped); + orig_link = 0; + + /* + * Now decide on what to do with the dropped object: + * merge, delete, or do nothing. + */ + + /* If it's not a shadow, we're not to touch it */ + if (!(dropped->flags & VM_OBJ_SHADOW)) + return 0; + + /* If the object has no links left, we can delete it */ + if (dropped->nlinks == 0) { + BUG_ON(dropped->shadows != 0); + printf("Deleting object:\n"); + vm_object_print(dropped); + vm_object_delete(dropped); + } + + /* + * Only one link and one shadow left. + * Merge it with its only shadow + */ + if (dropped->nlinks == 1 && + dropped->shadows == 1) { + printf("Merging object:\n"); + vm_object_print(dropped); + vma_merge_object(dropped); + } + } + + return 0; +} + /* TODO: * - Why not allocate a swap descriptor in vma_create_shadow() rather than * a bare vm_object? It will be needed. @@ -259,8 +316,8 @@ struct page *copy_to_new_page(struct page *orig) */ struct page *copy_on_write(struct fault_data *fault) { - struct vm_obj_link *vmo_link, *shadow_link, *copier_link; - struct vm_object *vmo, *shadow; + struct vm_obj_link *vmo_link, *shadow_link; + struct vm_object *shadow; struct page *page, *new_page; struct vm_area *vma = fault->vma; unsigned long file_offset = fault_to_file_offset(fault); @@ -287,10 +344,10 @@ struct page *copy_on_write(struct fault_data *fault) /* Initialise the shadow */ shadow = shadow_link->obj; - shadow->refcnt = 1; shadow->orig_obj = vmo_link->obj; shadow->flags = VM_OBJ_SHADOW | VM_WRITE; shadow->pager = &swap_pager; + vmo_link->obj->shadows++; /* * Add the shadow in front of the original: @@ -303,18 +360,16 @@ struct page *copy_on_write(struct fault_data *fault) list_add(&shadow_link->list, &vma->vm_obj_list); /* Add object to original's shadower list */ - list_add(&shadow->shref, &shadow->orig_obj->shadowers); + list_add(&shadow->shref, &shadow->orig_obj->shdw_list); /* Add to global object list */ list_add(&shadow->list, &vm_object_list); - /* Shadow is the copier object */ - copier_link = shadow_link; } else { dprintf("No new shadows. Going to add to " "topmost r/w shadow object\n"); /* No new shadows, the topmost r/w vmo is the copier object */ - copier_link = vmo_link; + shadow_link = vmo_link; /* * We start page search on read-only objects. If the first @@ -347,7 +402,7 @@ struct page *copy_on_write(struct fault_data *fault) /* Update page details */ spin_lock(&new_page->lock); new_page->refcnt = 0; - new_page->owner = copier_link->obj; + new_page->owner = shadow_link->obj; new_page->offset = file_offset; new_page->virtual = 0; BUG_ON(!list_empty(&new_page->list)); @@ -362,40 +417,21 @@ struct page *copy_on_write(struct fault_data *fault) /* * Finished handling the actual fault, now check for possible - * shadow collapses. Does the copier completely shadow the one + * shadow collapses. Does the shadow completely shadow the one * underlying it? */ - if (!(vmo_link = vma_next_link(&copier_link->list, + if (!(vmo_link = vma_next_link(&shadow_link->list, &vma->vm_obj_list))) { /* Copier must have an object under it */ printf("Copier must have had an object under it!\n"); BUG(); } - - /* Compare whether page caches overlap */ - if (vm_object_is_subset(copier_link->obj, vmo_link->obj)) { - /* - * They do overlap, so keep reference to object but - * drop and delete the vma link. - */ - vmo = vmo_link->obj; - vma_drop_link(copier_link, vmo_link); - vmo_link = 0; - - /* - * vm object reference down to one - * and object is mergeable? - */ - if ((vmo->refcnt == 1) && - (vmo->flags != VM_OBJ_FILE)) - vma_merge_link(vmo_link); - } + vma_drop_merge_delete(shadow_link, vmo_link); } return new_page; } - /* * Handles the page fault, all entries here are assumed *legal* * faults, i.e. do_page_fault() should have already checked @@ -525,12 +561,12 @@ int vm_freeze_shadows(struct tcb *task) struct vm_obj_link, list); vmo = vmo_link->obj; - /* + /* * Is this a writeable shadow? * * The only R/W shadow in a vma object chain * can be the first one, so we don't check further - * objects if first one is not what we want. + * objects if first one is not what we want. */ if (!((vmo->flags & VM_OBJ_SHADOW) && (vmo->flags & VM_WRITE))) diff --git a/tasks/mm0/src/file.c b/tasks/mm0/src/file.c index 9b771fa..8f32939 100644 --- a/tasks/mm0/src/file.c +++ b/tasks/mm0/src/file.c @@ -121,7 +121,7 @@ int vfs_receive_sys_open(l4id_t sender, l4id_t opener, int fd, vm_file_to_vnum(vmfile) == vnum) { /* Add a reference to it from the task */ t->fd[fd].vmfile = vmfile; - vmfile->vm_obj.refcnt++; + vmfile->openers++; l4_ipc_return(0); return 0; } @@ -138,7 +138,7 @@ int vfs_receive_sys_open(l4id_t sender, l4id_t opener, int fd, vmfile->length = length; vmfile->vm_obj.pager = &file_pager; t->fd[fd].vmfile = vmfile; - vmfile->vm_obj.refcnt++; + vmfile->openers++; /* Add to global list */ list_add(&vmfile->vm_obj.list, &vm_file_list); @@ -365,6 +365,9 @@ int fd_close(l4id_t sender, int fd) if ((err = vfs_close(task->tid, fd)) < 0) return err; + /* Reduce file's opener count */ + task->fd[fd].vmfile->openers--; + task->fd[fd].vnum = 0; task->fd[fd].cursor = 0; task->fd[fd].vmfile = 0; diff --git a/tasks/mm0/src/mmap.c b/tasks/mm0/src/mmap.c index 4059fc7..ede9104 100644 --- a/tasks/mm0/src/mmap.c +++ b/tasks/mm0/src/mmap.c @@ -533,8 +533,11 @@ int do_mmap(struct vm_file *mapfile, unsigned long file_offset, kfree(new); return -ENOMEM; } - vmo_link->obj = &mapfile->vm_obj; - mapfile->vm_obj.refcnt++; + + /* Attach link to object */ + vm_link_object(vmo_link, &mapfile->vm_obj); + + /* ADd link to vma list */ list_add_tail(&vmo_link->list, &new->vm_obj_list); /* @@ -556,8 +559,7 @@ int do_mmap(struct vm_file *mapfile, unsigned long file_offset, kfree(vmo_link); return -ENOMEM; } - vmo_link2->obj = &dzero->vm_obj; - dzero->vm_obj.refcnt++; + vm_link_object(vmo_link2, &dzero->vm_obj); list_add_tail(&vmo_link2->list, &new->vm_obj_list); } diff --git a/tasks/mm0/src/shm.c b/tasks/mm0/src/shm.c index 1c6873c..e4e4290 100644 --- a/tasks/mm0/src/shm.c +++ b/tasks/mm0/src/shm.c @@ -81,7 +81,7 @@ static void *do_shmat(struct vm_file *shm_file, void *shm_addr, int shmflg, */ /* First user? */ - if (!shm_file->vm_obj.refcnt) + if (!shm_file->vm_obj.nlinks) if (mmap_address_validate(task, (unsigned long)shm_addr, vmflags)) shm->shm_addr = shm_addr; diff --git a/tasks/mm0/src/vm_object.c b/tasks/mm0/src/vm_object.c index 80e15ed..705d887 100644 --- a/tasks/mm0/src/vm_object.c +++ b/tasks/mm0/src/vm_object.c @@ -27,9 +27,9 @@ void vm_object_print(struct vm_object *vmo) { struct vm_file *f; - printf("Object type: %s %s. Refs: %d. Pages in cache: %d.\n", + printf("Object type: %s %s. links: %d, shadows: %d, Pages in cache: %d.\n", vmo->flags & VM_WRITE ? "writeable" : "read-only", - vmo->flags & VM_OBJ_FILE ? "file" : "shadow", vmo->refcnt, + vmo->flags & VM_OBJ_FILE ? "file" : "shadow", vmo->nlinks, vmo->shadows, vmo->npages); if (vmo->flags & VM_OBJ_FILE) { f = vm_object_to_file(vmo); @@ -63,8 +63,9 @@ struct vm_object *vm_object_init(struct vm_object *obj) { INIT_LIST_HEAD(&obj->list); INIT_LIST_HEAD(&obj->shref); - INIT_LIST_HEAD(&obj->shadowers); + INIT_LIST_HEAD(&obj->shdw_list); INIT_LIST_HEAD(&obj->page_cache); + INIT_LIST_HEAD(&obj->link_list); return obj; } @@ -123,8 +124,10 @@ int vm_object_delete(struct vm_object *vmo) list_del(&vmo->list); /* Check any references */ - BUG_ON(vmo->refcnt); - BUG_ON(!list_empty(&vmo->shadowers)); + BUG_ON(vmo->nlinks); + BUG_ON(vmo->shadows); + BUG_ON(!list_empty(&vmo->shdw_list)); + BUG_ON(!list_empty(&vmo->link_list)); BUG_ON(!list_empty(&vmo->page_cache)); /* Obtain and free via the base object */