Skip to contentSkip to conversation

Rewrite mmap_find_vma() to work fine on 64-bit hosts with 32-bit targets

PATCH New Martin Mohring 1 participant 1 message Aug 13, 2009

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Rewrite mmap_find_vma() to work fine on 64-bit hosts with 32-bit targets

Hi,

quite some time ago this patch had been sent by Kirill to the QEMU ml. At that time, the patch was rejected. Now we found out why the current user mode memory allocator sometimes fails:

- Kernel Bug linux/fs/proc/task_mmu.c (fixed after 2.6.27)
http://bugzilla.kernel.org/attachment.cgi?id=17219
- use of proc file system to find memory mappings => bad idea

So I please apply the attached patch from Kirill to qemu to fix this longstanding bug, because it causes all older linux distros (using kernel 2.6.26 or older) to fail the QEMU memory allocator in user mode.

Cheers, Martin

Quoted 1 line

From c77c088e30c44fc8677d7d95cb2180d8dfb43b1c Mon Sep 17 00:00:00 2001

From: Kirill A. Shutemov <[email protected]>
Date: Wed, 17 Jun 2009 15:14:43 +0200
Subject: [PATCH 19/33] Rewrite mmap_find_vma() to work fine on 64-bit hosts with 32-bit targets

From: Kirill A. Shutemov <[email protected]>

qemu's page table can be incomple if /proc/self/maps is unavailable or host allocating a memory with mmap(), so we can't use it to find free memory area.

New version mmap_find_vma() uses mmap() without MAP_FIXED to find free memory.

From: Kirill A. Shutemov <[email protected]>

---
 linux-user/mmap.c |   79 +++++++++++++++++++++++++++++------------------------
 1 files changed, 43 insertions(+), 36 deletions(-)
linux-user/mmap.c +43 -36
index 9ca8f6f..8d94783 100644
@@ -277,52 +277,59 @@ static abi_ulong mmap_next_start = 0x40000000;
 
 unsigned long last_brk;
 
-/* find a free memory area of size 'size'. The search starts at
-   'start'. If 'start' == 0, then a default start address is used.
-   Return -1 if error.
-*/
-/* page_init() marks pages used by the host as reserved to be sure not
-   to use them. */
+/*
+ * Find and reserve a free memory area of size 'size'. The search
+ * starts at 'start'.
+ * It must be called with mmap_lock() held.
+ * Return -1 if error.
+ */
 abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
 {
-    abi_ulong addr, addr1, addr_start;
-    int prot;
-    unsigned long new_brk;
-
-    new_brk = (unsigned long)sbrk(0);
-    if (last_brk && last_brk < new_brk && last_brk == (target_ulong)last_brk) {
-        /* This is a hack to catch the host allocating memory with brk().
-           If it uses mmap then we loose.
-           FIXME: We really want to avoid the host allocating memory in
-           the first place, and maybe leave some slack to avoid switching
-           to mmap.  */
-        page_set_flags(last_brk & TARGET_PAGE_MASK,
-                       TARGET_PAGE_ALIGN(new_brk),
-                       PAGE_RESERVED); 
-    }
-    last_brk = new_brk;
+    void *ptr;
+    abi_ulong addr;
 
     size = HOST_PAGE_ALIGN(size);
-    start = start & qemu_host_page_mask;
+    start &= qemu_host_page_mask;
+
+    /* If 'start' == 0, then a default start address is used. */
+    if (start == 0)
+        start = mmap_next_start;
+
     addr = start;
-    if (addr == 0)
-        addr = mmap_next_start;
-    addr_start = addr;
+
     for(;;) {
-        prot = 0;
-        for(addr1 = addr; addr1 < (addr + size); addr1 += TARGET_PAGE_SIZE) {
-            prot |= page_get_flags(addr1);
-        }
-        if (prot == 0)
+        /*
+         * Reserve needed memory area to avoid a race.
+         * It should be discarded using:
+         *  - mmap() with MAP_FIXED flag
+         *  - mremap() with MREMAP_FIXED flag
+         *  - shmat() with SHM_REMAP flag
+         */
+        ptr = mmap((void *)(unsigned long)addr, size, PROT_NONE,
+                   MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
+
+        /* ENOMEM, if host address space has no memory */
+        if (ptr == MAP_FAILED)
+            return (abi_ulong)-1;
+
+        /* If address fits target address space we've found what we need */
+        if ((unsigned long)ptr + size - 1 <= (abi_ulong)-1)
             break;
+
+        /* Unmap and try again with new page */
+        munmap(ptr, size);
         addr += qemu_host_page_size;
-        /* we found nothing */
-        if (addr == addr_start)
+
+        /* ENOMEM if we check whole of target address space */
+        if (addr == start)
             return (abi_ulong)-1;
     }
-    if (start == 0)
-        mmap_next_start = addr + size;
-    return addr;
+
+    /* Update default start address */
+    if (start == mmap_next_start)
+        mmap_next_start = (unsigned long)ptr + size;
+
+    return h2g(ptr);
 }
 
 #define SNDRV_PCM_MMAP_OFFSET_STATUS  0x80000000
Signature · 1 line
1.6.2.1