Blame SOURCES/0302-mm-Allow-dynamically-requesting-additional-memory-re.patch

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