More updates to vm object handling.

Added a list of links for vm objects so they can follow
the links that point at them.

More succinct handling of the case where a vm object
is dropped. Now depending on the object's number of link
references and shadow references, upon a drop it could
either be merged, deleted or kept.

Added opener reference count for vm files. Now files
have opener count, objects have shadow and link count.
Link count is also meaningful for how many tasks have
mmap'ed that object.
This commit is contained in:
Bahadir Balban
2008-08-29 12:35:07 +03:00
parent 2217349b60
commit 63e9d059c8
7 changed files with 142 additions and 76 deletions

View File

@@ -121,9 +121,11 @@ struct vm_pager {
*/ */
struct vm_object { struct vm_object {
int npages; /* Number of pages in memory */ 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 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 */ struct vm_object *orig_obj; /* Original object that this one shadows */
unsigned int flags; /* Defines the type and flags of the object */ unsigned int flags; /* Defines the type and flags of the object */
struct list_head list; /* List of all vm objects in memory */ 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. */ /* In memory representation of either a vfs file, a device. */
struct vm_file { struct vm_file {
int openers;
unsigned long length; unsigned long length;
unsigned int type; unsigned int type;
struct list_head list; struct list_head list;
@@ -143,9 +146,28 @@ struct vm_file {
/* To create per-vma vm_object lists */ /* To create per-vma vm_object lists */
struct vm_obj_link { struct vm_obj_link {
struct list_head list; struct list_head list;
struct list_head linkref;
struct vm_object *obj; 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) #define vm_object_to_file(obj) container_of(obj, struct vm_file, vm_obj)
/* /*

View File

@@ -38,8 +38,8 @@ int copy_vmas(struct tcb *to, struct tcb *from)
/* Create a new link */ /* Create a new link */
new_link = vm_objlink_create(); new_link = vm_objlink_create();
/* Copy object field from original link. */ /* Link object with new link */
new_link->obj = vmo_link->obj; vm_link_object(new_link, vmo_link->obj);
/* Add the new link to vma in object order */ /* Add the new link to vma in object order */
list_add_tail(&new_link->list, &new_vma->vm_obj_list); list_add_tail(&new_link->list, &new_vma->vm_obj_list);

View File

@@ -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. */ /* 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_object *vma_drop_link(struct vm_obj_link *shadower_link,
struct vm_obj_link *orig_link) struct vm_obj_link *orig_link)
{ {
struct vm_object *dropped = orig_link->obj;
/* Remove object link from vma's list */ /* Remove object link from vma's list */
list_del(&orig_link->list); list_del(&orig_link->list);
/* Reduce object's ref count */ /* Unlink the link from object */
orig_link->obj->refcnt--; vm_unlink_object(orig_link);
/* /*
* Refcount could go as low as 1 but not zero because shortly * Reduce object's shadow count since its not shadowed
* after it goes down to one, it is removed from the link * by this shadower anymore.
* chain so it can never exist with a refcount less than 1
* in the chain.
*/ */
if (orig_link->obj->refcnt < 1) { dropped->shadows--;
printf("%s: Shadower:\n", __FUNCTION__);
vm_object_print(shadower_link->obj);
printf("%s: Original:\n", __FUNCTION__);
vm_object_print(orig_link->obj);
BUG();
}
/* /*
* Remove the shadower from original's shadower list. * 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 */ /* Delete the original link */
kfree(orig_link); kfree(orig_link);
return 0; return dropped;
} }
/* /*
@@ -135,18 +128,19 @@ int vm_object_is_subset(struct vm_object *copier,
* Front Redundant Next * Front Redundant Next
* Shadow Shadow Object (E.g. shadow or file) * 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 */ /* The redundant shadow object */
struct vm_object *redundant = redundant_shlink->obj;
struct vm_object *front; /* Shadow in front of redundant */ struct vm_object *front; /* Shadow in front of redundant */
struct vm_obj_link *last_link;
struct page *p1, *p2; struct page *p1, *p2;
/* Check refcount is really 1 */ /* Check link and shadow count is really 1 */
BUG_ON(redundant->refcnt != 1); BUG_ON(redundant->nlinks != 1);
BUG_ON(redundant->shadows != 1);
/* Get the last shadower object in front */ /* Get the last shadower object in front */
front = list_entry(redundant->shadowers.next, front = list_entry(redundant->shdw_list.next,
struct vm_object, shref); struct vm_object, shref);
/* Move all non-intersecting pages to front shadow. */ /* 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); list_del_init(&front->shref);
/* Check that there really was one shadower of redundant left */ /* 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 */ /* Redundant won't be a shadow of its next object */
list_del(&redundant->shref); list_del(&redundant->shref);
/* Front is now a shadow of redundant's next object */ /* 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; 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. */ /* Delete the redundant shadow along with all its pages. */
vm_object_delete(redundant); vm_object_delete(redundant);
/* Delete the last link for the object */
kfree(redundant_shlink);
return 0; return 0;
} }
@@ -193,6 +193,7 @@ struct vm_obj_link *vm_objlink_create(void)
if (!(vmo_link = kzalloc(sizeof(*vmo_link)))) if (!(vmo_link = kzalloc(sizeof(*vmo_link))))
return PTR_ERR(-ENOMEM); return PTR_ERR(-ENOMEM);
INIT_LIST_HEAD(&vmo_link->list); INIT_LIST_HEAD(&vmo_link->list);
INIT_LIST_HEAD(&vmo_link->linkref);
return vmo_link; return vmo_link;
} }
@@ -214,7 +215,8 @@ struct vm_obj_link *vma_create_shadow(void)
return 0; return 0;
} }
vmo->flags = VM_OBJ_SHADOW; vmo->flags = VM_OBJ_SHADOW;
vmo_link->obj = vmo;
vm_link_object(vmo_link, vmo);
return vmo_link; return vmo_link;
} }
@@ -244,6 +246,61 @@ struct page *copy_to_new_page(struct page *orig)
return new; 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: /* TODO:
* - Why not allocate a swap descriptor in vma_create_shadow() rather than * - Why not allocate a swap descriptor in vma_create_shadow() rather than
* a bare vm_object? It will be needed. * 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 page *copy_on_write(struct fault_data *fault)
{ {
struct vm_obj_link *vmo_link, *shadow_link, *copier_link; struct vm_obj_link *vmo_link, *shadow_link;
struct vm_object *vmo, *shadow; struct vm_object *shadow;
struct page *page, *new_page; struct page *page, *new_page;
struct vm_area *vma = fault->vma; struct vm_area *vma = fault->vma;
unsigned long file_offset = fault_to_file_offset(fault); 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 */ /* Initialise the shadow */
shadow = shadow_link->obj; shadow = shadow_link->obj;
shadow->refcnt = 1;
shadow->orig_obj = vmo_link->obj; shadow->orig_obj = vmo_link->obj;
shadow->flags = VM_OBJ_SHADOW | VM_WRITE; shadow->flags = VM_OBJ_SHADOW | VM_WRITE;
shadow->pager = &swap_pager; shadow->pager = &swap_pager;
vmo_link->obj->shadows++;
/* /*
* Add the shadow in front of the original: * 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); list_add(&shadow_link->list, &vma->vm_obj_list);
/* Add object to original's shadower 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 */ /* Add to global object list */
list_add(&shadow->list, &vm_object_list); list_add(&shadow->list, &vm_object_list);
/* Shadow is the copier object */
copier_link = shadow_link;
} else { } else {
dprintf("No new shadows. Going to add to " dprintf("No new shadows. Going to add to "
"topmost r/w shadow object\n"); "topmost r/w shadow object\n");
/* No new shadows, the topmost r/w vmo is the copier object */ /* 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 * 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 */ /* Update page details */
spin_lock(&new_page->lock); spin_lock(&new_page->lock);
new_page->refcnt = 0; new_page->refcnt = 0;
new_page->owner = copier_link->obj; new_page->owner = shadow_link->obj;
new_page->offset = file_offset; new_page->offset = file_offset;
new_page->virtual = 0; new_page->virtual = 0;
BUG_ON(!list_empty(&new_page->list)); 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 * 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? * underlying it?
*/ */
if (!(vmo_link = vma_next_link(&copier_link->list, if (!(vmo_link = vma_next_link(&shadow_link->list,
&vma->vm_obj_list))) { &vma->vm_obj_list))) {
/* Copier must have an object under it */ /* Copier must have an object under it */
printf("Copier must have had an object under it!\n"); printf("Copier must have had an object under it!\n");
BUG(); BUG();
} }
vma_drop_merge_delete(shadow_link, vmo_link);
/* 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);
}
} }
return new_page; return new_page;
} }
/* /*
* Handles the page fault, all entries here are assumed *legal* * Handles the page fault, all entries here are assumed *legal*
* faults, i.e. do_page_fault() should have already checked * faults, i.e. do_page_fault() should have already checked

View File

@@ -121,7 +121,7 @@ int vfs_receive_sys_open(l4id_t sender, l4id_t opener, int fd,
vm_file_to_vnum(vmfile) == vnum) { vm_file_to_vnum(vmfile) == vnum) {
/* Add a reference to it from the task */ /* Add a reference to it from the task */
t->fd[fd].vmfile = vmfile; t->fd[fd].vmfile = vmfile;
vmfile->vm_obj.refcnt++; vmfile->openers++;
l4_ipc_return(0); l4_ipc_return(0);
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->length = length;
vmfile->vm_obj.pager = &file_pager; vmfile->vm_obj.pager = &file_pager;
t->fd[fd].vmfile = vmfile; t->fd[fd].vmfile = vmfile;
vmfile->vm_obj.refcnt++; vmfile->openers++;
/* Add to global list */ /* Add to global list */
list_add(&vmfile->vm_obj.list, &vm_file_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) if ((err = vfs_close(task->tid, fd)) < 0)
return err; return err;
/* Reduce file's opener count */
task->fd[fd].vmfile->openers--;
task->fd[fd].vnum = 0; task->fd[fd].vnum = 0;
task->fd[fd].cursor = 0; task->fd[fd].cursor = 0;
task->fd[fd].vmfile = 0; task->fd[fd].vmfile = 0;

View File

@@ -533,8 +533,11 @@ int do_mmap(struct vm_file *mapfile, unsigned long file_offset,
kfree(new); kfree(new);
return -ENOMEM; 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); 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); kfree(vmo_link);
return -ENOMEM; return -ENOMEM;
} }
vmo_link2->obj = &dzero->vm_obj; vm_link_object(vmo_link2, &dzero->vm_obj);
dzero->vm_obj.refcnt++;
list_add_tail(&vmo_link2->list, &new->vm_obj_list); list_add_tail(&vmo_link2->list, &new->vm_obj_list);
} }

View File

@@ -81,7 +81,7 @@ static void *do_shmat(struct vm_file *shm_file, void *shm_addr, int shmflg,
*/ */
/* First user? */ /* First user? */
if (!shm_file->vm_obj.refcnt) if (!shm_file->vm_obj.nlinks)
if (mmap_address_validate(task, (unsigned long)shm_addr, if (mmap_address_validate(task, (unsigned long)shm_addr,
vmflags)) vmflags))
shm->shm_addr = shm_addr; shm->shm_addr = shm_addr;

View File

@@ -27,9 +27,9 @@ void vm_object_print(struct vm_object *vmo)
{ {
struct vm_file *f; 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_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); vmo->npages);
if (vmo->flags & VM_OBJ_FILE) { if (vmo->flags & VM_OBJ_FILE) {
f = vm_object_to_file(vmo); 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->list);
INIT_LIST_HEAD(&obj->shref); 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->page_cache);
INIT_LIST_HEAD(&obj->link_list);
return obj; return obj;
} }
@@ -123,8 +124,10 @@ int vm_object_delete(struct vm_object *vmo)
list_del(&vmo->list); list_del(&vmo->list);
/* Check any references */ /* Check any references */
BUG_ON(vmo->refcnt); BUG_ON(vmo->nlinks);
BUG_ON(!list_empty(&vmo->shadowers)); 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)); BUG_ON(!list_empty(&vmo->page_cache));
/* Obtain and free via the base object */ /* Obtain and free via the base object */