[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 1/2] mm: Adjust new region size to take management cost into acco
From: |
Zhang Boyang |
Subject: |
[PATCH 1/2] mm: Adjust new region size to take management cost into account |
Date: |
Tue, 29 Nov 2022 22:17:33 +0800 |
When grub_memalign() encounters out-of-memory, it will try
grub_mm_add_region_fn() to request more memory from system firmware.
However, the size passed to it doesn't take region management cost into
account. Adding a memory area of `size` bytes will result in a heap
region of less than `size` bytes truely avaliable. Thus, the new region
might not adequate for current allocation request, confusing
out-of-memory handling code.
This patch introduces GRUB_MM_MAX_COST to address the region management
cost (e.g. metadata, alignment). The value of this new constant should
make `grub_malloc (size)` always success after a successful call to
`grub_mm_init_region (addr, size + GRUB_MM_MAX_COST)`.
The new region size is now set to `size + GRUB_MM_MAX_COST`, thus if
grub_mm_add_region_fn() succeeded, current allocation request can always
success.
Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
---
grub-core/kern/mm.c | 15 ++++++++++++---
include/grub/mm_private.h | 9 +++++++++
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
index ae2279133..973cb6b15 100644
--- a/grub-core/kern/mm.c
+++ b/grub-core/kern/mm.c
@@ -410,7 +410,9 @@ grub_memalign (grub_size_t align, grub_size_t size)
{
grub_mm_region_t r;
grub_size_t n = ((size + GRUB_MM_ALIGN - 1) >> GRUB_MM_ALIGN_LOG2) + 1;
+ grub_size_t bound;
int count = 0;
+ grub_size_t grow;
if (!grub_mm_base)
goto fail;
@@ -418,10 +420,13 @@ grub_memalign (grub_size_t align, grub_size_t size)
if (size > ~(grub_size_t) align)
goto fail;
+ /* If largest free chunk in region >= bound, allocation can always success */
+ bound = size + align;
+
/* We currently assume at least a 32-bit grub_size_t,
so limiting allocations to <adress space size> - 1MiB
in name of sanity is beneficial. */
- if ((size + align) > ~(grub_size_t) 0x100000)
+ if (bound > ~(grub_size_t) 0x100000)
goto fail;
align = (align >> GRUB_MM_ALIGN_LOG2);
@@ -443,11 +448,15 @@ grub_memalign (grub_size_t align, grub_size_t size)
switch (count)
{
case 0:
+ /* Adjust heap growth to address the region management cost. */
+ if (grub_add (bound, GRUB_MM_MAX_COST, &grow))
+ goto fail;
+
/* Request additional pages, contiguous */
count++;
if (grub_mm_add_region_fn != NULL &&
- grub_mm_add_region_fn (size, GRUB_MM_ADD_REGION_CONSECUTIVE) ==
GRUB_ERR_NONE)
+ grub_mm_add_region_fn (grow, GRUB_MM_ADD_REGION_CONSECUTIVE) ==
GRUB_ERR_NONE)
goto again;
/* fallthrough */
@@ -462,7 +471,7 @@ grub_memalign (grub_size_t align, grub_size_t size)
* Try again even if this fails, in case it was able to partially
* satisfy the request
*/
- grub_mm_add_region_fn (size, GRUB_MM_ADD_REGION_NONE);
+ grub_mm_add_region_fn (grow, GRUB_MM_ADD_REGION_NONE);
goto again;
}
diff --git a/include/grub/mm_private.h b/include/grub/mm_private.h
index 96c2d816b..f212110e4 100644
--- a/include/grub/mm_private.h
+++ b/include/grub/mm_private.h
@@ -95,6 +95,15 @@ typedef struct grub_mm_region
}
*grub_mm_region_t;
+/*
+ * Set an upper bound of management cost of each region,
+ * with sizeof(struct grub_mm_region), sizeof(struct grub_mm_header) and
+ * any possible alignment or padding taken into account.
+ * The value should make `grub_malloc (size)` always success after a successful
+ * call to `grub_mm_init_region (addr, size + GRUB_MM_MAX_COST)`.
+ */
+#define GRUB_MM_MAX_COST 0x1000
+
#ifndef GRUB_MACHINE_EMU
extern grub_mm_region_t EXPORT_VAR (grub_mm_base);
#endif
--
2.30.2