861fb30b3e
Resolves: #2007427 Signed-off-by: Robbie Harwood <rharwood@redhat.com>
131 lines
4.5 KiB
Diff
131 lines
4.5 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
From: Patrick Steinhardt <ps@pks.im>
|
||
Date: Thu, 21 Apr 2022 15:24:18 +1000
|
||
Subject: [PATCH] mm: Allow dynamically requesting additional memory regions
|
||
|
||
Currently, all platforms will set up their heap on initialization of the
|
||
platform code. While this works mostly fine, it poses some limitations
|
||
on memory management on us. Most notably, allocating big chunks of
|
||
memory in the gigabyte range would require us to pre-request this many
|
||
bytes from the firmware and add it to the heap from the beginning on
|
||
some platforms like EFI. As this isn't needed for most configurations,
|
||
it is inefficient and may even negatively impact some usecases when,
|
||
e.g., chainloading. Nonetheless, allocating big chunks of memory is
|
||
required sometimes, where one example is the upcoming support for the
|
||
Argon2 key derival function in LUKS2.
|
||
|
||
In order to avoid pre-allocating big chunks of memory, this commit
|
||
implements a runtime mechanism to add more pages to the system. When
|
||
a given allocation cannot be currently satisfied, we'll call a given
|
||
callback set up by the platform's own memory management subsystem,
|
||
asking it to add a memory area with at least "n" bytes. If this
|
||
succeeds, we retry searching for a valid memory region, which should
|
||
now succeed.
|
||
|
||
If this fails, we try asking for "n" bytes, possibly spread across
|
||
multiple regions, in hopes that region merging means that we end up
|
||
with enough memory for things to work out.
|
||
|
||
Signed-off-by: Patrick Steinhardt <ps@pks.im>
|
||
Signed-off-by: Daniel Axtens <dja@axtens.net>
|
||
Tested-by: Stefan Berger <stefanb@linux.ibm.com>
|
||
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
|
||
Tested-by: Patrick Steinhardt <ps@pks.im>
|
||
(cherry picked from commit 887f98f0db43e33fba4ec1f85e42fae1185700bc)
|
||
---
|
||
grub-core/kern/mm.c | 30 ++++++++++++++++++++++++++++++
|
||
include/grub/mm.h | 18 ++++++++++++++++++
|
||
2 files changed, 48 insertions(+)
|
||
|
||
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
|
||
index 1825dc8289..f2e27f263b 100644
|
||
--- a/grub-core/kern/mm.c
|
||
+++ b/grub-core/kern/mm.c
|
||
@@ -28,6 +28,9 @@
|
||
- multiple regions may be used as free space. They may not be
|
||
contiguous.
|
||
|
||
+ - if existing regions are insufficient to satisfy an allocation, a new
|
||
+ region can be requested from firmware.
|
||
+
|
||
Regions are managed by a singly linked list, and the meta information is
|
||
stored in the beginning of each region. Space after the meta information
|
||
is used to allocate memory.
|
||
@@ -81,6 +84,7 @@
|
||
|
||
|
||
grub_mm_region_t grub_mm_base;
|
||
+grub_mm_add_region_func_t grub_mm_add_region_fn;
|
||
|
||
/* Get a header from the pointer PTR, and set *P and *R to a pointer
|
||
to the header and a pointer to its region, respectively. PTR must
|
||
@@ -444,6 +448,32 @@ grub_memalign (grub_size_t align, grub_size_t size)
|
||
count++;
|
||
goto again;
|
||
|
||
+ case 1:
|
||
+ /* 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)
|
||
+ goto again;
|
||
+
|
||
+ /* fallthrough */
|
||
+
|
||
+ case 2:
|
||
+ /* Request additional pages, anything at all */
|
||
+ count++;
|
||
+
|
||
+ if (grub_mm_add_region_fn != NULL)
|
||
+ {
|
||
+ /*
|
||
+ * 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);
|
||
+ goto again;
|
||
+ }
|
||
+
|
||
+ /* fallthrough */
|
||
+
|
||
default:
|
||
break;
|
||
}
|
||
diff --git a/include/grub/mm.h b/include/grub/mm.h
|
||
index d81623d226..7c6f925ffd 100644
|
||
--- a/include/grub/mm.h
|
||
+++ b/include/grub/mm.h
|
||
@@ -20,6 +20,7 @@
|
||
#ifndef GRUB_MM_H
|
||
#define GRUB_MM_H 1
|
||
|
||
+#include <grub/err.h>
|
||
#include <grub/types.h>
|
||
#include <grub/symbol.h>
|
||
#include <grub/err.h>
|
||
@@ -29,6 +30,23 @@
|
||
# define NULL ((void *) 0)
|
||
#endif
|
||
|
||
+#define GRUB_MM_ADD_REGION_NONE 0
|
||
+#define GRUB_MM_ADD_REGION_CONSECUTIVE (1 << 0)
|
||
+
|
||
+/*
|
||
+ * Function used to request memory regions of `grub_size_t` bytes. The second
|
||
+ * parameter is a bitfield of `GRUB_MM_ADD_REGION` flags.
|
||
+ */
|
||
+typedef grub_err_t (*grub_mm_add_region_func_t) (grub_size_t, unsigned int);
|
||
+
|
||
+/*
|
||
+ * Set this function pointer to enable adding memory-regions at runtime in case
|
||
+ * a memory allocation cannot be satisfied with existing regions.
|
||
+ */
|
||
+#ifndef GRUB_MACHINE_EMU
|
||
+extern grub_mm_add_region_func_t EXPORT_VAR(grub_mm_add_region_fn);
|
||
+#endif
|
||
+
|
||
void grub_mm_init_region (void *addr, grub_size_t size);
|
||
void *EXPORT_FUNC(grub_calloc) (grub_size_t nmemb, grub_size_t size);
|
||
void *EXPORT_FUNC(grub_malloc) (grub_size_t size);
|