summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjdike <jdike>2004-01-19 21:02:31 +0000
committerjdike <jdike>2004-01-19 21:02:31 +0000
commit744f53dd89458c1a25cbfa4d053faa6f98775988 (patch)
treed4b3339192545341fc0b6935f6b875e0456497e1
parenta70c9e4349f37ee495ef09eaf257549901b4cfd4 (diff)
downloaduml-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.c18
-rw-r--r--arch/um/include/mem.h1
-rw-r--r--arch/um/kernel/physmem.c133
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);
}