Blame SOURCES/0299-mm-When-adding-a-region-merge-with-region-after-as-w.patch

fd0330
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
fd0330
From: Daniel Axtens <dja@axtens.net>
fd0330
Date: Thu, 21 Apr 2022 15:24:15 +1000
fd0330
Subject: [PATCH] mm: When adding a region, merge with region after as well as
fd0330
 before
fd0330
fd0330
On x86_64-efi (at least) regions seem to be added from top down. The mm
fd0330
code will merge a new region with an existing region that comes
fd0330
immediately before the new region. This allows larger allocations to be
fd0330
satisfied that would otherwise be the case.
fd0330
fd0330
On powerpc-ieee1275, however, regions are added from bottom up. So if
fd0330
we add 3x 32MB regions, we can still only satisfy a 32MB allocation,
fd0330
rather than the 96MB allocation we might otherwise be able to satisfy.
fd0330
fd0330
  * Define 'post_size' as being bytes lost to the end of an allocation
fd0330
    due to being given weird sizes from firmware that are not multiples
fd0330
    of GRUB_MM_ALIGN.
fd0330
fd0330
  * Allow merging of regions immediately _after_ existing regions, not
fd0330
    just before. As with the other approach, we create an allocated
fd0330
    block to represent the new space and the pass it to grub_free() to
fd0330
    get the metadata right.
fd0330
fd0330
Signed-off-by: Daniel Axtens <dja@axtens.net>
fd0330
Tested-by: Stefan Berger <stefanb@linux.ibm.com>
fd0330
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
fd0330
Tested-by: Patrick Steinhardt <ps@pks.im>
fd0330
(cherry picked from commit 052e6068be622ff53f1238b449c300dbd0a8abcd)
fd0330
---
fd0330
 grub-core/kern/mm.c       | 128 +++++++++++++++++++++++++++++-----------------
fd0330
 include/grub/mm_private.h |   9 ++++
fd0330
 2 files changed, 91 insertions(+), 46 deletions(-)
fd0330
fd0330
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
fd0330
index 1cbf98c7ab..7be33e23bf 100644
fd0330
--- a/grub-core/kern/mm.c
fd0330
+++ b/grub-core/kern/mm.c
fd0330
@@ -130,53 +130,88 @@ grub_mm_init_region (void *addr, grub_size_t size)
fd0330
 
fd0330
   /* Attempt to merge this region with every existing region */
fd0330
   for (p = &grub_mm_base, q = *p; q; p = &(q->next), q = *p)
fd0330
-    /*
fd0330
-     * Is the new region immediately below an existing region? That
fd0330
-     * is, is the address of the memory we're adding now (addr) + size
fd0330
-     * of the memory we're adding (size) + the bytes we couldn't use
fd0330
-     * at the start of the region we're considering (q->pre_size)
fd0330
-     * equal to the address of q? In other words, does the memory
fd0330
-     * looks like this?
fd0330
-     *
fd0330
-     * addr                          q
fd0330
-     *   |----size-----|-q->pre_size-|<q region>|
fd0330
-     */
fd0330
-    if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q)
fd0330
-      {
fd0330
-	/*
fd0330
-	 * Yes, we can merge the memory starting at addr into the
fd0330
-	 * existing region from below. Align up addr to GRUB_MM_ALIGN
fd0330
-	 * so that our new region has proper alignment.
fd0330
-	 */
fd0330
-	r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
fd0330
-	/* Copy the region data across */
fd0330
-	*r = *q;
fd0330
-	/* Consider all the new size as pre-size */
fd0330
-	r->pre_size += size;
fd0330
+    {
fd0330
+      /*
fd0330
+       * Is the new region immediately below an existing region? That
fd0330
+       * is, is the address of the memory we're adding now (addr) + size
fd0330
+       * of the memory we're adding (size) + the bytes we couldn't use
fd0330
+       * at the start of the region we're considering (q->pre_size)
fd0330
+       * equal to the address of q? In other words, does the memory
fd0330
+       * looks like this?
fd0330
+       *
fd0330
+       * addr                          q
fd0330
+       *   |----size-----|-q->pre_size-|<q region>|
fd0330
+       */
fd0330
+      if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q)
fd0330
+        {
fd0330
+          /*
fd0330
+           * Yes, we can merge the memory starting at addr into the
fd0330
+           * existing region from below. Align up addr to GRUB_MM_ALIGN
fd0330
+           * so that our new region has proper alignment.
fd0330
+           */
fd0330
+          r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
fd0330
+          /* Copy the region data across */
fd0330
+          *r = *q;
fd0330
+          /* Consider all the new size as pre-size */
fd0330
+          r->pre_size += size;
fd0330
 
fd0330
-	/*
fd0330
-	 * If we have enough pre-size to create a block, create a
fd0330
-	 * block with it. Mark it as allocated and pass it to
fd0330
-	 * grub_free (), which will sort out getting it into the free
fd0330
-	 * list.
fd0330
-	 */
fd0330
-	if (r->pre_size >> GRUB_MM_ALIGN_LOG2)
fd0330
-	  {
fd0330
-	    h = (grub_mm_header_t) (r + 1);
fd0330
-	    /* block size is pre-size converted to cells */
fd0330
-	    h->size = (r->pre_size >> GRUB_MM_ALIGN_LOG2);
fd0330
-	    h->magic = GRUB_MM_ALLOC_MAGIC;
fd0330
-	    /* region size grows by block size converted back to bytes */
fd0330
-	    r->size += h->size << GRUB_MM_ALIGN_LOG2;
fd0330
-	    /* adjust pre_size to be accurate */
fd0330
-	    r->pre_size &= (GRUB_MM_ALIGN - 1);
fd0330
-	    *p = r;
fd0330
-	    grub_free (h + 1);
fd0330
-	  }
fd0330
-	/* Replace the old region with the new region */
fd0330
-	*p = r;
fd0330
-	return;
fd0330
-      }
fd0330
+          /*
fd0330
+           * If we have enough pre-size to create a block, create a
fd0330
+           * block with it. Mark it as allocated and pass it to
fd0330
+           * grub_free (), which will sort out getting it into the free
fd0330
+           * list.
fd0330
+           */
fd0330
+          if (r->pre_size >> GRUB_MM_ALIGN_LOG2)
fd0330
+            {
fd0330
+              h = (grub_mm_header_t) (r + 1);
fd0330
+              /* block size is pre-size converted to cells */
fd0330
+              h->size = (r->pre_size >> GRUB_MM_ALIGN_LOG2);
fd0330
+              h->magic = GRUB_MM_ALLOC_MAGIC;
fd0330
+              /* region size grows by block size converted back to bytes */
fd0330
+              r->size += h->size << GRUB_MM_ALIGN_LOG2;
fd0330
+              /* adjust pre_size to be accurate */
fd0330
+              r->pre_size &= (GRUB_MM_ALIGN - 1);
fd0330
+              *p = r;
fd0330
+              grub_free (h + 1);
fd0330
+            }
fd0330
+          /* Replace the old region with the new region */
fd0330
+          *p = r;
fd0330
+          return;
fd0330
+        }
fd0330
+
fd0330
+      /*
fd0330
+       * Is the new region immediately above an existing region? That
fd0330
+       * is:
fd0330
+       *   q                       addr
fd0330
+       *   |<q region>|-q->post_size-|----size-----|
fd0330
+       */
fd0330
+      if ((grub_uint8_t *) q + sizeof (*q) + q->size + q->post_size ==
fd0330
+	  (grub_uint8_t *) addr)
fd0330
+	{
fd0330
+	  /*
fd0330
+	   * Yes! Follow a similar pattern to above, but simpler.
fd0330
+	   * Our header starts at address - post_size, which should align us
fd0330
+	   * to a cell boundary.
fd0330
+	   *
fd0330
+	   * Cast to (void *) first to avoid the following build error:
fd0330
+	   *   kern/mm.c: In function ‘grub_mm_init_region’:
fd0330
+	   *   kern/mm.c:211:15: error: cast increases required alignment of target type [-Werror=cast-align]
fd0330
+	   *     211 |           h = (grub_mm_header_t) ((grub_uint8_t *) addr - q->post_size);
fd0330
+	   *         |               ^
fd0330
+	   * It is safe to do that because proper alignment is enforced in grub_mm_size_sanity_check().
fd0330
+	   */
fd0330
+	  h = (grub_mm_header_t)(void *) ((grub_uint8_t *) addr - q->post_size);
fd0330
+	  /* our size is the allocated size plus post_size, in cells */
fd0330
+	  h->size = (size + q->post_size) >> GRUB_MM_ALIGN_LOG2;
fd0330
+	  h->magic = GRUB_MM_ALLOC_MAGIC;
fd0330
+	  /* region size grows by block size converted back to bytes */
fd0330
+	  q->size += h->size << GRUB_MM_ALIGN_LOG2;
fd0330
+	  /* adjust new post_size to be accurate */
fd0330
+	  q->post_size = (q->post_size + size) & (GRUB_MM_ALIGN - 1);
fd0330
+	  grub_free (h + 1);
fd0330
+	  return;
fd0330
+	}
fd0330
+    }
fd0330
 
fd0330
   /* Allocate a region from the head.  */
fd0330
   r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
fd0330
@@ -195,6 +230,7 @@ grub_mm_init_region (void *addr, grub_size_t size)
fd0330
   r->first = h;
fd0330
   r->pre_size = (grub_addr_t) r - (grub_addr_t) addr;
fd0330
   r->size = (h->size << GRUB_MM_ALIGN_LOG2);
fd0330
+  r->post_size = size - r->size;
fd0330
 
fd0330
   /* Find where to insert this region. Put a smaller one before bigger ones,
fd0330
      to prevent fragmentation.  */
fd0330
diff --git a/include/grub/mm_private.h b/include/grub/mm_private.h
fd0330
index a688b92a83..96c2d816be 100644
fd0330
--- a/include/grub/mm_private.h
fd0330
+++ b/include/grub/mm_private.h
fd0330
@@ -81,8 +81,17 @@ typedef struct grub_mm_region
fd0330
    */
fd0330
   grub_size_t pre_size;
fd0330
 
fd0330
+  /*
fd0330
+   * Likewise, the post-size is the number of bytes we wasted at the end
fd0330
+   * of the allocation because it wasn't a multiple of GRUB_MM_ALIGN
fd0330
+   */
fd0330
+  grub_size_t post_size;
fd0330
+
fd0330
   /* How many bytes are in this region? (free and allocated) */
fd0330
   grub_size_t size;
fd0330
+
fd0330
+  /* pad to a multiple of cell size */
fd0330
+  char padding[3 * GRUB_CPU_SIZEOF_VOID_P];
fd0330
 }
fd0330
 *grub_mm_region_t;
fd0330