]> git.proxmox.com Git - mirror_edk2.git/commitdiff
ArmPkg/UncachedMemoryAllocationLib: Track uncached memory allocations
authorOlivier Martin <olivier.martin@arm.com>
Fri, 10 Oct 2014 11:24:11 +0000 (11:24 +0000)
committeroliviermartin <oliviermartin@6f19259b-4bc3-4df7-8a09-765794883524>
Fri, 10 Oct 2014 11:24:11 +0000 (11:24 +0000)
Keeping track of uncached memory allocations prevents doing expensive
cache operations (eg: clean & invalidate) on newly allocated regions
by reusing regions where possible

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Olivier Martin <olivier.martin@arm.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16205 6f19259b-4bc3-4df7-8a09-765794883524

ArmPkg/ArmPkg.dec
ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.c
ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.inf

index 05bc1dcd6d613c042c01f304b6ee13aac5a5de9e..2a1947b9ca42f54188456b678ff95096b5ecc3e7 100644 (file)
@@ -77,6 +77,9 @@
   gArmTokenSpaceGuid.PcdVFPEnabled|0|UINT32|0x00000024\r
 \r
   gArmTokenSpaceGuid.PcdArmUncachedMemoryMask|0x0000000080000000|UINT64|0x00000002\r
+  # This PCD will free the unallocated buffers if their size reach this threshold.\r
+  # We set the default value to 512MB.\r
+  gArmTokenSpaceGuid.PcdArmFreeUncachedMemorySizeThreshold|0x20000000|UINT64|0x00000043\r
   gArmTokenSpaceGuid.PcdArmCacheOperationThreshold|1024|UINT32|0x00000003\r
   gArmTokenSpaceGuid.PcdCpuVectorBaseAddress|0xffff0000|UINT32|0x00000004\r
   gArmTokenSpaceGuid.PcdCpuResetAddress|0x00000000|UINT32|0x00000005\r
index 1209b926c1f7970a1640ed55511657f32a2f6a9a..e70d8777d76d47706565cbd69e92dc15ff200399 100644 (file)
@@ -3,6 +3,7 @@
   a buffer.\r
 \r
   Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>\r
+  Copyright (c) 2014, AMR Ltd. All rights reserved.<BR>\r
 \r
   This program and the accompanying materials\r
   are licensed and made available under the terms and conditions of the BSD License\r
@@ -46,60 +47,228 @@ UncachedInternalAllocateAlignedPages (
 UINT64  gAttributes;\r
 \r
 typedef struct {\r
-  VOID        *Allocation;\r
-  UINTN       Pages;\r
-  LIST_ENTRY  Link;\r
+  EFI_PHYSICAL_ADDRESS  Base;\r
+  VOID                  *Allocation;\r
+  UINTN                 Pages;\r
+  EFI_MEMORY_TYPE       MemoryType;\r
+  BOOLEAN               Allocated;\r
+  LIST_ENTRY            Link;\r
 } FREE_PAGE_NODE;\r
 \r
-LIST_ENTRY  mPageList = INITIALIZE_LIST_HEAD_VARIABLE (mPageList);\r
+STATIC LIST_ENTRY  mPageList = INITIALIZE_LIST_HEAD_VARIABLE (mPageList);\r
+// Track the size of the non-allocated buffer in the linked-list\r
+STATIC UINTN   mFreedBufferSize = 0;\r
 \r
-VOID\r
-AddPagesToList (\r
-  IN VOID   *Allocation,\r
-  UINTN     Pages\r
+/**\r
+ * This function firstly checks if the requested allocation can fit into one\r
+ * of the previously allocated buffer.\r
+ * If the requested allocation does not fit in the existing pool then\r
+ * the function makes a new allocation.\r
+ *\r
+ * @param MemoryType    Type of memory requested for the new allocation\r
+ * @param Pages         Number of requested page\r
+ * @param Alignment     Required alignment\r
+ * @param Allocation    Address of the newly allocated buffer\r
+ *\r
+ * @return EFI_SUCCESS  If the function manage to allocate a buffer\r
+ * @return !EFI_SUCCESS If the function did not manage to allocate a buffer\r
+ */\r
+STATIC\r
+EFI_STATUS\r
+AllocatePagesFromList (\r
+  IN EFI_MEMORY_TYPE  MemoryType,\r
+  IN UINTN            Pages,\r
+  IN UINTN            Alignment,\r
+  OUT VOID            **Allocation\r
   )\r
 {\r
+  EFI_STATUS       Status;\r
+  LIST_ENTRY      *Link;\r
+  FREE_PAGE_NODE  *Node;\r
   FREE_PAGE_NODE  *NewNode;\r
+  UINTN            AlignmentMask;\r
+  EFI_PHYSICAL_ADDRESS Memory;\r
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;\r
+\r
+  // Alignment must be a power of two or zero.\r
+  ASSERT ((Alignment & (Alignment - 1)) == 0);\r
+\r
+  //\r
+  // Look in our list for the smallest page that could satisfy the new allocation\r
+  //\r
+  NewNode = NULL;\r
+  for (Link = mPageList.ForwardLink; Link != &mPageList; Link = Link->ForwardLink) {\r
+    Node = BASE_CR (Link, FREE_PAGE_NODE, Link);\r
+    if ((Node->Allocated == FALSE) && (Node->MemoryType == MemoryType)) {\r
+      // We have a node that fits our requirements\r
+      if (((UINTN)Node->Base & (Alignment - 1)) == 0) {\r
+        // We found a page that matches the page size\r
+        if (Node->Pages == Pages) {\r
+          Node->Allocated  = TRUE;\r
+          Node->Allocation = (VOID*)(UINTN)Node->Base;\r
+          *Allocation      = Node->Allocation;\r
+\r
+          // Update the size of the freed buffer\r
+          mFreedBufferSize  -= Pages * EFI_PAGE_SIZE;\r
+          return EFI_SUCCESS;\r
+        } else if (Node->Pages > Pages) {\r
+          if (NewNode == NULL) {\r
+            // It is the first node that could contain our new allocation\r
+            NewNode = Node;\r
+          } else if (NewNode->Pages > Node->Pages) {\r
+            // This node offers a smaller number of page.\r
+            NewNode = Node;\r
+          }\r
+        }\r
+      }\r
+    }\r
+  }\r
+  // Check if we have found a node that could contain our new allocation\r
+  if (NewNode != NULL) {\r
+    NewNode->Allocated = TRUE;\r
+    Node->Allocation   = (VOID*)(UINTN)Node->Base;\r
+    *Allocation        = Node->Allocation;\r
+    return EFI_SUCCESS;\r
+  }\r
 \r
-  NewNode = AllocatePool (sizeof (LIST_ENTRY));\r
+  //\r
+  // Otherwise, we need to allocate a new buffer\r
+  //\r
+\r
+  // We do not want to over-allocate in case the alignment requirement does not\r
+  // require extra pages\r
+  if (Alignment > EFI_PAGE_SIZE) {\r
+    AlignmentMask  = Alignment - 1;\r
+    Pages          += EFI_SIZE_TO_PAGES (Alignment);\r
+  } else {\r
+    AlignmentMask  = 0;\r
+  }\r
+\r
+  Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, Pages, &Memory);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gDS->GetMemorySpaceDescriptor (Memory, &Descriptor);\r
+  if (!EFI_ERROR (Status)) {\r
+    // We are making an assumption that all of memory has the same default attributes\r
+    gAttributes = Descriptor.Attributes;\r
+  } else {\r
+    gBS->FreePages (Memory, Pages);\r
+    return Status;\r
+  }\r
+\r
+  Status = gDS->SetMemorySpaceAttributes (Memory, EFI_PAGES_TO_SIZE (Pages), EFI_MEMORY_WC);\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->FreePages (Memory, Pages);\r
+    return Status;\r
+  }\r
+\r
+  NewNode = AllocatePool (sizeof (FREE_PAGE_NODE));\r
   if (NewNode == NULL) {\r
     ASSERT (FALSE);\r
-    return;\r
+    gBS->FreePages (Memory, Pages);\r
+    return EFI_OUT_OF_RESOURCES;\r
   }\r
 \r
-  NewNode->Allocation = Allocation;\r
+  NewNode->Base       = Memory;\r
+  NewNode->Allocation = (VOID*)(((UINTN)Memory + AlignmentMask) & ~AlignmentMask);\r
   NewNode->Pages      = Pages;\r
+  NewNode->Allocated  = TRUE;\r
+  NewNode->MemoryType = MemoryType;\r
 \r
   InsertTailList (&mPageList, &NewNode->Link);\r
+\r
+  *Allocation = NewNode->Allocation;\r
+  return EFI_SUCCESS;\r
 }\r
 \r
+/**\r
+ * Free the memory allocation\r
+ *\r
+ * This function will actually try to find the allocation in the linked list.\r
+ * And it will then mark the entry as freed.\r
+ *\r
+ * @param  Allocation  Base address of the buffer to free\r
+ *\r
+ * @return EFI_SUCCESS            The allocation has been freed\r
+ * @return EFI_NOT_FOUND          The allocation was not found in the pool.\r
+ * @return EFI_INVALID_PARAMETER  If Allocation is NULL\r
+ *\r
+ */\r
+STATIC\r
+EFI_STATUS\r
+FreePagesFromList (\r
+  IN  VOID  *Allocation\r
+  )\r
+{\r
+  LIST_ENTRY      *Link;\r
+  FREE_PAGE_NODE  *Node;\r
+\r
+  if (Allocation == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
 \r
-VOID\r
-RemovePagesFromList (\r
-  OUT VOID  *Allocation,\r
-  OUT UINTN *Pages\r
+  for (Link = mPageList.ForwardLink; Link != &mPageList; Link = Link->ForwardLink) {\r
+    Node = BASE_CR (Link, FREE_PAGE_NODE, Link);\r
+    if ((UINTN)Node->Allocation == (UINTN)Allocation) {\r
+      Node->Allocated = FALSE;\r
+\r
+      // Update the size of the freed buffer\r
+      mFreedBufferSize  += Node->Pages * EFI_PAGE_SIZE;\r
+\r
+      // If the size of the non-allocated reaches the threshold we raise a warning.\r
+      // It might be an expected behaviour in some cases.\r
+      // We might device to free some of these buffers later on.\r
+      if (mFreedBufferSize > PcdGet64 (PcdArmFreeUncachedMemorySizeThreshold)) {\r
+        DEBUG ((EFI_D_WARN, "Warning: The list of non-allocated buffer has reach the threshold.\n"));\r
+      }\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ * This function is automatically invoked when the driver exits\r
+ * It frees all the non-allocated memory buffer.\r
+ * This function is not responsible to free allocated buffer (eg: case of memory leak,\r
+ * runtime allocation).\r
+ */\r
+EFI_STATUS\r
+EFIAPI\r
+UncachedMemoryAllocationLibDestructor (\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
   )\r
 {\r
   LIST_ENTRY      *Link;\r
   FREE_PAGE_NODE  *OldNode;\r
 \r
-  *Pages = 0;\r
+  // Test if the list is empty\r
+  Link = mPageList.ForwardLink;\r
+  if (Link == &mPageList) {\r
+    return EFI_SUCCESS;\r
+  }\r
 \r
-  for (Link = mPageList.ForwardLink; Link != &mPageList; Link = Link->ForwardLink) {\r
+  // Free all the pages and nodes\r
+  do {\r
     OldNode = BASE_CR (Link, FREE_PAGE_NODE, Link);\r
-    if (OldNode->Allocation == Allocation) {\r
-      *Pages = OldNode->Pages;\r
+    // Point to the next entry\r
+    Link = Link->ForwardLink;\r
 \r
+    // We only free the non-allocated buffer\r
+    if (OldNode->Allocated == FALSE) {\r
+      gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN)OldNode->Base, OldNode->Pages);\r
       RemoveEntryList (&OldNode->Link);\r
       FreePool (OldNode);\r
-      return;\r
     }\r
-  }\r
+  } while (Link != &mPageList);\r
 \r
-  return;\r
+  return EFI_SUCCESS;\r
 }\r
 \r
-\r
 /**\r
   Converts a cached or uncached address to a physical address suitable for use in SoC registers.\r
 \r
@@ -175,76 +344,21 @@ UncachedInternalAllocateAlignedPages (
   IN UINTN            Alignment\r
   )\r
 {\r
-  EFI_STATUS                        Status;\r
-  EFI_PHYSICAL_ADDRESS              Memory;\r
-  EFI_PHYSICAL_ADDRESS              AlignedMemory;\r
-  UINTN                             AlignmentMask;\r
-  UINTN                             UnalignedPages;\r
-  UINTN                             RealPages;\r
-  EFI_GCD_MEMORY_SPACE_DESCRIPTOR   Descriptor;\r
-\r
-  //\r
-  // Alignment must be a power of two or zero.\r
-  //\r
-  ASSERT ((Alignment & (Alignment - 1)) == 0);\r
+  EFI_STATUS Status;\r
+  VOID   *Allocation;\r
 \r
   if (Pages == 0) {\r
     return NULL;\r
   }\r
-  if (Alignment > EFI_PAGE_SIZE) {\r
-    //\r
-    // Caculate the total number of pages since alignment is larger than page size.\r
-    //\r
-    AlignmentMask  = Alignment - 1;\r
-    RealPages      = Pages + EFI_SIZE_TO_PAGES (Alignment);\r
-    //\r
-    // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not overflow.\r
-    //\r
-    ASSERT (RealPages > Pages);\r
-\r
-    Status         = gBS->AllocatePages (AllocateAnyPages, MemoryType, RealPages, &Memory);\r
-    if (EFI_ERROR (Status)) {\r
-      return NULL;\r
-    }\r
-    AlignedMemory  = ((UINTN) Memory + AlignmentMask) & ~AlignmentMask;\r
-    UnalignedPages = EFI_SIZE_TO_PAGES (AlignedMemory - (UINTN) Memory);\r
-    if (UnalignedPages > 0) {\r
-      //\r
-      // Free first unaligned page(s).\r
-      //\r
-      Status = gBS->FreePages (Memory, UnalignedPages);\r
-      ASSERT_EFI_ERROR (Status);\r
-    }\r
-    Memory         = (EFI_PHYSICAL_ADDRESS) (AlignedMemory + EFI_PAGES_TO_SIZE (Pages));\r
-    UnalignedPages = RealPages - Pages - UnalignedPages;\r
-    if (UnalignedPages > 0) {\r
-      //\r
-      // Free last unaligned page(s).\r
-      //\r
-      Status = gBS->FreePages (Memory, UnalignedPages);\r
-      ASSERT_EFI_ERROR (Status);\r
-    }\r
-  } else {\r
-    //\r
-    // Do not over-allocate pages in this case.\r
-    //\r
-    Status = gBS->AllocatePages (AllocateAnyPages, MemoryType, Pages, &Memory);\r
-    if (EFI_ERROR (Status)) {\r
-      return NULL;\r
-    }\r
-    AlignedMemory  = (UINTN) Memory;\r
-  }\r
 \r
-  Status = gDS->GetMemorySpaceDescriptor (Memory, &Descriptor);\r
-  if (!EFI_ERROR (Status)) {\r
-    // We are making an assumption that all of memory has the same default attributes\r
-    gAttributes = Descriptor.Attributes;\r
+  Allocation = NULL;\r
+  Status = AllocatePagesFromList (MemoryType, Pages, Alignment, &Allocation);\r
+  if (EFI_ERROR (Status)) {\r
+    ASSERT_EFI_ERROR (Status);\r
+    return NULL;\r
+  } else {\r
+    return Allocation;\r
   }\r
-\r
-  Status = gDS->SetMemorySpaceAttributes (Memory, EFI_PAGES_TO_SIZE (Pages), EFI_MEMORY_WC);\r
-  ASSERT_EFI_ERROR (Status);\r
-\r
-  return (VOID *)(UINTN)Memory;\r
 }\r
 \r
 \r
@@ -255,21 +369,10 @@ UncachedFreeAlignedPages (
   IN UINTN  Pages\r
   )\r
 {\r
-  EFI_STATUS            Status;\r
-  EFI_PHYSICAL_ADDRESS  Memory;\r
-\r
-  ASSERT (Pages != 0);\r
-\r
-  Memory = (EFI_PHYSICAL_ADDRESS) (UINTN) Buffer;\r
-  Status = gDS->SetMemorySpaceAttributes (Memory, EFI_PAGES_TO_SIZE (Pages), gAttributes);\r
-\r
-  Status = gBS->FreePages (Memory, Pages);\r
-  ASSERT_EFI_ERROR (Status);\r
+  FreePagesFromList (Buffer);\r
 }\r
 \r
 \r
-\r
-\r
 VOID *\r
 UncachedInternalAllocateAlignedPool (\r
   IN EFI_MEMORY_TYPE  PoolType,\r
@@ -293,8 +396,6 @@ UncachedInternalAllocateAlignedPool (
     return NULL;\r
   }\r
 \r
-  AddPagesToList ((VOID *)(UINTN)AlignedAddress, EFI_SIZE_TO_PAGES (AllocationSize));\r
-\r
   return (VOID *) AlignedAddress;\r
 }\r
 \r
@@ -432,11 +533,7 @@ UncachedFreeAlignedPool (
   IN VOID   *Allocation\r
   )\r
 {\r
-  UINTN   Pages;\r
-\r
-  RemovePagesFromList (Allocation, &Pages);\r
-\r
-  UncachedFreePages (Allocation, Pages);\r
+  UncachedFreePages (Allocation, 0);\r
 }\r
 \r
 VOID *\r
index 3fc16699c917c485eabb8376da22df649380c32d..0a0b6cbcc8149991efb22118f9fc4b233fb18120 100644 (file)
@@ -23,6 +23,8 @@
   VERSION_STRING                 = 1.0\r
   LIBRARY_CLASS                  = UncachedMemoryAllocationLib\r
 \r
+  DESTRUCTOR                     = UncachedMemoryAllocationLibDestructor\r
+\r
 [Sources.common]\r
   UncachedMemoryAllocationLib.c\r
 \r
@@ -34,5 +36,8 @@
   BaseLib\r
   ArmLib\r
   MemoryAllocationLib\r
+  PcdLib\r
   DxeServicesTableLib\r
 \r
+[Pcd]\r
+  gArmTokenSpaceGuid.PcdArmFreeUncachedMemorySizeThreshold\r