diff options
author | jdike <jdike> | 2004-01-19 21:02:31 +0000 |
---|---|---|
committer | jdike <jdike> | 2004-01-19 21:02:31 +0000 |
commit | 744f53dd89458c1a25cbfa4d053faa6f98775988 (patch) | |
tree | d4b3339192545341fc0b6935f6b875e0456497e1 | |
parent | a70c9e4349f37ee495ef09eaf257549901b4cfd4 (diff) | |
download | uml-history-744f53dd89458c1a25cbfa4d053faa6f98775988.tar.gz |
Fixed the bug caused by pages remaining mapped after their device has been
closed.
-rw-r--r-- | arch/um/drivers/ubd_kern.c | 18 | ||||
-rw-r--r-- | arch/um/include/mem.h | 1 | ||||
-rw-r--r-- | arch/um/kernel/physmem.c | 133 |
3 files changed, 128 insertions, 24 deletions
diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index d82f409..b94be36 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c @@ -733,12 +733,17 @@ __initcall(ubd_init); static void ubd_close(struct ubd *dev) { + if(ubd_do_mmap) + physmem_forget_descriptor(dev->fd); os_close_file(dev->fd); - if(dev->cow.file != NULL) { - os_close_file(dev->cow.fd); - vfree(dev->cow.bitmap); - dev->cow.bitmap = NULL; - } + if(dev->cow.file != NULL) + return; + + if(ubd_do_mmap) + physmem_forget_descriptor(dev->cow.fd); + os_close_file(dev->cow.fd); + vfree(dev->cow.bitmap); + dev->cow.bitmap = NULL; } static int ubd_open_dev(struct ubd *dev) @@ -781,7 +786,8 @@ static int ubd_open_dev(struct ubd *dev) err = read_cow_bitmap(dev->fd, dev->cow.bitmap, dev->cow.bitmap_offset, dev->cow.bitmap_len); - if(err < 0) goto error; + if(err < 0) + goto error; flags = dev->openflags; flags.w = 0; diff --git a/arch/um/include/mem.h b/arch/um/include/mem.h index 053375d..7706ede 100644 --- a/arch/um/include/mem.h +++ b/arch/um/include/mem.h @@ -13,6 +13,7 @@ extern int phys_mapping(unsigned long phys, __u64 *offset_out); extern int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w); extern int is_remapped(void *virt); extern int physmem_remove_mapping(void *virt); +extern void physmem_forget_descriptor(int fd); #endif diff --git a/arch/um/kernel/physmem.c b/arch/um/kernel/physmem.c index 787c59c..5509c58 100644 --- a/arch/um/kernel/physmem.c +++ b/arch/um/kernel/physmem.c @@ -31,6 +31,7 @@ struct phys_desc { __u64 offset; void *virt; unsigned long phys; + struct list_head list; }; struct virtmem_table virtmem_hash; @@ -49,16 +50,59 @@ static int virt_hash(void *virt) DEF_HASH(static, virtmem, struct phys_desc, virt_ptrs, void *, virt, virt_cmp, virt_hash); +LIST_HEAD(descriptor_mappings); + +struct desc_mapping { + int fd; + struct list_head list; + struct list_head pages; +}; + +static struct desc_mapping *find_mapping(int fd) +{ + struct desc_mapping *desc; + struct list_head *ele; + + list_for_each(ele, &descriptor_mappings){ + desc = list_entry(ele, struct desc_mapping, list); + if(desc->fd == fd) + return(desc); + } + + return(NULL); +} + +static struct desc_mapping *descriptor_mapping(int fd) +{ + struct desc_mapping *desc; + + desc = find_mapping(fd); + if(desc != NULL) + return(desc); + + desc = kmalloc(sizeof(*desc), GFP_ATOMIC); + if(desc == NULL) + return(NULL); + + *desc = ((struct desc_mapping) + { .fd = fd, + .list = LIST_HEAD_INIT(desc->list), + .pages = LIST_HEAD_INIT(desc->pages) }); + list_add(&desc->list, &descriptor_mappings); + + return(desc); +} + int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w) { + struct desc_mapping *fd_maps; struct phys_desc *desc; unsigned long phys; int err; - virt = (void *) ((unsigned long) virt & PAGE_MASK); - err = os_map_memory(virt, fd, offset, PAGE_SIZE, 1, w, 0); - if(err) - goto out; + fd_maps = descriptor_mapping(fd); + if(fd_maps == NULL) + return(-ENOMEM); phys = __pa(virt); if(find_virtmem_hash(&virtmem_hash, virt) != NULL) @@ -69,45 +113,98 @@ int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w) if(desc == NULL) goto out; - *desc = ((struct phys_desc) { .virt_ptrs = { NULL, NULL }, - .fd = fd, - .offset = offset, - .virt = virt, - .phys = __pa(virt) }); + *desc = ((struct phys_desc) + { .virt_ptrs = { NULL, NULL }, + .fd = fd, + .offset = offset, + .virt = virt, + .phys = __pa(virt), + .list = LIST_HEAD_INIT(desc->list) }); insert_virtmem_hash(&virtmem_hash, desc); - err = 0; + + list_add(&desc->list, &fd_maps->pages); + + virt = (void *) ((unsigned long) virt & PAGE_MASK); + err = os_map_memory(virt, fd, offset, PAGE_SIZE, 1, w, 0); + if(!err) + goto out; + + remove_virtmem_hash(&virtmem_hash, desc); + kfree(desc); out: return(err); } static int physmem_fd = -1; -int physmem_remove_mapping(void *virt) +static void remove_mapping(struct phys_desc *desc) { - struct phys_desc *desc; + void *virt = desc->virt; int err; - virt = (void *) ((unsigned long) virt & PAGE_MASK); - desc = find_virtmem_hash(&virtmem_hash, virt); - if(desc == NULL) - return(0); - remove_virtmem_hash(&virtmem_hash, desc); + list_del(&desc->list); kfree(desc); err = os_map_memory(virt, physmem_fd, __pa(virt), PAGE_SIZE, 1, 1, 0); if(err) panic("Failed to unmap block device page from physical memory, " "errno = %d", -err); +} + +int physmem_remove_mapping(void *virt) +{ + struct phys_desc *desc; + + virt = (void *) ((unsigned long) virt & PAGE_MASK); + desc = find_virtmem_hash(&virtmem_hash, virt); + if(desc == NULL) + return(0); + + remove_mapping(desc); return(1); } +void physmem_forget_descriptor(int fd) +{ + struct desc_mapping *desc; + struct phys_desc *page; + struct list_head *ele, *next; + __u64 offset; + void *addr; + int err; + + desc = find_mapping(fd); + if(desc == NULL) + return; + + list_for_each_safe(ele, next, &desc->pages){ + page = list_entry(ele, struct phys_desc, list); + offset = page->offset; + addr = page->virt; + remove_mapping(page); + err = os_seek_file(fd, offset); + if(err) + panic("physmem_forget_descriptor - failed to seek " + "to %lld in fd %d, error = %d\n", + offset, fd, -err); + err = os_read_file(fd, addr, PAGE_SIZE); + if(err < 0) + panic("physmem_forget_descriptor - failed to read " + "from fd %d to 0x%p, error = %d\n", + fd, addr, -err); + } + + list_del(&desc->list); + kfree(desc); +} + void arch_free_page(struct page *page, int order) { void *virt; int i; - for(i = 0; i < 1 << order; i++){ + for(i = 0; i < (1 << order); i++){ virt = __va(page_to_phys(page + i)); physmem_remove_mapping(virt); } |