0
0
mirror of https://github.com/termux/termux-packages.git synced 2025-05-11 20:13:22 +00:00
Files
termux-packages/packages/ghc/rts-heap-reservation.patch
Aditya Alok e21578f662 bump(main/ghc): 9.12.2
Patched `rts` heap reservation logic on Android:

  * Need for patch

  - There seems to be a bug on Android. Sometimes simple commands like `ghc --help` usage 90% - 100% cpu and hangs.

  - It doesn't happen every time, but ~25% of the times (at least in my
    testing).

  * Cause of the bug

  - The function `osReserveHeapMemory` tries to allocate virtual space starting
    from `0x4200000000`. The allocated space has to start at an address >= this address.

  - If the kernel doesn't allocate inside this space, it keeps on repeating the
    `mmap` call with increasing starting point.

  - Now, on Android the kernel sometimes return an address above (as in counting)
    this `hint` address. It repeatedly returns the same address for subsequent calls.

  - Thus, an infinite loop occurs.

  References:
    - 383be28ffd/rts/posix/OSMem.c (L461)
    - https://github.com/termux/termux-packages/pull/22991#issuecomment-2759137291

  * Solution (proposed by Robert Kirkman):

  - It introduces a new helper function `osTryReserveHeapMemoryRecursive`.
    This transforms the heap reservation logic into a recursive one.

  - `osTryReserveHeapMemory()` is run multiple times without unmapping the
    undesired addresses. Thus, forcing the kernel to map subsequent calls of
    `mmap` to a new, unique address until an address above the `0x4200000000`
    mark is obtained.

  - After which each recursive call unmaps its undesired address before returning
    the desired address (in order from last mapped to first mapped).

  References:
    - https://gitlab.haskell.org/ghc/ghc/-/merge_requests/14164
    - https://github.com/termux/termux-packages/pull/22991#issuecomment-2761325484

Co-authored-by: Robert Kirkman <rkirkman@termux.dev>
Signed-off-by: Aditya Alok <alok@termux.dev>
2025-04-08 18:17:01 +02:00

133 lines
5.5 KiB
Diff

From fdd0c67a38ef8435b9aa46c0d3c9d4460191cdcf Mon Sep 17 00:00:00 2001
From: Robert Kirkman <rkirkman@termux.dev>
Date: Fri, 28 Mar 2025 16:53:16 -0500
Subject: [PATCH] rts: reattempt heap reservation recursively before unmapping
addresses below the 8GB mark
This patch works around Android's mmap() occasionally repeatedly mapping the exact
same block of memory at an address below the 8GB mark that was just
unmapped, which would cause the 'ghc --help' command to fall into an infinite loop while
osTryReserveHeapMemory() repeatedly returned the same unwanted address. This
moves the heap reservation attempt logic into a recursive function that
runs osTryReserveHeapMemory() multiple times without unmapping the
undesired addresses, to force the mapping of new, unique addresses
until an address above the 8GB mark is obtained,
after which each recursive call unmaps its undesired address before
returning the desired address, in order from last mapped to first mapped.
First discussed here: https://github.com/termux/termux-packages/pull/22991
---
rts/posix/OSMem.c | 88 +++++++++++++++++++++++++----------------------
1 file changed, 46 insertions(+), 42 deletions(-)
diff --git a/rts/posix/OSMem.c b/rts/posix/OSMem.c
index 94c60f441ac9..2f1638bb5123 100644
--- a/rts/posix/OSMem.c
+++ b/rts/posix/OSMem.c
@@ -493,11 +493,53 @@ osTryReserveHeapMemory (W_ len, void *hint)
return start;
}
-void *osReserveHeapMemory(void *startAddressPtr, W_ *len)
+static void *
+osTryReserveHeapMemoryRecursive(W_ minimumAddress, W_ startAddress, int attempt, W_ *len)
{
- int attempt;
- void *at;
+ *len &= ~MBLOCK_MASK;
+
+ if (*len < MBLOCK_SIZE) {
+ // Give up if the system won't even give us 16 blocks worth of heap
+ barf("osReserveHeapMemory: Failed to allocate heap storage");
+ }
+
+ void *hint = (void*)(startAddress + attempt * BLOCK_SIZE);
+ void *at = osTryReserveHeapMemory(*len, hint);
+ attempt++;
+
+ if (at == NULL) {
+ // This means that mmap failed which we take to mean that we asked
+ // for too much memory. This can happen due to POSIX resource
+ // limits. In this case we reduce our allocation request by a
+ // fraction of the current size and try again.
+ //
+ // Note that the previously would instead decrease the request size
+ // by a factor of two; however, this meant that significant amounts
+ // of memory will be wasted (e.g. imagine a machine with 512GB of
+ // physical memory but a 511GB ulimit). See #14492.
+ *len -= *len / 8;
+ // debugBelch("Limit hit, reduced len: %zu\n", *len);
+ return osTryReserveHeapMemoryRecursive(minimumAddress, startAddress, attempt, len);
+ } else if ((W_)at >= minimumAddress) {
+ // Success! We were given a block of memory starting above the 8 GB
+ // mark, which is what we were looking for.
+
+ return at;
+ } else {
+ // We got addressing space but it wasn't above the 8GB mark.
+ // Try again recursively first, unmap after, because on aarch64 Android,
+ // sometimes mmap() will continuously map the same address regardless of
+ // the hint changing, if that address has already been unmapped.
+ void *next_at = osTryReserveHeapMemoryRecursive(minimumAddress, startAddress, attempt, len);
+ if (munmap(at, *len) < 0) {
+ sysErrorBelch("unable to release reserved heap");
+ }
+ return next_at;
+ }
+}
+void *osReserveHeapMemory(void *startAddressPtr, W_ *len)
+{
/* We want to ensure the heap starts at least 8 GB inside the address space,
since we want to reserve the address space below that address for code.
Specifically, we need to make sure that any dynamically loaded code will
@@ -585,45 +627,7 @@ void *osReserveHeapMemory(void *startAddressPtr, W_ *len)
}
#endif
- attempt = 0;
- while (1) {
- *len &= ~MBLOCK_MASK;
-
- if (*len < MBLOCK_SIZE) {
- // Give up if the system won't even give us 16 blocks worth of heap
- barf("osReserveHeapMemory: Failed to allocate heap storage");
- }
-
- void *hint = (void*)(startAddress + attempt * BLOCK_SIZE);
- at = osTryReserveHeapMemory(*len, hint);
- if (at == NULL) {
- // This means that mmap failed which we take to mean that we asked
- // for too much memory. This can happen due to POSIX resource
- // limits. In this case we reduce our allocation request by a
- // fraction of the current size and try again.
- //
- // Note that the previously would instead decrease the request size
- // by a factor of two; however, this meant that significant amounts
- // of memory will be wasted (e.g. imagine a machine with 512GB of
- // physical memory but a 511GB ulimit). See #14492.
- *len -= *len / 8;
- // debugBelch("Limit hit, reduced len: %zu\n", *len);
- } else if ((W_)at >= minimumAddress) {
- // Success! We were given a block of memory starting above the 8 GB
- // mark, which is what we were looking for.
-
- break;
- } else {
- // We got addressing space but it wasn't above the 8GB mark.
- // Try again.
- if (munmap(at, *len) < 0) {
- sysErrorBelch("unable to release reserved heap");
- }
- }
- attempt++;
- }
-
- return at;
+ return osTryReserveHeapMemoryRecursive(minimumAddress, startAddress, 0, len);
}
void osCommitMemory(void *at, W_ size)
--
GitLab