qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 12/13] linux-user: Fix shmat emulation by honori


From: Laurent Vivier
Subject: Re: [Qemu-devel] [PATCH 12/13] linux-user: Fix shmat emulation by honoring host SHMLBA
Date: Wed, 22 May 2019 11:40:40 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.8.0

On 19/05/2019 22:19, Richard Henderson wrote:
For those hosts with SHMLBA > getpagesize, we don't automatically
select a guest address that is compatible with the host.  We can
achieve this by boosting the alignment of guest_base and by adding
an extra alignment argument to mmap_find_vma.

Signed-off-by: Richard Henderson <address@hidden>
---
  linux-user/qemu.h    |  2 +-
  linux-user/elfload.c | 17 +++++-----
  linux-user/mmap.c    | 74 +++++++++++++++++++++++---------------------
  linux-user/syscall.c |  3 +-
  4 files changed, 52 insertions(+), 44 deletions(-)

diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index ef400cb78a..82d33d7e93 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -443,7 +443,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong 
old_size,
                         abi_ulong new_addr);
  extern unsigned long last_brk;
  extern abi_ulong mmap_next_start;
-abi_ulong mmap_find_vma(abi_ulong, abi_ulong);
+abi_ulong mmap_find_vma(abi_ulong, abi_ulong, abi_ulong);
  void mmap_fork_start(void);
  void mmap_fork_end(int child);
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index ef42e02d82..fe9f07843e 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -3,6 +3,7 @@
  #include <sys/param.h>
#include <sys/resource.h>
+#include <sys/shm.h>
#include "qemu.h"
  #include "disas/disas.h"
@@ -2012,6 +2013,8 @@ unsigned long init_guest_space(unsigned long host_start,
                                 unsigned long guest_start,
                                 bool fixed)
  {
+    /* In order to use host shmat, we must be able to honor SHMLBA.  */
+    unsigned long align = MAX(SHMLBA, qemu_host_page_size);
      unsigned long current_start, aligned_start;
      int flags;
@@ -2029,7 +2032,7 @@ unsigned long init_guest_space(unsigned long host_start,
      }
/* Setup the initial flags and start address. */
-    current_start = host_start & qemu_host_page_mask;
+    current_start = host_start & -align;
      flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE;
      if (fixed) {
          flags |= MAP_FIXED;
@@ -2065,8 +2068,8 @@ unsigned long init_guest_space(unsigned long host_start,
              return (unsigned long)-1;
          }
          munmap((void *)real_start, host_full_size);
-        if (real_start & ~qemu_host_page_mask) {
-            /* The same thing again, but with an extra qemu_host_page_size
+        if (real_start & (align - 1)) {
+            /* The same thing again, but with extra
               * so that we can shift around alignment.
               */
              unsigned long real_size = host_full_size + qemu_host_page_size;
@@ -2079,7 +2082,7 @@ unsigned long init_guest_space(unsigned long host_start,
                  return (unsigned long)-1;
              }
              munmap((void *)real_start, real_size);
-            real_start = HOST_PAGE_ALIGN(real_start);
+            real_start = ROUND_UP(real_start, align);
          }
          current_start = real_start;
      }
@@ -2106,7 +2109,7 @@ unsigned long init_guest_space(unsigned long host_start,
          }
/* Ensure the address is properly aligned. */
-        if (real_start & ~qemu_host_page_mask) {
+        if (real_start & (align - 1)) {
              /* Ideally, we adjust like
               *
               *    pages: [  ][  ][  ][  ][  ]
@@ -2134,7 +2137,7 @@ unsigned long init_guest_space(unsigned long host_start,
              if (real_start == (unsigned long)-1) {
                  return (unsigned long)-1;
              }
-            aligned_start = HOST_PAGE_ALIGN(real_start);
+            aligned_start = ROUND_UP(real_start, align);
          } else {
              aligned_start = real_start;
          }
@@ -2171,7 +2174,7 @@ unsigned long init_guest_space(unsigned long host_start,
           * because of trouble with ARM commpage setup.
           */
          munmap((void *)real_start, real_size);
-        current_start += qemu_host_page_size;
+        current_start += align;
          if (host_start == current_start) {
              /* Theoretically possible if host doesn't have any suitably
               * aligned areas.  Normally the first mmap will fail.
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index e0249efe4f..10796b37ac 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -202,49 +202,52 @@ unsigned long last_brk;
/* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk
     of guest address space.  */
-static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size)
+static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size,
+                                        abi_ulong align)
  {
-    abi_ulong addr;
-    abi_ulong end_addr;
+    abi_ulong addr, end_addr, incr = qemu_host_page_size;
      int prot;
-    int looped = 0;
+    bool looped = false;
if (size > reserved_va) {
          return (abi_ulong)-1;
      }
- size = HOST_PAGE_ALIGN(size);
-    end_addr = start + size;
-    if (end_addr > reserved_va) {
-        end_addr = reserved_va;
-    }
-    addr = end_addr - qemu_host_page_size;
+    /* Note that start and size have already been aligned by mmap_find_vma. */
+ end_addr = start + size;
+    if (start > reserved_va - size) {
+        /* Start at the top of the address space.  */
+        end_addr = ((reserved_va - size) & -align) + size;
+        looped = true;
+    }
+
+    /* Search downward from END_ADDR, checking to see if a page is in use.  */
+    addr = end_addr;
      while (1) {
+        addr -= incr;
          if (addr > end_addr) {
              if (looped) {
+                /* Failure.  The entire address space has been searched.  */
                  return (abi_ulong)-1;
              }
-            end_addr = reserved_va;
-            addr = end_addr - qemu_host_page_size;
-            looped = 1;
-            continue;
+            /* Re-start at the top of the address space.  */
+            addr = end_addr = ((reserved_va - size) & -align) + size;
+            looped = true;
+        } else {
+            prot = page_get_flags(addr);
+            if (prot) {
+                /* Page in use.  Restart below this page.  */
+                addr = end_addr = ((addr - size) & -align) + size;
+            } else if (addr && addr + size == end_addr) {
+                /* Success!  All pages between ADDR and END_ADDR are free.  */
+                if (start == mmap_next_start) {
+                    mmap_next_start = addr;
+                }
+                return addr;
+            }
          }
-        prot = page_get_flags(addr);
-        if (prot) {
-            end_addr = addr;
-        }
-        if (addr && addr + size == end_addr) {
-            break;
-        }
-        addr -= qemu_host_page_size;
      }
-
-    if (start == mmap_next_start) {
-        mmap_next_start = addr;
-    }
-
-    return addr;
  }
/*
@@ -253,7 +256,7 @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, 
abi_ulong size)
   * 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 mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align)
  {
      void *ptr, *prev;
      abi_ulong addr;
@@ -265,11 +268,12 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
      } else {
          start &= qemu_host_page_mask;
      }
+    start = ROUND_UP(start, align);
size = HOST_PAGE_ALIGN(size); if (reserved_va) {
-        return mmap_find_vma_reserved(start, size);
+        return mmap_find_vma_reserved(start, size, align);
      }
addr = start;
@@ -299,7 +303,7 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
          if (h2g_valid(ptr + size - 1)) {
              addr = h2g(ptr);
- if ((addr & ~TARGET_PAGE_MASK) == 0) {
+            if ((addr & (align - 1)) == 0) {
                  /* Success.  */
                  if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) {
                      mmap_next_start = addr + size;
@@ -313,12 +317,12 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size)
                  /* Assume the result that the kernel gave us is the
                     first with enough free space, so start again at the
                     next higher target page.  */
-                addr = TARGET_PAGE_ALIGN(addr);
+                addr = ROUND_UP(addr, align);
                  break;
              case 1:
                  /* Sometimes the kernel decides to perform the allocation
                     at the top end of memory instead.  */
-                addr &= TARGET_PAGE_MASK;
+                addr &= -align;
                  break;
              case 2:
                  /* Start over at low memory.  */
@@ -416,7 +420,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int 
prot,
      if (!(flags & MAP_FIXED)) {
          host_len = len + offset - host_offset;
          host_len = HOST_PAGE_ALIGN(host_len);
-        start = mmap_find_vma(real_start, host_len);
+        start = mmap_find_vma(real_start, host_len, TARGET_PAGE_SIZE);
          if (start == (abi_ulong)-1) {
              errno = ENOMEM;
              goto fail;
@@ -710,7 +714,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong 
old_size,
      } else if (flags & MREMAP_MAYMOVE) {
          abi_ulong mmap_start;
- mmap_start = mmap_find_vma(0, new_size);
+        mmap_start = mmap_find_vma(0, new_size, TARGET_PAGE_SIZE);
if (mmap_start == -1) {
              errno = ENOMEM;
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index f960556bf8..1feb740f66 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -3912,7 +3912,8 @@ static inline abi_ulong do_shmat(CPUArchState *cpu_env,
      else {
          abi_ulong mmap_start;
- mmap_start = mmap_find_vma(0, shm_info.shm_segsz);
+        /* In order to use the host shmat, we need to honor host SHMLBA.  */
+        mmap_start = mmap_find_vma(0, shm_info.shm_segsz, MAX(SHMLBA, shmlba));
if (mmap_start == -1) {
              errno = ENOMEM;


Applied to my linux-user branch.

Thanks,
Laurent



reply via email to

[Prev in Thread] Current Thread [Next in Thread]