X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fum%2Fkernel%2Fphysmem.c;h=22c40d3085f7f838302c2a92e0e66728c60d86fd;hb=16c70f8c1b54b61c3b951b6fb220df250fe09b32;hp=d0e0f50dce90e6b281cfef2700e25b5eaa342a9b;hpb=c449269f45c2cdf53af08c8d0af37472f66539d9;p=linux-2.6.git diff --git a/arch/um/kernel/physmem.c b/arch/um/kernel/physmem.c index d0e0f50dc..22c40d308 100644 --- a/arch/um/kernel/physmem.c +++ b/arch/um/kernel/physmem.c @@ -1,13 +1,15 @@ -/* +/* * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) * Licensed under the GPL */ #include "linux/mm.h" -#include "linux/ghash.h" +#include "linux/rbtree.h" #include "linux/slab.h" #include "linux/vmalloc.h" #include "linux/bootmem.h" +#include "linux/module.h" +#include "linux/pfn.h" #include "asm/types.h" #include "asm/pgtable.h" #include "kern_util.h" @@ -19,36 +21,8 @@ #include "kern.h" #include "init.h" -#if 0 -static pgd_t physmem_pgd[PTRS_PER_PGD]; - -static struct phys_desc *lookup_mapping(void *addr) -{ - pgd = &physmem_pgd[pgd_index(addr)]; - if(pgd_none(pgd)) - return(NULL); - - pmd = pmd_offset(pgd, addr); - if(pmd_none(pmd)) - return(NULL); - - pte = pte_offset_kernel(pmd, addr); - return((struct phys_desc *) pte_val(pte)); -} - -static struct add_mapping(void *addr, struct phys_desc *new) -{ -} -#endif - -#define PHYS_HASHSIZE (8192) - -struct phys_desc; - -DEF_HASH_STRUCTS(virtmem, PHYS_HASHSIZE, struct phys_desc); - struct phys_desc { - struct virtmem_ptrs virt_ptrs; + struct rb_node rb; int fd; __u64 offset; void *virt; @@ -56,21 +30,48 @@ struct phys_desc { struct list_head list; }; -struct virtmem_table virtmem_hash; +static struct rb_root phys_mappings = RB_ROOT; -static int virt_cmp(void *virt1, void *virt2) +static struct rb_node **find_rb(void *virt) { - return(virt1 != virt2); + struct rb_node **n = &phys_mappings.rb_node; + struct phys_desc *d; + + while(*n != NULL){ + d = rb_entry(*n, struct phys_desc, rb); + if(d->virt == virt) + return(n); + + if(d->virt > virt) + n = &(*n)->rb_left; + else + n = &(*n)->rb_right; + } + + return(n); } -static int virt_hash(void *virt) +static struct phys_desc *find_phys_mapping(void *virt) { - unsigned long addr = ((unsigned long) virt) >> PAGE_SHIFT; - return(addr % PHYS_HASHSIZE); + struct rb_node **n = find_rb(virt); + + if(*n == NULL) + return(NULL); + + return(rb_entry(*n, struct phys_desc, rb)); } -DEF_HASH(static, virtmem, struct phys_desc, virt_ptrs, void *, virt, virt_cmp, - virt_hash); +static void insert_phys_mapping(struct phys_desc *desc) +{ + struct rb_node **n = find_rb(desc->virt); + + if(*n != NULL) + panic("Physical remapping for %p already present", + desc->virt); + + rb_link_node(&desc->rb, rb_parent(*n), n); + rb_insert_color(&desc->rb, &phys_mappings); +} LIST_HEAD(descriptor_mappings); @@ -106,7 +107,7 @@ static struct desc_mapping *descriptor_mapping(int fd) if(desc == NULL) return(NULL); - *desc = ((struct desc_mapping) + *desc = ((struct desc_mapping) { .fd = fd, .list = LIST_HEAD_INIT(desc->list), .pages = LIST_HEAD_INIT(desc->pages) }); @@ -127,7 +128,8 @@ int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w) return(-ENOMEM); phys = __pa(virt); - if(find_virtmem_hash(&virtmem_hash, virt) != NULL) + desc = find_phys_mapping(virt); + if(desc != NULL) panic("Address 0x%p is already substituted\n", virt); err = -ENOMEM; @@ -135,14 +137,13 @@ 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, + *desc = ((struct phys_desc) + { .fd = fd, .offset = offset, .virt = virt, .phys = __pa(virt), .list = LIST_HEAD_INIT(desc->list) }); - insert_virtmem_hash(&virtmem_hash, desc); + insert_phys_mapping(desc); list_add(&desc->list, &fd_maps->pages); @@ -151,7 +152,7 @@ int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w) if(!err) goto out; - remove_virtmem_hash(&virtmem_hash, desc); + rb_erase(&desc->rb, &phys_mappings); kfree(desc); out: return(err); @@ -164,7 +165,7 @@ static void remove_mapping(struct phys_desc *desc) void *virt = desc->virt; int err; - remove_virtmem_hash(&virtmem_hash, desc); + rb_erase(&desc->rb, &phys_mappings); list_del(&desc->list); kfree(desc); @@ -179,7 +180,7 @@ int physmem_remove_mapping(void *virt) struct phys_desc *desc; virt = (void *) ((unsigned long) virt & PAGE_MASK); - desc = find_virtmem_hash(&virtmem_hash, virt); + desc = find_phys_mapping(virt); if(desc == NULL) return(0); @@ -221,7 +222,11 @@ void physmem_forget_descriptor(int fd) kfree(desc); } -void arch_free_page(struct page *page, int order) +EXPORT_SYMBOL(physmem_forget_descriptor); +EXPORT_SYMBOL(physmem_remove_mapping); +EXPORT_SYMBOL(physmem_subst_mapping); + +int arch_free_page(struct page *page, int order) { void *virt; int i; @@ -230,27 +235,21 @@ void arch_free_page(struct page *page, int order) virt = __va(page_to_phys(page + i)); physmem_remove_mapping(virt); } + + return 0; } int is_remapped(void *virt) { - return(find_virtmem_hash(&virtmem_hash, virt) != NULL); + struct phys_desc *desc = find_phys_mapping(virt); + + return(desc != NULL); } /* Changed during early boot */ unsigned long high_physmem; -extern unsigned long physmem_size; - -void *to_virt(unsigned long phys) -{ - return((void *) uml_physmem + phys); -} - -unsigned long to_phys(void *virt) -{ - return(((unsigned long) virt) - uml_physmem); -} +extern unsigned long long physmem_size; int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem) { @@ -269,11 +268,11 @@ int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem) highmem_len = highmem_pages * sizeof(struct page); total_pages = phys_pages + iomem_pages + highmem_pages; - total_len = phys_len + iomem_pages + highmem_len; + total_len = phys_len + iomem_len + highmem_len; if(kmalloc_ok){ map = kmalloc(total_len, GFP_KERNEL); - if(map == NULL) + if(map == NULL) map = vmalloc(total_len); } else map = alloc_bootmem_low_pages(total_len); @@ -283,51 +282,26 @@ int init_maps(unsigned long physmem, unsigned long iomem, unsigned long highmem) for(i = 0; i < total_pages; i++){ p = &map[i]; - set_page_count(p, 0); + memset(p, 0, sizeof(struct page)); SetPageReserved(p); INIT_LIST_HEAD(&p->lru); } - mem_map = map; max_mapnr = total_pages; return(0); } -struct page *phys_to_page(const unsigned long phys) -{ - return(&mem_map[phys >> PAGE_SHIFT]); -} - -struct page *__virt_to_page(const unsigned long virt) -{ - return(&mem_map[__pa(virt) >> PAGE_SHIFT]); -} - -unsigned long page_to_phys(struct page *page) -{ - return((page - mem_map) << PAGE_SHIFT); -} - -pte_t mk_pte(struct page *page, pgprot_t pgprot) -{ - pte_t pte; - - pte_val(pte) = page_to_phys(page) + pgprot_val(pgprot); - if(pte_present(pte)) pte_mknewprot(pte_mknewpage(pte)); - return(pte); -} - /* Changed during early boot */ static unsigned long kmem_top = 0; unsigned long get_kmem_end(void) { - if(kmem_top == 0) + if(kmem_top == 0) kmem_top = CHOOSE_MODE(kmem_end_tt, kmem_end_skas); return(kmem_top); } -void map_memory(unsigned long virt, unsigned long phys, unsigned long len, +void map_memory(unsigned long virt, unsigned long phys, unsigned long len, int r, int w, int x) { __u64 offset; @@ -335,15 +309,20 @@ void map_memory(unsigned long virt, unsigned long phys, unsigned long len, fd = phys_mapping(phys, &offset); err = os_map_memory((void *) virt, fd, offset, len, r, w, x); - if(err) + if(err) { + if(err == -ENOMEM) + printk("try increasing the host's " + "/proc/sys/vm/max_map_count to /4096\n"); panic("map_memory(0x%lx, %d, 0x%llx, %ld, %d, %d, %d) failed, " "err = %d\n", virt, fd, offset, len, r, w, x, err); + } } -#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +extern int __syscall_stub_start; void setup_physmem(unsigned long start, unsigned long reserve_end, - unsigned long len, unsigned long highmem) + unsigned long len, unsigned long long highmem) { unsigned long reserve = reserve_end - start; int pfn = PFN_UP(__pa(reserve_end)); @@ -353,13 +332,19 @@ void setup_physmem(unsigned long start, unsigned long reserve_end, physmem_fd = create_mem_file(len + highmem); offset = uml_reserved - uml_physmem; - err = os_map_memory((void *) uml_reserved, physmem_fd, offset, + err = os_map_memory((void *) uml_reserved, physmem_fd, offset, len - offset, 1, 1, 0); if(err < 0){ os_print_error(err, "Mapping memory"); exit(1); } + /* Special kludge - This page will be mapped in to userspace processes + * from physmem_fd, so it needs to be written out there. + */ + os_seek_file(physmem_fd, __pa(&__syscall_stub_start)); + os_write_file(physmem_fd, &__syscall_stub_start, PAGE_SIZE); + bootmap_size = init_bootmem(pfn, pfn + delta); free_bootmem(__pa(reserve_end) + bootmap_size, len - bootmap_size - reserve); @@ -367,8 +352,7 @@ void setup_physmem(unsigned long start, unsigned long reserve_end, int phys_mapping(unsigned long phys, __u64 *offset_out) { - struct phys_desc *desc = find_virtmem_hash(&virtmem_hash, - __va(phys & PAGE_MASK)); + struct phys_desc *desc = find_phys_mapping(__va(phys & PAGE_MASK)); int fd = -1; if(desc != NULL){ @@ -381,9 +365,9 @@ int phys_mapping(unsigned long phys, __u64 *offset_out) } else if(phys < __pa(end_iomem)){ struct iomem_region *region = iomem_regions; - + while(region != NULL){ - if((phys >= region->phys) && + if((phys >= region->phys) && (phys < region->phys + region->size)){ fd = region->fd; *offset_out = phys - region->phys; @@ -419,12 +403,14 @@ __uml_setup("mem=", uml_mem_setup, unsigned long find_iomem(char *driver, unsigned long *len_out) { struct iomem_region *region = iomem_regions; - + while(region != NULL){ if(!strcmp(region->driver, driver)){ *len_out = region->size; return(region->virt); } + + region = region->next; } return(0); @@ -437,7 +423,7 @@ int setup_iomem(void) int err; while(region != NULL){ - err = os_map_memory((void *) iomem_start, region->fd, 0, + err = os_map_memory((void *) iomem_start, region->fd, 0, region->size, 1, 1, 0); if(err) printk("Mapping iomem region for driver '%s' failed, "