Fix nommu MAP_SHARED handling
Subsystems (3)
PATCH 1/1 Fix nommu MAP_SHARED handling
The attached patch does the following things:
(1) It uniquifies permitted overlapping VMAs (eg: MAP_SHARED on chardevs) in nommu_vma_tree. Identical entries break the assumptions on which rbtrees work. Since we don't need to share VMAs in this case, we uniquify such VMAs by using the pointer to the VMA. They're only kept in the tree for /proc/maps visibility.
(2) Extracts VMA unlinking into its own function so that the source is adjacent to the VMA linking function.
(3) No longer releases memory belonging to a shared chardev or file (the underlying driver is expected to provide mappable memory).
(4) Frees the file attached to a VMA whether or not that VMA is shared or is a memory-mapped I/O mapping.
---
warthog1>diffstat nommu-rb-2610rc3.diff
nommu.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 files changed, 50 insertions(+), 16 deletions(-)
mm/nommu.c +50 -16
@@ -315,25 +315,51 @@ static inline struct vm_area_struct *finstatic void add_nommu_vma (struct vm_area_struct *vma) {struct vm_area_struct *pvma; +struct address_space *mapping;struct rb_node **p = &nommu_vma_tree.rb_node;struct rb_node *parent =NULL ; +/* add the VMA to the master list */ while (*p) { parent = *p; pvma = rb_entry(parent, struct vm_area_struct, vm_rb); -if (vma->vm_start < pvma->vm_start) +if (vma->vm_start < pvma->vm_start) { p = &(*p)->rb_left; -else if (vma->vm_start > pvma->vm_start) + } +else if (vma->vm_start > pvma->vm_start) { p = &(*p)->rb_right; -else -BUG ();/* shouldn't happen by this point */ + } +else { +/* mappings are at the same address - this can only +* happen for shared-mem chardevs and shared file +* mappings backed by ramfs/tmpfs */ +BUG_ON (!(pvma->vm_flags &VM_SHARED )); + +if (vma < pvma) + p = &(*p)->rb_left; +else if (vma > pvma) + p = &(*p)->rb_right; +else +BUG (); + } }rb_link_node (&vma->vm_rb, parent, p);rb_insert_color (&vma->vm_rb, &nommu_vma_tree); } +static void delete_nommu_vma (struct vm_area_struct *vma) +{ +struct address_space *mapping; + +/* remove from the master list */ +rb_erase (&vma->vm_rb, &nommu_vma_tree); +} + +/* +* handle mapping creation for uClinux +*/ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr, unsigned long len,
@@ -633,27 +659,33 @@ unsigned long do_mmap_pgoff(struct filereturn -ENOMEM ; } +/* +* handle mapping disposal for uClinux +*/ static void put_vma (struct vm_area_struct *vma) {if (vma) {down_write (&nommu_vma_sem);if (atomic_dec_and_test (&vma->vm_usage)) { -rb_erase (&vma->vm_rb, &nommu_vma_tree); +delete_nommu_vma (vma);if (vma->vm_ops && vma->vm_ops->close) vma->vm_ops->close (vma); -if (!(vma->vm_flags &VM_IO ) && vma->vm_start) { +/* IO memory and memory shared directly out of the pagecache from +* ramfs/tmpfs mustn't be released here */ +if (!(vma->vm_flags & (VM_IO |VM_SHARED )) && vma->vm_start) { realalloc -=kobjsize ((void *) vma->vm_start); askedalloc -= vma->vm_end - vma->vm_start; -if (vma->vm_file) -fput (vma->vm_file);kfree ((void *) vma->vm_start); } realalloc -=kobjsize (vma); askedalloc -=sizeof (*vma); + +if (vma->vm_file) +fput (vma->vm_file);kfree (vma); }
@@ -664,6 +696,7 @@ static void put_vma(struct vm_area_strucint do_munmap (struct mm_struct *mm,unsigned long addr,size_t len) {struct vm_list_struct *vml, **parent; +unsigned long end = addr + len;#ifdef MAGIC_ROM_PTR /* For efficiency's sake, if the pointer is obviously in ROM,
@@ -677,15 +710,16 @@ int do_munmap(struct mm_struct *mm, unsi#endif for (parent = &mm->context.vmlist; *parent; parent = &(*parent)->next) -if ((*parent)->vma->vm_start == addr) -break ; - vml = *parent; +if ((*parent)->vma->vm_start == addr && + (*parent)->vma->vm_end == end) + goto found; -if (!vml) { -printk ("munmap of non-mmaped memory by process %d (%s): %p\n" , - current->pid, current->comm, (void *) addr); -return -EINVAL ; - } +printk ("munmap of non-mmaped memory by process %d (%s): %p\n" , + current->pid, current->comm, (void *) addr); +return -EINVAL ; + + found: + vml = *parent;put_vma (vml->vma);
The attached patch includes prio-tree support and adds cross-referencing of VMAs with address spaces back in, as is done under normal MMU Linux.
---
warthog>diffstat nommu-prio-2610rc3.diff
Makefile | 4 ++-- nommu.c | 22 ++++++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-)
mm/Makefile +2 -2
@@ -5,12 +5,12 @@ mmu-y := nommu.o mmu-$(CONFIG_MMU) := fremap.o highmem.o madvise.o memory.o mincore.o \ mlock.o mmap.o mprotect.o mremap.o msync.o rmap.o \ - vmalloc.o prio_tree.o + vmalloc.o obj-y := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \ page_alloc.o page-writeback.o pdflush.o \ readahead.o slab.o swap.o truncate.o vmscan.o \ - $(mmu-y) + prio_tree.o $(mmu-y) obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o obj-$(CONFIG_HUGETLBFS) += hugetlb.o
mm/nommu.c +18 -4
@@ -48,10 +48,6 @@ DECLARE_RWSEM(nommu_vma_sem);struct vm_operations_struct generic_file_vm_ops = { }; -void __init prio_tree_init (void ) -{ -} -/* * Handle all mappings that got truncated by a "truncate()" * system call.
@@ -319,6 +315,15 @@ static void add_nommu_vma(struct vm_areastruct rb_node **p = &nommu_vma_tree.rb_node;struct rb_node *parent =NULL ; +/* add the VMA to the mapping */ +if (vma->vm_file) { + mapping = vma->vm_file->f_mapping; + +flush_dcache_mmap_lock (mapping); +vma_prio_tree_insert (vma, &mapping->i_mmap); +flush_dcache_mmap_unlock (mapping); + } +/* add the VMA to the master list */ while (*p) { parent = *p;
@@ -353,6 +358,15 @@ static void delete_nommu_vma(struct vm_a {struct address_space *mapping; +/* remove the VMA from the mapping */ +if (vma->vm_file) { + mapping = vma->vm_file->f_mapping; + +flush_dcache_mmap_lock (mapping); +vma_prio_tree_remove (vma, &mapping->i_mmap); +flush_dcache_mmap_unlock (mapping); + } +/* remove from the master list */ rb_erase (&vma->vm_rb, &nommu_vma_tree); }
PATCH 1/1 Permit nommu MAP_SHARED of memory backed files
In reply to David HowellsThe attached patch applies some further fixes and extensions to the nommu mmap implementation:
(1) /proc/maps distinguishes shareable private mappings and real shared mappings by marking the former with 's' and the latter with 'S'.
(2) Rearrange and optimise the checking portion of do_mmap_pgoff() to make it easier to follow.
(3) Only set VM_SHARED on MAP_SHARED mappings. Its presence indicates that the backing memory is supplied by the underlying file or chardev.
VM_MAYSHARE indicates that a VMA may be shared if it's a private VMA. The memory for a private VMA is allocated by do_mmap_pgoff() from a kmalloc slab and then the file contents are read into it before returning.
(4) Permit MAP_SHARED + PROT_WRITE on memory-backed files[*] and chardevs to indicate a contiguous area of memory when its get_unmapped_area() is called if the backing fs/chardev is willing.
[*] file->f_mapping->backing_dev_info->memory_backed == 1
(5) Require chardevs and files that support to provide a get_unmapped_area() file operation.
(6) Made sure a private mapping of /dev/zero is possible. Shared mappings of /dev/zero are not currently supported because this'd need greater interaction of mmap with the chardev driver than is currently supported.
(7) Add in some extra checks from mm/mmap.c: security, file having write access for a writable shared mapping, file not being in append mode.
(8) Only account the mapping memory if it's allocated here; memory belonging to a shared chardev or file is not accounted.
With this patch it should be possible to map contiguous flash files directly out of ROM simply by providing get_unmapped_area() for a read-only/shared mapping.
I think that it might be worth splitting do_mmap_pgoff() up into smaller
subfunctions: one to handle the checking, one to handle shared mappings and one to handle private mappings.
---
warthog1>diffstat nommu-mmap-2610rc3.diff
fs/proc/nommu.c | 2 mm/nommu.c | 182 +++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 124 insertions(+), 60 deletions(-)
fs/proc/nommu.c +1 -1
@@ -62,7 +62,7 @@ static int nommu_vma_list_show(struct se flags &VM_READ ?'r' :'-' , flags &VM_WRITE ?'w' :'-' , flags &VM_EXEC ?'x' :'-' , - flags &VM_MAYSHARE ?'s' :'p' , + flags &VM_MAYSHARE ? flags &VM_SHARED ?'S' :'s' :'p' , vma->vm_pgoff <<PAGE_SHIFT , MAJOR(dev), MINOR(dev), ino, &len);
mm/nommu.c +123 -59
280 lines not on this page · raw message
PATCH 1/1 Cross-reference nommu VMAs with mappings
In reply to David HowellsThe attached patch includes prio-tree support and adds cross-referencing of VMAs with address spaces back in, as is done under normal MMU Linux.
---
warthog>diffstat nommu-prio-2610rc3.diff
Makefile | 4 ++-- nommu.c | 22 ++++++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-)
mm/Makefile +2 -2
@@ -5,12 +5,12 @@ mmu-y := nommu.o mmu-$(CONFIG_MMU) := fremap.o highmem.o madvise.o memory.o mincore.o \ mlock.o mmap.o mprotect.o mremap.o msync.o rmap.o \ - vmalloc.o prio_tree.o + vmalloc.o obj-y := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \ page_alloc.o page-writeback.o pdflush.o \ readahead.o slab.o swap.o truncate.o vmscan.o \ - $(mmu-y) + prio_tree.o $(mmu-y) obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o obj-$(CONFIG_HUGETLBFS) += hugetlb.o
mm/nommu.c +18 -4
@@ -48,10 +48,6 @@ DECLARE_RWSEM(nommu_vma_sem);struct vm_operations_struct generic_file_vm_ops = { }; -void __init prio_tree_init (void ) -{ -} -/* * Handle all mappings that got truncated by a "truncate()" * system call.
@@ -319,6 +315,15 @@ static void add_nommu_vma(struct vm_areastruct rb_node **p = &nommu_vma_tree.rb_node;struct rb_node *parent =NULL ; +/* add the VMA to the mapping */ +if (vma->vm_file) { + mapping = vma->vm_file->f_mapping; + +flush_dcache_mmap_lock (mapping); +vma_prio_tree_insert (vma, &mapping->i_mmap); +flush_dcache_mmap_unlock (mapping); + } +/* add the VMA to the master list */ while (*p) { parent = *p;
@@ -353,6 +358,15 @@ static void delete_nommu_vma(struct vm_a {struct address_space *mapping; +/* remove the VMA from the mapping */ +if (vma->vm_file) { + mapping = vma->vm_file->f_mapping; + +flush_dcache_mmap_lock (mapping); +vma_prio_tree_remove (vma, &mapping->i_mmap); +flush_dcache_mmap_unlock (mapping); + } +/* remove from the master list */ rb_erase (&vma->vm_rb, &nommu_vma_tree); }