Multiple updates on sleeping, vma dropping and thread suspend.

- Updated sleeping paths such that a task is atomically put into
  a runqueue and made RUNNABLE, or removed from a runqueue and made SLEEPING.
- Modified vma dropping sources to handle both copy_on_write() and exit() cases
  in a common function.
- Added the first infrastructure to have a pager to suspend a task and wait for
  suspend completion from the scheduler.
This commit is contained in:
Bahadir Balban
2008-10-13 12:22:10 +03:00
parent f6d0a79298
commit 0db0f7e334
23 changed files with 416 additions and 200 deletions

View File

@@ -30,4 +30,5 @@ int pager_update_stats(struct tcb *sender, unsigned long vnum,
int pager_notify_fork(struct tcb *sender, l4id_t parid,
l4id_t chid, unsigned long utcb_address);
int pager_notify_exit(struct tcb *sender, l4id_t tid);
#endif /* __FS0_SYSCALLS_H__ */

View File

@@ -113,6 +113,9 @@ void handle_fs_requests(void)
ret = pager_notify_fork(sender, (l4id_t)mr[0], (l4id_t)mr[1],
(unsigned long)mr[2]);
break;
case L4_IPC_TAG_NOTIFY_EXIT:
ret = pager_notify_exit(sender, (l4id_t)mr[0]);
break;
default:
printf("%s: Unrecognised ipc tag (%d) "

View File

@@ -33,46 +33,6 @@ struct tcb *find_task(int tid)
return 0;
}
/*
* Asks pager to send information about currently running tasks. Since this is
* called during initialisation, there can't be that many, so we assume message
* registers are sufficient. First argument tells how many there are, the rest
* tells the tids.
*/
int receive_pager_taskdata_orig(l4id_t *tdata)
{
int err;
/* Make the actual ipc call */
if ((err = l4_sendrecv(PAGER_TID, PAGER_TID,
L4_IPC_TAG_TASKDATA)) < 0) {
printf("%s: L4 IPC Error: %d.\n", __FUNCTION__, err);
return err;
}
/* Check if call itself was successful */
if ((err = l4_get_retval()) < 0) {
printf("%s: Error: %d.\n", __FUNCTION__, err);
return err;
}
/* Read total number of tasks. Note already used one mr. */
if ((tdata[0] = (l4id_t)read_mr(L4SYS_ARG0)) >= MR_UNUSED_TOTAL) {
printf("%s: Error: Too many tasks to read. Won't fit in mrs.\n",
__FUNCTION__);
BUG();
}
// printf("%s: %d Total tasks.\n", __FUNCTION__, tdata[0]);
/* Now read task ids. */
for (int i = 0; i < (int)tdata[0]; i++) {
tdata[1 + i] = (l4id_t)read_mr(L4SYS_ARG1 + i);
// printf("%s: Task id: %d\n", __FUNCTION__, tdata[1 + i]);
}
return 0;
}
/* Allocate a task struct and initialise it */
struct tcb *create_tcb(void)
{
@@ -89,6 +49,15 @@ struct tcb *create_tcb(void)
return t;
}
void destroy_tcb(struct tcb *t)
{
kfree(t->fdpool);
list_del(&t->list);
tcb_head.total--;
kfree(t);
}
/*
* Attaches to task's utcb. FIXME: Add SHM_RDONLY and test it.
* FIXME: This calls the pager and is a potential for deadlock
@@ -156,6 +125,24 @@ int pager_notify_fork(struct tcb *sender, l4id_t parid,
}
/*
* Pager tells us that a task is exiting by this call.
*/
int pager_notify_exit(struct tcb *sender, l4id_t tid)
{
struct tcb *task;
printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
BUG_ON(!(task = find_task(tid)));
destroy_tcb(task);
printf("%s/%s: Exiting...\n", __TASKNAME__, __FUNCTION__);
return 0;
}
/* Read task information into the utcb page, since it won't fit into mrs. */
struct task_data_head *receive_pager_taskdata(void)
{

View File

@@ -59,6 +59,6 @@
#define L4_IPC_TAG_PAGER_CLOSE 44 /* Pager notifies vfs of file close */
#define L4_IPC_TAG_PAGER_UPDATE_STATS 45 /* Pager updates file stats in vfs */
#define L4_IPC_TAG_NOTIFY_FORK 46 /* Pager notifies vfs of process fork */
#define L4_IPC_TAG_NOTIFY_EXIT 46 /* Pager notifies vfs of process exit */
#define L4_IPC_TAG_NOTIFY_EXIT 47 /* Pager notifies vfs of process exit */
#endif /* __IPCDEFS_H__ */

View File

@@ -121,10 +121,12 @@ int task_setup_regions(struct vm_file *file, struct tcb *task,
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);
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);
struct tcb *task_create(struct tcb *orig,
struct task_ids *ids,
unsigned int ctrl_flags,

View File

@@ -139,9 +139,9 @@ struct vm_object {
/* In memory representation of either a vfs file, a device. */
struct vm_file {
int openers;
struct list_head list;
unsigned long length;
unsigned int type;
struct list_head list;
struct vm_object vm_obj;
void *priv_data; /* Device pagers use to access device info */
};
@@ -230,6 +230,7 @@ struct vm_file *vm_file_alloc_init(void);
struct vm_object *vm_object_alloc_init(void);
struct vm_object *vm_object_create(void);
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);
@@ -254,4 +255,7 @@ static inline void task_add_vma(struct tcb *task, struct vm_area *vma)
/* Main page fault entry point */
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);
#endif /* __VM_AREA_H__ */

View File

@@ -22,7 +22,7 @@ int vfs_notify_fork(struct tcb *child, struct tcb *parent)
{
int err = 0;
printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
// printf("%s/%s\n", __TASKNAME__, __FUNCTION__);
l4_save_ipcregs();

View File

@@ -60,36 +60,18 @@ struct vm_obj_link *vma_next_link(struct list_head *link,
}
/* Unlinks orig_link from its vma and deletes it but keeps the object. */
struct vm_object *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 *link)
{
struct vm_object *dropped = orig_link->obj;
struct vm_object *dropped;
/* Remove object link from vma's list */
list_del(&orig_link->list);
list_del(&link->list);
/* Unlink the link from object */
vm_unlink_object(orig_link);
/*
* Reduce object's shadow count since its not shadowed
* by this shadower anymore.
*
* FIXME: Is every object drop because of shadows???
* What about exiting tasks?
*
*/
dropped->shadows--;
/*
* Remove the shadower from original's shadower list.
* We know shadower is deleted from original's list
* because each shadow can shadow a single object.
*/
list_del(&shadower_link->obj->shref);
dropped = vm_unlink_object(link);
/* Delete the original link */
kfree(orig_link);
kfree(link);
return dropped;
}
@@ -128,8 +110,8 @@ int vm_object_is_subset(struct vm_object *shadow,
static inline int vm_object_is_droppable(struct vm_object *shadow,
struct vm_object *original)
{
if (vm_object_is_subset(shadow, original)
&& (original->flags & VM_OBJ_SHADOW))
if (shadow->npages == original->npages &&
(original->flags & VM_OBJ_SHADOW))
return 1;
else
return 0;
@@ -191,8 +173,9 @@ int vma_merge_object(struct vm_object *redundant)
/* 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);
/* Drop the last link to the object */
vma_drop_link(last_link);
/* Redundant shadow has no shadows anymore */
redundant->shadows--;
@@ -266,54 +249,136 @@ struct page *copy_to_new_page(struct page *orig)
* 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.
*/
int vma_drop_merge_delete(struct vm_obj_link *shadow_link,
struct vm_obj_link *orig_link)
int vma_drop_merge_delete(struct vm_area *vma, struct vm_obj_link *link)
{
/* Can we drop one link? */
if (vm_object_is_droppable(shadow_link->obj, orig_link->obj)) {
struct vm_object *dropped;
struct vm_obj_link *prev, *next;
struct vm_object *obj;
dprintf("VM OBJECT is a subset of its shadow.\nShadow:\n");
vm_object_print(shadow_link->obj);
dprintf("Original:\n");
vm_object_print(orig_link->obj);
/* Get previous and next links, if they exist */
prev = (link->list.prev == &vma->vm_obj_list) ? 0 :
list_entry(link->list.prev, struct vm_obj_link, list);
/* We can drop the link to original object */
dropped = vma_drop_link(shadow_link, orig_link);
dprintf("Dropped link to object:\n");
vm_object_print(dropped);
orig_link = 0;
next = (link->list.next == &vma->vm_obj_list) ? 0 :
list_entry(link->list.next, struct vm_obj_link, list);
/* 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 (prev) {
BUG_ON(!(prev->obj->flags & VM_OBJ_SHADOW));
obj->shadows--;
list_del_init(&prev->obj->shref);
}
/*
* 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);
next->obj->shadows--;
list_del_init(&obj->shref);
/*
* Now decide on what to do with the dropped object:
* merge, delete, or do nothing.
* Furthermore, if there was an object in front,
* that means front will become a shadow of after.
*/
/* 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);
dprintf("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) {
dprintf("Merging object:\n");
vm_object_print(dropped);
vma_merge_object(dropped);
if (prev) {
list_add(&prev->obj->shref,
&next->obj->shdw_list);
prev->obj->orig_obj = next->obj;
next->obj->shadows++;
}
}
/* Now deal with the object itself */
/* If it's not a shadow, we're not to touch it.
*
* TODO: In the future we can check if a vm_file's
* openers are 0 and take action here. (i.e. keep,
* delete or swap it)
*/
if (!(obj->flags & VM_OBJ_SHADOW))
return 0;
/* If the object has no links left, we can delete it */
if (obj->nlinks == 0) {
BUG_ON(obj->shadows != 0);
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->nlinks == 1 &&
obj->shadows == 1) {
dprintf("Merging object:\n");
vm_object_print(obj);
vma_merge_object(obj);
}
return 0;
}
/*
* A scenario that pretty much covers every exit() case.
*
* T = vma on a unique task
* l = link
* Sobj = Shadow object
* Fobj = File object
*
* Every l links to the object on the nearest
* row to it and on the same column.
*
* l l l l l l T
* Sobj Sobj
*
* Sobj Sobj Sobj Fobj
*
* Sobj Sobj
* l l l l l l T
*
* l l l l l l l T
* Sobj
*
*/
/* This version is used when exiting. */
int vma_drop_merge_delete_all(struct vm_area *vma)
{
struct vm_obj_link *vmo_link;
/* Get the first link on the vma */
BUG_ON(list_empty(&vma->vm_obj_list));
vmo_link = list_entry(vma->vm_obj_list.next,
struct vm_obj_link, list);
/* Traverse and get rid of all links */
do {
vma_drop_merge_delete(vma, vmo_link);
} while((vmo_link = vma_next_link(&vmo_link->list,
&vma->vm_obj_list)));
return 0;
}
@@ -421,11 +486,11 @@ struct page *copy_on_write(struct fault_data *fault)
/* Update page details */
spin_lock(&new_page->lock);
BUG_ON(!list_empty(&new_page->list));
new_page->refcnt = 0;
new_page->owner = shadow_link->obj;
new_page->offset = file_offset;
new_page->virtual = 0;
BUG_ON(!list_empty(&new_page->list));
spin_unlock(&page->lock);
/* Add the page to owner's list of in-memory pages */
@@ -446,7 +511,8 @@ struct page *copy_on_write(struct fault_data *fault)
printf("Copier must have had an object under it!\n");
BUG();
}
vma_drop_merge_delete(shadow_link, vmo_link);
if (vm_object_is_droppable(shadow_link->obj, vmo_link->obj))
vma_drop_merge_delete(vma, vmo_link);
}
return new_page;

View File

@@ -158,8 +158,11 @@ int do_open(struct tcb *task, int fd, unsigned long vnum, unsigned long length)
task->files->fd[fd].vmfile = vmfile;
vmfile->openers++;
/* Add to global list */
list_add(&vmfile->vm_obj.list, &vm_file_list);
/* Add to file list */
list_add(&vmfile->list, &vm_file_list);
/* Add to object list */
list_add(&vmfile->vm_obj.list, &vm_object_list);
return 0;
}
@@ -399,7 +402,8 @@ int do_close(struct tcb *task, int fd)
return err;
/* Reduce file's opener count */
task->files->fd[fd].vmfile->openers--;
if (!(--task->files->fd[fd].vmfile->openers))
vm_file_delete(task->files->fd[fd].vmfile);
task->files->fd[fd].vnum = 0;
task->files->fd[fd].cursor = 0;

View File

@@ -46,18 +46,15 @@ int default_release_pages(struct vm_object *vm_obj)
struct page *p, *n;
list_for_each_entry_safe(p, n, &vm_obj->page_cache, list) {
list_del(&p->list);
list_del_init(&p->list);
BUG_ON(p->refcnt);
/* Reinitialise the page */
page_init(p);
/* Return page back to allocator */
free_page((void *)page_to_phys(p));
/*
* Reset the page structure.
* No freeing for page_array pages
*/
memset(p, 0, sizeof(*p));
/* Reduce object page count */
BUG_ON(--vm_obj->npages < 0);
}
@@ -103,7 +100,7 @@ int file_page_out(struct vm_object *vm_obj, unsigned long page_offset)
/* Unmap it from vfs */
l4_unmap(vaddr, 1, VFS_TID);
/* Update page details */
/* Clear dirty flag */
page->flags &= ~VM_DIRTY;
return 0;

View File

@@ -197,6 +197,7 @@ struct vm_file *shm_new(key_t key, unsigned long npages)
shm_file->vm_obj.pager = &swap_pager;
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);

View File

@@ -104,6 +104,16 @@ struct tcb *tcb_alloc_init(unsigned int flags)
return task;
}
/* 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--;
kfree(task);
return 0;
}
/*
* Copy all vmas from the given task and populate each with
@@ -147,6 +157,27 @@ int copy_vmas(struct tcb *to, struct tcb *from)
return 0;
}
/*
* Traverse all vmas, release all links to vm_objects.
* Used when a task or thread group with a shared vm is exiting.
*/
int task_release_vmas(struct task_vma_head *vma_head)
{
struct vm_area *vma, *n;
list_for_each_entry_safe(vma, n, &vma_head->list, list) {
/* Release all links */
vma_drop_merge_delete_all(vma);
/* Delete the vma from task's vma list */
list_del(&vma->list);
/* Free the vma */
kfree(vma);
}
return 0;
}
int copy_tcb(struct tcb *to, struct tcb *from, unsigned int flags)
{
/* Copy program segment boundary information */

View File

@@ -133,6 +133,9 @@ int vm_object_delete(struct vm_object *vmo)
/* Obtain and free via the base object */
if (vmo->flags & VM_OBJ_FILE) {
f = vm_object_to_file(vmo);
BUG_ON(!list_empty(&f->list));
if (f->priv_data)
kfree(f->priv_data);
kfree(f);
} else if (vmo->flags & VM_OBJ_SHADOW)
kfree(vmo);
@@ -141,3 +144,12 @@ int vm_object_delete(struct vm_object *vmo)
return 0;
}
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);
}