From: David Howells The attached patch further changes the nommu stuff previously changed. These new changes do the following: (0) Some additional variables have been defined to make nommu even compile. (1) Get rid of the alternate vm_area_struct. The nommu mmap now uses the normal one. There's a refcount field added to the normal one, contingent on !CONFIG_MMU. (2) vm_rb is now used to keep track of the VMAs in an rbtree rather than adding a separate list. (3) mm_tblock_struct is now vm_list_struct. (4) put_vma() now calls vma->vm_ops->close() if available on nommu. (5) A dummy generic_file_vm_ops has been provided. It does nothing, but permits tiny-shmem to compile. tiny-shmem and ramfs still need attention, such that files contained therein can be mmapped shared-writably to some extent on nommu. Signed-Off-By: David Howells Signed-off-by: Andrew Morton --- 25-akpm/include/linux/mm.h | 30 +++---- 25-akpm/mm/nommu.c | 177 +++++++++++++++++++++++++++++++-------------- 25-akpm/mm/tiny-shmem.c | 2 3 files changed, 134 insertions(+), 75 deletions(-) diff -puN include/linux/mm.h~further-nommu-changes include/linux/mm.h --- 25/include/linux/mm.h~further-nommu-changes 2004-11-16 23:39:58.200306360 -0800 +++ 25-akpm/include/linux/mm.h 2004-11-16 23:39:58.208305144 -0800 @@ -62,7 +62,6 @@ extern int sysctl_legacy_va_layout; * space that has a special rule for the page-fault handlers (ie a shared * library, the executable area etc). */ -#ifdef CONFIG_MMU struct vm_area_struct { struct mm_struct * vm_mm; /* The address space we belong to. */ unsigned long vm_start; /* Our start address within vm_mm. */ @@ -111,34 +110,29 @@ struct vm_area_struct { struct file * vm_file; /* File we map to (can be NULL). */ void * vm_private_data; /* was vm_pte (shared mem) */ +#ifndef CONFIG_MMU + atomic_t vm_usage; /* refcount (VMAs shared if !MMU) */ +#endif #ifdef CONFIG_NUMA struct mempolicy *vm_policy; /* NUMA policy for the VMA */ #endif }; -#else - -struct vm_area_struct { - struct list_head vm_link; /* system object list */ - atomic_t vm_usage; /* count of refs */ - unsigned long vm_start; - unsigned long vm_end; - pgprot_t vm_page_prot; /* access permissions of this VMA */ - unsigned long vm_flags; - unsigned long vm_pgoff; - struct file *vm_file; /* file or device mapped */ -}; - -struct mm_tblock_struct { - struct mm_tblock_struct *next; +/* + * This struct defines the per-mm list of VMAs for uClinux. If CONFIG_MMU is + * disabled, then there's a single shared list of VMAs maintained by the + * system, and mm's subscribe to these individually + */ +struct vm_list_struct { + struct vm_list_struct *next; struct vm_area_struct *vma; }; -extern struct list_head nommu_vma_list; +#ifndef CONFIG_MMU +extern struct rb_root nommu_vma_tree; extern struct rw_semaphore nommu_vma_sem; extern unsigned int kobjsize(const void *objp); - #endif /* diff -puN mm/nommu.c~further-nommu-changes mm/nommu.c --- 25/mm/nommu.c~further-nommu-changes 2004-11-16 23:39:58.202306056 -0800 +++ 25-akpm/mm/nommu.c 2004-11-16 23:39:58.210304840 -0800 @@ -36,14 +36,18 @@ atomic_t vm_committed_space = ATOMIC_INI int sysctl_overcommit_memory = OVERCOMMIT_GUESS; /* heuristic overcommit */ int sysctl_overcommit_ratio = 50; /* default is 50% */ int sysctl_max_map_count = DEFAULT_MAX_MAP_COUNT; +int heap_stack_gap = 0; EXPORT_SYMBOL(sysctl_max_map_count); EXPORT_SYMBOL(mem_map); /* list of shareable VMAs */ -LIST_HEAD(nommu_vma_list); +struct rb_root nommu_vma_tree = RB_ROOT; DECLARE_RWSEM(nommu_vma_sem); +struct vm_operations_struct generic_file_vm_ops = { +}; + void __init prio_tree_init(void) { } @@ -273,19 +277,63 @@ static inline unsigned long calc_vm_flag #ifdef DEBUG static void show_process_blocks(void) { - struct mm_tblock_struct *tblock; + struct vm_list_struct *vml; printk("Process blocks %d:", current->pid); - for (tblock = ¤t->mm->context.tblock; tblock; tblock = tblock->next) { - printk(" %p: %p", tblock, tblock->rblock); - if (tblock->rblock) - printk(" (%d @%p #%d)", kobjsize(tblock->rblock->kblock), tblock->rblock->kblock, tblock->rblock->refcount); - printk(tblock->next ? " ->" : ".\n"); + for (vml = ¤t->mm->context.vmlist; vml; vml = vml->next) { + printk(" %p: %p", vml, vml->vma); + if (vml->vma) + printk(" (%d @%lx #%d)", + kobjsize((void *) vml->vma->vm_start), + vml->vma->vm_start, + atomic_read(&vml->vma->vm_usage)); + printk(vml->next ? " ->" : ".\n"); } } #endif /* DEBUG */ +static inline struct vm_area_struct *find_nommu_vma(unsigned long start) +{ + struct vm_area_struct *vma; + struct rb_node *n = nommu_vma_tree.rb_node; + + while (n) { + vma = rb_entry(n, struct vm_area_struct, vm_rb); + + if (start < vma->vm_start) + n = n->rb_left; + else if (start > vma->vm_start) + n = n->rb_right; + else + return vma; + } + + return NULL; +} + +static void add_nommu_vma(struct vm_area_struct *vma) +{ + struct vm_area_struct *pvma; + struct rb_node **p = &nommu_vma_tree.rb_node; + struct rb_node *parent = NULL; + + while (*p) { + parent = *p; + pvma = rb_entry(parent, struct vm_area_struct, vm_rb); + + if (vma->vm_start < pvma->vm_start) + p = &(*p)->rb_left; + else if (vma->vm_start > pvma->vm_start) + p = &(*p)->rb_right; + else + BUG(); /* shouldn't happen by this point */ + } + + rb_link_node(&vma->vm_rb, parent, p); + rb_insert_color(&vma->vm_rb, &nommu_vma_tree); +} + unsigned long do_mmap_pgoff(struct file *file, unsigned long addr, unsigned long len, @@ -293,9 +341,9 @@ unsigned long do_mmap_pgoff(struct file unsigned long flags, unsigned long pgoff) { - struct mm_tblock_struct *tblock = NULL; - struct vm_area_struct *vma = NULL, *pvma; - struct list_head *p; + struct vm_list_struct *vml = NULL; + struct vm_area_struct *vma = NULL; + struct rb_node *rb; unsigned int vm_flags; void *result; int ret, chrdev; @@ -334,10 +382,10 @@ unsigned long do_mmap_pgoff(struct file return -EINVAL; /* we're going to need to record the mapping if it works */ - tblock = kmalloc(sizeof(struct mm_tblock_struct), GFP_KERNEL); - if (!tblock) - goto error_getting_tblock; - memset(tblock, 0, sizeof(*tblock)); + vml = kmalloc(sizeof(struct vm_list_struct), GFP_KERNEL); + if (!vml) + goto error_getting_vml; + memset(vml, 0, sizeof(*vml)); /* Do simple checking here so the lower-level routines won't have * to. we assume access permissions have been handled by the open @@ -376,7 +424,9 @@ unsigned long do_mmap_pgoff(struct file unsigned long pglen = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; unsigned long vmpglen; - list_for_each_entry(vma, &nommu_vma_list, vm_link) { + for (rb = rb_first(&nommu_vma_tree); rb; rb = rb_next(rb)) { + vma = rb_entry(rb, struct vm_area_struct, vm_rb); + if (!(vma->vm_flags & VM_SHARED)) continue; @@ -399,7 +449,7 @@ unsigned long do_mmap_pgoff(struct file /* we've found a VMA we can share */ atomic_inc(&vma->vm_usage); - tblock->vma = vma; + vml->vma = vma; result = (void *) vma->vm_start; goto shared; } @@ -422,7 +472,8 @@ unsigned long do_mmap_pgoff(struct file if (!vma) goto error_getting_vma; - INIT_LIST_HEAD(&vma->vm_link); + memset(vma, 0, sizeof(*vma)); + INIT_LIST_HEAD(&vma->anon_vma_node); atomic_set(&vma->vm_usage, 1); if (file) get_file(file); @@ -432,7 +483,7 @@ unsigned long do_mmap_pgoff(struct file vma->vm_end = addr + len; vma->vm_pgoff = pgoff; - tblock->vma = vma; + vml->vma = vma; /* * determine the object being mapped and call the appropriate @@ -533,19 +584,13 @@ unsigned long do_mmap_pgoff(struct file current->mm->total_vm += len >> PAGE_SHIFT; - list_for_each(p, &nommu_vma_list) { - pvma = list_entry(p, struct vm_area_struct, vm_link); - if (pvma->vm_start > vma->vm_start) - break; - } - list_add_tail(&vma->vm_link, p); - + add_nommu_vma(vma); shared: - realalloc += kobjsize(tblock); - askedalloc += sizeof(*tblock); + realalloc += kobjsize(vml); + askedalloc += sizeof(*vml); - tblock->next = current->mm->context.tblock; - current->mm->context.tblock = tblock; + vml->next = current->mm->context.vmlist; + current->mm->context.vmlist = vml; up_write(&nommu_vma_sem); @@ -560,7 +605,7 @@ unsigned long do_mmap_pgoff(struct file kfree(result); error: up_write(&nommu_vma_sem); - kfree(tblock); + kfree(vml); if (vma) { fput(vma->vm_file); kfree(vma); @@ -570,19 +615,19 @@ unsigned long do_mmap_pgoff(struct file sharing_violation: up_write(&nommu_vma_sem); printk("Attempt to share mismatched mappings\n"); - kfree(tblock); + kfree(vml); return -EINVAL; error_getting_vma: up_write(&nommu_vma_sem); - kfree(tblock); - printk("Allocation of tblock for %lu byte allocation from process %d failed\n", + kfree(vml); + printk("Allocation of vml for %lu byte allocation from process %d failed\n", len, current->pid); show_free_areas(); return -ENOMEM; - error_getting_tblock: - printk("Allocation of tblock for %lu byte allocation from process %d failed\n", + error_getting_vml: + printk("Allocation of vml for %lu byte allocation from process %d failed\n", len, current->pid); show_free_areas(); return -ENOMEM; @@ -592,8 +637,12 @@ static void put_vma(struct vm_area_struc { if (vma) { down_write(&nommu_vma_sem); + if (atomic_dec_and_test(&vma->vm_usage)) { - list_del_init(&vma->vm_link); + rb_erase(&vma->vm_rb, &nommu_vma_tree); + + if (vma->vm_ops && vma->vm_ops->close) + vma->vm_ops->close(vma); if (!(vma->vm_flags & VM_IO) && vma->vm_start) { realalloc -= kobjsize((void *) vma->vm_start); @@ -607,13 +656,14 @@ static void put_vma(struct vm_area_struc askedalloc -= sizeof(*vma); kfree(vma); } + up_write(&nommu_vma_sem); } } int do_munmap(struct mm_struct *mm, unsigned long addr, size_t len) { - struct mm_tblock_struct *tblock, **parent; + struct vm_list_struct *vml, **parent; #ifdef MAGIC_ROM_PTR /* For efficiency's sake, if the pointer is obviously in ROM, @@ -626,23 +676,23 @@ int do_munmap(struct mm_struct *mm, unsi printk("do_munmap:\n"); #endif - for (parent = &mm->context.tblock; *parent; parent = &(*parent)->next) + for (parent = &mm->context.vmlist; *parent; parent = &(*parent)->next) if ((*parent)->vma->vm_start == addr) break; - tblock = *parent; + vml = *parent; - if (!tblock) { + if (!vml) { printk("munmap of non-mmaped memory by process %d (%s): %p\n", current->pid, current->comm, (void *) addr); return -EINVAL; } - put_vma(tblock->vma); + put_vma(vml->vma); - *parent = tblock->next; - realalloc -= kobjsize(tblock); - askedalloc -= sizeof(*tblock); - kfree(tblock); + *parent = vml->next; + realalloc -= kobjsize(vml); + askedalloc -= sizeof(*vml); + kfree(vml); mm->total_vm -= len >> PAGE_SHIFT; #ifdef DEBUG @@ -655,7 +705,7 @@ int do_munmap(struct mm_struct *mm, unsi /* Release all mmaps. */ void exit_mmap(struct mm_struct * mm) { - struct mm_tblock_struct *tmp; + struct vm_list_struct *tmp; if (mm) { #ifdef DEBUG @@ -664,8 +714,8 @@ void exit_mmap(struct mm_struct * mm) mm->total_vm = 0; - while ((tmp = mm->context.tblock)) { - mm->context.tblock = tmp->next; + while ((tmp = mm->context.vmlist)) { + mm->context.vmlist = tmp->next; put_vma(tmp->vma); realalloc -= kobjsize(tmp); @@ -709,7 +759,7 @@ unsigned long do_mremap(unsigned long ad unsigned long old_len, unsigned long new_len, unsigned long flags, unsigned long new_addr) { - struct mm_tblock_struct *tblock = NULL; + struct vm_list_struct *vml = NULL; /* insanity checks first */ if (new_len == 0) @@ -718,29 +768,29 @@ unsigned long do_mremap(unsigned long ad if (flags & MREMAP_FIXED && new_addr != addr) return (unsigned long) -EINVAL; - for (tblock = current->mm->context.tblock; tblock; tblock = tblock->next) - if (tblock->vma->vm_start == addr) + for (vml = current->mm->context.vmlist; vml; vml = vml->next) + if (vml->vma->vm_start == addr) goto found; return (unsigned long) -EINVAL; found: - if (tblock->vma->vm_end != tblock->vma->vm_start + old_len) + if (vml->vma->vm_end != vml->vma->vm_start + old_len) return (unsigned long) -EFAULT; - if (tblock->vma->vm_flags & VM_MAYSHARE) + if (vml->vma->vm_flags & VM_MAYSHARE) return (unsigned long) -EPERM; if (new_len > kobjsize((void *) addr)) return (unsigned long) -ENOMEM; /* all checks complete - do it */ - tblock->vma->vm_end = tblock->vma->vm_start + new_len; + vml->vma->vm_end = vml->vma->vm_start + new_len; askedalloc -= old_len; askedalloc += new_len; - return tblock->vma->vm_start; + return vml->vma->vm_start; } struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr) @@ -778,3 +828,20 @@ void arch_unmap_area(struct vm_area_stru { } +void update_mem_hiwater(void) +{ + struct task_struct *tsk = current; + + if (likely(tsk->mm)) { + if (tsk->mm->hiwater_rss < tsk->mm->rss) + tsk->mm->hiwater_rss = tsk->mm->rss; + if (tsk->mm->hiwater_vm < tsk->mm->total_vm) + tsk->mm->hiwater_vm = tsk->mm->total_vm; + } +} + +void unmap_mapping_range(struct address_space *mapping, + loff_t const holebegin, loff_t const holelen, + int even_cows) +{ +} diff -puN mm/tiny-shmem.c~further-nommu-changes mm/tiny-shmem.c --- 25/mm/tiny-shmem.c~further-nommu-changes 2004-11-16 23:39:58.203305904 -0800 +++ 25-akpm/mm/tiny-shmem.c 2004-11-16 23:39:58.211304688 -0800 @@ -112,9 +112,7 @@ int shmem_zero_setup(struct vm_area_stru if (vma->vm_file) fput(vma->vm_file); vma->vm_file = file; -#ifdef CONFIG_MMU vma->vm_ops = &generic_file_vm_ops; -#endif return 0; } _