[PATCH 1/3] Further nommu changes

From: David Howells
Date: Tue Nov 16 2004 - 14:34:57 EST



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 <dhowells@xxxxxxxxxx>
---
warthog>diffstat nommu-2610rc2mm1.diff
include/linux/mm.h | 30 +++-----
mm/nommu.c | 177 ++++++++++++++++++++++++++++++++++++-----------------
mm/tiny-shmem.c | 2
3 files changed, 134 insertions(+), 75 deletions(-)

diff -uNrp /warthog/kernels/linux-2.6.10-rc2-mm1/include/linux/mm.h linux-2.6.10-rc2-mm1-frv/include/linux/mm.h
--- /warthog/kernels/linux-2.6.10-rc2-mm1/include/linux/mm.h 2004-11-16 11:31:47.000000000 +0000
+++ linux-2.6.10-rc2-mm1-frv/include/linux/mm.h 2004-11-16 17:18:59.712288944 +0000
@@ -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. */
@@ -112,34 +111,29 @@ struct vm_area_struct {
void * vm_private_data; /* was vm_pte (shared mem) */
unsigned long vm_truncate_count;/* truncate_count or restart_addr */

+#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 -uNrp /warthog/kernels/linux-2.6.10-rc2-mm1/mm/nommu.c linux-2.6.10-rc2-mm1-frv/mm/nommu.c
--- /warthog/kernels/linux-2.6.10-rc2-mm1/mm/nommu.c 2004-11-16 11:31:48.000000000 +0000
+++ linux-2.6.10-rc2-mm1-frv/mm/nommu.c 2004-11-16 18:39:13.318704407 +0000
@@ -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 = &current->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 = &current->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 -uNrp /warthog/kernels/linux-2.6.10-rc2-mm1/mm/tiny-shmem.c linux-2.6.10-rc2-mm1-frv/mm/tiny-shmem.c
--- /warthog/kernels/linux-2.6.10-rc2-mm1/mm/tiny-shmem.c 2004-11-16 11:31:48.000000000 +0000
+++ linux-2.6.10-rc2-mm1-frv/mm/tiny-shmem.c 2004-11-16 17:41:20.247808644 +0000
@@ -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;
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/