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 <richard.henderson@linaro.org>
---
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;
--
2.17.1
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 <richard.henderson@linaro.org>
> ---
> 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
© 2016 - 2025 Red Hat, Inc.